3 ms·
If the compiler’s optimization passes determine that the bitcasted-from and bitcasted-to objects are never simultaneously live, then copying can be easily elide
by 10000truths 5y ago
If the compiler’s optimization passes determine that the bitcasted-from and bitcasted-to objects are never simultaneously live, then copying can be easily elided.
- tsimionescu 5y agoUsually, you either care about 0-copy, in which case you don't want to rely on "if the compile can tell", or you don't care about 0-copy, in which case it doesn't matter that the compiler did it. reinterpret_cast guarantees that it is in-place, so it's the right tool for 0-copy.
- ncmncm 5y agoOr, you check. Elided copy is very rarely UB, something that cannot be said of uses of reinterpret_cast.
- tsimionescu 5y agoYou check today. What if tomorrow something changes? What if your network application starts dropping packets because it can't process them fast enough because an apparently unrelated change suddenly messed with a two year old optimization?
- ncmncm 5y agoThen you fix it. Correct code is rarely hard to make faster. What when your compiler starts to take advantage of latitude extended by the Standard, leading your program to produce sporadic, non-reproducible wrong results? It is best not to code any UB. But it is often hard to be certain, around reinterpret_cast.
- tsimionescu 5y ago> Correct code is rarely hard to make faster. You rarely want to be spending time fiddling with the optimizer. Your code shouldn't rely on the optimizer to be efficient: don't use constructs whose semantics mean you are making a copy in places where you don't want a copy. reintrepret_cast is hard to use correctly, but you only have to get it right once, and it has the right semantics, so no compiler changes will suddenly make it wrong or slow. memcpy and bit_cast have the wrong semantics, so they may change for unexpected reasons. In fact, it would be perfectly reasonable for a future version of the compiler to never optimize away the copy implied by memcpy and bit_cast.
- 10000truths 5y agoDetermining local variable lifetimes is a fundamental feature of a compiler. Literally any compiler that does register allocation can and will merge local variables that aren't simultaneously live. Because it turns out that the problem of fitting lots of variables into minimum registers is remarkably similar to the problem of fitting lots of variables into minimum registers + minimum stack space. So yes, you can confidently rely on gcc or clang to merge local variables, even at -O1. If they suddenly don't do that after an update, it's a serious regression, and their mailing lists should be more than happy to receive that bug report.
- tsimionescu 5y agoFor local variables. Once you're passing the data around to other functions, the problem becomes much more complex, and it becomes much less clear when and why the compiler will be able to understand what's happening. Not to mention, in an example like processing packets, the two views of the data WILL be simultaneously live: you're taking raw bytes on one side, interpreting some part of them as a struct to decide what's happening next, then sending the raw bytes somewhere else. EDIT: Basically the code would look something like this: char* buf = new char[100]; read(buf, sizeof(header)); header h; memcpy(&h, buf, sizeof(header)) get_destination(h).write(buf); Will this copy the header or not? How about this? char* buf = new char[100]; read(buf, sizeof(header)); header *h = reinterpret_cast<header*>(buf); //assume we are compiling with "-fno-strict-alias", and that we know there are no alignment/packing concerns get_destination(h).write(buf); Also note that in C we have a somewhat cleaner solution: union buf_header { header h; char buf[100]; } buffer; read(buffer.buf, sizeof(header)); get_destination(buffer.h).write(buffer.buf); This would of course be UB in C++ (reading from a union member that is not the last one written to).
- gpderetta 5y agoThe problem is that if you are bitcasting, say, from a memory buffer coming from a read() call it is very hard for the compiler to prove that bitcasted copy is the only live one, especially across function calls. For small objects bitcast works just fine because the result end up in registers were you would have loaded the original objects anyway.