Compare commits

..
Author SHA1 Message Date
weaselbot 9adf6f44f4 Install mc in the msan CI job
CI / pre-commit (pull_request) Successful in 1m59s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (pull_request) Successful in 3m16s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (pull_request) Successful in 3m10s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (pull_request) Successful in 3m6s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (pull_request) Successful in 3m10s
CI / release (amd64, ubuntu-latest-amd64) (pull_request) Successful in 5m5s
CI / release (arm64, ubuntu-latest-arm64) (pull_request) Successful in 2m15s
CI / coverage (pull_request) Successful in 3m42s
CI / msan (pull_request) Failing after 3m38s
The msan job uploads test results to MinIO with `mc cp`, but unlike the
test/release/coverage jobs it never installed the mc binary.  When
MinIO credentials are configured (i.e. after merge) the upload step
would fail.  Install mc to match the other jobs.
2026-07-22 18:48:49 -04:00
weaselbot 321a23f1dc Avoid reading uninitialized end.p for point writes/reads
For point writes and point reads (end.len == 0), end.p is not part of
the API contract and callers may leave it uninitialized -- both the C
and C++ API smoke tests and the fuzz test driver do so.  However,
insertPointWritesOrSorted and check::Job::init unconditionally built a
TrivialSpan from end.p, reading the uninitialized pointer even though
it is never used for point operations.

Move the end span construction into the range-write/range-read branches
so end.p is only read when end.len > 0.  This is semantically identical
but removes the uninitialized reads that MemorySanitizer reports.
2026-07-22 18:48:46 -04:00
andrewandweaselbot 0510f01fe1 ci: add MemorySanitizer CI job
Add build_msan_toolchain.sh to produce a tarball containing an
MSan-instrumented libc++/libc++abi/libunwind toolchain, and add an msan
CI job that downloads the tarball and runs the test suite under MSan.

