Readit News logoReadit News
senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
1718627440 · 10 days ago
> the order of operations might affect the precision of ints/floats

That's only the problem of floats, with ints this issue doesn't exist.

Why do you write (x % 2 == 0 && x % 3 == 0) instead of (x % 2 == 0 & x % 3 == 0), when the latter is what you think you mean?

Are you sure, that dividing by 6 is actually faster, than dividing by 2 and 3? A division operation is quite costly compared to other arithmetic and 2 and 3 are likely to have some special optimization (2 is a bitshift), which isn't necessary the case for 6.

senfiaj · 5 days ago
> That's only the problem of floats, with ints this issue doesn't exist.

With ints the results can be dramatically different (often even worse than floats) even though in pure mathematics the order doesn't matter:

  1 * 2 * 3 * 4 / 8 --> 3
  3 * 4 / 8 * 1 * 2 --> 2
This is a trivial example, but it shows why it's extremely hard for compilers to optimize expressions and why they usually leave this task to humans.

But x % 2 == 0 && x % 3 == 0 isn't such case, swapping operands of && has no side effects, nor swapping operands of each ==.

> Are you sure, that dividing by 6 is actually faster

Compilers usually transform divisions into multiplications when the denominator is a constant.

I wrote another example in other comment but I'll write again.

I also tried this

  bool is_divisible_by_15(int x) {
      return x % 3 == 0 && x % 5 == 0;
  }

  bool is_divisible_by_15_optimal(int x) {
      return x % 15 == 0;
  }
is_divisible_by_15 still has a branch, while is_divisible_by_15_optimal does not

  is_divisible_by_15(int):
        imul    eax, edi, -1431655765
        add     eax, 715827882
        cmp     eax, 1431655764
        jbe     .LBB0_2
        xor     eax, eax
        ret
  .LBB0_2:
        imul    eax, edi, -858993459
        add     eax, 429496729
        cmp     eax, 858993459
        setb    al
        ret

  is_divisible_by_15_optimal(int):
        imul    eax, edi, -286331153
        add     eax, 143165576
        cmp     eax, 286331153
        setb    al
        ret
My point is that the compiler still doesn't notice that 2 functions are equivalent. Even when choosing 3 and 5 (to eliminate the questionable bit check trick for 2) the 1st function appears less optimal (more code + branch).

senfiaj commented on The past was not that cute   juliawise.net/the-past-wa... · Posted by u/mhb
rhubarbtree · 8 days ago
I think it’s really revealing to see so many folks defending views like “hunter gathering was better” and “the past wasn’t dickensian.”

I remember the first time I encountered the former view from a person, they were an artist living in London and a communist. I nearly spat out my beer when he told me that hunter gathering was a better life for humans.

It seems to be some kind of desire to rage against progress, because industrialisation brings many downsides e.g, pollution climate change etc. Maybe because they hate the rich and powerful capitalists that rule the world.

But what they always miss from their arguments is a clear conception of just how incredibly privileged and fortunate they are to be born into an industrialised society. People are very very bad at appreciating what they are given, it seems to be an innate human trait to exhibit breathtaking ingratitude for what already is. We’re pretty good at anticipating and appreciating the new, but if it’s already there then, like a spoilt child living in a luxury home, we take it for granted.

I think one solution to this problem is to remove as many comforts from your life, temporarily. For example, for a week in winter don’t use your heating or hot water. For me, it was travelling to poor countries and living without potable or warm water, decent transport, good food, etc. that made me grateful (at least for a while).

senfiaj · 8 days ago
We are definitely better at survival and safety. In modern societies we are less likely to starve, die in infancy / childhood, have longer life expectancy, etc.

But when we compare by other metrics, such as mental and physical health, it becomes more complicated. The problem is that out brains and bodies aren't well adapted to the modern world. In the past there were stresses (predators, hunger, conflict), but they were more acute, big spike of stress, but you usually had a lot of time to recover. For example, predator appears, huge spike in stress, run/fight, either you die or it's over. But afterwards (if you survived) you usually had a lot of rest. Also you more or less directly saw the results of your actions. For example, you hunt means you eat, you build shelter means stay dry, etc.

Meanwhile, modern people tend to have chronic low-level stress caused by the complicated and fast paced society: money worries, grind, bureaucracy, deadlines, school / college / university, burnout, job insecurity, notifications, news doomscrolling. Our stress systems are constantly activated which is devastating for long-term mental health. It's no wonder that we have higher rates of depression, anxiety and suicidality. Today's stress is more akin to death by thousands of small cuts. The same is for our physical health.

