Compare commits
1
Commits
main
..
494e836aae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
494e836aae |
+37
-70
@@ -19,8 +19,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
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 update -qq
|
||||||
sudo apt-get install -y git nodejs pre-commit
|
sudo apt-get install -y \
|
||||||
|
clang-21 git nodejs pre-commit
|
||||||
|
for tool in clang clang++; do
|
||||||
|
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
|
||||||
|
done
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -38,72 +45,43 @@ 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 dependencies
|
||||||
run: |
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y \
|
|
||||||
build-essential ccache cmake gcc g++ libc6-dbg \
|
|
||||||
ninja-build python3 valgrind 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
|
|
||||||
|
|
||||||
- name: Install LLVM toolchain
|
|
||||||
if: matrix.name != 'gcc'
|
|
||||||
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
|
build-essential ccache clang-21 cmake gcc g++ \
|
||||||
|
libc6-dbg llvm-21 lld-21 mold ninja-build python3 valgrind zstd
|
||||||
|
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-amd64/mc" \
|
||||||
|
-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
|
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
|
- uses: actions/cache@v4
|
||||||
if: matrix.msan_url != ''
|
with:
|
||||||
run: |
|
path: .ccache
|
||||||
curl -Ls "${{ matrix.msan_url }}" -o /tmp/msan-toolchain.tar.zst
|
key: ccache-${{ matrix.name }}-${{ gitea.sha }}
|
||||||
sudo mkdir -p /opt/msan
|
restore-keys: |
|
||||||
sudo tar --zstd -xf /tmp/msan-toolchain.tar.zst -C /opt/msan
|
ccache-${{ matrix.name }}-
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
@@ -145,10 +123,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
|
||||||
@@ -165,23 +141,16 @@ jobs:
|
|||||||
echo "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-21 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 \
|
||||||
build-essential ccache clang-21 cmake devscripts \
|
biber build-essential ccache clang-21 cmake devscripts \
|
||||||
libc6-dbg llvm-21 lld-21 mold ninja-build rpm \
|
latexmk libc6-dbg llvm-21 lld-21 mold ninja-build rpm \
|
||||||
valgrind zstd
|
texlive-bibtex-extra texlive-fonts-recommended \
|
||||||
sudo curl -Ls "https://minio.weaselab.dev/public/$(uname -m)/mc.RELEASE.2025-08-13T08-35-41Z" \
|
texlive-latex-extra texlive-pictures valgrind zstd
|
||||||
|
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-$(dpkg --print-architecture)/mc" \
|
||||||
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
|
-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
|
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
|
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Install paper build dependencies
|
|
||||||
if: matrix.arch == 'amd64'
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y \
|
|
||||||
biber latexmk \
|
|
||||||
texlive-bibtex-extra texlive-fonts-recommended \
|
|
||||||
texlive-latex-extra texlive-pictures
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: .ccache
|
path: .ccache
|
||||||
@@ -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
|
||||||
@@ -270,7 +237,7 @@ jobs:
|
|||||||
sudo apt-get install -y \
|
sudo apt-get install -y \
|
||||||
build-essential ccache clang-21 cmake gcovr \
|
build-essential ccache clang-21 cmake gcovr \
|
||||||
libc6-dbg llvm-21 lld-21 mold ninja-build python3 valgrind zstd
|
libc6-dbg llvm-21 lld-21 mold ninja-build python3 valgrind zstd
|
||||||
sudo curl -Ls "https://minio.weaselab.dev/public/$(uname -m)/mc.RELEASE.2025-08-13T08-35-41Z" \
|
sudo curl -Ls "https://dl.min.io/client/mc/release/linux-amd64/mc" \
|
||||||
-o /usr/local/bin/mc && sudo chmod +x /usr/local/bin/mc
|
-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
|
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
|
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
|
||||||
|
|||||||
+2
-37
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.1.0
|
VERSION 0.0.14
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||||
@@ -103,13 +103,6 @@ option(USE_SIMD_FALLBACK
|
|||||||
|
|
||||||
option(DISABLE_TSAN "Disable TSAN" OFF)
|
option(DISABLE_TSAN "Disable TSAN" OFF)
|
||||||
|
|
||||||
set(MSAN_TOOLCHAIN_PATH
|
|
||||||
""
|
|
||||||
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
|
||||||
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
||||||
@@ -260,7 +253,7 @@ 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)
|
||||||
@@ -275,34 +268,6 @@ 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)
|
||||||
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||||
|
|||||||
+86
-104
@@ -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;
|
||||||
@@ -3882,17 +3864,17 @@ PRESERVE_NONE void right_side_iter(Job *job, Context *context) {
|
|||||||
void Job::init(const ConflictSet::ReadRange *read, ConflictSet::Result *result,
|
void Job::init(const ConflictSet::ReadRange *read, ConflictSet::Result *result,
|
||||||
Node *root, int64_t oldestVersionFullPrecision) {
|
Node *root, int64_t oldestVersionFullPrecision) {
|
||||||
auto begin = TrivialSpan(read->begin.p, read->begin.len);
|
auto begin = TrivialSpan(read->begin.p, read->begin.len);
|
||||||
|
auto end = TrivialSpan(read->end.p, read->end.len);
|
||||||
if (read->readVersion < oldestVersionFullPrecision) [[unlikely]] {
|
if (read->readVersion < oldestVersionFullPrecision) [[unlikely]] {
|
||||||
*result = ConflictSet::TooOld;
|
*result = ConflictSet::TooOld;
|
||||||
continuation = complete;
|
continuation = complete;
|
||||||
} else if (read->end.len == 0) {
|
} else if (end.size() == 0) {
|
||||||
this->begin = begin;
|
this->begin = begin;
|
||||||
this->n = root;
|
this->n = root;
|
||||||
this->readVersion = InternalVersionT(read->readVersion);
|
this->readVersion = InternalVersionT(read->readVersion);
|
||||||
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 = end;
|
||||||
this->n = root;
|
this->n = root;
|
||||||
@@ -5064,8 +5046,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const auto &w = writes[i];
|
const auto &w = writes[i];
|
||||||
auto begin = TrivialSpan(w.begin.p, w.begin.len);
|
auto begin = TrivialSpan(w.begin.p, w.begin.len);
|
||||||
|
auto end = TrivialSpan(w.end.p, w.end.len);
|
||||||
if (w.end.len > 0) {
|
if (w.end.len > 0) {
|
||||||
auto end = TrivialSpan(w.end.p, w.end.len);
|
|
||||||
addWriteRange(rootParent->children[0], begin, end,
|
addWriteRange(rootParent->children[0], begin, end,
|
||||||
InternalVersionT(writeVersion), &writeContext);
|
InternalVersionT(writeVersion), &writeContext);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
FROM ubuntu:rolling
|
||||||
|
|
||||||
|
WORKDIR /tmp
|
||||||
|
ENV HOME=/tmp
|
||||||
|
RUN chmod -R 777 /tmp
|
||||||
|
|
||||||
|
# Install apt dependencies
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get upgrade -y
|
||||||
|
RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
|
biber \
|
||||||
|
build-essential \
|
||||||
|
ccache \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
devscripts \
|
||||||
|
gcovr \
|
||||||
|
git \
|
||||||
|
latexmk \
|
||||||
|
libc6-dbg \
|
||||||
|
llvm \
|
||||||
|
mold \
|
||||||
|
ninja-build \
|
||||||
|
nodejs \
|
||||||
|
pre-commit \
|
||||||
|
python3-requests \
|
||||||
|
rpm \
|
||||||
|
texlive-bibtex-extra \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-latex-extra \
|
||||||
|
texlive-pictures \
|
||||||
|
valgrind \
|
||||||
|
wget \
|
||||||
|
zstd
|
||||||
|
|
||||||
|
# MinIO client, for uploading build artifacts
|
||||||
|
RUN curl -Ls "https://dl.min.io/client/mc/release/linux-$(dpkg --print-architecture)/mc" \
|
||||||
|
-o /usr/local/bin/mc && chmod +x /usr/local/bin/mc
|
||||||
|
|
||||||
|
ENV CC=clang
|
||||||
|
ENV CXX=clang++
|
||||||
|
|
||||||
|
# Try to have all the pre-commit hooks we'll need already initialized
|
||||||
|
COPY .pre-commit-config.yaml /tmp/
|
||||||
|
RUN git init && pre-commit install-hooks
|
||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -99,8 +99,16 @@ force_inline bool getCharacter(const KeyInfo &ki, int character,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool operator<(const KeyInfo &lhs, const KeyInfo &rhs) {
|
bool operator<(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||||
|
// An empty key is a prefix of every key, so it sorts before any non-empty
|
||||||
|
// key. Handle empty keys up front: an empty std::span may have
|
||||||
|
// data() == nullptr, which must never be passed to memcmp.
|
||||||
|
if (lhs.key.size() == 0 || rhs.key.size() == 0) {
|
||||||
|
if (lhs.key.size() != rhs.key.size())
|
||||||
|
return lhs.key.size() < rhs.key.size();
|
||||||
|
return extra_ordering(lhs) < extra_ordering(rhs);
|
||||||
|
}
|
||||||
int i = std::min(lhs.key.size(), rhs.key.size());
|
int i = std::min(lhs.key.size(), rhs.key.size());
|
||||||
int c = i > 0 ? memcmp(lhs.key.data(), rhs.key.data(), i) : 0;
|
int c = memcmp(lhs.key.data(), rhs.key.data(), i);
|
||||||
if (c != 0)
|
if (c != 0)
|
||||||
return c < 0;
|
return c < 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
LLVM_VERSION="${LLVM_VERSION:-21}"
|
|
||||||
MSAN_PREFIX="${MSAN_PREFIX:-$PWD/msan}"
|
|
||||||
JOBS="${JOBS:-$(nproc)}"
|
|
||||||
|
|
||||||
cd /tmp
|
|
||||||
rm -rf libcxx-msan
|
|
||||||
mkdir libcxx-msan
|
|
||||||
cd libcxx-msan
|
|
||||||
|
|
||||||
git clone --depth=1 "https://github.com/llvm/llvm-project.git" -b "release/${LLVM_VERSION}.x"
|
|
||||||
|
|
||||||
cmake -S llvm-project/runtimes -B build_msan \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_CXX_COMPILER="clang++-${LLVM_VERSION}" \
|
|
||||||
-DCMAKE_C_COMPILER="clang-${LLVM_VERSION}" \
|
|
||||||
-DCMAKE_INSTALL_PREFIX="${MSAN_PREFIX}" \
|
|
||||||
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
|
|
||||||
-DLLVM_USE_SANITIZER=MemoryWithOrigins
|
|
||||||
|
|
||||||
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}" \
|
|
||||||
-DLLVM_ENABLE_RUNTIMES="libunwind"
|
|
||||||
|
|
||||||
cmake --build build_nomsan -j"${JOBS}"
|
|
||||||
cmake --install build_nomsan
|
|
||||||
|
|
||||||
VERSION="$(clang-"${LLVM_VERSION}" --version | head -n1 | sed -E 's/.*clang version ([0-9.]+).*/\1/')"
|
|
||||||
TARBALL="msan-toolchain-${VERSION}.tar.zst"
|
|
||||||
|
|
||||||
tar --zstd -cf "${TARBALL}" -C "${MSAN_PREFIX}" .
|
|
||||||
|
|
||||||
TARBALL_PATH="$(pwd)/${TARBALL}"
|
|
||||||
echo "Created: ${TARBALL_PATH}"
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user