Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> "the software has become bloated"

Casey Muratori has a 20 minute talk on YouTube called "Clean code, horrible performance"[1]. Using an oft-repeated example in C++ he rewrites it uncleanly and then benchmarks.

Removing Classes/subclasses/polymorphism: 1.5x faster. That overhead was like reducing an iPhone 14 to an iPhone 11 performance.

Replacing Encapsulation with a table-driven calculation: 10x faster. That overhead was like reverting an average desktop CPU from 2023 back to 2010 performance.

Adding basic SIMD: 20x faster.

His position is that the principles of Clean Code wiped out 15 - 20 years of hardware progress, for a subjective ideal of maintainability, and even if they deliver on that, the cost is too high. It shouldn't/cannot cost a decade of hardware performance to make programmer's lives easier.

[1] https://www.youtube.com/watch?v=tD5NrevFtbU



While I agree with a lot of what he says (complexity and layers of abstraction impact performance), it's probably worth noting that his examples are not necessarily relevant to all software.

Does my web browser feel slow because of pointer indirection or virtual function calls? Or is it because I have to download 15 MB of Javascript to load this page, which then goes out to 35 other domains to fetch other Javascript and ads and CSS?

In the insanely tight compute loop that he's looking at all of those things definitely make a difference, but a lot of software is slow not because it's inefficiently written (which it probably is) but because it's inefficiently designed and it's doing too much.

In a game engine, where you have 16ms to do 100% of your simulation work, update all your state, and then send all the data out to be rendered, definitely this sort of thing is an issue. In an Electron app? There are a thousand other things that are wasting far more of your time and responsiveness than the misguided 'cleanliness' of your code.


Counterpoint. I have a Dell laptop from 2017 at work. I keep it around for plugging in to network equipment and that sort of thing. I've had Win 11 on it for the last few years, and it was getting unusably slow. Also, the battery, even though replaced in the last year, would only last about an hour of normal use. I had a similar belief about browsers and the web. No matter how light your OS, the browser and web experience is the bottleneck. But then a couple weeks ago, I installed Arch and IceWM with Firefox. What a difference. The thing is snappy, even in the browser. And the battery lasts all day. I was blown away.


Did the same in 2012 with hp 2510p that had windows vista pre installed on it. I could literally brew a cup of coffee and make it halfway through while this poor boy was booting up.

Bit the bullet, installed arch and dwm. I remember my initial impressions on how fast the boot went, how silent the hdd is, how the whole things works reporting just a fraction of RAM in use(right after boot).

It was the first hands-on YAGNI experience. Windows(and macos and the likes of ubuntu) loads and starts everything it has and keeps the system busy at all times in terms of cpu, disk io, network io.

Barebone linux without beefy desktop environment was like “I boot into essentials and will lazy load anything upon request”.


Exactly this. It's the design and architecture where the bloat is. Not the need for some microoptimization..


The video is exactly about design and architecture and not even slightly about microoptimization. How do you have it completely inverted?


I write clean code with lots of encapsulation. Not C++ just an ecomm webapp but our business hinges heavily on time to load and we do great there.

Clean code or messy code the thing that all fast code has in common - including your example is this: YOU FOCUSED ON IT.

You measured it, then improved against the benchmark. You spent time and effort on it so it improved.

That's it. That's the secret.


I object on a few fronts.

One is that the rewrite has 'mechanical sympathy' with the machine e.g. arrays without pointer chasing can fit more data in CPU cache with fewer stalls while it reads over the main memory bus and waits after every item. That should not be a surprise, it's knowable in advance. Why deliberately ignore knowledge about the machine when designing the code, then come back and use that knowledge?

Another objection is to the idea of "measure then improve". Imagine a delivery truck which loads parcels without checking their weight first, then drives the truck onto a weigh machine (profiling), then if the truck is overweight they unload each parcel, weigh them individually, find the one heavy one filled with lead weights, then repack the truck without it. That would be silly and inefficient, right? Now imagine they unload the truck and there's no single parcel which is surprisingly heavy and instead the goods have been packed with 'lead foam'. Who could forsee that would cause problems with the weight on the delivery truck? (Anyone!). Now what's the fix? Unpack and repack every parcel, rewrite the whole code. There's no accidentally quadratic here to remove, instead every tiny piece takes a few more microseconds than it needs to and those add up.

