News:

Download Pelles C here: http://www.pellesc.se

Main Menu

Silly test for msvcrt and ucrt sprintf

Started by TimoVJL, February 06, 2026, 11:28:10 AM

Previous topic - Next topic

TimoVJL

Just a silly test for msvcrt and ucrt sprintf.

pocrt: 3.141593
 452 ticks
msvcrt: 3.141593
 827 ticks
ucrt: 3.141593
 749 ticks

test_sprintf3pocrt:  3.141593
 439.653040
msvcrt: 3.141593
 795.308200
ucrt:   3.141593
 691.476080
May the source be with you

jack


jack

will give a good test Monday, I am off to visit my family and won't be back till Monday

jack

@TimoVJL I am back early
trying to link against ucrt with Pelles C is not straightforward but it easy using llvm-mingw gcc from https://github.com/mstorsjo/llvm-mingw/releases
using the 64-bit version I compiled the following code: gcc sprintf-timing.c -o sprintf-timing
sprintf-timing.c
#include <stdio.h>
#include <windows.h>

#define ITERATIONS 1000000

int main() {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
    char buffer[256];
    LARGE_INTEGER frequency, start, end;
    double x=1.2345678901234567890;
    double total_time = 0;
   
    QueryPerformanceFrequency(&frequency);
   
    for (int i = 0; i < ITERATIONS; i++) {
        QueryPerformanceCounter(&start);
       
        sprintf(buffer, "%.15g", x);
       
        QueryPerformanceCounter(&end);
       
        total_time += (double)(end.QuadPart - start.QuadPart) / frequency.QuadPart;
    }
   
    printf("Average sprintf time: %.2f nanoseconds\n",
           (total_time / ITERATIONS) * 1e9);
}
as you mentioned to Vortex, ucrtbase does not provide a straight printf or sprintf but it does provide stdio_common_vfprintf and stdio_common_vsprintf
I ran the following from the msys2 shell
objdump.exe -d -M intel,x86-64 sprintf-timing.exe >sprintf-timing.txt
and then searched the disassembly for stdio_common_vsprintf, here are the relevant pieces
0000000140001f30 <printf>:
   140001f30: 56                    push   rsi
   140001f31: 57                    push   rdi
   140001f32: 48 83 ec 38          sub    rsp,0x38
   140001f36: 48 89 ce              mov    rsi,rcx
   140001f39: 48 89 54 24 58        mov    QWORD PTR [rsp+0x58],rdx
   140001f3e: 4c 89 44 24 60        mov    QWORD PTR [rsp+0x60],r8
   140001f43: 4c 89 4c 24 68        mov    QWORD PTR [rsp+0x68],r9
   140001f48: 48 8d 44 24 58        lea    rax,[rsp+0x58]
   140001f4d: 48 89 44 24 30        mov    QWORD PTR [rsp+0x30],rax
   140001f52: e8 99 07 00 00        call   1400026f0 <__local_stdio_printf_options>
   140001f57: 48 8b 38              mov    rdi,QWORD PTR [rax]
   140001f5a: b9 01 00 00 00        mov    ecx,0x1
   140001f5f: e8 ac 07 00 00        call   140002710 <__acrt_iob_func>
   140001f64: 48 8b 4c 24 30        mov    rcx,QWORD PTR [rsp+0x30]
   140001f69: 48 89 4c 24 20        mov    QWORD PTR [rsp+0x20],rcx
   140001f6e: 48 89 f9              mov    rcx,rdi
   140001f71: 48 89 c2              mov    rdx,rax
   140001f74: 49 89 f0              mov    r8,rsi
   140001f77: 45 31 c9              xor    r9d,r9d
   140001f7a: e8 c1 09 00 00        call   140002940 <__stdio_common_vfprintf>
   140001f7f: 90                    nop
   140001f80: 48 83 c4 38          add    rsp,0x38
   140001f84: 5f                    pop    rdi
   140001f85: 5e                    pop    rsi
   140001f86: c3                    ret
   140001f87: cc                    int3
   140001f88: cc                    int3
   140001f89: cc                    int3
   140001f8a: cc                    int3
   140001f8b: cc                    int3
   140001f8c: cc                    int3
   140001f8d: cc                    int3
   140001f8e: cc                    int3
   140001f8f: cc                    int3