I'm not claiming hunter gatherers' lives were not challenging. There were a lot risks, physical hardship, famines, etc. But evolutionary speaking, our bodies / minds were more equipped to deal with those types stresses. Here is a good video that talks about this: https://www.youtube.com/watch?v=Mo1A45ShcMo

senfiaj commented on 'Life being stressful is not an illness' – GPs on mental health over-diagnosis   bbc.com/news/articles/cx2... · Posted by u/jnord
gambiting · 9 days ago
The "problem" is that it's all comparative, even if in absolute terms you are right - life is undeniably better than it used to be by every metric.

But to give you an example - my grandma had 8 siblings, out of which only 4 survived into adulthood. They mostly died before even turning 1, the typical "one day he was fine, the next day he turned purple and died, the nearest doctor was 2 hours away if your neighbour let you borrow a horse cart, so that's just what it was" case. Her father and her uncle were taken into Auschwitz and miraculously returned but never wanted to talk about the horrors they have seen while there. For the rest of her life she endured living under communism, seeing her peers in other countries have access to riches she could only dream of.

So now when you talk to her, how can you blame her for thinking that kids nowadays are spoiled, if they have everything provided for them, they have never experienced physical violence of any kind, but they are all depressed and sad about life being shit and saying how it was better in the good old days.

Like, from her perspective, it's impossible to understand.

But also, from the kids perspectives nowadays, all they know is a world where the "old" people have houses, jobs, stable incomes, and they cannot even hope for any of it. 50 year mortgages, 700 job applications with no interview, social media blasting images of a life that they know they can't ever have.....of course they are depressed. Just showing them stats that say "you live better, comfier, safer andd healthier lives than pretty much anyone else ever in the history of humanity" is not going to help if all they know is how much "worse" they have it than the people who came before them.

senfiaj · 9 days ago
I think what definitely has improved, is the survival. We are less likely to starve, die in infancy / childhood, have longer life expectancy, etc. In the past there were also stresses. But I think the stresses were different then. They were less chronic and were more occasional instead (although probably more intense). However, after an acute stress you had a lot of time to recover. Evolutionary speaking, our brains have been adapted for that. It was necessary for our survival.

However, nowadays the stresses are different, they are more chronic / frequent. You have less time to recover from them. This is partially the result of our more complex and fast paced society / economy. Our brains are not well adapted for the modern work / educational environments and to the stresses associated with them, despite they are usually milder in intensity. Today's stress is more like to death by a thousand small cuts. Nowadays people have more anxiety, depression and suicidality. Here is a good video that talks about the modern stress: https://www.youtube.com/watch?v=Mo1A45ShcMo .

senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
NobodyNada · 11 days ago
Let's throw this into godbolt: https://clang.godbolt.org/z/qW3qx13qT

    is_divisible_by_6(int):
        test    dil, 1
        jne     .LBB0_1
        imul    eax, edi, -1431655765
        add     eax, 715827882
        cmp     eax, 1431655765
        setb    al
        ret
    .LBB0_1:
        xor     eax, eax
        ret

    is_divisible_by_6_optimal(int):
        imul    eax, edi, -1431655765
        add     eax, 715827882
        ror     eax
        cmp     eax, 715827883
        setb    al
        ret
By themselves, the mod 6 and mod 3 operations are almost identical -- in both cases the compiler used the reciprocal trick to transform the modulo into an imul+add+cmp, the only practical difference being that the %6 has one extra bit shift.

But note the branch in the first function! The original code uses the && operator, which is short-circuiting -- so from the compiler's perspective, perhaps the programmer expects that x % 2 will usually be false, and so we can skip the expensive 3 most of the time. The "suboptimal" version is potentially quite a bit faster in the best case, but also potentially quite a bit slower in the worst case (since that branch could be mispredicted). There's not really a way for the compiler to know which version is "better" without more context, so deferring to "what the programmer wrote" makes sense.

That being said, I don't know that this is really a case of "the compiler knows best" rather than just not having that kind of optimization implemented. If we write 'x % 6 && x % 3', the compiler pointlessly generates both operations. And GCC generates branchless code for 'is_divisible_by_6', which is just worse than 'is_divisible_by_6_optimal' in all cases.

senfiaj · 11 days ago
I also tried this

  bool is_divisible_by_15(int x) {
      return x % 3 == 0 && x % 5 == 0;
  }

  bool is_divisible_by_15_optimal(int x) {
      return x % 15 == 0;
  }
