Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c++ Subject: Re: A better benchmark Date: Mon, 07 Feb 2022 06:57:31 -0800 Organization: A noiseless patient Spider Lines: 182 Message-ID: <86y22miv1g.fsf@linuxsc.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="792c2a82c28252afd7e8a94f82466189"; logging-data="23609"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18ISFOcZ1PgtQyOkanfJ7mgS/lZgNb6GFY=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:1P8OyX1Qr46NnGcVx7d/sGOH1ik= sha1:siHTdJ61hHVNVOJLINNVQM5gECI= Xref: csiph.com comp.lang.c++:82956 Bonita Montero writes: [.. some minor editing to fix line-wrapping problems ..] > Now I have an even distribution from 1 to 20 digits. > > #include > #include > #include > #include > #include > #include > #include > #include > > using namespace std; > using namespace chrono; > > #if defined(_MSC_VER) > #pragma warning(disable : 4996) // disable deprecated sprintf > #pragma warning(disable : 6200) // array out of range > #endif > > template > requires same_as || same_as > char_t const *formatClockCyclesMine( uint64_t clockCycles ); > char const *formatClockCyclesTim( uint64_t clockCycles ); > char const *formatClockCyclesBen( uint64_t clockCycles ); > > int main() > { > auto bench = []( FccFn fn ) > requires > requires( FccFn fn, uint64_t clockCycles ) { > { *fn( clockCycles ) } -> convertible_to; } > { > static size_t const N_VALUES = 10'000, N = 1'000; > vector values( N_VALUES ); > mt19937_64 mt; > uniform_int_distribution uidLength( 1, 20 ); > uniform_int_distribution uidDigit( 0, 9 ); > auto getNumber = [&]() -> uint64_t > { > uint64_t number = 0, nextNumber; > size_t initialLength = uidLength( mt ), length = initialLength; > unsigned digit; > do > if( > (nextNumber = number * 10 + (digit = uidDigit( mt ))) > >= number > ) > number = nextNumber; > else > number = 0, > length = initialLength + 1; > while( --length ); > return number; > }; > for( uint64_t &v : values ) > v = getNumber(); > char volatile sum = 0; > auto start = high_resolution_clock::now(); > for( size_t n = N; n--; ) > for( uint64_t v : values ) > sum += *fn( v ); > return > (double)(int64_t)duration_cast( > high_resolution_clock::now() - start > ).count() / ((double)N * (double)N_VALUES); > }; > cout << "mine: " << bench( formatClockCyclesMine ) << endl; > cout << "Tim: " << bench( formatClockCyclesTim ) << endl; > cout << "Ben: " << bench( formatClockCyclesBen ) << endl; > } > > #if defined(_MSC_VER) > #define NOINLINE __declspec(noinline) > #elif defined(__GNUC__) || defined(__llvm__) > #define NOINLINE __attribute__((noinline)) > #elif > #define NOINLINE > #endif > > template > requires same_as || same_as > NOINLINE > char_t const *formatClockCyclesMine( uint64_t clockCycles ) > { > using dig_arr_t = array; // max 20 digits for 64 bit > using dig_arr_it = typename dig_arr_t::iterator; > dig_arr_t reverseDigits; > dig_arr_it rDigitsEnd = reverseDigits.begin(); > do > *rDigitsEnd++ = clockCycles % 10, > clockCycles /= 10; > while( clockCycles ); > size_t > digitsLen = rDigitsEnd - reverseDigits.begin(), > groups = (digitsLen - 1) / 3; > using str_arr_t = array; > using str_arr_it = typename str_arr_t::iterator; > thread_local str_arr_t str; > str_arr_it wrt = str.begin() + digitsLen + groups; > *wrt = '\0'; > dig_arr_it digit = reverseDigits.begin(); > for( > dig_arr_it groupsEnd = reverseDigits.begin() + groups * 3; > digit != groupsEnd; > wrt -= 4, digit += 3 > ) > wrt[-1] = digit[0] + '0', > wrt[-2] = digit[1] + '0', > wrt[-3] = digit[2] + '0', > wrt[-4] = '.'; > do > *--wrt = *digit++ + '0'; > while( digit != rDigitsEnd ); > return &*wrt; > } > > NOINLINE > char const *formatClockCyclesTim( uint64_t clockCycles ) > { > thread_local char result[20 + (20 - 1) / 3 + 1]; > size_t n = > (unsigned)sprintf( result, "%llu", > (unsigned long long) clockCycles ); > char *ep = (&result)[1]; > *--ep = 0; > while( n > 3 ) > memmove( ep -= 3, &result[n -= 3], 3 ), > *--ep = '.'; > do > *--ep = result[--n]; > while( n ); > return ep; > } > > NOINLINE > char const *formatClockCyclesBen( uint64_t clockCycles ) > { > thread_local char result[20 + (20 - 1) / 3 + 1]; > char *ep = (&result)[1]; > do > { > // initially with a missing cast, actually would have > // run only on little endian machines > sprintf( ep - 4, "%03lu", (unsigned long)(clockCycles % 1000) ); > if( ep != (&result)[1] ) > ep[-1] = '.'; > ep -= 4; > } while( clockCycles /= 1000 ); > for( ; *ep == '0' && ep[1]; ++ep ); > return ep; > } > > These are the resuls: > > MSVC 2022: > mine: 29.8306 > Tim: 109.97 > Ben: 320.64 > > g++ 11.1.0: > mine: 28.3386 > Tim: 110.586 > Ben: 317.135 > > clang++ 10.0.0: > mine: 29.7089 > Tim: 100.721 > Ben: 311.768 The code that I posted was meant only to show an improvement on the code in the posting to which I was responding. It wasn't meant as any kind of comparison with any code you posted earlier. After seeing the posting above, it wasn't hard to write a simple routine that is both shorter and faster than the formatClockCyclesMine() function shown above (using the code shown above in main() to do the performance testing).