9 ms·
Horrible Code, Clean Performance
- dack 4y agocan't read the article because the site timed out. maybe the code behind his blog is too clean!
- Freedom2 4y agoA capital joke, kudos to you! Made me chuckle.
- ch_123 4y agoIt is absolutely true that some hot-path code needs to be mangled into an ugly mess to reach performance requirements. The problem is that I have encountered people who somehow take this as a blanket justification for writing unreadable code, and who operate on a false dichotomy about a choice between readable code and performant code. It is important to keep in mind that: 1) Most code, i.e. at least 80% of the code in a codebase, will never be a performance hotspot and does not need specific optimizations (i.e. as long as the code does not do stupidly inefficient things, it's probably good enough). 2) Even in performance hotspot codepath, you should not write unnecessarily hard to read code unless it is strictly necessary to achieve required performance. In both cases, the key point is to benchmark and profile to find the specific places where the ugly hacks need to be introduced, and do not introduce more than is strictly necessary to get the job done.
- louthy 4y agoAgree on this. Although the 80% figure is probably more like 99% (I have no metrics, but 80% feels too low to make this point)
- WalterBright 4y agoIt's about 99% if you haven't run it through a profiler and optimized the hot spots. With such, you can get it down to 90% or so.
- pclmulqdq 4y agoI have noticed that great code and terrible code often have the same characteristics on profiles: in both cases, slowness is spread around everywhere.
- preseinger 4y agoi've noticed the opposite, usually performance is bottlenecked by specific and unpredictable hot spots
- kolbe 4y agoIn this video, David Gross says something along the lines of "if your data is laid out in a memory inefficient manner, it will be the bottleneck everywhere." If you're dependent on a database query for everything, that will be your bottleneck. If your in-memory structures are cache inefficient, that will be your bottleneck. But once you've fixed all of this (if you can), hot paths and whatnot matter https://m.youtube.com/watch?v=8uAW5FQtcvE https://m.youtube.com/watch?v=8uAW5FQtcvE
- systematical 4y agoSaved me a lot of typing.
- thethirdone 4y agoI definitely agree on the false dichotomy between performance and readable code. > 1) Most code, i.e. at least 80% of the code in a codebase, will never be a performance hotspot and does not need specific optimizations (i.e. as long as the code does not do stupidly inefficient things, it's probably good enough). The hard part is knowing what "stupidly inefficient things" are. If you never do excessive optimization, its easy to drift towards less efficient over time because your baseline for what performance is possible slows down. Knowing how to do performance optimization that is not worth it and knowing what the best level of efficiency to shoot for is the mark of a good engineer.
- jackmott42 4y agoYeah, absent an effort to speed up a code base, it will tend to get slower, unless the team has a culture of performance, everyone is looking for easy performance wins and taking them, and common performance mistakes and getting rid of them. To do that the team needs experience with performance, and most of internet programmer culture is just to lecture people about how programmers are cheaper than hardware if anyone asks or talks about performance. So many people don't get that experience.
- radlad 4y ago> programmers are cheaper than hardware Did you mean the reverse of this, I assume?
- 908B64B197 4y agoDepends on your install base/target. At Apple scale, paying a few performance engineers 1M/y is much cheaper than shipping bibber and more powerful CPU in each iPhone. Same thing for AWS or Azure.
- josephg 4y agoModern macos is pretty unusably slow on old apple hardware. The same hardware ran earlier versions of the OS just fine. I’m not sure what’s going wrong at apple, but I don’t think they have enough performance engineers working on the Mac. I run a CPU monitor in my dash (and have for years). The efficiency cores are very often busy doing some rubbish tasks I didn’t ask for while my computer is idle. Things like reindexing files, or re-scanning my photos for faces or something like that. I’ve started to think of them as the CPU cores apple put in my computer to keep their useless internal projects from ruining their hardware. Linux annoys me regularly, but at least my cores are almost totally idle whenever my hands aren’t on the keyboard.
- dahfizz 4y agoThis makes assumptions about what the performance requirements are. In latency sensitive code, _all_ code needs to be optimized. It doesn't matter if your slow function is only called once - it adds N microseconds of latency when it doesn't need to.
- preseinger 4y agooptimizations always have to be data-driven, from profiles, starting at the slowest stuff and working your way down, until you satisfy your latency requirements unless your slow function materially impacts your performance metrics in a way that you can reproducibly demonstrate, it's a mistake to optimize that code to shave off a few microseconds
- __d 4y agoIf there's a choice of spending X hours on performance tuning, fix the thing which gives the biggest win. There's no sense spending time saving 3us when you could have saved 3ms. And sure, once that's done, you can keep going to picoseconds, but there's only a limited number of application areas where that makes economic sense. It's also important to consider the entire system: you can optimize your code to the picosecond level, occupying the time of expensive engineers for months, but perhaps that $500k is better deployed on a PCIe5 bus, or a better Ethernet card, or a kernel-bypass stack, or even shorter network cables (every 300mm is a nanosecond, after all).
- jeffreyrogers 4y agoThe problem is that if you don't know what is required to write fast code you won't design your program to be performant from the start. Lots of applications can be sped up dramatically by rearchitecting them, but you're not going to hit on the right architecture without thinking really hard about memory layout, data access, network usage, etc. It's not really true that 80% of the code doesn't matter. 80% of the code might not matter the way it is currently designed, but that doesn't mean a different design isn't dramatically better. Benchmarking and profiling doesn't help with that. Real expertise is required and that is developed over time by people who care about improving performance and make it a priority.
- szundi 4y ago80% does not matter, period. I mean 20% is a lot. Hotspots are - spots. Like 0,5% of your code. Not UI for example. You know, when money is flowing in, you have to hire some good people, let them spend time on fixing hotspots and in some rare occasions, rearchitecture a part or maybe two. Probably won’t pay off, but if you are growing fast enough, it will pay 100x. Also you’re decreasing all future development speed with all this unreadable shit everywhere, so beware of optimizing unnecessary stuff.
- jeffreyrogers 4y agoFor seriously high performance systems like webservers or exchanges, you need to get the architecture right from the start. You can't rearchitect a part or two or solve design problems by making skilled hires later on. The 80% part is a distraction (it's also factually untrue that 80% of code doesn't matter. Some applications don't have hotspots, the performance impact is basically smeared all across the code. It really depends on the application. Sqlite found major speedups by implementing hundreds of little changes that were each almost undistinguishable from noise). High performance doesn't mean unreadable. Microoptimizations can make code less readable, true, but they also give you the least amount of speedup (in the best case you can get several times speedup if you found some hotspot that can be vectorized, but that's pretty rare). You can get huge performance improvements by optimizing networking and system calls and doing that doesn't make much of an impact on readability.
- zwieback 4y agoI think that's where the craft of programming comes in. I feel like after 30 years in the field I have a reasonable idea how and when to optimize but it's not something I learned from classes or textbooks.
- flavius29663 4y ago80% seems very low, in my experience it's 1% or less of the code that is a hotspot. On the other hand, we should talk about "doing stupid things", or having a bad design/architecture. For example, if you architect your application such that each web request is serviced by 20 micro-services, and those micro-services make 20 requests of their own...no matter how fast your code is, the application will be slow.
- atq2119 4y agoI suspect the 1% number is typical only of programs that haven't been optimized. Once people start to care about performance and address these hotspots, which are often actually quite low hanging fruit, the profile starts to become flatter relatively quickly.
- patrulek 4y agoIts true for services/applications. In a case you write library, others will import into their projects, you should always treat performance seriously imo.
- xg15 4y ago> 1) Most code, i.e. at least 80% of the code in a codebase, will never be a performance hotspot and does not need specific optimizations (i.e. as long as the code does not do stupidly inefficient things, it's probably good enough). That's true, however, I've seen enough code where the author used this argument as a justification to do stupidly inefficient things, such as building monstrous mountains of abstraction layers (or re-scan the entire codebase at runtime because why not? [1]) all covered by the "premature optimization is the root of all evil" mantra. You can absolutely write code that is both opaque and inefficient. [1] https://docs.spring.io/spring-framework/docs/3.0.0.M4/spring-framework-reference/html/ch03s10.html https://docs.spring.io/spring-framework/docs/3.0.0.M4/spring...
- taeric 4y agoThe problem is that so many practices we have gravitated to are actively harmful to performance. Worse, they are borderline harmful for maintenance. Specifically, some practices are better for larger teams than they are smaller teams. Difficulty in that last is that the best practice for how to maintain code changes as you get older on the project. This is easy to see in code that is a bit of a mungled mess. But it is also visible on code that it is a beautiful stalled out mess of needing too much to get small contributions in.
- Shorel 4y agoI agree so much with you, "in theory". In practice, most of the industry has been writing ridiculously inefficient code for decades, and I wish more people would pay attention to that 20%, or the 3% that Knuth described. Or better yet, I wish some groups don't irreversible sacrifice performance with frameworks and architectures that are impossible to optimize, no matter how the code is written.
- commandlinefan 4y ago> I wish more people would pay attention to that 20%, I agree with both of you - there's no reason why software should be as inefficient as it is, and there's also no reason why code should be as unreadable as it is, and those goals aren't mutually exclusive.
- simplotek 4y ago> I agree with both of you - there's no reason why software should be as inefficient as it is, Except that it is, and it tends to be development speed. For example, real world React apps tend to suffer a lot from performance issues, specially when compared with vanilla html+javascript or even the old but true server-side rendered HTML, but it also makes it trivial to implement and rewrite complex UIs that UX designers love to put together. Would it make sense to argue for performance options which lead to productivity hits?
- commandlinefan 4y ago> argue for performance options which lead to productivity hits Always.
- tombert 4y agoI had a manager who would write the most hideous, impossible-to-debug code imaginable and always have some sort of microbenchmark as some kind of justification. At the time I was young enough in my career to just think he was wiser than I was, but upon reflection (and remembering some of the stuff he did involving FFI-ing C into Haskell all the time), I realized that he just didn't he didn't like people criticizing his code, though the first hint should have been when he refused to answer my questions about actual performance profiling of our codebase.
- commandlinefan 4y ago> blanket justification for writing unreadable code Well, ironically, the site itself is down (so the code must be beautiful?) but assuming I understand the content from the title, another thing that most of these "code quality doesn't matter" types overlook is that software changes over time, quite a bit. That's why it's called "soft"ware, it's supposed to be soft. Readable is changeable. If all the mattered were performance, we'd engrave the code path onto a circuit board.
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- jjav 4y ago> Most code, i.e. at least 80% of the code in a codebase, will never be a performance hotspot Yes, and no. That's what makes it hard to write high performance systems, you really need people with solid experience in designing for performance. Sure, always go for the low hanging fruit which are the hotspots identified by profiling and improve those. But what often happens after that is that all the code is slow everywhere! So there are no big hotspots, profiling shows nothing egregious. Teams without high performance systems experience might conclude the code is about as good as it can get given the absence of hotspots. Seen this happen often. But there might be 10x or 100x improvements remaining.
- bick_nyers 4y agoIn other words, what is the speed of light solution? The fastest possible solution that could be achieved?
- tincholio 4y agoAt the same time, one should not automatically assume that "clean code" is actually readable. I've had to deal with architecture astronauts of the Uncle Bob school of "clean code", and their code was anything but readable.
- wfurney 4y agohttps://archive.is/Q82MY https://archive.is/Q82MY
- jackmott42 4y agoThe website seems to have gone down under load, if this is a snarky response to Casey's pro performance take previously, then the irony level is high! I really wish people would stop pushing back on people who are interested or curious about how to make code perform better. Of course you don't want people scattering weird SIMD intrinsics all over mundane parts of your web backend in an unhinged attempt to shave microseconds off a response. But in my career I've never really seen people do this anyway. What you do want though is programmers who have spent some time understanding how to structure code to perform well on modern hardware. Because often times that structure is NOT a mess, it may even be easier to work with that the usual idioms in your industry or language. Grouping data into adjacent chunks with arrays or array backed data structures has more benefits than just leveraging the L1 cache. It is sometimes also convenient.
- wk_end 4y agoI'd say it's orthogonal to Casey's take, which was about trading abstraction for (arguable) simplicity + performance. This was an article about aggressively optimizing a simple loop, thus trading simplicity for performance. Given that it was from a consultancy advertising its optimization services, the irony of it going down is still pretty high, though.
- TremendousJudge 4y agoWhat I didn't like about the "Clean code, horrible performance" video is that he talked about "clean" code as though it was this unjustified, useless way of thinking about programming. This is maybe something you have to think about if you want the most performant code possible, but I don't think it's a good mantra in all situations the way it's presented in the video. Number one priority should always be "understandable code". If you aren't always thinking about the best way to get your point across to future programmers (including yourself) you're going to have a bad time.
- jesse__ 4y ago> he talked about "clean" code as though it was this unjustified, useless way of thinking about programming. That was kind of the point. There is no empirical evidence showing that the "clean code" way of programming is any better in terms of cognitive load, but there is empirical evidence that it makes your program perform very poorly. The video also did not aim to produce "the most performance code possible", which would certainly become a tangled web of arcane bit tricks and CPU minutiae. It was demonstrating what reasonable performance might look like, if you wrote simple code that the compiler can do a good job at optimizing. In addition, he demonstrated very clearly that doing it the "clean" way was significantly worse in terms of being able to see that the optimizations he did were even possible, so, that observation argues that the "clean" principals not only make your code slow as shit, but ALSO harder to reason about. I program in a similar style to Casey all day, every day, and I typically have no trouble going to code I wrote years ago and figuring out what's going on. If that doesn't convince you that it's an unjustified and useless way of thinking about programming, I'm not sure what would.
- acl777 4y agoI remember this was a holy war amongst programmers (even myself!) - readability OR performance. With tools like ChatGPT - we can have both readability AND performance, right?? Or am I missing something?
- circuit10 4y agoIf you mean automatically optimising code, the reliability isn’t quite there (it can’t check its own code) and the limited context length means it might struggle to reason about the codebase as a whole
- mlajtos 4y agoCould you be the human judge please? https://mlajtos.mu/posts/no-code-clean-performance https://mlajtos.mu/posts/no-code-clean-performance
- pjc50 4y agoCorrectness? I've not seen anyone seriously attempting to benchmark chatgpt output, without heavily cherry picking it first.
- jerf 4y agoDunno about "readibility" but I fully expect ChatGPT to produce vast swathes of code that nobody understands. While I'll cop to being more skeptical than the average HN denizen about ChatGPT, this isn't actually cynicism about ChatGPT, but about human cognition. If we do have something that produced large swathes of mostly correct code, it won't be long before the humans using it aren't even checking it anymore. Same reason that cars can do a little bit of assisting, and if they could do 100% of the driving that might be safe, but 98% of the driving is not a very good idea at all. In 20 years, someone will ask CodeGPT 8.3 to explain what some code does, and it'll give a perfectly understandable explanation back, except the human still won't understand it because the human doesn't actually understand how computers work anymore. They'll think they understand it, though.
- dragonelite 4y ago
- attractivechaos 4y agoThe blog post is effectively implementing memmem() or strstr() that searches a short string in a long string. If we are allowed to use GNU extensions, the cleanest solution is to call memmem(). Without memmem(), I would implement Boyer–Moore or Knuth–Morris–Pratt, which will be more scalable than the O(MN) implementation in the blog post. Time complexity matters.
- wk_end 4y agoYes, this was my takeaway - it was a very poor choice of example, because a known faster algorithm will crush the sort of micro-optimizations done here and be more readable. Or perhaps it was actually an incredibly good example, despite itself: a great illustration of precisely why jumping to micro-optimization isn't always the right solution.
- pclmulqdq 4y agoThis is also a problem where the algorithmically fastest solutions are also pretty much guaranteed to be faster, since they are equally cache-friendly and will almost always work out to fewer instructions. That is not always the case, but this is one of the times when it is.
- mlajtos 4y agoYou seem like you know a thing about C++, would you please take a look at this garbage? https://mlajtos.mu/posts/no-code-clean-performance https://mlajtos.mu/posts/no-code-clean-performance
- verall 4y ago> I really hate reading C++ so I really haven't checked any of the produced snippets for correctness. So they're almost certainly wrong...
- bena 4y agoUnfortunately, there are many reasons to write horrible code. I work in-house for a non-tech related company. I'm often the sole developer, sometimes I have help. I'm also the defacto DBA and systems administrator for the machines the software runs on. As the defacto DBA, I'm also roped into data analysis as well. There are also certain events that are non-compromisable. These events take place whether we are ready or not. Having nothing is not really an option. That means shelving the project until next year. Because the next event must be prepared for. So, at the end of the day, "done is best". If I have the time, I can go back and refactor everything into something better. But often, there's "the next thing".
- fwlr 4y agoI have long been in the habit of leaving in the old pre-optimized code, commented out, above the optimized code. I generally don’t think that performant code ends up all that mangled, but it costs ~nothing to leave the old code in a comment. I picked this practice up from an older engineer who would set SLOW = false at the top of the file and then wrap old code in a if (SLOW) { … } block for the compiler to do dead code elimination on. (I found linters and compilers complained less about commented-out code, but he preferred his way so it would always have syntax highlighting.)
- capableweb 4y ago> but he preferred his way so it would always have syntax highlighting On that topic, probably one of the top-3 features from Clojure I miss in other languages, is having the option of making comments being a part of the language itself so everything that works on the code (linters, evaluation, syntax highlighting, etc) works in the comment as well. The `comment` macro really is a godsend in disguise as it's awfully simple implementation-wise. Its cousin `#_` is also a great tool. See https://clojuredocs.org/clojure.core/comment https://clojuredocs.org/clojure.core/comment for more examples
- lozenge 4y agoAnother option is to keep both versions and write a test that they return the same value, e.g. using property-based testing.
- fwlr 4y agoAlthough I agree that you must test optimized code for correctness, I think it should be approached very cautiously, as tests can become checkpoints that constrain optimization. I had a memorable experience of that, actually - for a long time we had a Trello card titled “faster to operate on the bits instead”, and the team looked at that card fondly during development. After we started getting traction, it was finally time to optimize! We conscientiously wrote tests to ensure correctness, and then began an incredibly enjoyable informal hackathon. We blazed through every step, replacing the logic piece by piece with bit operations. This step was 2x faster, that step was 5x faster, and so on. A few steps were slower, which was a bit weird, but we were having so much fun shouting out our gains to each other across the room, racing to improve. By the afternoon we had optimized each step and gotten an order of magnitude improvement. “Great stuff, team.” “I thought it would have been faster.” “Yeah, same.” We did some evening profiling and noticed a lot of time being spent in a dozen or so functions with names like “toRepr”, “reconstitute”, stuff like that. Actually it was like most of the time - what!? And then we saw, at each step we converted to raw bits, performed the equivalent logic with fast bit operations, then converted back to the representation for handoff to the next step. Wait, why we are converting to and from bits like thirty times each run? That can’t be right. Realization strikes - right before most handoffs there was a commented-out line that ran out tests on the representation to ensure it was still correct. Someone pointed out we wrote that test before we had done any investigation of performance (I remember it almost word for word actually, “When we wrote the test, all we knew was it would be faster in bits, but not how. And the test doesn’t even look at bits!”). So we ripped out all the conversions and stayed in bits from step to step. Without changing the bit logic it was now almost three orders of magnitude performance gain, basically running in milliseconds instead of seconds. Two OOM that we had earned, but left on the table! Tests introduced arbitrary checkpoints and imposed irrelevant constraints on the problem, without us noticing until afterwards. We caught the problem pretty easily that time, but I do wonder how many other times we (or others) didn’t because it wasn’t as obvious. The service that accepted the output maybe should have been accepting raw bits instead of the representation. The service that gave us the input definitely should have been staying in the bits. So, do test your optimizations for correctness, but be really careful. Tests can easily and subtly constrain your optimization space in major and arbitrary ways!
- xg15 4y agoI would say this is not horrible code - or at least, it's "hollywood horrible". [1] Yes, it's not directly intuitive and probably would need some illustrating comments, but it's still reasonably close to the "naive" algorithm that you could quickly figure out how it works. More importantly, it's still localized, concrete and side-effects free: The code has a clear goal and accomplishes that goal completely inside that one function. The non-intuitive parts also have a clear reason. I think actual horrible code is more something that uses global mutable state, has logic spread over half a dozen units or does have weird roundabout implementations - however not because of a particular reason but because of unclear goals, big-ball-of-mud architecture, evolving codebase etc. [1] https://tvtropes.org/pmwiki/pmwiki.php/Main/HollywoodHomely https://tvtropes.org/pmwiki/pmwiki.php/Main/HollywoodHomely
- KineticLensman 4y agoOh No! I've already been down one TvTropes rabbit hole today.
- moomoo11 4y agoI mean it’s possible to have both clean and performant. If you have a high performance requirement you should extract that code into its own “space” and not try to make the existing system shitty. Use API contracts between systems and don’t muddy up existing systems.
- strken 4y agoIt's interesting to think about the difference between isolated horrible code that sits in a function and does its own thing, vs architecturally horrible code that tends to spread outward and infect the rest of the program. In cases like ECS (in gaming) or virtual DOM (in web), the "horrible" architecture comes out the other side and becomes almost clean again.
- 0xbadcafebee 4y agoclean, adjective 1. Free from dirt, stain, or impurities; unsoiled. 2. Free from foreign matter or pollution; unadulterated. 3. Not infected. 4. Honest or fair, or showing that you have not done anything illegal Clean isn't a word that can be used to describe software. It just doesn't have any relevance to an observable, objective quality of software. There is no definition of what 'clean' code is; you can't show me code and say objectively "this is not clean", because there's no real definition of "clean code". It's a loan word, seemingly to indicate a lack of desirability or quality. And it also implies things that don't have anything to do with software. But there really is no concrete example or evidence of what clean software is. So it's nonsense. We talk about a computer "science". But in reality it's not a science, it's a cargo cult. We use imprecise language with no concrete definition of what we're actually saying and doing. And we wonder why the end result is so poor.
- javier_e06 4y agoI had to ask ChatGPT what is the meaning of "cargo cult" Notwithstanding the fact that most of us don't know how ChatGPT collects data and follow cue from its output: "Originally a religious movement that emerged in Melanesia during World War II. The movement professed that Western technology and prosperity were result of supernatural powers" I don't scoff at people using the phrase "clean code". I know what is conveying. I do find ChatGPT supernatural.
- 0xbadcafebee 4y agoSo if I show you some code, you will be able to immediately point at the unclean code and tell me why it's unclean, and everyone else will agree with you?
- javier_e06 4y agoThe term clean is somewhat esoteric and leaves room for dissent. We also use the term "code smell" Which Wikipedia refers as: "Smells are certain structures in the code that indicate violation of fundamental design principles and negatively impact design quality" When people asked me about fundamental design principles I point them to S.O.L.I.D https://lostechies.com/wp-content/uploads/2011/03/pablos_solid_ebook.pdf https://lostechies.com/wp-content/uploads/2011/03/pablos_sol...
- 1letterunixname 4y agoBoth writing an algorithm from scratch and copypasta lead to ineffective software engineering and computer science. I would first look in the 5 book TAOCP for an algorithm recipe. Then, I would look to other FOSS implementations that solved a particular problem before. If you need to write something totally new, consider first why you must put dense engineering effort into something that may already exist. Do you really need to be solving this problem?
- akasakahakada 4y agoBecause in order to achieve the highest performance, the only way to do it is draw a direct path from origin to the destination. Often the system is build around certain data structure to gain the maximum performance and cannot hot swap that part. And you don't need to pull everything from amateur FOSS software to bloat your project.
- 1letterunixname 4y agoYou sound like ChatGPT with hyperbolic, loaded language. I wouldn't call the Python dict implementation amateur. Furthermore, the only way to arrive at optimal implementations for various platforms is to benchmark them. You won't get that just guessing or labeling things you know nothing about "amateur".
- fulafel 4y ago> But compilers never do that automatically. This is a thing to think about in itself. One problem here is languages that have semantics hostile to significant data layout transformations. Practically to get there the language design and the data-optimizing compiler would have to co-evolve I think.