I would want to see the actual application profiled and showing that strlen was a significant time sink (and exactly which strlens were burning the time) before even thinking of going down that rabbit hole, and then I would want to convince myself that a better high level algorithm was not a better fix then optimising strlen or fucking with compiler options.I agree, synthetic benchmarks like these do have some academic merit, but at the end of the day most production code will have bigger bottleneck problems elsewhere.
While it is possible for strlen to be the bottleneck, and for some applications it probably is (I remember from CPPCON that Facebook spent a lot of time optimising string handling because it turns out to matter to their workload), I would bet that for 99.9% of all C programs, the cost of strlen is noise.
Regards, Dan.
I would also note that the micro benchmark may well depend on exactly how things line up in memory, usually in C, my string manipulation is like strncpy (dest +5, source+3, ...) rather then copies from the start of memory regions that are aligned.Exactly. I like to use a Xorshift64* PRNG (because it is very "random" – especially if you use the high 32 bits of the result only; but very, very fast) to randomize the access pattern, but use the same seed for each function; so that while the offsets and length varies, each different function gets the exact same parameters.
These benchmarks are interesting to compiler authors, but I would bet that for most real code the time in standard lib calls is in the noise.
OTOH, one function that has proven (to me at least) hard to beat for general sorting is qsort() - on many platforms, for small or large datasets alike.I have the same experience.
-s to the linker (or GCC if you are doing a single command), it will be stripped of all the symbol information and should be much smaller.The large size of the GCC binaries is probably due more to the fact that they are not stripped by default. On Linux the GNU linker emits dynamically-linked executables by default, and I assume the same is true on Windows. If you passCode: [Select]-sto the linker (or GCC if you are doing a single command), it will be stripped of all the symbol information and should be much smaller.
It would be interesting to compare the assembly emitted to understand why there is such a large performance difference here. If you have forced MSVC to call out to the library, and GCC should be doing the same unless you have done something weird, I don't understand why the compiler would have any meaningful effect here at all; the only difference you could measure is the test loop and any call setup. So at a guess I'd say it's most likely due to something with the way you are compiling the code (differing options used for the compilers), or something with the way your microbenchmark is set up / optimized.
Source code?
As a side thought, if getting the length of your strings is, or is part of, a bottleneck in a given code, you may want to implement your own strings with a length field associated with the characters. Then getting the length is O(1). Some languages already have such strings built-in.
test_3_memcpy_movsb_intr_cl_15.exe 6656
test_4_memcpy_movsb_asm_cl_15.exe 6656
test_5a_msvcrt_dll_cl_15.exe 6656
test_6_forloop_cl_15.exe 6656
test_9_ucrt_dll_cl_19.exe 10752
test_2_AgnerFog_cl_15.exe 15360
test_1_Intel_icl_19.exe 41472
test_7_forloop_O2_gcc_10.exe 41472
test_8_forloop_O3_gcc_10.exe 42496
test_5b_msvcrt_static_cl_15.exe 68096
test_9_ucrt_static_cl_19.exe 124416
I would want to see the actual application profiled and showing that strlen was a significant time sink (and exactly which strlens were burning the time) before even thinking of going down that rabbit hole, and then I would want to convince myself that a better high level algorithm was not a better fix then optimising strlen or fucking with compiler options.
While it is possible for strlen to be the bottleneck, and for some applications it probably is (I remember from CPPCON that Facebook spent a lot of time optimising string handling because it turns out to matter to their workload), I would bet that for 99.9% of all C programs, the cost of strlen is noise.
I am surprised by the large difference in performance of these simple functions, and by the fact that the C for() loop is so bad in comparison.
typedef struct {
char name[7];
volatile char flag;
} mystruct;
my_strlen(mystruct_inst->name);
And implementing an "efficient" character-counting function for UTF-8 strings is not that easy - in particular, it's not nearly as easy to do this handling several bytes at a time (reading them as wider words / using vector operations / whatever...)Yup. You have 1,114,112 (U+0000 through U+10FFFF, inclusive) code points, some of which combine to produce a single glyph. So, you really have byte count, code point count, and glyph count.
The C for loop is operating 1 byte at a time. It has to work regardless of the alignment of the inputs/outputs and whether the length is a multiple of any particular word size. If the compiler were to convert it to multi-byte operations it would need to add alignment checks at the beginning of the call that either choose a fast vs. slow implementation or handle the ends slowly and let the fast algorithm do the middle, and you have to do that in a way that doesn't cause a big performance hit for short buffers which are probably the most common.Yep. That's why having proper alignment and sufficient padding makes a big difference for the rare workloads having to compare and copy strings.
I compiled with "-s" (strip) and "-ffunction-sections -fdata-sections -Wl,-gc-sections" (remove unused functions).
Without the "-s" option, the binary has 293175 bytes!
Mingw gcc uses a mix of its own startup libs and the MSVCRT.DLL.
The Mingw binary has 11 different sections, MS binaries have only 4. I have no clue, what gcc is doing here.
This is mainly a test of the different libc library functions (Intel Performance Lib, Agner Fogs asmlib and Microsoft MSVCRT).I totally misunderstood your purpose here. I had thought your inclusion of a GCC result was to compare optimizers with a naive for loop, not the standard library implementations.
In addition i added the simplest possible C for() loop as a base line reference, and added an optimzed AVX2 assembler version to show what is possible with modern CPUs.
The gcc compiler with the -O3 -march=native options produces good vectorized AVX2 code the last time i tested, but after the Mingw update to 10.2 last week gcc does not produce AVX2 output anymore...I don't *think* GCC is capable of vectorizing search loops, I believe it will only vectorize if the length is known at compile time.