Also add USE_MSAN CMake option that disables conflicting sanitizers,
switches to lld, and propagates -fsanitize=memory to the shared library
and fuzz_driver targets.
2026-07-22 18:42:10 -04:00
6 changed files with 251 additions and 2044 deletions
+96 -46
View File
@@ -38,43 +38,21 @@ jobs:
matrix: matrix:
include: include:
- name: 64-bit-versions - name: 64-bit-versions
runner: ubuntu-latest-amd64
arch: amd64
llvm_version: "21"
msan_url: ""
cmake_args: -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1 cmake_args: -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1
- name: debug - name: debug
runner: ubuntu-latest-amd64 cmake_args: -DCMAKE_BUILD_TYPE=Debug
arch: amd64
llvm_version: "21"
msan_url: https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst
cmake_args: -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan
- name: simd-fallback - name: simd-fallback
runner: ubuntu-latest-amd64
arch: amd64
llvm_version: "21"
msan_url: ""
cmake_args: -DUSE_SIMD_FALLBACK=ON cmake_args: -DUSE_SIMD_FALLBACK=ON
- name: gcc - name: gcc
runner: ubuntu-latest-amd64
arch: amd64
llvm_version: "21"
msan_url: ""
cmake_args: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ cmake_args: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
- name: debug-arm64 runs-on: ubuntu-latest-amd64
runner: ubuntu-latest-arm64
arch: arm64
llvm_version: "22"
msan_url: https://minio.weaselab.dev/public/aarch64/msan-toolchain-22.1.8.tar.zst
cmake_args: -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan
runs-on: ${{ matrix.runner }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: /var/cache/apt/archives path: /var/cache/apt/archives
key: apt-${{ matrix.arch }}-${{ hashFiles('.gitea/workflows/ci.yml') }} key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install common dependencies - name: Install common dependencies
run: | run: |
@@ -90,21 +68,14 @@ jobs:
run: | run: |
. /etc/os-release . /etc/os-release
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${{ matrix.llvm_version }} main" | sudo tee /etc/apt/sources.list.d/llvm.list echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-21 main" | sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -y \ sudo apt-get install -y \
clang-${{ matrix.llvm_version }} llvm-${{ matrix.llvm_version }} lld-${{ matrix.llvm_version }} mold clang-21 llvm-21 lld-21 mold
for tool in clang clang++ llvm-ar llvm-nm llvm-ranlib llvm-objcopy llvm-cov llvm-symbolizer lld ld.lld; do for tool in clang clang++ llvm-ar llvm-nm llvm-ranlib llvm-objcopy llvm-cov llvm-symbolizer lld ld.lld; do
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-${{ matrix.llvm_version }} 100 sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
done done
- name: Download MSan toolchain
if: matrix.msan_url != ''
run: |
curl -Ls "${{ matrix.msan_url }}" -o /tmp/msan-toolchain.tar.zst
sudo mkdir -p /opt/msan
sudo tar --zstd -xf /tmp/msan-toolchain.tar.zst -C /opt/msan
- name: Build - name: Build
run: | run: |
export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache" export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache"
@@ -145,10 +116,8 @@ jobs:
include: include:
- runner: ubuntu-latest-amd64 - runner: ubuntu-latest-amd64
arch: amd64 arch: amd64
cmake_args: -DMSAN_TOOLCHAIN_PATH=/opt/msan
- runner: ubuntu-latest-arm64 - runner: ubuntu-latest-arm64
arch: arm64 arch: arm64
cmake_args: ""
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -189,25 +158,23 @@ jobs:
restore-keys: | restore-keys: |
ccache-release-${{ matrix.arch }}- ccache-release-${{ matrix.arch }}-
- name: Download MSan toolchain
if: matrix.arch == 'amd64'
run: |
curl -Ls "https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst" -o /tmp/msan-toolchain.tar.zst
sudo mkdir -p /opt/msan
sudo tar --zstd -xf /tmp/msan-toolchain.tar.zst -C /opt/msan
- name: Build - name: Build
run: | run: |
export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache" export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache"
rm -rf build rm -rf build
cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS=-DNVALGRIND ${{ matrix.cmake_args }} cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS=-DNVALGRIND
ninja -C build ninja -C build
ccache -s ccache -s
- name: Test - name: Test
run: | run: |
cd build cd build
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j "$(nproc)" --timeout 90 > /dev/null # On arm64, valgrind needs the MAKE_MEM_DEFINED client requests for
# https://git.weaselab.dev/weaselab/conflict-set/issues/39, but this
# build has -DNVALGRIND, which compiles them out. Skip valgrind
# tests here; they run annotated in the test job.
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 ${{ matrix.arch == 'arm64' && '-E valgrind' || '' }} -T Test -j "$(nproc)" --timeout 90 > /dev/null
- name: Package - name: Package
run: | run: |
cd build cd build
@@ -251,6 +218,89 @@ jobs:
--link "https://minio.weaselab.dev/jenkins/conflict-set/${{ gitea.run_number }}/release-${{ matrix.arch }}/Test.xml.zst" \ --link "https://minio.weaselab.dev/jenkins/conflict-set/${{ gitea.run_number }}/release-${{ matrix.arch }}/Test.xml.zst" \
| tee -a "$GITHUB_STEP_SUMMARY" | tee -a "$GITHUB_STEP_SUMMARY"
msan:
runs-on: ubuntu-latest-amd64
env:
MSAN_VERSION: "21.1.8"
MSAN_URL: "https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst"
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
- name: Install dependencies
run: |
. /etc/os-release
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-21 main" | sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update -qq
sudo apt-get install -y \
build-essential ccache clang-21 cmake libc6-dbg \
llvm-21 lld-21 mold ninja-build python3 zstd
sudo curl -Ls "https://minio.weaselab.dev/public/$(uname -m)/mc.RELEASE.2025-08-13T08-35-41Z" \
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
for tool in clang clang++ llvm-ar llvm-nm llvm-ranlib llvm-objcopy llvm-cov llvm-symbolizer lld ld.lld; do
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
done
- name: Download MSan toolchain
run: |
curl -Ls "${MSAN_URL}" -o /tmp/msan-toolchain.tar.zst
sudo mkdir -p /opt/msan
sudo tar --zstd -xf /tmp/msan-toolchain.tar.zst -C /opt/msan
- uses: actions/cache@v4
with:
path: .ccache
key: ccache-msan-${{ gitea.sha }}
restore-keys: |
ccache-msan-
- name: Build
run: |
export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache"
rm -rf build
cmake -S . -B build -G Ninja \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=Debug \
-DDISABLE_TSAN=ON \
-DUSE_MSAN=ON \
-DCMAKE_CXX_FLAGS="-fsanitize=memory -stdlib=libc++ -I/opt/msan/include/c++/v1 -L/opt/msan/lib -UNDEBUG" \
-DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -Wl,-rpath,/opt/msan/lib" \
-DCMAKE_SHARED_LINKER_FLAGS="-stdlib=libc++ -Wl,-rpath,/opt/msan/lib"
ninja -C build
ccache -s
- name: Test
run: |
cd build
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j "$(nproc)" --timeout 300 > /dev/null
- name: Upload test results to MinIO
if: always()
env:
MINIO_ACCESS_KEY: ${{ secrets.MINIO_ACCESS_KEY }}
MC_HOST_minio: https://${{ secrets.MINIO_ACCESS_KEY }}:${{ secrets.MINIO_SECRET_KEY }}@minio.weaselab.dev
run: |
if [ -z "$MINIO_ACCESS_KEY" ]; then
echo "MinIO credentials not configured; skipping upload"
exit 0
fi
zstd build/Testing/*/Test.xml
mc cp build/Testing/*/Test.xml.zst "minio/jenkins/conflict-set/${{ gitea.run_number }}/msan/"
- name: Test summary
if: always()
run: |
python3 ctest_summary.py build/Testing/*/Test.xml \
--link "https://minio.weaselab.dev/jenkins/conflict-set/${{ gitea.run_number }}/msan/Test.xml.zst" \
| tee -a "$GITHUB_STEP_SUMMARY"
coverage: coverage:
runs-on: ubuntu-latest-amd64 runs-on: ubuntu-latest-amd64
steps: steps:
+31 -38
View File
@@ -49,6 +49,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
${LLVM_OBJCOPY} ${LLVM_OBJCOPY}
CACHE FILEPATH "path to objcopy binary" FORCE) CACHE FILEPATH "path to objcopy binary" FORCE)
endif() endif()
if(USE_MSAN)
find_program(LLD_LINKER lld)
if(LLD_LINKER)
set(CMAKE_LINKER_TYPE
"LLD"
CACHE STRING "Use LLD linker" FORCE)
set(CMAKE_LINKER
${LLD_LINKER}
CACHE FILEPATH "path to linker binary" FORCE)
add_link_options("-fuse-ld=lld")
endif()
endif()
endif() endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -103,12 +115,7 @@ option(USE_SIMD_FALLBACK
option(DISABLE_TSAN "Disable TSAN" OFF) option(DISABLE_TSAN "Disable TSAN" OFF)
set(MSAN_TOOLCHAIN_PATH option(USE_MSAN "Build with MemorySanitizer (disables ASan/UBSan)" OFF)
""
CACHE
PATH
"Path to an MSan-instrumented libc++ toolchain. When set, an MSan fuzz_driver target is added. Must contain include/c++/v1 and lib. See build_msan_toolchain.sh to produce one."
)
# This is encouraged according to # This is encouraged according to
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq # https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
@@ -239,8 +246,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
target_compile_definitions(conflict_set_main PRIVATE ENABLE_MAIN) target_compile_definitions(conflict_set_main PRIVATE ENABLE_MAIN)
target_link_libraries(conflict_set_main PRIVATE nanobench) target_link_libraries(conflict_set_main PRIVATE nanobench)
if(NOT APPLE) if(NOT APPLE AND NOT USE_MSAN)
# libfuzzer target, to generate/manage corpus # libfuzzer target, to generate/manage corpus. MSan requires an instrumented
# libfuzzer runtime, which is not shipped with the compiler, so skip this
# target when building with MSan.
set(FUZZ_FLAGS "-fsanitize=fuzzer-no-link,address,undefined") set(FUZZ_FLAGS "-fsanitize=fuzzer-no-link,address,undefined")
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
cmake_push_check_state() cmake_push_check_state()
@@ -260,13 +269,18 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
endif() endif()
endif() endif()
# whitebox tests asan+ubsan # whitebox tests
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp) add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp)
target_compile_options(fuzz_driver PRIVATE ${TEST_FLAGS}) target_compile_options(fuzz_driver PRIVATE ${TEST_FLAGS})
if(NOT CMAKE_CROSSCOMPILING) if(NOT CMAKE_CROSSCOMPILING)
if(USE_MSAN)
target_compile_options(fuzz_driver PRIVATE -fsanitize=memory)
target_link_options(fuzz_driver PRIVATE -fsanitize=memory)
else()
target_compile_options(fuzz_driver PRIVATE -fsanitize=address,undefined) target_compile_options(fuzz_driver PRIVATE -fsanitize=address,undefined)
target_link_options(fuzz_driver PRIVATE -fsanitize=address,undefined) target_link_options(fuzz_driver PRIVATE -fsanitize=address,undefined)
endif() endif()
endif()
target_compile_definitions(fuzz_driver PRIVATE ENABLE_FUZZ) target_compile_definitions(fuzz_driver PRIVATE ENABLE_FUZZ)
target_include_directories(fuzz_driver target_include_directories(fuzz_driver
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
@@ -275,36 +289,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST}) add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST})
endforeach() endforeach()
# whitebox tests msan
if(MSAN_TOOLCHAIN_PATH)
add_executable(fuzz_driver_msan ConflictSet.cpp FuzzTestDriver.cpp)
target_compile_options(fuzz_driver_msan PRIVATE ${TEST_FLAGS})
if(NOT CMAKE_CROSSCOMPILING)
target_compile_options(
fuzz_driver_msan
PRIVATE -fsanitize=memory -fsanitize-memory-track-origins=2
$<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++>
-I${MSAN_TOOLCHAIN_PATH}/include/c++/v1)
target_link_options(
fuzz_driver_msan
PRIVATE
-fsanitize=memory
-fsanitize-memory-track-origins=2
-stdlib=libc++
-L${MSAN_TOOLCHAIN_PATH}/lib
LINKER:-rpath,${MSAN_TOOLCHAIN_PATH}/lib)
endif()
target_compile_definitions(fuzz_driver_msan PRIVATE ENABLE_FUZZ)
target_include_directories(fuzz_driver_msan
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
foreach(TEST ${CORPUS_TESTS})
get_filename_component(hash ${TEST} NAME)
add_test(NAME conflict_set_msan_${hash} COMMAND fuzz_driver_msan ${TEST})
endforeach()
endif()
# tsan tests # tsan tests
if(NOT CMAKE_CROSSCOMPILING AND NOT DISABLE_TSAN) if(NOT CMAKE_CROSSCOMPILING
AND NOT DISABLE_TSAN
AND NOT USE_MSAN)
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp) add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread) target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread)
target_link_options(tsan_driver PRIVATE -fsanitize=thread) target_link_options(tsan_driver PRIVATE -fsanitize=thread)
@@ -526,6 +514,11 @@ target_include_directories(
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>) $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>)
if(USE_MSAN)
target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=memory)
target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=memory)
endif()
set_target_properties( set_target_properties(
${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION} ${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}) SOVERSION ${PROJECT_VERSION_MAJOR})
+84 -103
View File
@@ -488,7 +488,6 @@ inline void Node3::copyChildrenAndKeyFrom(const Node16 &other) {
inline void Node16::copyChildrenAndKeyFrom(const Node3 &other) { inline void Node16::copyChildrenAndKeyFrom(const Node3 &other) {
copyCommon(*this, other); copyCommon(*this, other);
memset(index, 0, sizeof(index));
memcpy(index, other.index, Node3::kMaxNodes); memcpy(index, other.index, Node3::kMaxNodes);
memcpy(children, other.children, memcpy(children, other.children,
Node3::kMaxNodes * sizeof(children[0])); // NOLINT Node3::kMaxNodes * sizeof(children[0])); // NOLINT
@@ -500,12 +499,6 @@ inline void Node16::copyChildrenAndKeyFrom(const Node3 &other) {
assert(children[i]->parent == &other); assert(children[i]->parent == &other);
children[i]->parent = this; children[i]->parent = this;
} }
// The index bytes beyond numChildren are initialized so that the SIMD loads
// of the full 16 bytes are well-defined (reading indeterminate bytes is UB
// in C++). valgrind then treats them as undefined so that it can still flag
// any read that relies on them.
VALGRIND_MAKE_MEM_UNDEFINED(index + numChildren,
sizeof(index) - numChildren * sizeof(index[0]));
} }
inline void Node16::copyChildrenAndKeyFrom(const Node16 &other) { inline void Node16::copyChildrenAndKeyFrom(const Node16 &other) {
@@ -522,7 +515,6 @@ inline void Node16::copyChildrenAndKeyFrom(const Node16 &other) {
inline void Node16::copyChildrenAndKeyFrom(const Node48 &other) { inline void Node16::copyChildrenAndKeyFrom(const Node48 &other) {
copyCommon(*this, other); copyCommon(*this, other);
memset(index, 0, sizeof(index));
int i = 0; int i = 0;
other.bitSet.forEachSet([&](int c) { other.bitSet.forEachSet([&](int c) {
// Suppress a false positive -Waggressive-loop-optimizations warning // Suppress a false positive -Waggressive-loop-optimizations warning
@@ -536,12 +528,6 @@ inline void Node16::copyChildrenAndKeyFrom(const Node48 &other) {
++i; ++i;
}); });
memcpy(partialKey(), &other + 1, partialKeyLen); memcpy(partialKey(), &other + 1, partialKeyLen);
// The index bytes beyond numChildren are initialized so that the SIMD loads
// of the full 16 bytes are well-defined (reading indeterminate bytes is UB
// in C++). valgrind then treats them as undefined so that it can still flag
// any read that relies on them.
VALGRIND_MAKE_MEM_UNDEFINED(index + numChildren,
sizeof(index) - numChildren * sizeof(index[0]));
} }
inline void Node48::copyChildrenAndKeyFrom(const Node16 &other) { inline void Node48::copyChildrenAndKeyFrom(const Node16 &other) {
@@ -549,7 +535,6 @@ inline void Node48::copyChildrenAndKeyFrom(const Node16 &other) {
assert(numChildren == Node16::kMaxNodes); assert(numChildren == Node16::kMaxNodes);
memset(index, -1, sizeof(index)); memset(index, -1, sizeof(index));
memset(children, 0, sizeof(children)); memset(children, 0, sizeof(children));
memset(reverseIndex, 0, sizeof(reverseIndex));
const auto z = InternalVersionT::zero; const auto z = InternalVersionT::zero;
for (auto &v : childMaxVersion) { for (auto &v : childMaxVersion) {
v = z; v = z;
@@ -569,13 +554,6 @@ inline void Node48::copyChildrenAndKeyFrom(const Node16 &other) {
std::max(maxOfMax[i >> Node48::kMaxOfMaxShift], childMaxVersion[i]); std::max(maxOfMax[i >> Node48::kMaxOfMaxShift], childMaxVersion[i]);
++i; ++i;
} }
// The reverseIndex bytes beyond numChildren are initialized so that the
// SIMD loads of a full 16-byte page in scan16 are well-defined (reading
// indeterminate bytes is UB in C++). valgrind then treats them as undefined
// so that it can still flag any read that relies on them.
VALGRIND_MAKE_MEM_UNDEFINED(reverseIndex + numChildren,
sizeof(reverseIndex) -
numChildren * sizeof(reverseIndex[0]));
} }
inline void Node48::copyChildrenAndKeyFrom(const Node48 &other) { inline void Node48::copyChildrenAndKeyFrom(const Node48 &other) {
@@ -602,7 +580,6 @@ inline void Node48::copyChildrenAndKeyFrom(const Node256 &other) {
copyCommon(*this, other); copyCommon(*this, other);
memset(index, -1, sizeof(index)); memset(index, -1, sizeof(index));
memset(children, 0, sizeof(children)); memset(children, 0, sizeof(children));
memset(reverseIndex, 0, sizeof(reverseIndex));
const auto z = InternalVersionT::zero; const auto z = InternalVersionT::zero;
for (auto &v : childMaxVersion) { for (auto &v : childMaxVersion) {
v = z; v = z;
@@ -624,13 +601,6 @@ inline void Node48::copyChildrenAndKeyFrom(const Node256 &other) {
++i; ++i;
}); });
memcpy(partialKey(), &other + 1, partialKeyLen); memcpy(partialKey(), &other + 1, partialKeyLen);
// The reverseIndex bytes beyond numChildren are initialized so that the
// SIMD loads of a full 16-byte page in scan16 are well-defined (reading
// indeterminate bytes is UB in C++). valgrind then treats them as undefined
// so that it can still flag any read that relies on them.
VALGRIND_MAKE_MEM_UNDEFINED(reverseIndex + numChildren,
sizeof(reverseIndex) -
numChildren * sizeof(reverseIndex[0]));
} }
inline void Node256::copyChildrenAndKeyFrom(const Node48 &other) { inline void Node256::copyChildrenAndKeyFrom(const Node48 &other) {
@@ -1362,8 +1332,8 @@ TaggedNodePointer getChildGeq(Node *self, int child) {
TaggedNodePointer getFirstChild(Node0 *) { return nullptr; } TaggedNodePointer getFirstChild(Node0 *) { return nullptr; }
TaggedNodePointer getFirstChild(Node3 *self) { TaggedNodePointer getFirstChild(Node3 *self) {
// Don't prefetch since self->children[1] might be uninitialized and msan // Improves scan performance
// doesn't like that self->children[1].prefetch();
return self->children[0]; return self->children[0];
} }
TaggedNodePointer getFirstChild(Node16 *self) { TaggedNodePointer getFirstChild(Node16 *self) {
@@ -2025,8 +1995,6 @@ Node *erase(Node *self, WriteContext *writeContext, bool logical) {
parent48->childMaxVersion[lastChildrenIndex] = writeContext->zero; parent48->childMaxVersion[lastChildrenIndex] = writeContext->zero;
VALGRIND_MAKE_MEM_UNDEFINED(parent48->children + lastChildrenIndex, VALGRIND_MAKE_MEM_UNDEFINED(parent48->children + lastChildrenIndex,
sizeof(parent48->children[0])); sizeof(parent48->children[0]));
VALGRIND_MAKE_MEM_UNDEFINED(parent48->reverseIndex + lastChildrenIndex,
sizeof(parent48->reverseIndex[0]));
if (needsDownsize(parent48)) { if (needsDownsize(parent48)) {
downsize(parent48, writeContext); downsize(parent48, writeContext);
@@ -2119,52 +2087,6 @@ compare16_avx512(const InternalVersionT *vs, InternalVersionT rv) {
} }
#endif #endif
#ifdef HAS_ARM_NEON
// Returns a uint8x16_t whose byte i is 0xff if vs[i] > readVersion and 0
// otherwise. When USE_64_BIT is set the comparison uses the full 64-bit value
// (via vcgtq_s64); otherwise it falls back to the 32-bit subtract-and-compare
// used by the original NEON fast path. The caller narrows this 16-byte vector
// into the nibble-packed (4 bits per version) mask expected by scan16 and
// checkMaxBetweenExclusiveImpl.
inline uint8x16_t conflictMask16(const InternalVersionT *vs,
InternalVersionT readVersion) {
#if USE_64_BIT
int64_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_s64(rv);
int32x2_t r32[8];
const auto *vsp = reinterpret_cast<const int64_t *>(vs);
for (int j = 0; j < 8; ++j) {
r32[j] = vmovn_s64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec));
}
uint32x4_t w4[4];
for (int k = 0; k < 4; ++k) {
w4[k] = vreinterpretq_u32_s32(vcombine_s32(r32[2 * k], r32[2 * k + 1]));
}
return vcombine_u8(
vmovn_u16(vcombine_u16(vmovn_u32(w4[0]), vmovn_u32(w4[1]))),
vmovn_u16(vcombine_u16(vmovn_u32(w4[2]), vmovn_u32(w4[3]))));
#else
uint32x4_t w4[4];
memcpy(w4, vs, sizeof(w4));
uint32_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_u32(rv);
int32x4_t z;
memset(&z, 0, sizeof(z));
uint16x4_t conflicting[4];
for (int i = 0; i < 4; ++i) {
conflicting[i] =
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
}
return vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
vmovn_u16(vcombine_u16(conflicting[2], conflicting[3])));
#endif
}
#endif
// Returns true if v[i] <= readVersion for all i such that begin <= is[i] < end // Returns true if v[i] <= readVersion for all i such that begin <= is[i] < end
// Preconditions: begin <= end, end - begin < 256 // Preconditions: begin <= end, end - begin < 256
template <bool kAVX512> template <bool kAVX512>
@@ -2185,10 +2107,26 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
uint64_t mask = vget_lane_u64( uint64_t mask = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0); vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(results), 4)), 0);
uint32x4_t w4[4];
memcpy(w4, vs, sizeof(w4));
uint32_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_u32(rv);
int32x4_t z;
memset(&z, 0, sizeof(z));
uint16x4_t conflicting[4];
for (int i = 0; i < 4; ++i) {
conflicting[i] =
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
}
auto combined =
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
vmovn_u16(vcombine_u16(conflicting[2], conflicting[3])));
uint64_t compared = vget_lane_u64( uint64_t compared = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
vreinterpretq_u16_u8(conflictMask16(vs, readVersion)), 4)),
0);
return !(compared & mask); return !(compared & mask);
@@ -2215,14 +2153,12 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; }; auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
uint32_t compared = 0; uint32_t compared = 0;
for (int i = 0; i < 16; ++i) {
compared |= (vs[i] > readVersion) << i;
}
uint32_t mask = 0; uint32_t mask = 0;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
if (vs[i] > readVersion) { mask |= inBounds(is[i]) << i;
compared |= 1u << i;
if (inBounds(is[i])) {
mask |= 1u << i;
}
}
} }
return !(compared & mask); return !(compared & mask);
@@ -2238,10 +2174,26 @@ bool scan16(const InternalVersionT *vs, int begin, int end,
assert(begin <= end); assert(begin <= end);
#if defined(HAS_ARM_NEON) #if defined(HAS_ARM_NEON)
uint32x4_t w4[4];
memcpy(w4, vs, sizeof(w4));
uint32_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_u32(rv);
int32x4_t z;
memset(&z, 0, sizeof(z));
uint16x4_t conflicting[4];
for (int i = 0; i < 4; ++i) {
conflicting[i] =
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
}
auto combined =
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
vmovn_u16(vcombine_u16(conflicting[2], conflicting[3])));
uint64_t conflict = vget_lane_u64( uint64_t conflict = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
vreinterpretq_u16_u8(conflictMask16(vs, readVersion)), 4)),
0);
conflict &= end == 16 ? -1 : (uint64_t(1) << (end << 2)) - 1; conflict &= end == 16 ? -1 : (uint64_t(1) << (end << 2)) - 1;
conflict >>= begin << 2; conflict >>= begin << 2;
@@ -2298,9 +2250,17 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; }; auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
uint32_t mask = 0; uint32_t mask = 0;
for (int i = 0; i < self->numChildren; ++i) { for (int i = 0; i < Node3::kMaxNodes; ++i) {
mask |= inBounds(self->index[i]) << i; mask |= inBounds(self->index[i]) << i;
} }
mask &= (1 << self->numChildren) - 1;
#ifdef __aarch64__
// The bits surviving the mask above don't derive from uninitialized slots,
// but clang 21+ on aarch64 lowers inBounds through flags+csel, which
// memcheck models imprecisely, tainting bits the mask provably clears.
// https://git.weaselab.dev/weaselab/conflict-set/issues/39
VALGRIND_MAKE_MEM_DEFINED(&mask, sizeof(mask));
#endif
if (!mask) { if (!mask) {
return true; return true;
} }
@@ -2308,11 +2268,17 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
const bool firstRangeOk = const bool firstRangeOk =
!child->entryPresent || child->entry.rangeVersion <= readVersion; !child->entryPresent || child->entry.rangeVersion <= readVersion;
uint32_t compared = 0; uint32_t compared = 0;
for (int i = 0; i < self->numChildren; ++i) { for (int i = 0; i < Node3::kMaxNodes; ++i) {
compared |= (self->childMaxVersion[i] > readVersion) << i; compared |= (self->childMaxVersion[i] > readVersion) << i;
} }
return !(compared & mask) && firstRangeOk; uint32_t compared_masked = compared & mask;
#ifdef __aarch64__
// Same imprecise csel modeling as above.
// https://git.weaselab.dev/weaselab/conflict-set/issues/39
VALGRIND_MAKE_MEM_DEFINED(&compared_masked, sizeof(compared_masked));
#endif
return !compared_masked && firstRangeOk;
} }
template <bool kAVX512> template <bool kAVX512>
@@ -2355,11 +2321,26 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
const bool firstRangeOk = const bool firstRangeOk =
!child->entryPresent || child->entry.rangeVersion <= readVersion; !child->entryPresent || child->entry.rangeVersion <= readVersion;
uint32x4_t w4[4];
memcpy(w4, self->childMaxVersion, sizeof(w4));
uint32_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_u32(rv);
int32x4_t z;
memset(&z, 0, sizeof(z));
uint16x4_t conflicting[4];
for (int i = 0; i < 4; ++i) {
conflicting[i] =
vmovn_u32(vcgtq_s32(vreinterpretq_s32_u32(vsubq_u32(w4[i], rvVec)), z));
}
auto combined =
vcombine_u8(vmovn_u16(vcombine_u16(conflicting[0], conflicting[1])),
vmovn_u16(vcombine_u16(conflicting[2], conflicting[3])));
uint64_t compared = vget_lane_u64( uint64_t compared = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(conflictMask16( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(combined), 4)), 0);
self->childMaxVersion, readVersion)),
4)),
0);
return !(compared & mask) && firstRangeOk; return !(compared & mask) && firstRangeOk;
@@ -2394,9 +2375,10 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; }; auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
uint32_t mask = 0; uint32_t mask = 0;
for (int i = 0; i < self->numChildren; ++i) { for (int i = 0; i < 16; ++i) {
mask |= inBounds(self->index[i]) << i; mask |= inBounds(self->index[i]) << i;
} }
mask &= (1 << self->numChildren) - 1;
if (!mask) { if (!mask) {
return true; return true;
} }
@@ -2404,7 +2386,7 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
const bool firstRangeOk = const bool firstRangeOk =
!child->entryPresent || child->entry.rangeVersion <= readVersion; !child->entryPresent || child->entry.rangeVersion <= readVersion;
uint32_t compared = 0; uint32_t compared = 0;
for (int i = 0; i < self->numChildren; ++i) { for (int i = 0; i < 16; ++i) {
compared |= (self->childMaxVersion[i] > readVersion) << i; compared |= (self->childMaxVersion[i] > readVersion) << i;
} }
return !(compared & mask) && firstRangeOk; return !(compared & mask) && firstRangeOk;
@@ -3892,9 +3874,8 @@ void Job::init(const ConflictSet::ReadRange *read, ConflictSet::Result *result,
this->result = result; this->result = result;
continuation = check::point_read_state_machine::begin; continuation = check::point_read_state_machine::begin;
} else { } else {
auto end = TrivialSpan(read->end.p, read->end.len);
this->begin = begin; this->begin = begin;
this->end = end; this->end = TrivialSpan(read->end.p, read->end.len);
this->n = root; this->n = root;
this->readVersion = InternalVersionT(read->readVersion); this->readVersion = InternalVersionT(read->readVersion);
this->result = result; this->result = result;
+23 -23
View File
@@ -7,10 +7,10 @@ Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-3
``` ```
$ clang++ --version $ clang++ --version
Ubuntu clang version 21.1.8 (6ubuntu1) Ubuntu clang version 20.0.0 (++20241120082228+86734c857724-1~exp1~20241120202359.554)
Target: x86_64-pc-linux-gnu Target: x86_64-pc-linux-gnu
Thread model: posix Thread model: posix
InstalledDir: /usr/lib/llvm-21/bin InstalledDir: /usr/lib/llvm-20/bin
``` ```
# Microbenchmark # Microbenchmark
@@ -19,30 +19,30 @@ InstalledDir: /usr/lib/llvm-21/bin
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
| 164.29 | 6,086,873.38 | 0.0% | 3,107.03 | 604.19 | 5.142 | 558.59 | 0.0% | 1.96 | `point reads` | 161.29 | 6,200,056.17 | 0.1% | 3,014.03 | 831.04 | 3.627 | 504.59 | 0.0% | 1.93 | `point reads`
| 161.05 | 6,209,395.38 | 0.1% | 3,036.76 | 592.21 | 5.128 | 539.35 | 0.0% | 1.93 | `prefix reads` | 158.32 | 6,316,160.64 | 0.1% | 2,954.16 | 815.80 | 3.621 | 490.17 | 0.0% | 1.89 | `prefix reads`
| 239.55 | 4,174,539.38 | 0.1% | 3,722.71 | 880.68 | 4.227 | 692.00 | 0.0% | 2.86 | `range reads` | 237.39 | 4,212,409.50 | 0.2% | 3,592.41 | 1,233.96 | 2.911 | 629.31 | 0.0% | 2.84 | `range reads`
| 354.75 | 2,818,919.14 | 0.7% | 4,523.64 | 1,304.75 | 3.467 | 720.22 | 2.0% | 4.23 | `point writes` | 442.11 | 2,261,878.94 | 0.0% | 4,450.57 | 2,314.25 | 1.923 | 707.92 | 2.1% | 5.28 | `point writes`
| 345.32 | 2,895,878.47 | 0.1% | 4,484.57 | 1,270.31 | 3.530 | 705.00 | 1.8% | 4.12 | `prefix writes` | 439.89 | 2,273,308.53 | 0.1% | 4,410.22 | 2,302.29 | 1.916 | 694.74 | 2.1% | 5.25 | `prefix writes`
| 193.48 | 5,168,547.42 | 0.1% | 2,224.10 | 711.72 | 3.125 | 377.17 | 3.3% | 2.32 | `range writes` | 290.96 | 3,436,936.78 | 0.0% | 2,315.38 | 1,528.68 | 1.515 | 396.69 | 3.3% | 3.49 | `range writes`
| 404.89 | 2,469,777.50 | 2.4% | 6,855.96 | 1,489.70 | 4.602 | 1,227.82 | 1.3% | 0.05 | `monotonic increasing point writes` | 476.93 | 2,096,762.02 | 0.6% | 6,999.33 | 2,484.94 | 2.817 | 1,251.73 | 1.3% | 0.06 | `monotonic increasing point writes`
| 134,231.80 | 7,449.80 | 1.9% | 812,045.25 | 495,770.40 | 1.638 | 151,246.50 | 0.9% | 0.01 | `worst case for radix tree` | 131,736.57 | 7,590.91 | 1.1% | 807,444.50 | 704,941.71 | 1.145 | 144,584.60 | 0.9% | 0.01 | `worst case for radix tree`
| 37.80 | 26,454,311.17 | 0.4% | 701.00 | 139.14 | 5.038 | 102.00 | 0.0% | 0.01 | `create and destroy` | 45.50 | 21,978,369.95 | 1.1% | 902.00 | 232.36 | 3.882 | 132.00 | 0.0% | 0.01 | `create and destroy`
## Radix tree (this implementation) ## Radix tree (this implementation)
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
| 12.89 | 77,565,115.56 | 0.1% | 244.55 | 47.43 | 5.155 | 34.21 | 0.6% | 0.15 | `point reads` | 12.36 | 80,885,626.43 | 0.2% | 243.56 | 63.62 | 3.828 | 31.07 | 0.6% | 0.15 | `point reads`
| 15.11 | 66,162,047.76 | 0.1% | 297.79 | 55.60 | 5.356 | 43.23 | 0.4% | 0.18 | `prefix reads` | 14.18 | 70,502,196.81 | 0.1% | 297.72 | 73.13 | 4.071 | 40.31 | 0.5% | 0.17 | `prefix reads`
| 36.29 | 27,559,358.29 | 0.1% | 783.16 | 133.44 | 5.869 | 109.52 | 0.2% | 0.43 | `range reads` | 33.44 | 29,901,623.04 | 0.1% | 767.90 | 172.42 | 4.454 | 101.32 | 0.2% | 0.40 | `range reads`
| 20.53 | 48,719,405.55 | 0.1% | 381.81 | 75.51 | 5.057 | 51.04 | 0.5% | 0.25 | `point writes` | 19.48 | 51,342,564.70 | 0.3% | 374.45 | 100.43 | 3.728 | 48.92 | 0.5% | 0.23 | `point writes`
| 39.37 | 25,402,042.40 | 0.1% | 685.00 | 144.83 | 4.730 | 106.72 | 0.3% | 0.47 | `prefix writes` | 37.46 | 26,694,471.44 | 0.1% | 672.00 | 193.14 | 3.479 | 101.28 | 0.3% | 0.45 | `prefix writes`
| 43.78 | 22,843,841.63 | 0.1% | 800.40 | 161.06 | 4.970 | 127.36 | 0.1% | 0.53 | `range writes` | 38.78 | 25,784,784.34 | 0.0% | 738.26 | 199.93 | 3.693 | 111.59 | 0.1% | 0.47 | `range writes`
| 78.37 | 12,760,008.75 | 1.0% | 1,452.61 | 288.24 | 5.040 | 278.69 | 0.1% | 0.01 | `monotonic increasing point writes` | 76.05 | 13,148,995.74 | 0.7% | 1,450.77 | 397.16 | 3.653 | 275.72 | 0.0% | 0.01 | `monotonic increasing point writes`
| 322,885.50 | 3,097.07 | 1.5% | 4,362,382.00 | 1,183,852.00 | 3.685 | 765,301.00 | 0.1% | 0.01 | `worst case for radix tree` | 286,920.33 | 3,485.29 | 0.4% | 4,117,948.00 | 1,521,352.00 | 2.707 | 714,833.00 | 0.1% | 0.01 | `worst case for radix tree`
| 99.99 | 10,000,718.79 | 0.4% | 1,775.00 | 367.93 | 4.824 | 288.00 | 0.0% | 0.01 | `create and destroy` | 95.66 | 10,453,798.72 | 0.5% | 1,986.00 | 495.04 | 4.012 | 315.00 | 0.0% | 0.01 | `create and destroy`
# "Real data" test # "Real data" test
@@ -51,13 +51,13 @@ Point queries only. Gc ratio is the ratio of time spent doing garbage collection
## skip list ## skip list
``` ```
Check: 4.62967 seconds, 352.195 MB/s, Add: 3.34177 seconds, 167.771 MB/s, Gc ratio: 37.9399%, Peak idle memory: 5.51852e+06 Check: 4.53508 seconds, 371.81 MB/s, Add: 3.81222 seconds, 150.919 MB/s, Gc ratio: 33.66%, Peak idle memory: 5.61007e+06
``` ```
## radix tree ## radix tree
``` ```
Check: 1.00477 seconds, 1622.8 MB/s, Add: 1.21142 seconds, 462.808 MB/s, Gc ratio: 39.4716%, Peak idle memory: 2.0226e+06 Check: 0.957735 seconds, 1760.6 MB/s, Add: 1.19942 seconds, 479.678 MB/s, Gc ratio: 38.6069%, Peak idle memory: 2.05667e+06
``` ```
## hash table ## hash table
@@ -65,6 +65,6 @@ Check: 1.00477 seconds, 1622.8 MB/s, Add: 1.21142 seconds, 462.808 MB/s, Gc rati
(The hash table implementation doesn't work on range queries, and its purpose is to provide an idea of how fast point queries can be) (The hash table implementation doesn't work on range queries, and its purpose is to provide an idea of how fast point queries can be)
``` ```
Check: 0.854254 seconds, 1908.74 MB/s, Add: 0.632626 seconds, 886.232 MB/s, Gc ratio: 41.0827%, Peak idle memory: 0 Check: 0.804598 seconds, 2095.69 MB/s, Add: 0.671221 seconds, 857.147 MB/s, Gc ratio: 35.0034%, Peak idle memory: 0
``` ```
+15 -15
View File
@@ -13,26 +13,26 @@ cd libcxx-msan
git clone --depth=1 "https://github.com/llvm/llvm-project.git" -b "release/${LLVM_VERSION}.x" git clone --depth=1 "https://github.com/llvm/llvm-project.git" -b "release/${LLVM_VERSION}.x"
cmake -S llvm-project/runtimes -B build_msan \ cmake -S llvm-project/runtimes -B build \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" \
-DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" \ -DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" \
-DCMAKE_INSTALL_PREFIX="${MSAN_PREFIX}" \ -DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" \
-DLLVM_USE_SANITIZER=MemoryWithOrigins \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \ -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
-DLLVM_USE_SANITIZER=MemoryWithOrigins -DLLVM_TARGETS_TO_BUILD=Native \
cmake --build build_msan -j"${JOBS}"
cmake --install build_msan
cmake -S llvm-project/runtimes -B build_nomsan \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" \
-DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" \
-DCMAKE_INSTALL_PREFIX="${MSAN_PREFIX}" \ -DCMAKE_INSTALL_PREFIX="${MSAN_PREFIX}" \
-DLLVM_ENABLE_RUNTIMES="libunwind" -DLIBCXXABI_USE_LLVM_UNWINDER=ON \
-DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
-DLIBCXX_USE_COMPILER_RT=ON \
-DLIBCXXABI_USE_COMPILER_RT=ON \
-DLIBUNWIND_USE_COMPILER_RT=ON \
-DLIBCXX_INCLUDE_TESTS=OFF \
-DLIBCXXABI_INCLUDE_TESTS=OFF \
-DLIBUNWIND_INCLUDE_TESTS=OFF \
-DLIBCXX_ENABLE_CLANG_TIDY=OFF
cmake --build build_nomsan -j"${JOBS}" cmake --build build -j"${JOBS}"
cmake --install build_nomsan cmake --install build
VERSION="$(clang-"${LLVM_VERSION}" --version | head -n1 | sed -E 's/.*clang version ([0-9.]+).*/\1/')" VERSION="$(clang-"${LLVM_VERSION}" --version | head -n1 | sed -E 's/.*clang version ([0-9.]+).*/\1/')"
TARBALL="msan-toolchain-${VERSION}.tar.zst" TARBALL="msan-toolchain-${VERSION}.tar.zst"
File diff suppressed because it is too large Load Diff