4
12
Fork
You've already forked LensorOS
4

libc: Optimise memcpy() #10

Merged
LensPlaysGames merged 2 commits from Sirraide/LensorOS:dev into main 2022年10月29日 12:24:41 +02:00
Contributor
Copy link

This pr fixes some bugs in libc and adds an optimised memcpy() implementation.

Below is a benchmark comparing this implementation (‘intrin’) to glibc (‘glibc’).

I have also attached a C++ program using libbenchmark that can be used to reproduce the benchmark (compile and run w/ gcc -std=c++23 -O3 -march=native test.cc -lstdc++ -lbenchmark -lfmt -o test && ./test).

-----------------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------------
glibc (16 bytes) 0.207 ns 0.207 ns 1000000000
intrin (16 bytes) 0.204 ns 0.204 ns 1000000000
glibc (128 bytes) 1.62 ns 1.62 ns 429990997
intrin (128 bytes) 0.203 ns 0.203 ns 1000000000
glibc (1,024 bytes) 7.08 ns 7.08 ns 95637792
intrin (1,024 bytes) 0.616 ns 0.616 ns 1000000000
glibc (3,175 bytes) 30.2 ns 30.2 ns 23306688
intrin (3,175 bytes) 1.83 ns 1.83 ns 381575116
glibc (4,096 bytes) 33.0 ns 33.0 ns 21048745
intrin (4,096 bytes) 26.4 ns 26.4 ns 26498355
glibc (16,384 bytes) 114 ns 114 ns 6106968
intrin (16,384 bytes) 106 ns 106 ns 6605032
glibc (1,048,576 bytes) 17045 ns 17038 ns 41071
intrin (1,048,576 bytes) 15788 ns 15781 ns 44592
glibc (1,000,000,000 bytes) 58602936 ns 58570277 ns 12
intrin (1,000,000,000 bytes) 52938424 ns 52901710 ns 13
This pr fixes some bugs in libc and adds an optimised `memcpy()` implementation. Below is a benchmark comparing this implementation (‘intrin’) to glibc (‘glibc’). I have also attached a C++ program using libbenchmark that can be used to reproduce the benchmark (compile and run w/ `gcc -std=c++23 -O3 -march=native test.cc -lstdc++ -lbenchmark -lfmt -o test && ./test`). ``` ----------------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------------- glibc (16 bytes) 0.207 ns 0.207 ns 1000000000 intrin (16 bytes) 0.204 ns 0.204 ns 1000000000 glibc (128 bytes) 1.62 ns 1.62 ns 429990997 intrin (128 bytes) 0.203 ns 0.203 ns 1000000000 glibc (1,024 bytes) 7.08 ns 7.08 ns 95637792 intrin (1,024 bytes) 0.616 ns 0.616 ns 1000000000 glibc (3,175 bytes) 30.2 ns 30.2 ns 23306688 intrin (3,175 bytes) 1.83 ns 1.83 ns 381575116 glibc (4,096 bytes) 33.0 ns 33.0 ns 21048745 intrin (4,096 bytes) 26.4 ns 26.4 ns 26498355 glibc (16,384 bytes) 114 ns 114 ns 6106968 intrin (16,384 bytes) 106 ns 106 ns 6605032 glibc (1,048,576 bytes) 17045 ns 17038 ns 41071 intrin (1,048,576 bytes) 15788 ns 15781 ns 44592 glibc (1,000,000,000 bytes) 58602936 ns 58570277 ns 12 intrin (1,000,000,000 bytes) 52938424 ns 52901710 ns 13 ```
8.9 KiB
@ -32,2 +32,4 @@
set(CMAKE_EXPORT_COMPILE_COMMANDS on)
# Check if Enhanced REP MOVSB is supported using asm.
# TODO: Figure out how to check this on Windows.
Author
Contributor
Copy link

There is a way to do this using cpuid

There is a way to do this using `cpuid`

I am going to merge, but we will eventually remove this section and assume rep movsb is enhanced (or not); we can eventually set _libc_have_erms based on syscall to OS during libc initialisation (as discussed) 👍

I am going to merge, but we will eventually remove this section and assume `rep movsb` is enhanced (or not); we can eventually set `_libc_have_erms` based on syscall to OS during libc initialisation (as discussed) 👍
@ -43,6 +58,7 @@ target_sources(
crt0.s
crti.s
crtn.s
stdio.cpp
Author
Contributor
Copy link

stdio.cpp was missing here for some reason.

`stdio.cpp` was missing here for some reason.
@ -53,2 +69,4 @@
PRIVATE
-fno-stack-protector
-nostdlib
-nostdinc
Author
Contributor
Copy link

Maybe the build system is adding these already, but I've addded them here just in case.

Maybe the build system is adding these already, but I've addded them here just in case.
@ -100,0 +72,4 @@
int snprintf(char* buffer, size_t, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
int sprintf(char* buffer, const char* fmt, ...) __attribute__((format(printf, 2, 3)));
int sscanf(const char* str, const char* fmt, ...) __attribute__((format(scanf, 2, 3)));
int vfprintf(FILE*, const char* fmt, __builtin_va_list) __attribute__((format(printf, 2, 0)));
Author
Contributor
Copy link

stdio.h doesn't define va_list, so we use __builtin_va_list instead

`stdio.h` doesn't define `va_list`, so we use `__builtin_va_list` instead
@ -0,0 +20,4 @@
#ifndef _LENSOR_OS_LIBC_DECLS_H
#define _LENSOR_OS_LIBC_DECLS_H
#ifdef __cplusplus

Are these backwards? We need extern "C" only in C++, right? In C, it's already in extern "C".

Are these backwards? We need `extern "C"` only in C++, right? In C, it's already in `extern "C"`.
Author
Contributor
Copy link

Oh goddamnit

Oh goddamnit
Author
Contributor
Copy link

Fixed

Fixed
LensPlaysGames marked this conversation as resolved

Just wondering here, letting my thoughts meander: would it be worth it to have memcpy be a function pointer that is set based on runtime CPU data gathered during libc initialization? Basically, use a syscall to LensorOS to figure out CPU capabilities that are enabled, then set memcpy function pointer to the appropriate function? Just a thought; could be horribly broken and not actually work, just seems like it may solve this confusing compile-time CPU capabilities (which are hard to think about for cross compilation)

Just wondering here, letting my thoughts meander: would it be worth it to have memcpy be a function pointer that is set based on runtime CPU data gathered during libc initialization? Basically, use a syscall to LensorOS to figure out CPU capabilities that are enabled, then set memcpy function pointer to the appropriate function? Just a thought; could be horribly broken and not actually work, just seems like it may solve this confusing compile-time CPU capabilities (which are hard to think about for cross compilation)
Author
Contributor
Copy link

You'd have to benchmark whether 1. there's any performance benefit to be gained by doing that, and 2. whether it'd be worth the extra overhead incurred by the indirect call.

Also, doing this would mean that the compiler wouldn't be able to inline it anymore, which if you check the benchmark can be the difference between 30ns and 1.8ns in some cases.

You'd have to benchmark whether 1. there's any performance benefit to be gained by doing that, and 2. whether it'd be worth the extra overhead incurred by the indirect call. Also, doing this would mean that the compiler wouldn't be able to inline it anymore, which if you check the benchmark can be the difference between 30ns and 1.8ns in some cases.
Sign in to join this conversation.
No reviewers
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
LensPlaysGames/LensorOS!10
Reference in a new issue
LensPlaysGames/LensorOS
No description provided.
Delete branch "Sirraide/LensorOS:dev"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?