Another objection is "YOU FOCUSED ON IT' - this implies that there is some way you can design and write code that doesn't need any focus. Part of the point of the video is that the faster code is not harder to write, there's no complex algorithms, no compiler intrinsics, no deep knowledge; it doesn't take a focused performance expert to write a switch(){} instead of a subclass.

Another objection is your implication that performance shouldn't be a consideration until you measure it and find a problem, and prove that it is. Which is like saying that aeroplane design weight doesn't matter until after you build it and measure it and prove that it matters. Computers are finite and limited, why have we got to the stage of assuming they are infinite and unlimited, and then demanding proof that they aren't, over and over on a case-by-case basis? A 3D game can render a virtual world at 100 frames per second. Does a program which takes 3 seconds to show a username/password login prompt need enough resources for 300 frames of game until proven otherwise?

Another objection is that you are defaulting to 'clean code is the default, performant code needs measuring and benchmarking to justify itself in every individual case. Why isn't that the other way around? Less resource-wasting code as the default, and 'clean code' only when maintainability has been measured and proved to be a problem, and only in the parts of the codebase which have the highest maintainability problems?

If all the clean code, encapsulation, isolation, abstraction layers, are providing the developer benefits that are claimed - why aren't programs better? If it's now so clean and easy to refactor, why doesn't that translate to software that gets better instead of software that gets worse? Casey's example is that Visual Studio debugger updated the watch window in realtime while stepping through code, on single core Pentium 4 with 512MB RAM, and now on a modern multicore machine with 64GB RAM and an M2 SSD it can't do that. (RemedyBG can, so it's not impossible).


Another small objection that I just remembered, Casey has an interview with Rico Mariani[1] who worked on performance at Microsoft for two decades, from their first C++ compiler, to .NET, to web browsers.

He coined the phrase "pit of success" after his team had spent months profiling and tuning the .NET startup to remove many milliseconds from it. One developer on another team called a default constructor for an XML class, and in that commit wiped out all their gains, three times over, he didn't know the default path was slow and there was another way. Rico was giving a talk and said that isn't a good way to design things, success can't be the hard way that only a few experts can find, it has to be like 'falling into a pit', the default way to do things has to be the good way.

Anyway, with one browser related performance regression, they profiled and found that the layout engine was using a lot more CPU. He believed the layout engine was good enough, looked elsewhere and found a commit which invalidated a node in a tree and all subnodes. That was cheap and didn't stand out in the trace, but far away in the codebase it triggered a lot of layout updates. Separating things out to focus on just one part at a time, and then the profiler can reveal where the problems are, neither of those things worked as intended.

[1] https://www.youtube.com/watch?v=48Rig6v-xYU


Not to forget the fact that websites nowadays can be real memory hogs, are dynamically assembled on your machine and burn cycles on pure eye candy UX like animations and transparency. The web's focus on design and cleverness over content and functionality is partly to blame.


n=1 but my macbook pro constantly runs out of hard disk and memory and the two biggest culprits are jira/confluence/docs and other web apps (like 1gb+ of real memory consumed by a single instance), and the other is dev tools which litters my disk with 100s of gb of caches...

its crazy how our devices have become basically dumping grounds for terribly optimized software


Yes, but that's a game developers perspective. Hardware performance is not be-all end-all (an argument can be made on environmental reasons that it should be - but bear with me in the first instance).

Software is a tool working within a socio-technical system. Some systems have low user workflow diversity and a low rate of change - a game being a perfect example. Games get patched, but the diversity is purely in user data, not in feature use - everyone uses the same engine, the same textures, the same game logic. Some systems have high user workflow diversity - such as business software.

