Live data from Hacker News

When static makes your C code 10 times faster

mazzo.li

71–80 of 113 posts

Re: When static makes your C code 10 times faster

#71
post #4

This can be accomplished more simply and reliably by marking modulus as const. The compiler currently has to reason about the whole compilation unit to determine that modulus is not modified, which works. However, if future code modifies modulus (either on purpose or accidentally) or something changes that prevents the compiler from performing global reasoning, the optimization will be lost. By marking the actual int…

> It's really easy to forget to const something, which forces the compiler to do global reasoning or to generate worse code. Global mutable state is pure evil. Don’t write globals. It shouldn’t be easy to forget const on a global because a mutable global should produce immediate revulsion and nausea. (I don’t really consider a const global to be “a global”. So ordinarily I’d just say globals are evil don’t write glob…

This is one of those sayings that I don't think is helpful. What it should be is: limit variable scope to the smallest thing it can be.

Nobody is being fooled about global state when you have a singleton database connection, event bus router, or network stack. I don't think your program is better when you pass in i/o functionality to every single class context in the constructor.

Similarly a mega-class that encapsulates everything your program does is also a code smell. There's no point to a private variable when everything can access it.

Re: When static makes your C code 10 times faster

#72

Title rephrase: When the compiler can assume your values don't change magically, it can optimize their use. This is true for restricted pointers, for global-scope variables which can only be accessed in the same translation unit, for stuff in inlined functions (often), etc. -------------------------------------------- const is a bit shifty. const makes the compiler restrict what it allows you to write, but it can sti…

Specifically it's well-defined behavior to mutate an object after const_cast-ing away constness if the object wasn't const to begin with (const references or const pointers can refer to non-const objects).

In your first example you have `int x = 1;` which isn't const, so the compiler has to assume that `f` may mutate it after const casting.

In your second example you have `const int x = 1;` which is const so the compiler can assume the value will never change.

Re: When static makes your C code 10 times faster

#73

Title rephrase: When the compiler can assume your values don't change magically, it can optimize their use. This is true for restricted pointers, for global-scope variables which can only be accessed in the same translation unit, for stuff in inlined functions (often), etc. -------------------------------------------- const is a bit shifty. const makes the compiler restrict what it allows you to write, but it can sti…

Specifically it's well-defined behavior to mutate an object after const_cast-ing away constness if the object wasn't const to begin with (const references or const pointers can refer to non-const objects). In your first example you have `int x = 1;` which isn't const, so the compiler has to assume that `f` may mutate it after const casting. In your second example you have `const int x = 1;` which is const so the comp…

You must be right. But - it's so easy to forget that! Or - never to be told that in the first place.

Re: When static makes your C code 10 times faster

#74
post #17

Earlier quoted context omitted.

Are you sure? Isn't it UB to modify a const object? It will probably end up in a non-writable memory page. EDIT: gcc seems to agree with me: you can see the optimized version here[1] and the unoptimzed version if you remove "const". [1] https://godbolt.org/z/KWrW45rK8

This is why language standards specify what the compiler can assume and call out some behavior as undefined, exactly so compilers don't have to be paranoid and produce code that sucks. If an underlying object is const, the compiler is allowed to assume that it does not change (it is valid to cast away const on a pointer or reference, but not if the object itself was declared const).

Interestingly GCC will still optimize the loop function even if you have code that modifies the modulus. https://godbolt.org/z/EE9PnrY7s

Re: When static makes your C code 10 times faster

#75
post #71

Earlier quoted context omitted.

> It's really easy to forget to const something, which forces the compiler to do global reasoning or to generate worse code. Global mutable state is pure evil. Don’t write globals. It shouldn’t be easy to forget const on a global because a mutable global should produce immediate revulsion and nausea. (I don’t really consider a const global to be “a global”. So ordinarily I’d just say globals are evil don’t write glob…

This is one of those sayings that I don't think is helpful. What it should be is: limit variable scope to the smallest thing it can be. Nobody is being fooled about global state when you have a singleton database connection, event bus router, or network stack. I don't think your program is better when you pass in i/o functionality to every single class context in the constructor. Similarly a mega-class that encapsula…