is_divisible_by_15 still has a branch, while is_divisible_by_15_optimal does not

  is_divisible_by_15(int):
        imul    eax, edi, -1431655765
        add     eax, 715827882
        cmp     eax, 1431655764
        jbe     .LBB0_2
        xor     eax, eax
        ret
  .LBB0_2:
        imul    eax, edi, -858993459
        add     eax, 429496729
        cmp     eax, 858993459
        setb    al
        ret

  is_divisible_by_15_optimal(int):
        imul    eax, edi, -286331153
        add     eax, 143165576
        cmp     eax, 286331153
        setb    al
        ret

senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
abainbridge · 12 days ago
The examples are fun, but rather than yet another article saying how amazing optimizing compilers are (they are, I already know), I'd probably benefit more from an article explaining when obvious optimizations are missed and what to do about it.

Some boring examples I've just thought of...

eg 1:

    int bar(int num) { return num / 2; }
Doesn't get optimized to a single shift right, because the that won't work if num is negative. In this case we can change the ints to unsigneds to tell the compiler we know the number isn't negative. But it isn't always easy to express to the compiler everything you know about your data and use case. There is an art in knowing what kinds of things you need to tell the compiler in order to unlock optimizations.

eg 2:

    int foo(void) { return strlen("hello"); }
We all know that strlen will return 5, but some compilers don't: https://godbolt.org/z/M7x5qraE6

eg 3:

    int foo(char const *s) {
      if (strlen(s) < 3) return 0;
      if (strcmp(s, "hello") == 0)
        return 1;
      return 0;
    }
This function returns 1 if s is "hello". 0 otherwise. I've added a pointless strlen(). It seems like no compiler is clever enough to remove it. https://godbolt.org/z/Koj65eo5K. I can think of many reasons the compiler isn't able to spot this.

senfiaj · 12 days ago
Yeah, this one as well:

  bool is_divisible_by_6(int x) {
      return x % 2 == 0 && x % 3 == 0;
  }

  bool is_divisible_by_6_optimal(int x) {
      return x % 6 == 0;
  }
Mathematically x % 2 == 0 && x % 3 == 0 is exactly the same as x % 6 == 0 for all C/C++ int values but the compiler doesn't see them as identical, and produces less optimal code for is_divisible_by_6 than for is_divisible_by_6_optimal.

senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
ramon156 · 12 days ago
I don't know enough about ASM. Are u saying the first one is more optimal because it is faster or because it uses less instructions? Would this reflect a real world use case? Do any other compilers (e.g. V8) optimize modulo's into something else?
senfiaj · 12 days ago
The compiler didn't recognize that x % 2 == 0 && x % 3 == 0 is exactly the same as x % 6 == 0 for all C/C++ int values. In theory a compiler could detect that and generate identical code for both functions, but it isn't done because this case is "niche" despite being trivial. My point is not to over rely on optimizer for math expressions and algorithms.
senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
windward · 12 days ago
Those aren't isomorphic. The C spec says `is_divisible_by_6` short-circuits. You don't want the compiler optimising away null checks.

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

6.5.13, semantics

senfiaj · 12 days ago
So you claim that the compiler "knows about this but doesn't optimize because of some safety measures"? As far as I remember, compilers don't optimize math expressions / brackets, probably because the order of operations might affect the precision of ints/floats, also because of complexity.

But my example is trivial (x % 2 == 0 && x % 3 == 0 is exactly the same as x % 6 == 0 for all C/C++ int), yet the compiler produced different outputs (the outputs are different and most likely is_divisible_by_6 is slower). Also what null (you mean 0?) checks are you talking about? The denominator is not null/0. Regardless, my point about not over relying on compiler optimization (especially for macro algorithms (O notation) and math expressions) remains valid.

senfiaj commented on You can't fool the optimizer   xania.org/202512/03-more-... · Posted by u/HeliumHydride
senfiaj · 12 days ago
I wonder if compilers do multiple passes on the intermediate code in order to optimize / simplify it. For example, during each pass the optimizer searches some known harcoded patterns and replaces them with something else and repeats until no possible improvement is found.

Also optimizers have a limit, they can't reason as abstractly as humans, for example:

  bool is_divisible_by_6(int x) {
      return x % 2 == 0 && x % 3 == 0;
  }

  bool is_divisible_by_6_optimal(int x) {
      return x % 6 == 0;
  }
I tried with both gcc and clang, the asm code for is_divisible_by_6 is still less optimal. So no, there are plenty of easy ways to fool the optimizer by obfuscation.

The morale is that you still have to optimize algorithms (O notation) and math operations / expressions.

u/senfiaj

KarmaCake day137September 18, 2023
About
A software developer who is interested in science, philosophy and technology.

https://waspdev.com

View Original