Pair that with the fact that games, due to the nature of the system, have to optimise for low latency AND they run on the edge - and it's natural that the primary optimisation will be for CPU cycles. For business software, for which distributional advantage of running it through web + the high rate of feature change that is a result of the specification being opaque and a moving target - means you have plenty networking latency that can hide your CPU latency for long after it becomes a true problem for you.

Not to mention that "clean code" optimises for developer churn and business priority shift (which is a luxury games which are an upfront investement don't have) as a result of accelerating industry of software technology and greater saturation of developers.

Had software remained the domain of the same number of practitioners such as <1995, even given everything else, the organisational systems would have evolved to protect them at all cost because churn would be catastrophic, and then they would enjoy more power and would be able to structure code not optimising for brain shift, because they'd hold the context in their heads.

I'll leave as exercise for the reader what pushing AI into the software development equation does for the system and inevitable hardware throughput implications.


> "you have plenty networking latency that can hide your CPU latency"

Does this excuse making 75 network calls instead of 5? Or knowing that you make a lot of network calls but designing the code as if they were instant and have the bandwidth of a local SSD?

> "Not to mention that "clean code" optimises for developer churn" "structure code not optimising for brain shift, because they'd hold the context in their heads."

Based on what studies or evidence is this optimised or optimal? How is it easier to work through code which is atomised and abstracted until there appears to be nowhere that anything actually happens, where the method and variable and parameter names are long compound words, where everything is multiple layers of indirection and generalised, and you have to hold all that context in your head?


1/ network calls are what I find still gets optimised by grouping API requests and bloating the exchange contract; but yes, if you're strictly clean coding, this will suffer too - it just happens less often than what the author of the youtube video objects to

2/ That's a fair challenge - and I definitely have more trouble reading through an absolutely ramped to the max collection of C# code (which reinforces the behaviour you describe) than a superscript; but for interchangeability, the middle between those two ends is typically better - you're trying to minimise functional context for the thing that a software developer needs to do. This has the additional failure mode that the feature that is envisioned (of sufficient complexity) never actually gets delivered, but the component parts that can be well encapsulated do. And this is because no one holds the full system in their heads. But this can be explained away to business as "there's too much complexity, we need another cycle" and "we need to iterate" and therefore the cycle continues.

I think I actually convinced myself away from encapsulation and separation of concerns in that last comment.


To be fair, Objective C uses message passing, which doesn't have the same performance characteristics as C++ (worse initially dealing with the selector pool rather than a vtable offset; much better later when maintaining stability in your brittle C++ ABI requires pImpl->pImpl patterns jumping all around heap memory).


it is interesting how the early iphone got such good performance and low memory usage with objective-c compared to android (java)


My problem with Casey is that he is not a performance engineer, he is a performance maximalist. Software is a set of tradeoffs. If he truly cared about performance, the best thing for him to do would be to write clean code that had good performance, but he refuses to do that, since it's hard. Instead he insists that the only way to do performance is to write unmaintainable code which ironically enough makes it difficult to do performance optimizations because it's hard to lean on abstractions that allow for things to be refactored.


> he insists that the only way to do performance is to write unmaintainable code

This is new to me, where does he say this specifically?

Not to white knight Casey but his niche has been in rendering and graphics afaik , where everything he does have to fit inbetween frames, so it makes sense to be a "performance maximalist" in that environment.

It could be that you and he are working on different sets of problems with different tools.

But I'd be really interested I where he says you must write unmaintainable code.

Also (my take) : if you're looking at "clean code" to find abstractions that allow things to be refactored, then I think that's probably not the type of performance he's talking about for you. LLMs can do that type of work. FWIW I think he refactors from what things ought to be, not how they are currently


Obviously he's not going to call his own code unmaintainable, but he's basically against abstractions which let people write software that needs to survive past a frame. BTW I have found that LLMs are kind of bad at doing refactoring (like, they can mechanically do a refactor, but they cannot actually help you with picking what refactor you'd want to do)


That is not my memory at all. I remember him saying to not do all of the things but just do some of them & we will all be much better off.


I have found these same principles to be true in my own projects. More modern, “cleaner” implementations usually end up with a larger code base but a noticeable reduction in performance. Bloat ruins everything.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: