3 ms·
Seems like the right trade-off to make. Variadic functions are so rarely used in performance-critical code that a couple of extra hoops for making printf and co
by paulbaumgart 16y ago
Seems like the right trade-off to make. Variadic functions are so rarely used in performance-critical code that a couple of extra hoops for making printf and company work is probably worth it
It sounds like structs (at least the small ones?) get passed in registers also. I don't really understand that. Isn't it kinda a rule of C-programming that if you want your code to be fast, don't pass around structs? If structs just always went onto the stack, you wouldn't have to mess around figuring out whether its members are on the integer or floating point registers.
- tptacek 16y agoWhat's the performance sensitivity of variadic functions? I've never seen a varargs function hit the top of my profile. On i386, there's almost no performance difference between a va_arg and a positional arg. It's actually not a rule of performant C programming that you shouldn't pass structures by value. The contortions you end up going through to avoid it (decomposing the struct into arguments, creating temp structs to pass addresses instead of values, filling structs in from return-args) often cost more than just passing the damn thing to begin with.
- paulbaumgart 16y agoYeah, my point was it's OK to make variadic functions have a little extra logic and in some cases potentially be slower than the ix86 equivalent, because regular functions are definitely sped-up and those are much more common. On the subject of passing structs by value: it really comes down to whether you plan on copying the struct anyway or not, doesn't it?
- andrewf 16y agoI wonder if this is more for the sake of C++ than C, where primitive values are more likely to be 'boxed' into a struct/class along with some methods. For instance, with a typical non-debug standard library, vector<int>::iterator has one member - an int*. It'd be a shame if that couldn't go in a register.
- pmjordan 16y agoI'm fairly sure that as soon as it's no longer POD (plain old data), it can no longer be passed in a register, though, as it MUST have a permanent memory address. A struct or class loses its POD-ness by having constructors, a destructor, being a derived class, having virtual functions, public/protected/private sections or a bunch of other very strict conditions. Simple iterators may well be POD in many STL implementations, though.
- nelhage 16y agoThe Linux kernel, for example, uses the following definitions for page-table entries in different levels of amd64's four-level page tables: typedef struct { unsigned long pte; } pte_t; typedef struct { unsigned long pmd; } pmd_t; typedef struct { unsigned long pud; } pud_t; typedef struct { unsigned long pgd; } pgd_t; That way, even though they're all represented as 64-bit values, the compiler will catch you if you try to mix-and-match different types without doing appropriate work. Passing small structs in registers means that Linux can do that, and pass around `pte_t`s as parameters, without having to worry about paying a runtime cost for their typechecking.
- paulbaumgart 16y agoThat's a neat trick. Thanks for pointing that out.
- caf 16y agoThis is a nice demonstration that C is actually (moderately) strongly typed - it's just that the keyword that introduces a new type is "struct", not "typedef".