Live data from Hacker News

Performance optimization is hard because it's fundamentally a brute-force task

purplesyringa.moe

141–150 of 153 posts

Re: Performance optimization is hard because it's fundamentally a brute-force task

#141
post #112

Earlier quoted context omitted.

For many people performance optimization really starts with your second version. I'm not even sure what this is supposed to do exactly but the O(N^2) is just bad/sloppy code. Optimization is usually more about getting the right algorithms to execute faster. At least that's how I think about it.

Yet, as you'll see in this thread at least 1 other commentor is arguing that option 1 is better. What you and I see as bad/sloppy code, they see as the ideal starting point. A pretty common attitude that I've ran into. People take the "premature optimization" quote to mean "Never think about big oh when writing code".

Beware of microbenchmarks, but at least on my machine under the specific circumstances I ran it under, the double loop algorithm is about two times faster than the hash map algorithm while n < 100. That might not mean much if you spend your days working with large datasets, but in my day-to-day datasets with 100 entires or less is the norm. Readability is subjective, but at least from a performance perspective it is a reasonable starting point unless you know upfront that you are going to be working with large datasets – but in that case the optimization isn't premature, is it?

Re: Performance optimization is hard because it's fundamentally a brute-force task

#142
post #141

Earlier quoted context omitted.

Yet, as you'll see in this thread at least 1 other commentor is arguing that option 1 is better. What you and I see as bad/sloppy code, they see as the ideal starting point. A pretty common attitude that I've ran into. People take the "premature optimization" quote to mean "Never think about big oh when writing code".

Beware of microbenchmarks, but at least on my machine under the specific circumstances I ran it under, the double loop algorithm is about two times faster than the hash map algorithm while n < 100. That might not mean much if you spend your days working with large datasets, but in my day-to-day datasets with 100 entires or less is the norm. Readability is subjective, but at least from a performance perspective it is…

Here's the main issue I have with this reasoning. It builds in a fault to the program that doesn't need to be there.

If n is usually less than 100 then sure maybe it does make sense to do the more brute force approach. However, what's the impact of using the hash table first? If n is usually less than 100, now this method is slower usually, but is that a problem? Well, you should use a profiler to find out if it is or isn't. However, that may be entirely tolerable performance wise.

On the other hand, if your assumption that "n 100, or 1000, or 10000. Now all the sudden this method becomes a fault in the application you developed that needs to be rewritten.

What's devious about the "n > 100" problem is when you first write the code, you may have very well been correct that "n A lot of the code I've fixed has been due to this. I've seen the counter where really searching a list/array was the better option only a handful of times.

To me, premature optimization is using a known algorithmically inferior approach, of the same complexity as the superior approach, because you know that for small n, it is faster without knowing whether or not that will be the case.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#143
post #141

Earlier quoted context omitted.

Beware of microbenchmarks, but at least on my machine under the specific circumstances I ran it under, the double loop algorithm is about two times faster than the hash map algorithm while n < 100. That might not mean much if you spend your days working with large datasets, but in my day-to-day datasets with 100 entires or less is the norm. Readability is subjective, but at least from a performance perspective it is…

Here's the main issue I have with this reasoning. It builds in a fault to the program that doesn't need to be there. If n is usually less than 100 then sure maybe it does make sense to do the more brute force approach. However, what's the impact of using the hash table first? If n is usually less than 100, now this method is slower usually, but is that a problem? Well, you should use a profiler to find out if it is o…

> It builds in a fault to the program that doesn't need to be there.

Not really. You're just exploring an unknown frontier. Once the frontier is known and you actually understand what you are trying to solve you're still going to put in the right algorithm.

I mean, you might not if you're vibe coding, which I guess is apparently a thing somehow, but I'm assuming we're talking about actual engineering here, not speaking the sordid tales of cowboys. The "premature optimization" line was definitely not directed at those who think code is a feeling.

When the frontier is unknown, whatever algorithm gets something operating as soon as possible is the right one. The given example may be so contrived that the hash map would be what you see as the quickest and dirtiest solution anyway, but with a little more complexity loops do tend to be easier to reason about on a first-pass basis.

> Now all the sudden this method becomes a fault in the application you developed that needs to be rewritten.

In the same way that finding out that a blue button doesn't resonate with users, requiring you to rewrite your application to make the button green. Such is the life of building software. But that's why engineers get paid the big bucks – nobody else is stupid enough to do it.

> A lot of the code I've fixed has been due to this.

You're not really fixing it, you're updating it to reflect the information you gleaned during the exploratory phase. I mean, unless you're coding on vibes or something, but, again, assuming actual engineering is taking place here. Nobody should be coding non-prototypes by the guide of their feelings.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#144

Earlier quoted context omitted.

The routines were individually benchmarked using some custom tools (iterate repeatedly and use statistical analysis to converge on an estimate). Always compared against a plain C reference implementation. Then there was a system for benchmarking the software as a whole on a wide variety of architectures, including NUMA. With lots of plots and statistics. Usually you’d eventually end up at a point where the improvemen…

