felix86 26.09
This month was mostly spent on optimization, while also fixing some bugs.
Performance improvements
We improved performance substantially this month, across multiple integer and SIMD instructions, as well as compilation time. Below are some measurements of benchmarks running under felix86 26.08, felix86 26.09, and natively. These benchmarks are built for both x86-64 and RISC-V and have any hand-written assembly or intrinsics disabled, as the x86-64 side of benchmarks may have been more optimized.
The following runs are on a SpacemiT K3 board, pinned to a single core, and all of them except Stockfish do not start any threads.
By calculating the geometric mean, we find that felix86 26.09 is 30% faster than felix86 26.08 in these benchmarks.
Additionally, we measured the performance difference between felix86 26.09 and QEMU 10.2.1. In these runs, we use FELIX86_ALWAYS_TSO to enable TSO emulation, which QEMU does by default. We disable FELIX86_UNSAFE_FLAGS, which marks flags as dead across call and ret, as QEMU doesn’t perform this optimization.
Do note that while FELIX86_ALWAYS_TSO is enabled, both QEMU and felix86 will not enable TSO emulation until it is necessary, for example when a thread is spawned. Only Stockfish spawns a thread in these benchmarks, so felix86 and QEMU will emit fences on every store and load only in Stockfish, which explains the big performance regression compared to the previous graph.
Toolchain and build commands
| Architecture | gcc | C library | binutils | Compiler flags |
|---|---|---|---|---|
| x86_64 | 14.3.0 | glibc 2.41 | 2.43.1 | -O2 -static -fno-strict-aliasing -pipe -march=x86-64-v3 -mtune=generic |
| riscv64 | 14.3.0 | glibc 2.41 | 2.43.1 | -O2 -static -fno-strict-aliasing -pipe -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d |
| Benchmark | Version | Architecture | Build command |
|---|---|---|---|
| 7zip | 26.02 | x86_64 | make -C src/7zip-26.02/CPP/7zip/Bundles/Alone2 -f ../../cmpl_gcc.mak -j22 CROSS_COMPILE=toolchains/x86_64/bin/x86_64-buildroot-linux-gnu- 'MY_ARCH=-march=x86-64-v3 -mtune=generic' LDFLAGS=-static |
| riscv64 | make -C src/7zip-26.02/CPP/7zip/Bundles/Alone2 -f ../../cmpl_gcc.mak -j22 CROSS_COMPILE=toolchains/riscv64/bin/riscv64-buildroot-linux-gnu- 'MY_ARCH=-march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d' LDFLAGS=-static |
||
| coremark | v1.01 | x86_64 | make -C src/coremark-v1.01 clean PORT_DIR=linux64
make -C src/coremark-v1.01 link PORT_DIR=linux64 CC=toolchains/x86_64/bin/x86_64-buildroot-linux-gnu-gcc 'XCFLAGS=-O2 -static -fno-strict-aliasing -pipe -march=x86-64-v3 -mtune=generic -DMULTITHREAD=1 -DPERFORMANCE_RUN=1' 'LFLAGS_END=-static -lrt' |
| riscv64 | make -C src/coremark-v1.01 clean PORT_DIR=linux64
make -C src/coremark-v1.01 link PORT_DIR=linux64 CC=toolchains/riscv64/bin/riscv64-buildroot-linux-gnu-gcc 'XCFLAGS=-O2 -static -fno-strict-aliasing -pipe -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d -DMULTITHREAD=1 -DPERFORMANCE_RUN=1' 'LFLAGS_END=-static -lrt' |
||
| dhrystone | 2.1 | x86_64 | toolchains/x86_64/bin/x86_64-buildroot-linux-gnu-gcc -O2 -static -fno-strict-aliasing -pipe -march=x86-64-v3 -mtune=generic -static -std=gnu89 -DTIMES -DHZ=100 -fno-inline -fno-builtin -Isrc/dhrystone-2.1/patched -o bundle/bin/x86_64/dhrystone src/dhrystone-2.1/patched/dhry_1.c src/dhrystone-2.1/patched/dhry_2.c |
| riscv64 | toolchains/riscv64/bin/riscv64-buildroot-linux-gnu-gcc -O2 -static -fno-strict-aliasing -pipe -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d -static -std=gnu89 -DTIMES -DHZ=100 -fno-inline -fno-builtin -Isrc/dhrystone-2.1/patched -o bundle/bin/riscv64/dhrystone src/dhrystone-2.1/patched/dhry_1.c src/dhrystone-2.1/patched/dhry_2.c |
||
| openssl | 4.0.1 | x86_64 | src/openssl-4.0.1/Configure linux-x86_64 no-shared no-asm no-threads no-tests no-docs no-legacy -static -march=x86-64-v3 -mtune=generic
make -C src/openssl-4.0.1/build-x86_64 CROSS_COMPILE= -j22 build_programs |
| riscv64 | src/openssl-4.0.1/Configure linux64-riscv64 no-shared no-asm no-threads no-tests no-docs no-legacy -static -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d
make -C src/openssl-4.0.1/build-riscv64 CROSS_COMPILE= -j22 build_programs |
||
| sqlite | 3.53.4 | x86_64 | toolchains/x86_64/bin/x86_64-buildroot-linux-gnu-gcc -O2 -static -fno-strict-aliasing -pipe -march=x86-64-v3 -mtune=generic -static -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_TEMP_STORE=2 -Isrc/sqlite-3.53.4/sqlite-amalgamation-3530400 -o bundle/bin/x86_64/speedtest1 src/sqlite-3.53.4/sqlite-src-3530400/test/speedtest1.c src/sqlite-3.53.4/sqlite-amalgamation-3530400/sqlite3.c -lm |
| riscv64 | toolchains/riscv64/bin/riscv64-buildroot-linux-gnu-gcc -O2 -static -fno-strict-aliasing -pipe -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d -static -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_TEMP_STORE=2 -Isrc/sqlite-3.53.4/sqlite-amalgamation-3530400 -o bundle/bin/riscv64/speedtest1 src/sqlite-3.53.4/sqlite-src-3530400/test/speedtest1.c src/sqlite-3.53.4/sqlite-amalgamation-3530400/sqlite3.c -lm |
||
| stockfish | sf_18 | x86_64 | make -C src/stockfish-sf_18/src clean
make -C src/stockfish-sf_18/src net
make -C src/stockfish-sf_18/src -j22 build ARCH=general-64 COMP=gcc CXX=toolchains/x86_64/bin/x86_64-buildroot-linux-gnu-g++ 'EXTRACXXFLAGS=-march=x86-64-v3 -mtune=generic' EXTRALDFLAGS=-static |
| riscv64 | make -C src/stockfish-sf_18/src clean
make -C src/stockfish-sf_18/src net
make -C src/stockfish-sf_18/src -j22 build ARCH=riscv64 COMP=gcc CXX=toolchains/riscv64/bin/riscv64-buildroot-linux-gnu-g++ 'EXTRACXXFLAGS=-march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d' EXTRALDFLAGS=-static |
||
| x264 | b35605ac | x86_64 | make distclean
./configure --host=x86_64-unknown-linux-gnu --cross-prefix=toolchains/x86_64/bin/x86_64-buildroot-linux-gnu- --enable-static --disable-asm --disable-thread --disable-opencl --disable-avs --disable-swscale --disable-lavf --disable-ffms --disable-gpac --disable-lsmash '--extra-cflags=-march=x86-64-v3 -mtune=generic' --extra-ldflags=-static
make -C src/x264-b35605ac -j22 x264 |
| riscv64 | make distclean
./configure --host=riscv64-unknown-linux-gnu --cross-prefix=toolchains/riscv64/bin/riscv64-buildroot-linux-gnu- --enable-static --disable-asm --disable-thread --disable-opencl --disable-avs --disable-swscale --disable-lavf --disable-ffms --disable-gpac --disable-lsmash '--extra-cflags=-march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d' --extra-ldflags=-static
make -C src/x264-b35605ac -j22 x264 |
||
| zstd | v1.5.7 | x86_64 | make -C src/zstd-v1.5.7/programs clean
make -C src/zstd-v1.5.7/programs zstd -j22 CC=toolchains/x86_64/bin/x86_64-buildroot-linux-gnu-gcc 'CFLAGS=-O2 -static -fno-strict-aliasing -pipe -march=x86-64-v3 -mtune=generic -DZSTD_NO_INTRINSICS' LDFLAGS=-static HAVE_ZLIB=0 HAVE_LZMA=0 HAVE_LZ4=0 HAVE_THREAD=0 ZSTD_NO_ASM=1 |
| riscv64 | make -C src/zstd-v1.5.7/programs clean
make -C src/zstd-v1.5.7/programs zstd -j22 CC=toolchains/riscv64/bin/riscv64-buildroot-linux-gnu-gcc 'CFLAGS=-O2 -static -fno-strict-aliasing -pipe -march=rv64gcv_zba_zbb_zbs_zicond -mabi=lp64d -DZSTD_NO_INTRINSICS' LDFLAGS=-static HAVE_ZLIB=0 HAVE_LZMA=0 HAVE_LZ4=0 HAVE_THREAD=0 ZSTD_NO_ASM=1 |
From these benchmarks, we find that felix86 26.09 is 2.75x faster than QEMU 10.2.1, with both having TSO emulation enabled.
Since many x86 programs don’t rely on TSO, and felix86 allows disabling its emulation, performance can be significantly improved in multi-threaded applications. As seen in the Stockfish benchmark, where both felix86 and QEMU are forced to use fences for TSO emulation, the run with felix86 having TSO disabled is 11.6x faster than QEMU on this hardware, compared to 1.72x with TSO enabled.
To see what that looks like in practice, here’s a short video of some gaming on current RISC-V hardware.
Accuracy improvements
After days of debugging, we found a fix for the game Umamusume: Pretty Derby. This game has some nasty anti-debugger DRM on initialization. After last month’s fixes to our trap flag emulation, and a fix for a silly bug in the rotate instruction the game uses to decrypt its contents, it can finally run.
Pretty serious DRM for a silly game
It seems that other similar gacha games are fixed in this version as well.
This game used to crash on older versions of felix86
Some .NET games would use W^X mappings by default, which our current SMC detection cannot handle. We disable that option in the .NET runtime until we implement a proper fix.
Celeste 64, which uses .NET, can now run
Steam game profiles
felix86 will now check ~/.config/felix86/profiles/steam/<appid>.toml for a config when the SteamAppId environment variable is present, which happens when running Steam games. For example, if a specific game needs TSO but you don’t want to enable it for the Steam client, you can make a game-specific profile.
MangoHud integration
We now emit the same stats that FEX emits for MangoHud, so that you can view JIT load and 80-bit software emulation function hits per second, among other things.
Refer to https://felix86.com/docs/users/usage-guide#mangohud for more info.
Pretty cool!
Thanks for reading this post.
If you like this project, please give us a star on GitHub: https://github.com/OFFTKP/felix86
