COSMO VS. CYGWIN SPAWNING COMPARISON The following is a no-bull assessment (Oct 2023) of where Cosmo v3 currently stands in terms of process creation performance on WIN32 compared to the venerable decades-old Cygwin project. COSMO BENCHMARK RESULTS jtunn@gothbox:/C/Users/jtunn$ cosmo/spawn_bench.com cosmo/life-pe.com fork() + exec(tiny) 12,490,700 ns posix_spawn(tiny) 2,524,350 ns <-- IMPORTANT mmap(128mb, MAP_SHARED) + fork() + exec(tiny) 23,118,850 ns mmap(128mb, MAP_SHARED) + posix_spawn(tiny) 2,870,390 ns <-- IMPORTANT malloc(128mb) + fork() + exec(tiny) 154,522,980 ns malloc(128mb) + posix_spawn(tiny) 2,992,780 ns <-- IMPORTANT jtunn@gothbox:/C/Users/jtunn$ uname -a Windows gothbox 10.0-19045 Cosmopolitan 3.0-alpha x86_64 GNU/Linux CYGWIN BENCHMARK RESULTS jtunn@gothbox:/cygdrive/c/Users/jtunn/scratch$ ./spawn_bench life-pe.com fork() + exec(tiny) 10,883,920 ns posix_spawn(tiny) 9,723,830 ns <-- IMPORTANT spawnvp(tiny) 557,700 ns mmap(128mb, MAP_SHARED) + fork() + exec(tiny) 15,703,750 ns mmap(128mb, MAP_SHARED) + posix_spawn(tiny) 16,344,610 ns <-- IMPORTANT mmap(128mb, MAP_SHARED) + spawnvp(tiny) 580,250 ns malloc(128mb) + fork() + exec(tiny) 51,615,770 ns malloc(128mb) + posix_spawn(tiny) 50,878,060 ns <-- IMPORTANT malloc(128mb) + spawnvp(tiny) 815,710 ns <-- cosmo wants this jtunn@gothbox:/cygdrive/c/Users/jtunn/scratch$ uname -a CYGWIN_NT-10.0-19045 gothbox 3.4.6-1.x86_64 2023-02-14 13:23 UTC x86_64 Cygwin CONCLUSION Cygwin has a nice Cygwin-specific spawnvp() API, but they didn't put much effort into their posix_spawn() implementation, which is likely just a naive wrapper around their fork() implementation. Cygwin also copies memory into forked child processes faster than Cosmo is doing currently (but for how much longer?) COSMO SPAWN_BENCH.C PROGRAM #include #include #include #include #include #include #include #include #include #include #include #include #include #define ITERATIONS 10 _Alignas(128) int a; _Alignas(128) int b; _Alignas(128) atomic_int lock; static struct timespec SubtractTime(struct timespec a, struct timespec b) { a.tv_sec -= b.tv_sec; if (a.tv_nsec < b.tv_nsec) { a.tv_nsec += 1000000000; a.tv_sec--; } a.tv_nsec -= b.tv_nsec; return a; } static time_t ToNanoseconds(struct timespec ts) { return ts.tv_sec * 1000000000 + ts.tv_nsec; } static char *Ithoa(char p[27], unsigned long x) { char m[26]; unsigned i; i = 0; do { m[i++] = x % 10 + '0'; x = x / 10; } while (x); for (;;) { *p++ = m[--i]; if (!i) break; if (!(i % 3)) *p++ = ','; } *p = '\0'; return p; } #define BENCH(name, x) \ { \ int i; \ char ibuf[27]; \ struct timespec t1, t2; \ clock_gettime(CLOCK_REALTIME, &t1); \ for (i = 0; i < ITERATIONS; ++i) { \ x; \ } \ clock_gettime(CLOCK_REALTIME, &t2); \ Ithoa(ibuf, ToNanoseconds(SubtractTime(t2, t1))/ITERATIONS); \ printf("%-50s %16s ns\n", name, ibuf); \ } void PosixSpawnWait(const char *prog) { int ws, err, pid; char *args[] = {(char *)prog, 0}; char *envs[] = {0}; if ((err = posix_spawn(&pid, prog, NULL, NULL, args, envs))) { perror(prog); exit(1); } if (waitpid(pid, &ws, 0) == -1) { perror("waitpid"); exit(1); } if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { puts("bad exit status"); exit(1); } } void ForkExecWait(const char *prog) { int ws; if (!fork()) { execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0}); _Exit(127); } if (wait(&ws) == -1) { perror("wait"); exit(1); } if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { puts("bad exit status"); exit(1); } } char *FillMemory(char *p, long n) { return memset(p, -1, n); } char *(*pFillMemory)(char *, long) = FillMemory; int main(int argc, char *argv[]) { long n; void *p; const char *prog; if (argc <= 1) { prog = "tiny64"; } else { prog = argv[1]; } BENCH("fork() + exec(tiny)", ForkExecWait(prog)); BENCH("posix_spawn(tiny)", PosixSpawnWait(prog)); n = 128L * 1024 * 1024; p = pFillMemory( mmap(0, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), n); BENCH("mmap(128mb, MAP_SHARED) + fork() + exec(tiny)", ForkExecWait(prog)); BENCH("mmap(128mb, MAP_SHARED) + posix_spawn(tiny)", PosixSpawnWait(prog)); munmap(p, n); n = 128L * 1024 * 1024; p = pFillMemory(malloc(n), n); BENCH("malloc(128mb) + fork() + exec(tiny)", ForkExecWait(prog)); BENCH("malloc(128mb) + posix_spawn(tiny)", PosixSpawnWait(prog)); free(p); } CYGWIN SPAWN_BENCH.C PROGRAM #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ITERATIONS 10 _Alignas(128) int a; _Alignas(128) int b; _Alignas(128) atomic_int lock; static struct timespec SubtractTime(struct timespec a, struct timespec b) { a.tv_sec -= b.tv_sec; if (a.tv_nsec < b.tv_nsec) { a.tv_nsec += 1000000000; a.tv_sec--; } a.tv_nsec -= b.tv_nsec; return a; } static time_t ToNanoseconds(struct timespec ts) { return ts.tv_sec * 1000000000 + ts.tv_nsec; } static char *Ithoa(char p[27], unsigned long x) { char m[26]; unsigned i; i = 0; do { m[i++] = x % 10 + '0'; x = x / 10; } while (x); for (;;) { *p++ = m[--i]; if (!i) break; if (!(i % 3)) *p++ = ','; } *p = '\0'; return p; } #define BENCH(name, x) \ { \ int i; \ char ibuf[27]; \ struct timespec t1, t2; \ clock_gettime(CLOCK_REALTIME, &t1); \ for (i = 0; i < ITERATIONS; ++i) { \ x; \ } \ clock_gettime(CLOCK_REALTIME, &t2); \ Ithoa(ibuf, ToNanoseconds(SubtractTime(t2, t1))/ITERATIONS); \ printf("%-50s %16s ns\n", name, ibuf); \ } void PosixSpawnWait(const char *prog) { int ws, err, pid; char *args[] = {(char *)prog, 0}; char *envs[] = {0}; if ((err = posix_spawn(&pid, prog, NULL, NULL, args, envs))) { perror(prog); exit(1); } if (waitpid(pid, &ws, 0) == -1) { perror("waitpid"); exit(1); } if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { puts("bad exit status"); exit(1); } } void ForkExecWait(const char *prog) { int ws; if (!fork()) { execve(prog, (char *[]){(char *)prog, 0}, (char *[]){0}); _Exit(127); } if (wait(&ws) == -1) { perror("wait"); exit(1); } if (!(WIFEXITED(ws) && WEXITSTATUS(ws) == 42)) { puts("bad exit status"); exit(1); } } void SpawnvpWait(const char *prog) { spawnvp(_P_WAIT, prog, (char *[]){(char *)prog, 0}); } char *FillMemory(char *p, long n) { return memset(p, -1, n); } char *(*pFillMemory)(char *, long) = FillMemory; int main(int argc, char *argv[]) { long n; void *p; const char *prog; if (argc <= 1) { prog = "tiny64"; } else { prog = argv[1]; } BENCH("fork() + exec(tiny)", ForkExecWait(prog)); BENCH("posix_spawn(tiny)", PosixSpawnWait(prog)); BENCH("spawnvp(tiny)", SpawnvpWait(prog)); n = 128L * 1024 * 1024; p = pFillMemory( mmap(0, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0), n); BENCH("mmap(128mb, MAP_SHARED) + fork() + exec(tiny)", ForkExecWait(prog)); BENCH("mmap(128mb, MAP_SHARED) + posix_spawn(tiny)", PosixSpawnWait(prog)); BENCH("mmap(128mb, MAP_SHARED) + spawnvp(tiny)", SpawnvpWait(prog)); munmap(p, n); n = 128L * 1024 * 1024; p = pFillMemory(malloc(n), n); BENCH("malloc(128mb) + fork() + exec(tiny)", ForkExecWait(prog)); BENCH("malloc(128mb) + posix_spawn(tiny)", PosixSpawnWait(prog)); BENCH("malloc(128mb) + spawnvp(tiny)", SpawnvpWait(prog)); free(p); } OUR TINIEST PORTABLE EXECUTABLE (COSMOPOLITAN/TOOL/HELLO/LIFE-PE.C) // $ cd cosmopolitan // $ make o//tool/hello/life-pe.com // ld -o life-pe.elf -q -e WinMain // elf2pe -o life-pe.com life-pe.elf __attribute__((__ms_abi__)) long WinMain(void) { return 42 << 8; } 00000000 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |MZ..............| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 |............@...| 00000040 50 45 00 00 64 86 02 00 d8 73 bc 64 00 00 00 00 |PE..d....s.d....| 00000050 00 00 00 00 70 00 2f 00 0b 02 0e 23 00 02 00 00 |....p./....#....| 00000060 00 02 00 00 00 00 00 00 10 10 00 00 00 10 00 00 |................| 00000070 00 00 40 00 00 00 00 00 00 10 00 00 00 02 00 00 |..@.............| 00000080 06 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 |................| 00000090 00 30 00 00 00 02 00 00 00 00 00 00 03 00 20 01 |.0............ .| 000000a0 00 00 80 00 00 00 00 00 00 00 01 00 00 00 00 00 |................| 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000c0 00 00 00 00 00 00 00 00 2e 74 65 78 74 00 00 00 |.........text...| 000000d0 18 00 00 00 00 10 00 00 00 02 00 00 00 02 00 00 |................| 000000e0 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 |............ ..`| 000000f0 2e 64 61 74 61 00 00 00 08 00 00 00 00 20 00 00 |.data........ ..| 00000100 00 02 00 00 00 04 00 00 00 00 00 00 00 00 00 00 |................| 00000110 00 00 00 00 40 00 00 c0 00 00 00 00 00 00 00 00 |....@...........| 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000200 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 |................| 00000210 66 90 b8 2a 00 00 00 c3 00 00 00 00 00 00 00 00 |f..*............| 00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000260 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000002f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000310 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000370 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000003f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000400 00 10 40 00 00 00 00 00 00 00 00 00 00 00 00 00 |..@.............| 00000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000004f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000500 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000540 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000550 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000570 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000590 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000005f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000600