That sounds like a pretty good set up with a lot of investment, HFT shop?

Codecs

Re: Performance optimization is hard because it's fundamentally a brute-force task

#145
I think her immediate recognition and characterization of "pessimizations" is relevant.

Not so much as collateral damage when different optimizations negatively interact though.

Even when optimization is not further possible[0] a careful avoidance of distinct pessimizations themselves can lead toward the same kind of beneficial outcomes.

[0] Or at the other extreme, not the least bit possible.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#146
post #34

I once had this kind of body recovery/stress level measuring thingy on me for a few days, and a doctor would then analyze my health and such. I was under some stress those days and (according to the measurements) I wasn't recovering properly even during the nights. But then there was this one, long, flat, deep green curve in the middle of my work day. I checked from my VCS what I was doing during that period: I was o…

Performance articles are often the most popular on HN. This explains why.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#147
post #141

Earlier quoted context omitted.

Yet, as you'll see in this thread at least 1 other commentor is arguing that option 1 is better. What you and I see as bad/sloppy code, they see as the ideal starting point. A pretty common attitude that I've ran into. People take the "premature optimization" quote to mean "Never think about big oh when writing code".

Beware of microbenchmarks, but at least on my machine under the specific circumstances I ran it under, the double loop algorithm is about two times faster than the hash map algorithm while n < 100. That might not mean much if you spend your days working with large datasets, but in my day-to-day datasets with 100 entires or less is the norm. Readability is subjective, but at least from a performance perspective it is…

We're not really benchmarking here. The author of the code does need to understand the size of the data they're operating on etc. I also didn't critique the hashmap version. Using hashmaps [for] everything is also "lazy".

Good code should follow something along these lines:

- A reasonable understanding of what needs to happen. What are the requirements. Including data set sizes. Including how "hot" this code path is.

- A reasonable pick of the algorithm to perform what needs to happen.

Sure. O(N^2) for very small data sets could outperform an O(NlogN) or O(N). The conflation of big oh notation with actual performance is just another form of sloppiness as is picking a bad algorithm. In both cases it reflects a lack of understanding of how computers work and how to do things efficiently.

Let's look at what we're trying to do here. We want to find all values that occur more than once in a vector. Using a hash map means you have to:

- Create this data structure on the fly.

- Hash the values.

- Do the insertions/lookup.

You should consider the size of your dataset, you should consider locality/caches etc.

100 entries squared is 10k though. If this is part of a critical code path, let's say your frontend code does this for every DOM element or every notification it needs to process this can end up the bulk of CPU usage. This sort of stuff is why I've seen applications cause the fans to go on or the browser to crash. I would say bad O(N^2) algorithms for large Ns are pretty common.

I would be surprised if e.g. a C++ implementation of the hash version doesn't win over the double loop for data sets of 100. But maybe that's borderline. For a dataset of 1000 there's no way the double loop is winning.

Other alternatives might be something like an efficient small bloom filter that can be stored in a register, sorting the array. I mean at least cut the iterations in half by not iterating over the entire range in the inner loop!

Then you won't have the trouble with:

  if (item != item2 && item.foo == item2.foo) {
being a bit less readable then you might think.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#148
post #111

Earlier quoted context omitted.

Which is why the defaults for perf always drive me crazy. You want to see the entire call tree with the cumulative and exclusive time spent in all the functions.

I’m honestly curious why the defaults are the way they are. I have basically never found them to be what I want. Surely the perf people aren’t doing something completely different than I am?

I think it boils down to what Brendan Gregg likes. He must be doing somewhat different type of work and so he likes these defaults.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#149
Compilers are typically not going to waste time looking for very complex, special case patterns which occur rarely (and are usually confounded by various considerations that have to be checked).

For this we need programmable languages in which users can code their own optimization cases.

There is such a thing as "I would like a certain optimization, that should not be upstreamed into the compiler".

That's exactly what's in this article.

The construct:

  HashSet::from([a, b]).contains(&c);
has some definite AST pattern (or can be made to have one with a sufficiently pinned-down language). We can think about adding a pattern match for that AST pattern to the compiler, along with a right hand side to rewrite it into.

Re: Performance optimization is hard because it's fundamentally a brute-force task

#150

Earlier quoted context omitted.

I’m honestly curious why the defaults are the way they are. I have basically never found them to be what I want. Surely the perf people aren’t doing something completely different than I am?

I almost never find graph usage useful, TBH (and flamegraphs are worse than useless). And perf's support for stack traces is always wonky _somehow_, so it's not easy to find good defaults for the cases where I need them (I tend to switch between fp, lbr and dwarf depending on a whole lot of factors).

Tell me about it!

I think I've only been able to get good call stacks when I build everything myself with the right compilation options. This is a big contrast with what I remember working with similar tools under MSFT environments (MS Profiler or vTune).

You can get it to work though but it's a pain.

Post reply on HN