Live data from Hacker News

Generics in C without void* or macros – enabled by psychec

github.com

71–74 of 74 posts

Re: Generics in C without void* or macros – enabled by psychec

#71
post #32

Earlier quoted context omitted.

Something that generates extra code that ends up in the instruction cache during execution is not zero cost. There's cost to transfer it through memory and cache hierarchies and there's cost when it causes the CPU to drop some other L1C cache line.

Well, if the exception is not thrown, no code related to the exceptional path goes into the instruction cache (not even checking for exception propagation). Assuming of course a non-completely dumb compiler that puts exceptional path code and unwinds tables in separate pages than hot code.

True.

I think I'll test whether use of exceptions can affect optimizations concerning register allocation and spilling, like function prologues and epilogues. Unwinding will also need to call destructors, and for that you'll need to have the object pointer somewhere in (stack frame) memory. But it might be in just a register. Unless, of course, the unwinding engine can look into registers as well.

I guess it's not a huge deal either way, especially in 32-bit x86 code. For large register file systems it might be a different story, forcing otherwise unnecessary spills. Regardless, I'll find out when I got some time.

Anyways, my point was more generic. So-called zero-cost abstractions are my pet peeve, because they're often not zero cost precisely because of larger generated code size. Typical microbenchmarks don't capture this, because their hot path can usually fit comfortably in L1 code and data caches.

Re: Generics in C without void* or macros – enabled by psychec

#72
post #32

Earlier quoted context omitted.

Something that generates extra code that ends up in the instruction cache during execution is not zero cost. There's cost to transfer it through memory and cache hierarchies and there's cost when it causes the CPU to drop some other L1C cache line.

I'm no compiler expert, but I would expect that compilers would put exception-handling code on a separate 64-byte cache line. You only use up "cache" if you actually hit the cache. If you're on a different L1 cache line (and never execute), you'll never be in L1 instruction cache, maybe never even in L2 or even L3 cache lines. Not that I'm an expert on compilers... but whenever I look at assembly, there are lots of "…

> I'm no compiler expert, but I would expect that compilers would put exception-handling code on a separate 64-byte cache line.

I'd sure hope they're on an entirely different page, not just cache line!

Concerning compiler code alignment: they seem to place my code on 16-byte boundaries - for a good reason too, the smallest code size while keeping optimal 16-byte aligned branch targets.

In past I actually had to drop to assembler to get 64-byte (cache line) alignment for functions (MSVC).

GCC does have optimize pragma, like:

  #pragma GCC optimize ("align-functions=64")
Although I want to point out one shouldn't usually worry about low level details like function alignment. Use profile guided optimization, etc. instead. Also remember due to L1 set associativity limitations excessive alignment can even cause pathological L1 churn in the worst case.

Re: Generics in C without void* or macros – enabled by psychec

#73
post #28

Earlier quoted context omitted.

Are you sure it isn't debugging information that bloats the executable? Try a strip --strip-all your.exe and see if it is still that big.

> Try a strip and see if it is still that big. ...I guarantee that it is nothing short of humongous xD Joking aside, maybe, I don't know, it's been a while since I used some C++ code, but I'll have a look into it the next time around. ...though it's probably unlikely, I use Code::Blocks and exclusively use the "Release" candidate, and uncheck the "Debug" version, and I'm quite confident the CB authors know what they…

I do not remember Code::Blocks' configurations, but a Release configuration may still contain debug symbols so you can debug your release executable.

Re: Generics in C without void* or macros – enabled by psychec

#74
post #71

Earlier quoted context omitted.

Well, if the exception is not thrown, no code related to the exceptional path goes into the instruction cache (not even checking for exception propagation). Assuming of course a non-completely dumb compiler that puts exceptional path code and unwinds tables in separate pages than hot code.

True. I think I'll test whether use of exceptions can affect optimizations concerning register allocation and spilling, like function prologues and epilogues. Unwinding will also need to call destructors, and for that you'll need to have the object pointer somewhere in (stack frame) memory. But it might be in just a register. Unless, of course, the unwinding engine can look into registers as well. I guess it's not a…

In theory the optimizer can optimize exactly as if the exceptions where not there, in practice some code motion optimizations might be restricted across exception boundaries simply because it might be too much work to make it work correctly. So it is possible on some tests cases to measure a difference.

Regarding spilling and register allocation, IIRC[1] the unwind tables contain a simple bytecode that is interpreted by the undwinder to fixup stack and restore registers (indexed by the current ip address), so no actual registration of object to destroy need to be done, nor does the frame pointer need to exist at all: the unwinder interprets the bytecode to recursively adjust each stack frame and registers to the expected values.

[1] At some point I had actually learned to write unwind tables by hand, of course I completely forgot everything about that: look at trampoline_thunk at https://github.com/gpderetta/delimited/blob/master/delimited... even with extensive comments is still write-only code. It doesn't help that there aren't a lot of DWARF CFI tutorials around.

Post reply on HN