I respectfully disagree on both points.

> I don't think your program is better when you pass in i/o functionality to every single class context in the constructor.

Abstracting over I/O transport is an excellent thing to do. This allows you to do things like easily record and replay a network stream. Which is useful for both debugging and automated tests.

I/O comes in a kazillion flavors. Networked, interprocess, serial port, file, synthetic, etc etc. It's definitely something that should be abstracted around and not doing so is something I've deeply regretted in the past.

> a mega-class that encapsulates everything your program does is also a code smell

Ok I agree it can have a foul odor. But even this can be advantageous.

Once upon a time Blizzard gave a GDC presentation about Overwatch. Kill-cam replays are notoriously difficult in video games.

Blizzard's solution to this was delightfully elegant. They made two copies of their world. One perpetually runs on latest. One takes snapshots of the world every N frames. When a player dies their viewport switches to the old snapshot which then simulates and renders for ~6-10 seconds. When the replay finishes or skips the viewport switches back to the main game, which never stopped receiving updates. This was a relatively trivial implementation given the complete lack of globals and singletons.

A mega-class lets you run parallel instances of your "world". It's also a nice pattern when you want to build-up and tear-down your world in-process and guarantee no stale state. For example when running tests you likely want certain tests to "start clean". It's nice to be able to do this without restarting the entire process.

I'll double-down that globals are evil. They are a sometimes necessary evil. Or the least bad choice. But my experience is that not using globals is almost always simpler, more elegant, more flexible, and ultimately preferable.

Re: When static makes your C code 10 times faster

#76
post #4

This can be accomplished more simply and reliably by marking modulus as const. The compiler currently has to reason about the whole compilation unit to determine that modulus is not modified, which works. However, if future code modifies modulus (either on purpose or accidentally) or something changes that prevents the compiler from performing global reasoning, the optimization will be lost. By marking the actual int…

You'd think the compiler would let you know you have a constant that is not labelled as such, like the way `tslint` complains about this incessantly. (I think `splint` for c/c++ may also do this, but I've only briefly used it.)

Re: When static makes your C code 10 times faster

#77

Const / static allows for "immediate" ASM instruction generation: which means the value is known at compile time so it can compare it directly inline as opposed to the overhead of comparing it to a labeled memory address. It's generally good practice whenever possible.

Isn't that constexpr?

Isn’t constexpr c++-only?

Re: When static makes your C code 10 times faster

#78
post #4

This can be accomplished more simply and reliably by marking modulus as const. The compiler currently has to reason about the whole compilation unit to determine that modulus is not modified, which works. However, if future code modifies modulus (either on purpose or accidentally) or something changes that prevents the compiler from performing global reasoning, the optimization will be lost. By marking the actual int…

You'd think the compiler would let you know you have a constant that is not labelled as such, like the way `tslint` complains about this incessantly. (I think `splint` for c/c++ may also do this, but I've only briefly used it.)

The compiler only operates on one unit (file) at a time so it has literally no way of telling this in C. There are legitimate uses for having a non-const global which is never modified by local source: library config options, hooks for external programs, and what not. As you say, this would be a job for the linter.

Re: When static makes your C code 10 times faster

#79
post #17

Earlier quoted context omitted.

This is why language standards specify what the compiler can assume and call out some behavior as undefined, exactly so compilers don't have to be paranoid and produce code that sucks. If an underlying object is const, the compiler is allowed to assume that it does not change (it is valid to cast away const on a pointer or reference, but not if the object itself was declared const).

Interestingly GCC will still optimize the loop function even if you have code that modifies the modulus. https://godbolt.org/z/EE9PnrY7s

That code is invalid, it would give a compiler warning and possibly a runtime exception.

Re: When static makes your C code 10 times faster

#80
post #62

I think this is beyond simply making the variable static/constant. It is the specific value of the constant that is allowing the division to be substituted with bitwise AND, which then makes it so much faster. I wonder how much the speedup would be if some other near-random value is there for the constant (which is likely beyond the purpose at hand).

Speed up would be less but not zero, almost any div or modulo can be replaced by a multiply by a magic constant plus a bit shift.
Post reply on HN