> We have computers which are ridiculous fast, but tend to write code which is freaking slow. It's sad that most programs could be 100x faster.
I feel this sort of comment misses the whole point of going with code which is patently slower than alternatives.
The main reason is that performance is a constraint but not a goal, and once it is good enough then there is absolutely nothing to be gained by wasting time on seeking performance gains.
Meanwhile, the main resource in software development is man*hours. The faster you write software (add features, fix bugs, etc) the cheaper it is. Thus, software projects benefit the most by adopting and using technology which favour turnaround time, which means higher-level languages, generic full-featured frameworks, and code reuse. They are slow and bloated and not optimized, and they are used without optimization in mind. But they work and they work acceptably.
Your client and project manager does not care if you go with a O(n2) implementation that you can whip out right now by reusing a package and has acceptable performance even if there is a O(n) alternative that requires you a few weeks to implement and forces you to test, debug, and maintain a lower level implementation.
Performance is meaningless once it's good enough. It matters nothing if your browser wastes 1GB of RAM even though it could just use 100MB because practically everyone already has 8GB to begin with, and if would be foolish to waste resources prioritizing that if no one is willing to pay for that improvement. It matters nothing if your server has a relatively low throughput because it is wasting 10s of milliseconds doing nothing in each response if all your servers barely break 50% utilization. It matters nothing if your frontend is wasting 10s of milliseconds rendering a page if end users don't even notice any delay. If performance is good enough, work is focused on where it matters.
We know it's possible to get formula1-level performance by using formula1-level of engineering and maintenance, but the world runs on Volkswagen hatchbacks.
In my experience, people waste performance and manhours at the same time. Many abstractions create more code instead of reducing it, at the same time making the code slower, harder to read and maintain. Would you rather maintain a couple hundred lines of straightforward code or thousands of lines of class hierarchies with delegates and whatnot?
> Would you rather maintain a couple hundred lines of straightforward code or thousands of lines of class hierarchies with delegates and whatnot?
I feel this is a gross misrepresentation of the problem.
With higher-level frameworks, you can add complex features with one-liners, which by their very nature (generic, extendable, and general purpose) are bloated and underperform when compared with the code you could roll yourself. However you need to write far more code than one-liners to reimplements those features, not to mention the time it you'd take your team to test, validate, and maintain it.
Therefore, contrary to your initial assumption, there is indeed a tradeoff between reusing someone else's general-purpose but battle-hardened code with your specialized, lean, but untested code, and the cost to go with rolling our own implementation hardly justifies the potential performance gains.
The truth is that the framework is built on another framework, which is built on another, which is built on another, until we get down to individual transistors.
The highest level framework isn't always the one best suited to solving the problem you have. Maybe it's a one-liner, but due to the overhead you have to run a giant distributed system instead of a single machine with share memory. Then the overhead of orchestrating all these machines might be more than writing 10 lines in a lower level framework.
I also feel like your post is another gross misrepresentation of the problem.
The issue with slow software is rarely the framework itself. Even frameworks like Rails and Django are more than fast enough for most things. If you need absurd performance (for, I don't know, HFT?), then there are other frameworks in other languages. There is no need for bespoke code! Fast frameworks do exist. Also, when frameworks are slow in some parts, someone can just go there and optimise for everyone!
However the issues we normally encounter regarding speed are often caused by convoluted bespoke architectures.
It's always because Database access has to go trough ten, twenty classes, and not only it's slow, it's also hard to maintain, as you lost control over what the SQL looks like. It's always because serialisation requires some crazy Reflection that is several orders of magnitude slower and more complex than a simple "to json" call. It's always because the hot-loops of your sorting algorithms have to go trough some unnecessary only-used-once abstraction that makes the whole hot loop slow.
Java is fast as heck but got a reputation of being slow among users. Also Enterprise Java projects had a reputation of being difficult to navigate and therefore more expensive. The issue wasn't Java: it was the convoluted bespoke architectures that plague it.
It is widely acknowledged by its proponents that those difficult architectures take more time to build. However there is zero evidence that such things help with maintainability. In fact I'd argue that those arcane architectures make it worse for the general-case scenarios of: bug fixing (because more classes mean more bugs and more places for bugs to hide), optimisation (because measurement is harder in complex programs, and optimising often requires dismantling and rebuilding things), adding features (because it was hard to build the first features, it's gonna be hard for future brand new features too) and even refactoring (if the problem is the complex architecture itself, refactoring in parts will lead to a messier program).
So there you go: waste of man-hours and of processors.
So no, the parent poster's complain has nothing to do with the reuse of frameworks or libraries.
This has been the prevailing mentality in the industry for a long time, being essentially a business dogma in IT since the 90s (for a good reasons, as you explained). I think Java is the personification of this concept (more specifically idiomatic corporate Java from the 00s).
But there is a recent small change of winds, where management is realising that being faster than the competition can have some business merit, being worth spending some man*hours. The lecturer explains it well in the beginning of the lecture. It is not a 180° turn, performance is not the priority (as it shouldn't be), but a relevance "differentiator". That is one of the drivers of the recent growth in compiled languages (Rust, Go, ...), Not the only one but it helped.
> That is one of the drivers of the recent growth in compiled languages (Rust, Go, ...), Not the only one but it helped.
No, not really. In either case (rust, Go) perfomance is at best a nice-to-have, while their main value proposition is, and has always been, turnaround time and consequently man*hours. Rust is marketed primarily due to their first-class support for safe programming constructs, and providing a far better developer experience over C and C++ at the expense of a small but negligible performance impact. Go is marketed primarily for it's first-class support for concurrency, and provide a far better developer experience than Java, C#, and C++ with little to no performance gains at all.
What matters is how long it takes developers to add value. That's it. Most of the times there is simply no value to be gained by shaving a megabyte or millisecond here or there, but undoubtedly there is value in shipping features, not having downtime, and eliminating bugs.
It seems that your argument throughout this discussion is based on two assumptions.
(1) Software already has acceptable performance.
(2) Further work to improve its performance is likely to have large development costs but deliver only small benefits.
I’m not sure either of those assumptions is safe. As the early parts of the lecture we’re discussing today demonstrated, sometimes there are dramatic performance improvements that can be made if you know what you’re doing and they can make a similarly dramatic difference to how beneficial the software is to its users.
> Performance is meaningless once it's good enough.
There is no level of performance that is "good enough".
One person's "good enough" is another person's "pain to use" and it's someone else's "I can't buy that computer that I would like because it will not run this software well".
Faster software means more options for the consumer, less energy usage and it will be easier to make computers because they don't have to be crazy fast.
I have a computer that is not one of the fastest in the world.
I love this computer.
It's tiny, beautiful and silent (no fans).
When using this computer I can clearly see how slow a lot of software is.
It's not the computer that is slow because it's definitely possible to write software that runs really fast on this computer.
One thing that programmers can do to make the situation better is to underclock your CPU when testing your software.
On Linux this is very easy; just write the value "800000" to the files that match "/sys/bus/cpu/devices/cpu*/cpufreq/scaling_max_freq".
Now your CPU will run no faster than 800Mhz.
This will help you to notice when your program gets slow much earlier.
And if you then think the performance is "good enough", it's very likely that your users will think so too because most people have a CPU that runs faster than 800MHz.
That's only considering part of the equation. We use software because it does things faster or more correctly than us. A slow and bloated spreadsheet software will still be usually way faster than manual calulation, and more correct. That way, you can understand why some people would prefer more features to faster software. The new feature allows people to do some things way faster than before. Because when a software can't do something, people still do it. The classic is exporting data in spreadsheets and doing stuff with it. That happens all the time in big organizations. And it's usually slower and less correct than having a way directly in the software to do that.
So there is, in fact, a performance level that's good enough: faster than doing it manually, or as fast/slower but more correct. That's the point at which software becomes useful.
That's a fair point of view, but I think it's wrong. Travelling might feel way better, because you're doing something, and not waiting, but still, it's slower.
All this sounds good and practical, until an organization operating in your market optimizes their technology requirements. Suddenly that organization has both exponentially faster technology support for everything they do, and their expense in doing so is exponentially less than your organization. Optimized technology is not "good enough", it changes the nature of the conflict to revenues.
> once it is good enough then there is absolutely nothing to be gained by wasting time on seeking performance gains.
Energy consumption, loading time, performance for anyone who does not have the latest computer/phone + fast and reliable internet.
In fact, every dev coding a user-facing application on a kitted-out laptop should be forced to test it with the shittiest machine + internet connection their users might have (or, say, 10% percentile or something). Sometimes it feels people live in a bubble where they assume the latest processors, 32GB of RAM, and stable 100Mbps+ internet is available everywhere.
> It matters nothing if your browser wastes 1GB of RAM even though it could just use 100MB because practically everyone already has 8GB to begin with
> Good enough” is defined by the inability of stakeholders to conceive of transitive benefits.
The whole point is that there are absolutely no benefits, at least relevant ones, once the performance is acceptable. It's a diminishing returns game. There is always a tradeoff between performance and cost, and once performance is acceptable then it's hard to justify wasting more resources to get nothing of value in return.
Once you are talking about tradeoffs, it's game over. The question is, do you want to have spent your programming career writing crap code? Or do you want to make each thing you do be something you can be proud of, that is better than you would have done last week?
There is always something that could be done to make code faster, or shorter, more parallel, or line up columns better. What matters is whether you are pushing yourself to improve every day. If you improve yourself by discovering ways to make code fast, you will always find new ways to improve, and your improvements will also often make life better for other people.
If the quality of your code has no effect on anybody's life, it is time to find something else to code.
I think it's just a matter of different industries having different situations.In game industry performance is a real business advantage thus it’s a goal. On consoles, every game developer works on the same machine, the one who can get the most computation out of the box can cram more visual effects or more complex AI behaviour into the game and win the race. (I know not all developers are into this photorealistic madness, but some big studios are still fighting over it) Even if you are working on mobile games, better performance means less battery drain (longer player engagement), less overheating, and being able to deploy onto older or weaker devices. (The performance scaling characteristic of the new Doom is so good that it can run on Switch) Again, these are real business values.
On the other hand, premium games generally generate little to none revenue after release. The gameplay and hardware would likely be different for the next title. For instance the rendering architecture changed from forward, deferred, to cluster in response to new hardware capabilities. Which means the maintainability and reusability of the game codebase is less important compared to other software projects. Also there is a tendency that game programmers receive less compensation compare to other industries (Higher supply of junior devs), so the calculation of man * hours would be different as well.
Meanwhile, the main resource in software development is manhours. The faster you write software (add features, fix bugs, etc) the cheaper it is.*
People have argued that developer time is the most precious resource since forever. In more recent times, people have also argued that pushing new features needs to happen as quickly as possible, particularly in the context of web and mobile apps.
I am rather sceptical about both claims.
Yes, developer time is important. Developer compensation is probably the largest single cost in most software development organisations and you want a good return on that investment.
Yes, the goal is acceptable performance rather than perfect optimality every time. The best is often the enemy of the good here.
And yes, that means it is foolish to invest weeks of developer time to implement an O(n) algorithm when you had an O(n²) algorithm that ran fast enough in practice.
However, what if you have a data processing job running on some cloud infrastructure that is charged according to usage, and carelessly using an O(n²) algorithm when you could have spent an extra day to write an O(n log n) one increased your AWS bill for that system by a factor of 10?
Performance can be important in other areas that sometimes get overlooked, too. In communities like HN, most of us probably enjoy the use of modern devices with relatively high specifications, but not everyone is so lucky. A classic “works for me” problem is developers running on high-spec equipment who don’t experience frustrations that their users with more modest equipment will run into, when maybe that “acceptable performance” shouldn’t be considered so acceptable after all.
And what about other types of software, such as embedded systems, where there are often tighter resource constraints and being able to use less powerful hardware components can have a significant impact on the overall cost to produce a device?
Meanwhile, I see little evidence that the relentless push to push changes around every five minutes is an automatic win. Yes, deploying changes like security updates and critical bug fixes quickly is important, but are users really happier — or, from a business perspective, willing to pay more for our software — because of the modern culture of continuous deployment and frequent updates of everything? That’s less clear.
It is undeniable that a lot of customers will still pay for poor quality software, which means shipping poor quality software can be an attractive and lucrative business model, which means paying lots of money to developers who will only produce poor quality software can work, which reduces the incentives for developers to do better. This is unfortunately the world we live in. But it doesn’t mean some of us running software businesses or working in software development can’t try!
I agree with you in general, but O(n²) is always dangerous. Perhaps this example you give is a little to the extreme.
Writing slow software is also a form of waste. Then it becomes a question of ranking waste and getting rid of the most wasteful, according to a cost/benefit ratio, but waste is never a good thing to tolerate in any cultural sense.
O(n²) is only dangerous if you’re scaling past the point where the n² behaviour outweighs any constant factors and lower order terms. When n is small, a fancy algorithm with lower big-O complexity could still be outperformed by a brute force O(n²) one in practical situations.
On the other hand, a poor choice for a critical algorithm working with a large data set could easily increase your costs by orders of magnitude, so if anything I’d say the example I gave (assuming you meant the AWS costs one) was conservative.
I agree that we shouldn’t be careless about being wasteful, but big-O complexity rarely tells the whole story when it comes to performance.
Sure, I guess there are cases where O(n²) is okay and sometimes even faster, but in my experiences with those runtimes I've usually typically regretted it. It tends to come back and bite as your original comment made clear. I prefer O(n) to O(n²)!
Yes, definitely agreed about big-O complexity analysis vs mechanical sympathy.
In fact, I'm currently pair-programming on an Eytzinger layout-based binary search at work for TigerBeetleDB.
Eytzinger layouts are a case in point where big-O fails nicely, since you have exactly the same log2(n) number of comparisons as per binary search, but you're getting better memory locality by grouping the first few nodes of the binary tree together into a single cache line.
At first this looks like less cache misses, but then we actually exploit this even further by telling the memory subsystem to prefetch all 16 great-great grandchildren of a node (whether we need them later or not), so now we're also doing more cache misses (!) but to achieve lower memory latency, by trading off against memory bandwidth, and thus something that's way faster than binary search.
The paper on which this is based is "Array Layouts for Comparison-Based Searching".
We have computers which are ridiculous fast, but tend to write code which is freaking slow. It's sad that most programs could be 100x faster.