This is so much of a win that I don't understand why everyone doesn't do it:
* Cross-platform counted/allocated/concatenated string semantics that with no annoying nits from one platform to another.
* "Native" support for printing IP addresses (%i), which gets rid of inet_ntoa and ilk, and binary.
* A registration system for adding new format codes, which is like being able to create Object#inspect functions in C code.
All of which is a roundabout way of saying I agree that we don't need to improve printf/scanf; we need to burn them with fire.
I'm going to tend to believe PHK if he thinks explicit structure packing is a win, noting at the same time how unlikely it is that any implementation of it is going to be a performance bottleneck compared with I/O.
I've spent a lot of my life writing parsers for various network formats, and one thing that I can say with authority is that at both my current company (Google) and at my previous company (Amazon) the CPU cost of parsing bytes off the network was noticeable enough to spend significant resources optimizing.
I haven't done benchmarks myself about eg. bitvectors, but I'm pretty sure I've heard that the performance of packed bitvectors is noticeably slower than non-packed. I also think the cost would be comparable to 64-bit math on 32-bit processors (ie. an overhead of 3-5 instructions per operation), which is a non-trivial cost.
The idea that Amazon has cycle-optimized network parsing code, and that they did it for a significant practical benefit... I have no reason to doubt you, but I'd like to hear more.
I've done a fair bit of high performance network code (not for Amazon or Google, but, for instance, for code watching most of the Internet's tier-1 backbone networks on a flow-by-flow basis) and I'm not sure I could have won much by counting the cycles it took me to pull (say) an NBO integer out of a packet.
This stuff always makes me think about:
I can speak better to Google, since it's my more recent experience. Google's internal data format is Protocol Buffers (and all the code is open-sourced, as you probably know). The C++ code that is generated to parse Protocol Buffers is fast (on the order of hundreds of MB/s) as a result of a lot of optimization. This has reached a rough ceiling of what I believe is possible with this approach (generated C++). Even so, Protocol Buffer parsing code shows up in company-wide CPU profiles, and certain teams in particular have performance issues where Protocol Buffer parsing is a significant concern for them.
To address these issues, I wrote a Protocol Buffer parser that improves performance in two ways:
- it is an event-based parser (like SAX) instead of the protobuf generated classes which are a more DOM-like approach (always parsing into a tree of data structures). With my parser you bind fields to callbacks, and you can parse into any data structure (or do pure stream processing).
- I wrote a JIT compiler that can translate a Protocol Buffer schema directly into x86-64 machine code that parses that schema. Without the intermediate C++ step, I can generate better machine code than the C++ compiler does. In an apples-to-apples test, I beat the generated C++ by 10-40%. If you do more pure stream parsing the win is even greater.
My protobuf work is open source: https://github.com/haberman/upb
> I'm not sure I could have won much by counting the cycles it took me to pull (say) an NBO integer out of a packet.
That's certainly different experience than mine. I don't know much about routers.
Making be/le/native conversion a job for the programmer is not only error-prone and a waste of time.
The compiler could safely optimize the byte-swizzles away on non-arithmetic operations, whereas most programmers tend to covert everything before they start working on it.
I'm certain this book will help me tremendously. :P
Where then? As far as I can tell, there's no sensible way to extend the functionality of printf()/scanf(), and compilers special-case their format-strings.
He's talking about pulling protocol packets off the wire - who increments values in a packet like that? Typically all you ever do is pull the relevant fields out into your own structure or pass them off as arguments to a function call. Which will always involve unaligned reads from buffer somewhere.
The only real way I can see it being misused in a way that results in poor performance is if you read data into an explicitly-packed struct and then pass a reference to it around everywhere, reading from it willy-nilly instead of extracting the values once.
When I first started programming in C I also had this fantasy that I could parse network formats with memcpy(). Since then I've become convinced that writing the actual parsing code is for the best.
Because it's more expressive, doesn't cost performance, and is less error-prone than writing field extraction code manually. And because it's the type of processing people actually use C for.
> Those tricks won't even work in many cases where certain fields indicate the length of certain other fields (which is quite common).
... it's common for a field to specify the size of the data portion of a packet, not other header fields. The one exception I can think of is a version number, which indicates the layout of the rest of the packet, which isn't exactly rocket science to model using explicitly-packed structs.
> When I first started programming in C I also had this fantasy that I could parse network formats with memcpy().
Nobody here has such a fantasy, they're expressing a desire for this to be made possible.
> Since then I've become convinced that writing the actual parsing code is for the best.
Given a programming language grammar, would you prefer to use an LR parser generator, to write an LR grammar by hand, or to write a recursive-descent LL parser by hand?
> As ugly as it is, even the printf()/scanf() format strings could be improved, by offering a sensible plugin mechanism, which the compiler can understand and use to issue warnings.
> Heck, even a simple basic object facility would be good addition, now that C++ have become this huge bloated monster language.
As a C user, I would not want to see any of these things in C.
C++ was originally named "C with Classes," and it didn't start out as a monstrosity. Adding "simple" classes to C because C++ is too complicated makes no sense. C++ got complicated because of all of the things that users end up wanting if you walk that path. There's no reason to think that "simple" classes added to C would stay simple.
Plugins for printf()/scanf()?? Not even remotely the kind of thing that needs to be in the core language libraries.
I don't think defined structure packing would really add much; if the idea is to memcpy() the raw data into your structure, then you're just making the actual data processing part more expensive because the compiler has to store the structure in a sub-optimal way (ie. with non-native endianness). Simple expressions like x.y++ could end up being much more expensive than expected.