Computers at that time were already pretty okay. Doom is from several years before this.
What you should instead be wondering is why so much stuff today fails to display even this level of interactivity.
HN user
Game designer. Current project: http://the-witness.net
Computers at that time were already pretty okay. Doom is from several years before this.
What you should instead be wondering is why so much stuff today fails to display even this level of interactivity.
[Context: I run a video game development studio.]
Whenever I interview people I explicitly make a section of the interview where I answer the candidate's questions, just in case it doesn't come up naturally or the candidate is a bit meek in that department. I think this is an important part of the interview. But if a candidate asked a bunch of these questions, it certainly would harm my opinion of them. I do not recommend using these questions as a guide.
Why not? For one, a lot of them are petty and kind of annoying, so you will come across as petty and kind of annoying. (Oh my God, if someone asks "tabs or spaces?" that is a no-hire right there.)
But also, note that many of these are leading questions for which there is supposedly a definite right or wrong answer ("Do you regularly correct technical debt? Do you use MVC or similar code structuring?" [This second question indicates a serious no-hire.]) Either these questions are appropriate, which implies you know more than the company does about how to develop software, or are at least its peer, in which case why are you interviewing there in a non-executive role, or why are you not off starting your own thing; or they are inappropriate, in which case you are signaling that you are going to be second-guessing everything the company does all the time, in a Dunning-Kruger kind of way, which is not something that anyone wants to deal with.
Either way, the attitude here is that the applicant can stand in judgement over the company, comprehensively across all these categories involved in programming, from the outside, with no real knowledge of what happens there day-to-day. This implies that the company cannot have much to teach the applicant or offer in terms of knowledge and technique, because if it could, then the answers to many of these questions would be surprising to the applicant or would even come across as "incorrect". So you're signaling that you think you know as much or more than the company about how to develop software well -- but news flash, most projects developed even with positive answers to all these questions are trash fires anyway. But some projects are very successful, so clearly there are factors much more important than are covered by these questions. If you treat these questions as important, you are signaling that you don't really know there are more important questions (while also being annoying).
Lastly, note that a lot of these questions are about minimizing the impact of the job on your life, "work-life balance" kinds of stuff. And there's a paradox here. On the one hand, I am not a fan of crunch or any kind of overworking people, and I do think it's reasonable for many people to avoid jobs that put you into that kind of situation. On the other hand, doing good work, for many people, is a primary vector for finding meaning in life; if you are not interested in investing time, energy and effort into a job, it probably is not meaningful to you, so these questions are kind of a checklist for finding a hollow meaningless job, actually. And if you ask a bunch of these kinds of questions from an employer, even one that is very careful about providing "work-life balance" (btw a phrase I never liked, I think it's a deep misconception, but at least we know what we are talking about), the employer is going to notice you keep asking this stuff and is going to get the feeling that you are someone who doesn't want to work very hard, unless you actively signal something like, I intend to work very hard within company hours but clock out exactly at 5pm, or whatever. But if you don't carefully do this kind of balanced signaling, and instead just ask a bunch of the questions from this list, you'll be giving the impression that you are someone most people don't want to hire, because why are you so preoccupied with minimizing work? It's a job, you are supposed to want to work there.
Questions I would recommend in lieu of those on this list:
* Why is the work important? What do I get from this job besides money (in terms of skills or other experience)?
* How is the company deciding what is "good" or "bad" in terms of the software or the development process? Aesthetically, what is considered good, what is considered bad?
* What kinds of decisions do I have the authority to make, and what kinds of decisions am I expected to defer upward?
* How closely will I be managed? What size of problem am I expected to solve independently, and what size of problem should be deferred upward?
* How far can I rise in my field by doing this job?
* How much of my time do I spend programming, and how much in meetings?
Note that none of these are questions with a pre-known right answer (except maybe a little bit the meetings one). They are also reasonable questions that should help maintain a good impression.
if you aren't precise to the millisecond things will jump around
It is correct that precision is very important here, but, a millisecond is way too coarse: at 120fps, a millisecond is 1/8 of the frame time, and you'd get horrible jitter.
If you can’t reverse a binary tree, or don’t even know how to approach the problem, you can’t do most of programming. Fixing this should be a high priority, but the author seems to have no awareness of this.
Programming is hard. It takes a lot of skill to do it well. If the author seemed interested in acknowledging this and developing skill, the article would not come off as whiny and pointless. But it does, because he’s not interested in identifying and fixing the problem, which resides in his own house.
(And I will admit here that "Rust doesn't allow you to X", as I said originally, is not an accurate statement objectively. Attempting to rephrase that objection to be better, I would say, by the time you are doing all this stuff, you are outside the gamut that Rust was designed for, so by doing that program in Rust you are taking a lot of friction, but not getting the benefit given to someone who stays inside that gamut, so, it seems like a bad idea.)
they ensure that the pointers don't last longer than the arena lives. That's it.
Sure, but my point is, when most things have lifetimes tied to the same arena, this becomes a almost a no-op. Both in the sense of, you are not really checking much (as Ayn Rand said, 'a is 'a), and you're paying a lot in terms of typing stuff into the program, and waiting around for the compiler to be not usefully checking all these things that are the same. Refactoring a program so that most things' lifetimes are the same does not feel to me like it's in the spirit of Rust, because then why have all these complicated systems, but maybe you feel that it is.
There is a bit of a different story when you are heavily using threads, because you want those threads to have allocators that are totally decoupled from each other (because otherwise waiting on the allocator becomes a huge source of inefficiency). So then there are more lifetimes. But here I am not convinced about the Rust story either, because here too I think there are simpler things to do that give you 95% of the benefit and are much lower-friction.
rather than sweeping, incorrect statements that lead people to believe things that aren't true
I agree, and if I say things that are incorrect, then I definitely want to fix them, because I value being correct.
But what I am meeting in this thread is people wanting to do some language-lawyer version of trying to prove I am incorrect, without addressing the substance of what I am actually saying. I think your replies have been the only exception to this (and only just).
I realize my original posting was pretty brusque, but, the article was very bad and I am very concerned with the ongoing deterioration of software quality, and the hivemind responses to articles like this on HN, I think, are part of the problem.
I know that Rust people are also concerned with software quality, and that's good. I just think most of Rust's theories about what will help, and most of the ways these are implemented semantically, are just wrong.
So if something I am saying doesn't seem to make sense, or seems "incorrect", well, maybe it's that I am just coming from a very different place in terms of what good programming looks like. The code that I write just looks way different from the code you guys write, the things I think about are way different, etc. So that probably makes communication much harder than it otherwise would be, and makes it much easier to misunderstand things.
On the technical topic being discussed here...
Using a bump allocator in the way you just did, on the stack for local code that uses the bump allocator right there, is semantically correct, but not a very useful usage pattern. In a long-running interactive application, that is being programmed according to a bulk allocation paradigm that maybe is "data oriented" or whatever the kids call it these days, there are pretty much 4 memory usage patterns that you ever care about:
(1) Data baked into the program, or that is initialized so early at startup that you don't have to worry about it. [This is 'static in Rust].
(2) Data that probably lives a long time, but not the whole lifetime of the program, and that will be deallocated capriciously at some point. (For example, an entry in a global state table).
(3) Data that lasts long enough that local code doesn't have to care about its lifetime, but that does not need to survive long-term. For example, a per-frame temporary arena, or a per-job arena that lasts the lifetime of an asynchronous task.
(4) Data that lives on the stack, thus that can't ever be used upward on the stack.
Now, the thing is that category (3) was not really acknowledged in a public way for a long time, and a lot of people still don't really think of it as its own thing. (I certainly didn't learn to think about this possibility in CS school, for example). But in cases of dynamic allocation, category (3) is strictly superior to (4) -- because it's approximately as fast, and you don't have to worry about your alloca trying to survive too long. You can whip up a temporary string and just return it from your function and nobody owns it and it's fine. So having your program really lean on (3) in a big way is very useful. This is what I was saying before about pretending to have a garbage collector, but you don't pay for it.
So if you are doing a fast data-oriented program (I don't really use the term "data-oriented" but I will use it here just for shorthand), dynamic allocations are going to be categories 1-3, and (4) is just for like your plain vanilla local variables on the stack, but these are so simple you just don't need to think about them much.
Insofar as I can tell, all this lifetime analysis stuff in Rust is geared toward (4). Rust wants you to be a good RAII citizen and have "resources" owned by authoritative things that drop at very specific times. (The weird thing about "resources" is that in reality this almost always means memory, and dealing with memory is very very different from dealing with something like a file descriptor, but this is genericized into "resources", which I think is generally a big mistake that many modern programming language people make).
With (1), you don't need any lifetime checking, because there is no problem. With (2), well, you can leak and whatever, but this is sort of just your problem to make sure it doesn't happen, because it is not amenable to static analysis. With (3), you could formalize a lifetime for it, but it is just one quasi-global lifetime that you are using for lots of different data, so this by definition cannot do very much work for you. You could use it to avoid setting a global to something in category (3), and that's useful to a degree, but in reality this problem is not hard to catch without that, and it doesn't seem worth it to me in terms of the amount of friction required to address this problem. Then there is (4), which, if you are not programming in RAII style, you don't really need checking very much??, because everything there is simple, and anyway, the vast majority of common stack violations are statically detectable even in C (the fact that C compilers did not historically do this is really dumb, and has been a source of much woe, but like, it is very easy to detect if you return a pointer to a local from a function, for example. Yes, this is not thorough in the way Rust's lifetime checking is, and this class of analysis will not catch everything Rust does, but honestly it will catch most of it, at no cost to the programmer).
So when I said "Rust does not allow you to do bulk memory allocation" what I am saying is, the way the language is intended to be used, you have most of your resources being of type (4), and it prevents you from assigning them incorrectly to other resources of type (4) but that have shorter lifetimes, or to (2) or (1).
But if almost everything in (4) is so simple you don't take pointers to it and whatnot, and if most of your resources are (3), they have the same lifetime as each other, all over the place, so there is no use checking them against each other. So now the only benefit you are getting is ensuring that you don't assign (3) to (2) or (1). But the nature of (3) is such that it is reset from a centralized place, so that it is easy, for example, to wipe the memory each frame with a known pattern to generate a crash if something is wrong, or, if you want something more like an analytical framework, to do a Boehm-style garbage collector thing on your heap (in Debug/checked builds only!) to ensure that nothing points into this space, which is a well-defined and easy thing to do because there is a specific place and time during which that space is supposed to be empty.
So to me "programming in Rust" involves living in (4) and heavily using constructors and destructors, whereas I tend to live in (3) and don't use constructors or destructors. (I do use initializers, which are the simple version of constructors where things can be assigned to constant values that do not require code execution and do not involve "resources" -- basically, could you memcpy the initial value of this struct from somewhere fixed in memory). Now the thing that is weird is that maybe "programming in Rust" has changed since last time I argued with Rust people. It seems that it used to be the sentiment that one should minimize use of unsafe, that it should just be for stuff like lockfree data structure implementation or using weird SIMD intrinsics or whatever, but people in this thread are saying, no man, you just use unsafe all over the place, you just totally go for it. And with regard to that, I can just say again what I said above, that if your main way of using memory is some unsafe stuff wrapped in a pretend safe function, then the program does not really have the memory safety that it is claiming it does, so why then be pretending to use Rust's checking facilities? And if not really using those, why use the language?
So that's what I don't get here. Rust is supposed to be all about memory safety ... isn't it? So the "spirit of Rust" is something about knowing your program is safe because the borrow checker checked it. If I am intentionally programming in a style that prevents the borrow checker from doing its job, is this not against the spirit of the language?
I'll just close this by saying that one of the main reasons to live in (3) and not do RAII is that code is a lot faster, and a lot simpler. The reason is because RAII encourages you conceptualize things as separately managed when they do not need to be. This seems to have been misunderstood in many of the replies above, as people thinking I am talking about particular features of Rust lifetimes or something. No, it is RAII at the conceptual level that is slow.
We had a keynote at Rustconf about how useful generational arenas are as a technique in Rust.
If that's the one I am thinking of, I replied to it at length on YouTube back in 2018.
that lets the compiler apply its memory safety checks to all the places that call it
My point is that those memory safety checks are now meaningless.
This is still a meaningful improvement to memory safety over C.
No, it really isn't. What you are describing is almost exactly what you get in C.
Okay, but if I do this everywhere, then I de facto don't have memory safety. Why, then should I use Rust and pretend like I am getting memory safety? Why wouldn't I use a lower-friction language with a faster compiler? It looks to me like the Rust community has this weird way of wanting to have its cake, and eat it too, about memory. Y'all want to advertise how important memory safety is, how great it is to have, and so forth. Then in cases like this, it's always "oh but you just use unsafe, it's fine". These stories are mutually inconsistent. Either you have memory safety or you don't. Paying the cost that Rust makes programmers pay for memory safety, and then not actually getting memory safety, is the worst of both worlds.
Then when you guys say I am making nonsense claims because of course you can have your cake and also eat it as long as you use the Rust programming language, well, it's just pretty weird at that point.
I'll try to further bridge some of the understanding gap.
People in this thread keep talking about "arena allocators" as if they are special things that you would use a few times (Grep Guy said this above, for example), or, here you imply they would be used internally to data structures, in a way that doesn't reach out to user-level.
That makes them not nearly as useful as they can be!
The game we are working on is currently 100k lines (150k lines if you count comments etc), and almost all allocations in the entire program are of this bulk type, in one way or another. Like if I want to copy a string to modify it a little bit to then look up a bitmap or make a filename, those are temporary allocations at user level and are not wrapped by anything. The string type is not some weird heavyweight C++ std::string kind of thing that wraps a bunch of functionality, it is just a length and a data pointer.
So the proposal to use unsafe in this kind of context doesn't make sense, since then you are putting unsafe everywhere in the program, which, then, why pretend you are checking things?
You can say, "well you as the end-user shouldn't be doing this stuff, everything should be wrapped in structures that were written by someone smarter than you I guess," but that is just not the model of programming that I am doing.
I understand how you can think the statement "Rust does not (allow you to do bulk memory operations)" is false, but when I say this, part of what I am including in "bulk memory operations" is the ability (as the end user) to pretend like you are in a garbage-collected language and not worry about the lifetime of your data, without having to take the performance penalty of using a garbage-collected language. So if you add back in worrying about lifetimes, it's not the same thing.
If you think "bulk memory allocation" is like, I have this big data structure that manages some API and it has some linked lists, and instead of allocating those nodes on the heap I get them from an arena or pool managed by the bigger structure ... that's fine, it is better than not doing it, but it doesn't help the end user write simpler code, and in practical terms it means that most of the allocations in the program are going to be non-bulk, because there's just too much friction on doing them broadly.
If it helps, I can revise my statement to "Rust enables you to do certain kinds of internal bulk memory allocation, but using bulk allocation broadly and freely across your program goes against the core spirit of the language" ... that sounds pretty uncontroversial? Then to bring it back to the original post, I would say, "This kind of broad use of bulk allocation is important for high performance and simplicity of the resulting code."
One last note, I am pretty tired of the "you don't understand Rust, therefore you are beneath us" line that everyone in the Rust community seems to deploy with even the slightest provocation -- not just when responding to me, but to anyone who doesn't just love Rust from top to bottom. Really it makes me feel that the only useful thing to do is just ignore Rust folks and go do useful things instead. I know I am not the only person who feels this way.
I am talking about RAII. RAII leads to programs that are inherently slow.
I guess I am confused by the question. The job of the borrow checker is to constrain what you are allowed to do, and it's well-understood that it constrains you to a subset of correct programs, so that you stay in a realm that is analyzable.
They're not a runtime construct, they have zero influence over what code does at run time (e.g. mrustc compiler doesn't implement lifetimes, but bootstraps the whole Rust compiler just fine).
This kind of reasoning seems like it makes sense, but actually it is false. ("Modern C++" people make the same arguments when arguing that you should use "zero-cost abstractions" all over the place). Abstractions determine how people write code, and the way they write the code determines the performance of the code.
When you conceptualize a bunch of stuff as different objects with different lifetimes, you are going to write code treating stuff as different objects with different lifetimes. That is slow.
If you create `Vec<Object>` in Rust, then all objects will be allocated and laid out together as one contiguous chunk of memory
Sure, and that covers a small percentage of the use cases I am talking about, but not most of them.
If you do not understand that "embarrassingly parallel" is a technical term and that it's generally understood that most programs are not easily parallelizable, there is not a discussion we can have here.
Bulk operations are not really about layout, they are about whether you mentally consider each little data structure to be an individual entity with its own lifetime, or not, because this determines what the code looks like, which determines how fast it is. (Though layout does help with regard to cache hits and so forth).
This alone, to me, says "to a first approximation, the speed of your program in 2021 is determined by the number of cores it uses" would be better than your statement. But I wouldn't even say that.
You chose an embarrassingly parallel problem, which most programs are not. So you cannot generalize this example across most software. When you try to parallelize a structurally complicated algorithm, the biggest issue is contention. I was leaving this out because it really is a 2nd order problem -- most software today would get faster if you just cleaned up its memory usage, than if you just tried to parallelize it. (Of course it'd get even faster if you did both, but memory is the E1).
There are many possible answers to this.
How come so few people are concerned with the answers to that question and which are true, but so many people are concerned with making performance claims?
Sure, but you are still going to be constrained greatly in terms of what those allocators are able to do, are you not?
This entire article is nonsense. To a first approximation, the speed of your program in 2021 is determined by locality of memory access and overhead with regard to allocation and deallocation. C allows you to do bulk memory operations, Rust does not (unless you turn off the things about Rust that everyone says are good). Thus C is tremendously faster.
There is this habit in both academia and industry where people say "as fast as C" and justify this by comparing to a tremendously slow C program, but don't even know they are doing it. It's the blind leading the blind.
The question you should be asking yourself is, "If all these claims I keep seeing about X being as fast as Y are true, then why does software keep getting slower over time?"
(If you don't get what I am saying here, it might help to know that performance programmers consider malloc to be tremendously slow and don't use it except at startup or in cases when it is amortized by a factor of 1000 or more).
Hi,
When I saw this announcement I was hoping that I could finally buy a laptop with a good trackpad, with buttons, and a good keyboard again. But looking at the announcement, it seems like trackpad and keyboard quality are far from anyone's mind, and it just looks like the laptop is copying Apple stylistically like everyone else, which means it is going to be kind-of unusable and I won't want to use it. (Especially when running Windows, those kinds of giant Apple-esque trackpads are death, because you'll keep accidentally moving files into places you didn't mean to, and then of course there's the general unresponsiveness once you add all the PalmCheck and turn-off-trackpad-for-n-secons after typing junk).
I like the idea of a laptop built for quality, but for me the #1 determinant of quality is my area of constant physical contact with the laptop, the keyboard and trackpad. And sadly, those look like afterthoughts here.
(For context -- I have bought and heavily used an average of more than one laptop per year, every year, since 1998, and have been dismayed to watch the quality trend constantly downward over that time).
Okay, but ... what is being gained here? The ability to package programs so they run on a computer without you worrying about it? That is an ability we had since the 1960s, that people gave up for "reasons" that are questionable at best.
Someday people will realize they don't have to make their computers orders of magnitude slower just in order to get back capabilities that used to come automatically.
But that day is not today, I guess.
I have heard this story multiple times. But if it's true, then why are roads pretty much the same, as concerns walking vs driving, wherever you go in the world?
If things would have naturally been different if only it weren't for some lobbyists, how come things aren't that way at least somewhere?
We went through all this BS, then signed up with them on a year contract.
Then we were shocked to find that it would take them two weeks to set up our first machine.
I noped out of it at that point and just paid the penalty to get the hell out of there. A competing service, that we already used, sets up machines in 1-2 days.
When stuff hits the fan and you really need things to happen, two weeks is just a crazy response time. That's like infinity in internet time.
He would be able to see a circle the same way you can see a sphere. Do you feel like you can only see discs, not spheres?
This is right.
The OP said he is "disillusioned with technology" but I didn't see actual technology being described as the problem with any point. So there's a conflation here happening between technology and "tech" companies. And I can only say the phrase "tech" companies while using sarcasm quotes around "tech", because almost nobody at any of these companies develops actual technology. And that's a big part of the problem.
Article is terrible.
First, these are short-term loans, with forgiveness being discussed for small businesses (not for Wall Street).
Second, there is money going out to individuals, so the "you don't" part of the title is BS. https://www.cnn.com/2020/04/01/politics/stimulus-money-distr...
Like does this guy even read the news? What is going on?
It's a technical term.
You are doing exactly the thing pg talks about in this essay -- making up a fictional version of what the other person said so that you can argue with it.
Obviously, but then the answer is, why? What the hell is our problem?
Even if you think shell scripting is a good idea (which I don’t), just fix all the obviously dumb toxic stuff like this and put out a shell that is minimally different with only semantic fixes. Emit warnings now for toxic semantics but still support them, and in a couple of years, turn off that support for good.
Toxic waste sites were ubiquitous too, but we put in the effort to clean them up.
Why are we still using this in 2020?