Re: git: af3c78886fd8 - main - Alter the prototype of qsort_r(3) to match POSIX, which adopted the glibc-based interface.
Date: Sun, 09 Oct 2022 01:24:32 UTC
On 10/8/22 12:07, Alexey Dokuchaev wrote: > On Sat, Oct 08, 2022 at 01:32:57PM +0000, Pedro Giffuni wrote: >> Complain here: >> https://www.austingroupbugs.net/view.php?id=900 > > Thanks, the first comment there more or less answers my question. Well, it probably did not. The FreeBSD implementation approach is mostly a template based implementation, so in the library qsort, qsort_r, historical qsort_r and qsort_s are different and independent routines, which are instantiated from the same template at compile time. Therefore, there is no such thing of performance penalty as they suggested. The only downside of this approach is that you get multiple copies of mostly identical code so the library could potentially be a little bit larger, this, however, is not really a big deal on modern hardware, because the actual size of these routines is small (~9kB). And by paying this price, we avoided all costs associated with their passing of third parameter at runtime for qsort() users. Indeed, it doesn't make a big difference to pass an extra register, which can well be reused for multiple calls, but having different comparator types is a clean way to do things. In fact, if there is no need to use a third (or first) parameter for the comparator, there is absolutely no point of using qsort_r() because now the caller is passing three parameter per each comparison instead of two, and if the code wasn't reentrant-safe, it won't be automatically become reentrant-safe by using qsort_r(). Now they say, it's making it easier to implement qsort(3) by using qsort_r(3). It might be if they don't make use of macros, but by doing so it would require casting a function pointer to another with different parameter type. Now this work _today_, but it's explicitly defined as UB in C standard, let's quote N1548, ยง6.3.2.3: A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. So they are basically suggesting that: a) passing an unused parameter via register has almost no cost. Yes this is mostly true, the compiler would save and restore the register if it's ever touched in the function anyway, and for caller the contents of the register can be reused during the iteration. b) it is Okay to rely on UB in the C library Well, they want to do this, good luck with that. Not in my backyard. By doing it their way, the cost would be: a) qsort(3) callers are penalized for all cost related to their qsort_r(3). It would be very small, but non-zero. b) Our historical qsort_r(3) API uses first parameter to accept the "context", which is a widely used calling convention in other languages. In fact, this is exactly why our qsort_b(3) is implemented as a wrapper around qsort_r(3) (now as a wrapper of the historical interface). The motivation of adopting their API is not because it's superior, but because it has been used by many applications already and is now standardized. Cheers,