0000000140001f90 <sprintf>:
   140001f90: 56                    push   rsi
   140001f91: 57                    push   rdi
   140001f92: 48 83 ec 38          sub    rsp,0x38
   140001f96: 48 89 d6              mov    rsi,rdx
   140001f99: 48 89 cf              mov    rdi,rcx
   140001f9c: 4c 89 44 24 60        mov    QWORD PTR [rsp+0x60],r8
   140001fa1: 4c 89 4c 24 68        mov    QWORD PTR [rsp+0x68],r9
   140001fa6: 48 8d 44 24 60        lea    rax,[rsp+0x60]
   140001fab: 48 89 44 24 30        mov    QWORD PTR [rsp+0x30],rax
   140001fb0: e8 3b 07 00 00        call   1400026f0 <__local_stdio_printf_options>
   140001fb5: 48 8b 08              mov    rcx,QWORD PTR [rax]
   140001fb8: 48 83 c9 02          or     rcx,0x2
   140001fbc: 48 8b 44 24 30        mov    rax,QWORD PTR [rsp+0x30]
   140001fc1: 48 89 44 24 28        mov    QWORD PTR [rsp+0x28],rax
   140001fc6: 48 c7 44 24 20 00 00 mov    QWORD PTR [rsp+0x20],0x0
   140001fcd: 00 00
   140001fcf: 48 89 fa              mov    rdx,rdi
   140001fd2: 49 c7 c0 ff ff ff ff mov    r8,0xffffffffffffffff
   140001fd9: 49 89 f1              mov    r9,rsi
   140001fdc: e8 6f 09 00 00        call   140002950 <__stdio_common_vsprintf>
   140001fe1: 90                    nop
   140001fe2: 48 83 c4 38          add    rsp,0x38
   140001fe6: 5f                    pop    rdi
   140001fe7: 5e                    pop    rsi
   140001fe8: c3                    ret
   140001fe9: cc                    int3
   140001fea: cc                    int3
   140001feb: cc                    int3
   140001fec: cc                    int3
   140001fed: cc                    int3
   140001fee: cc                    int3
   140001fef: cc                    int3

00000001400026f0 <__local_stdio_printf_options>:
   1400026f0: 48 8d 05 51 29 00 00 lea    rax,[rip+0x2951]        # 140005048 <options>
   1400026f7: c3                    ret
   1400026f8: cc                    int3
   1400026f9: cc                    int3
   1400026fa: cc                    int3
   1400026fb: cc                    int3
   1400026fc: cc                    int3
   1400026fd: cc                    int3
   1400026fe: cc                    int3
   1400026ff: cc                    int3

the only missing piece of the puzzle is 140005048 <options>

TimoVJL

#4
A that <options> is a local storage ?
#if _CRT_FUNCTIONS_REQUIRED
    // This function must not be inlined into callers to avoid ODR violations.  The
    // static local variable has different names in C and in C++ translation units.
    _Check_return_ _Ret_notnull_
    _CRT_INLINE_PURE_SECURITYCRITICAL_ATTRIBUTE
    __declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
    {
        static unsigned __int64 _OptionsStorage;
        return &_OptionsStorage;
    }

    // This function must not be inlined into callers to avoid ODR violations.  The
    // static local variable has different names in C and in C++ translation units.
    _Check_return_ _Ret_notnull_
    _CRT_INLINE_PURE_SECURITYCRITICAL_ATTRIBUTE
    __declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_scanf_options(void)
    {
        static unsigned __int64 _OptionsStorage;
        return &_OptionsStorage;
    }
#endif

#if defined _M_CEE && !defined _M_CEE_PURE
    #pragma managed(pop)
#endif

#define _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS (*__local_stdio_printf_options())
#define _CRT_INTERNAL_LOCAL_SCANF_OPTIONS  (*__local_stdio_scanf_options ())



#define _CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION (1ULL << 0)
#define _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR       (1ULL << 1)
#define _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS           (1ULL << 2)
#define _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY      (1ULL << 3)
#define _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS     (1ULL << 4)
#define _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING                (1ULL << 5)


#define _CRT_INTERNAL_SCANF_SECURECRT                   (1ULL << 0)
#define _CRT_INTERNAL_SCANF_LEGACY_WIDE_SPECIFIERS      (1ULL << 1)
#define _CRT_INTERNAL_SCANF_LEGACY_MSVCRT_COMPATIBILITY (1ULL << 2)
// CRT headers are included into some kinds of source files where only data type
// definitions and macro definitions are required but function declarations and
// inline function definitions are not.  These files include assembly files, IDL
// files, and resource files.  The tools that process these files often have a
// limited ability to process C and C++ code.  The _CRT_FUNCTIONS_REQUIRED macro
// is defined to 1 when we are compiling a file that actually needs functions to
// be declared (and defined, where applicable), and to 0 when we are compiling a
// file that does not.  This allows us to suppress declarations and definitions
// that are not compilable with the aforementioned tools.
#if !defined _CRT_FUNCTIONS_REQUIRED
    #if defined __assembler || defined __midl || defined RC_INVOKED
        #define _CRT_FUNCTIONS_REQUIRED 0
    #else
        #define _CRT_FUNCTIONS_REQUIRED 1
    #endif
#endif
May the source be with you

jack


jack

with uppercase option D I get a bit more info
0000000140005048 <options>:
   140005048: 24 00                and    al,0x0
   14000504a: 00 00                add    BYTE PTR [rax],al
   14000504c: 00 00                add    BYTE PTR [rax],al
...

TimoVJL

We can test this too ?
...
static unsigned __int64 _OptionsStorage; // local option storage
...
psprintf(((ULONGLONG)&_OptionsStorage|_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR), buf, sizeof(buf), fmt, NULL, vl);
...
May the source be with you

Vortex

Hi Jack,

It's easy to build and use the import library for ucrt :

Import libraries for the Universal C Runtime :

https://forum.pellesc.de/index.php?topic=11052.0

Universal C Runtime example :

https://forum.pellesc.de/index.php?topic=11054.0
Code it... That's all...

jack