forked from weaselab/conflict-set
Compare commits
38
Commits
main
...
79ae91e7d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79ae91e7d3 | ||
|
|
b19981ee3c | ||
|
|
12650e2132 | ||
|
|
6fed133212 | ||
|
|
9d15af772e | ||
|
|
22159c38c5 | ||
|
|
8afe5ec75b | ||
|
|
6eaa0799d5 | ||
|
|
cf783a0cdb | ||
|
|
16bd98ab12 | ||
|
|
374dcf13b7 | ||
|
|
d96d862cea | ||
|
|
2231c093df | ||
|
|
d479f16204 | ||
|
|
dee3a8f640 | ||
|
|
732d19efa1 | ||
|
|
9449190d02 | ||
|
|
4fcdc5d7e9 | ||
|
|
63f9a139da | ||
|
|
8f9f345c64 | ||
|
|
b9b2d69dd5 | ||
|
|
d3c8f4afc6 | ||
|
|
549724f09e | ||
|
|
52eb13cc0b | ||
|
|
f30887f280 | ||
|
|
ccd637deab | ||
|
|
971deb477c | ||
|
|
ff0722728a | ||
|
|
60881419b8 | ||
|
|
4515af3662 | ||
|
|
4dc5f7f75c | ||
|
|
7eaac2a184 | ||
|
|
e9c904a86b | ||
|
|
789ae8cbb9 | ||
|
|
d70e6a2455 | ||
|
|
8a5168f232 | ||
|
|
742d920aa1 | ||
|
|
6d8b939a81 |
+110
-70
@@ -2,62 +2,37 @@ name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest-amd64
|
||||
arch: amd64
|
||||
- runner: ubuntu-latest-arm64
|
||||
arch: arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to registry
|
||||
env:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
echo "$REGISTRY_TOKEN" \
|
||||
| docker login -u "$REGISTRY_USER" --password-stdin git.weaselab.dev
|
||||
|
||||
- name: Build and push image if changed
|
||||
run: |
|
||||
image=git.weaselab.dev/weaselab/conflict-set-ci
|
||||
hash="$(sha256sum Dockerfile .pre-commit-config.yaml | sha256sum | cut -c 1-16)"
|
||||
latest="$image:latest-${{ matrix.arch }}"
|
||||
current="$(docker buildx imagetools inspect "$latest" \
|
||||
--format '{{index .Image.Config.Labels "dev.weaselab.ci-hash"}}' 2> /dev/null || true)"
|
||||
if [ "$current" = "$hash" ]; then
|
||||
echo "$latest is up to date"
|
||||
else
|
||||
docker build --push --label "dev.weaselab.ci-hash=$hash" -t "$latest" .
|
||||
fi
|
||||
|
||||
pre-commit:
|
||||
needs: build-image
|
||||
runs-on: ubuntu-latest-amd64
|
||||
container:
|
||||
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
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: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y git nodejs pre-commit
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: Run pre-commit
|
||||
env:
|
||||
# use the hooks pre-installed in the image
|
||||
HOME: /tmp
|
||||
run: |
|
||||
git config --global --add safe.directory "$PWD"
|
||||
pre-commit run --all-files --show-diff-on-failure
|
||||
|
||||
test:
|
||||
needs: build-image
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -65,26 +40,48 @@ jobs:
|
||||
- name: 64-bit-versions
|
||||
cmake_args: -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1
|
||||
- name: debug
|
||||
cmake_args: -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake_args: -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan
|
||||
- name: simd-fallback
|
||||
cmake_args: -DUSE_SIMD_FALLBACK=ON
|
||||
- name: gcc
|
||||
cmake_args: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
|
||||
runs-on: ubuntu-latest-amd64
|
||||
container:
|
||||
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache
|
||||
key: ccache-${{ matrix.name }}-${{ gitea.sha }}
|
||||
restore-keys: |
|
||||
ccache-${{ matrix.name }}-
|
||||
path: /var/cache/apt/archives
|
||||
key: apt-amd64-${{ hashFiles('.gitea/workflows/ci.yml') }}
|
||||
|
||||
- name: Install common 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: |
|
||||
. /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 \
|
||||
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
|
||||
sudo update-alternatives --install /usr/bin/${tool} ${tool} /usr/bin/${tool}-21 100
|
||||
done
|
||||
|
||||
- name: Download MSan toolchain
|
||||
if: matrix.name == 'debug'
|
||||
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
|
||||
run: |
|
||||
@@ -120,24 +117,49 @@ jobs:
|
||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
release:
|
||||
needs: build-image
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest-amd64
|
||||
arch: amd64
|
||||
cmake_args: -DMSAN_TOOLCHAIN_PATH=/opt/msan
|
||||
- runner: ubuntu-latest-arm64
|
||||
arch: arm64
|
||||
cmake_args: ""
|
||||
runs-on: ${{ matrix.runner }}
|
||||
container:
|
||||
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-${{ matrix.arch }}
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: /var/cache/apt/archives
|
||||
key: apt-${{ matrix.arch }}-${{ 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 devscripts \
|
||||
libc6-dbg llvm-21 lld-21 mold ninja-build rpm \
|
||||
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
|
||||
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: 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
|
||||
with:
|
||||
path: .ccache
|
||||
@@ -145,11 +167,18 @@ jobs:
|
||||
restore-keys: |
|
||||
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
|
||||
run: |
|
||||
export CCACHE_DIR="$GITHUB_WORKSPACE/.ccache"
|
||||
rm -rf build
|
||||
cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS=-DNVALGRIND
|
||||
cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS=-DNVALGRIND ${{ matrix.cmake_args }}
|
||||
ninja -C build
|
||||
ccache -s
|
||||
|
||||
@@ -188,9 +217,6 @@ jobs:
|
||||
dest="minio/jenkins/conflict-set/${{ gitea.run_number }}/release-${{ matrix.arch }}/"
|
||||
zstd build/Testing/*/Test.xml
|
||||
mc cp build/Testing/*/Test.xml.zst "$dest"
|
||||
# This step runs even when a previous step failed, to upload test
|
||||
# results. The packages may never have been built though, so skip
|
||||
# them if they're missing.
|
||||
if compgen -G "build/*.deb" > /dev/null; then
|
||||
mc cp build/*.deb "$dest"
|
||||
fi
|
||||
@@ -209,16 +235,30 @@ jobs:
|
||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
coverage:
|
||||
needs: build-image
|
||||
runs-on: ubuntu-latest-amd64
|
||||
container:
|
||||
image: git.weaselab.dev/weaselab/conflict-set-ci:latest-amd64
|
||||
credentials:
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
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 gcovr \
|
||||
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" \
|
||||
-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
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache
|
||||
|
||||
@@ -37,15 +37,16 @@ ConflictSet::ReadRange singleton(Arena &arena, TrivialSpan key) {
|
||||
}
|
||||
|
||||
ConflictSet::ReadRange prefixRange(Arena &arena, TrivialSpan key) {
|
||||
int index;
|
||||
for (index = key.size() - 1; index >= 0; index--)
|
||||
if ((key[index]) != 255)
|
||||
int index = key.size() - 1;
|
||||
for (; index >= 0; index--)
|
||||
if (key[index] != 255)
|
||||
break;
|
||||
|
||||
// Must not be called with a string that consists only of zero or more '\xff'
|
||||
// bytes.
|
||||
// bytes, or with an empty string (which has no finite upper bound).
|
||||
if (index < 0) {
|
||||
assert(false);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
uint8_t *buf = new (arena) uint8_t[index + 1];
|
||||
|
||||
+70
-10
@@ -1,11 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(
|
||||
conflict-set
|
||||
VERSION 0.0.14
|
||||
VERSION 0.1.0
|
||||
DESCRIPTION
|
||||
"A data structure for optimistic concurrency control on ranges of bitwise-lexicographically-ordered keys."
|
||||
HOMEPAGE_URL "https://git.weaselab.dev/weaselab/conflict-set"
|
||||
LANGUAGES C CXX)
|
||||
LANGUAGES C CXX ASM)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/version.txt ${PROJECT_VERSION})
|
||||
@@ -103,6 +103,13 @@ option(USE_SIMD_FALLBACK
|
||||
|
||||
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
|
||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
||||
@@ -123,7 +130,15 @@ endif()
|
||||
|
||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
|
||||
|
||||
add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp)
|
||||
# Architecture-specific SIMD assembly. These functions operate on
|
||||
# potentially-indeterminate memory, which is UB in C++ but well-defined in
|
||||
# assembly.
|
||||
set(SIMD_ASM_FILES)
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 AND NOT USE_SIMD_FALLBACK)
|
||||
set(SIMD_ASM_FILES ${CMAKE_CURRENT_SOURCE_DIR}/simd_x86_64.S)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME}-object OBJECT ConflictSet.cpp ${SIMD_ASM_FILES})
|
||||
target_compile_options(${PROJECT_NAME}-object PRIVATE -fno-exceptions
|
||||
-fvisibility=hidden)
|
||||
target_include_directories(${PROJECT_NAME}-object
|
||||
@@ -139,6 +154,7 @@ add_custom_command(
|
||||
COMMAND_EXPAND_LISTS)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.o)
|
||||
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-object)
|
||||
set_target_properties(
|
||||
${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/radix_tree")
|
||||
@@ -155,6 +171,7 @@ if(HAS_VERSION_SCRIPT)
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME}-static STATIC ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.o)
|
||||
add_dependencies(${PROJECT_NAME}-static ${PROJECT_NAME}-object)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set_target_properties(${PROJECT_NAME}-static PROPERTIES LINKER_LANGUAGE CXX)
|
||||
else()
|
||||
@@ -224,7 +241,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
endif()
|
||||
|
||||
# ad hoc testing
|
||||
add_executable(conflict_set_main ConflictSet.cpp)
|
||||
add_executable(conflict_set_main ConflictSet.cpp ${SIMD_ASM_FILES})
|
||||
target_include_directories(conflict_set_main
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(conflict_set_main PRIVATE ENABLE_MAIN)
|
||||
@@ -240,7 +257,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
cmake_pop_check_state()
|
||||
|
||||
if(HAS_LIB_FUZZER)
|
||||
add_executable(conflict_set_fuzz_test ConflictSet.cpp)
|
||||
add_executable(conflict_set_fuzz_test ConflictSet.cpp ${SIMD_ASM_FILES})
|
||||
target_include_directories(conflict_set_fuzz_test
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(conflict_set_fuzz_test PRIVATE ENABLE_FUZZ)
|
||||
@@ -251,8 +268,9 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# whitebox tests
|
||||
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||
# whitebox tests asan+ubsan
|
||||
add_executable(fuzz_driver ConflictSet.cpp FuzzTestDriver.cpp
|
||||
${SIMD_ASM_FILES})
|
||||
target_compile_options(fuzz_driver PRIVATE ${TEST_FLAGS})
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
target_compile_options(fuzz_driver PRIVATE -fsanitize=address,undefined)
|
||||
@@ -266,9 +284,39 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
add_test(NAME conflict_set_fuzz_${hash} COMMAND fuzz_driver ${TEST})
|
||||
endforeach()
|
||||
|
||||
# whitebox tests msan
|
||||
if(MSAN_TOOLCHAIN_PATH)
|
||||
add_executable(fuzz_driver_msan ConflictSet.cpp FuzzTestDriver.cpp
|
||||
${SIMD_ASM_FILES})
|
||||
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
|
||||
if(NOT CMAKE_CROSSCOMPILING AND NOT DISABLE_TSAN)
|
||||
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp)
|
||||
add_executable(tsan_driver ConflictSet.cpp FuzzTestDriver.cpp
|
||||
${SIMD_ASM_FILES})
|
||||
target_compile_options(tsan_driver PRIVATE ${TEST_FLAGS} -fsanitize=thread)
|
||||
target_link_options(tsan_driver PRIVATE -fsanitize=thread)
|
||||
target_compile_definitions(tsan_driver PRIVATE ENABLE_FUZZ THREAD_TEST)
|
||||
@@ -383,13 +431,25 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
find_program(HARDENING_CHECK hardening-check)
|
||||
if(HARDENING_CHECK)
|
||||
# Not all versions of hardening-check support the same options, so query
|
||||
# the help output before using architecture-specific skips.
|
||||
execute_process(
|
||||
COMMAND ${HARDENING_CHECK} --help
|
||||
OUTPUT_VARIABLE hardening_check_help
|
||||
ERROR_VARIABLE hardening_check_help
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE)
|
||||
set(hardening_check_arch_flags "")
|
||||
# Control flow integrity (CET) is x86-only and branch protection (PAC/BTI)
|
||||
# is arm64-only, so ignore whichever doesn't apply.
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR
|
||||
STREQUAL arm64)
|
||||
set(hardening_check_arch_flags --nocfprotection)
|
||||
if(hardening_check_help MATCHES "nocfprotection")
|
||||
list(APPEND hardening_check_arch_flags --nocfprotection)
|
||||
endif()
|
||||
else()
|
||||
set(hardening_check_arch_flags --nobranchprotection)
|
||||
if(hardening_check_help MATCHES "nobranchprotection")
|
||||
list(APPEND hardening_check_arch_flags --nobranchprotection)
|
||||
endif()
|
||||
endif()
|
||||
add_test(
|
||||
NAME hardening_check
|
||||
|
||||
+42
-88
@@ -28,6 +28,7 @@ limitations under the License.
|
||||
#include "Internal.h"
|
||||
#include "LongestCommonPrefix.h"
|
||||
#include "Metrics.h"
|
||||
#include "simd.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
@@ -910,34 +911,11 @@ int getNodeIndexExists(Node3 *self, uint8_t index) {
|
||||
|
||||
int getNodeIndex(Node16 *self, uint8_t index) {
|
||||
|
||||
#ifdef HAS_AVX
|
||||
// Based on https://www.the-paper-trail.org/post/art-paper-notes/
|
||||
|
||||
// key_vec is 16 repeated copies of the searched-for byte, one for every
|
||||
// possible position in child_keys that needs to be searched.
|
||||
__m128i key_vec = _mm_set1_epi8(index);
|
||||
|
||||
// Compare all child_keys to 'index' in parallel. Don't worry if some of the
|
||||
// keys aren't valid, we'll mask the results to only consider the valid ones
|
||||
// below.
|
||||
__m128i indices;
|
||||
memcpy(&indices, self->index, Node16::kMaxNodes);
|
||||
__m128i results = _mm_cmpeq_epi8(key_vec, indices);
|
||||
|
||||
// Build a mask to select only the first node->num_children values from the
|
||||
// comparison (because the other values are meaningless)
|
||||
uint32_t mask = (1 << self->numChildren) - 1;
|
||||
|
||||
// Change the results of the comparison into a bitfield, masking off any
|
||||
// invalid comparisons.
|
||||
uint32_t bitfield = _mm_movemask_epi8(results) & mask;
|
||||
|
||||
// No match if there are no '1's in the bitfield.
|
||||
#if defined(__x86_64__) && !defined(USE_SIMD_FALLBACK)
|
||||
uint32_t bitfield =
|
||||
find_eq_16(self->index, index) & ((1 << self->numChildren) - 1);
|
||||
if (bitfield == 0)
|
||||
return -1;
|
||||
|
||||
// Find the index of the first '1' in the bitfield by counting the leading
|
||||
// zeros.
|
||||
return std::countr_zero(bitfield);
|
||||
#elif defined(HAS_ARM_NEON)
|
||||
// Based on
|
||||
@@ -970,13 +948,9 @@ int getNodeIndex(Node16 *self, uint8_t index) {
|
||||
|
||||
int getNodeIndexExists(Node16 *self, uint8_t index) {
|
||||
|
||||
#ifdef HAS_AVX
|
||||
__m128i key_vec = _mm_set1_epi8(index);
|
||||
__m128i indices;
|
||||
memcpy(&indices, self->index, Node16::kMaxNodes);
|
||||
__m128i results = _mm_cmpeq_epi8(key_vec, indices);
|
||||
uint32_t mask = (1 << self->numChildren) - 1;
|
||||
uint32_t bitfield = _mm_movemask_epi8(results) & mask;
|
||||
#if defined(__x86_64__) && !defined(USE_SIMD_FALLBACK)
|
||||
uint32_t bitfield =
|
||||
find_eq_16(self->index, index) & ((1 << self->numChildren) - 1);
|
||||
assume(bitfield != 0);
|
||||
return std::countr_zero(bitfield);
|
||||
#elif defined(HAS_ARM_NEON)
|
||||
@@ -1266,13 +1240,9 @@ TaggedNodePointer getChildGeq(Node16 *self, int child) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef HAS_AVX
|
||||
__m128i key_vec = _mm_set1_epi8(child);
|
||||
__m128i indices;
|
||||
memcpy(&indices, self->index, Node16::kMaxNodes);
|
||||
__m128i results = _mm_cmpeq_epi8(key_vec, _mm_min_epu8(key_vec, indices));
|
||||
int mask = (1 << self->numChildren) - 1;
|
||||
uint32_t bitfield = _mm_movemask_epi8(results) & mask;
|
||||
#if defined(__x86_64__) && !defined(USE_SIMD_FALLBACK)
|
||||
uint32_t bitfield =
|
||||
find_ge_16(self->index, child) & ((1 << self->numChildren) - 1);
|
||||
return bitfield == 0 ? nullptr : self->children[std::countr_zero(bitfield)];
|
||||
#elif defined(HAS_ARM_NEON)
|
||||
uint8x16_t indices;
|
||||
@@ -1332,8 +1302,8 @@ TaggedNodePointer getChildGeq(Node *self, int child) {
|
||||
|
||||
TaggedNodePointer getFirstChild(Node0 *) { return nullptr; }
|
||||
TaggedNodePointer getFirstChild(Node3 *self) {
|
||||
// Improves scan performance
|
||||
self->children[1].prefetch();
|
||||
// Don't prefetch since self->children[1] might be uninitialized and msan
|
||||
// doesn't like that
|
||||
return self->children[0];
|
||||
}
|
||||
TaggedNodePointer getFirstChild(Node16 *self) {
|
||||
@@ -2130,13 +2100,9 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
||||
|
||||
return !(compared & mask);
|
||||
|
||||
#elif defined(HAS_AVX)
|
||||
#elif defined(__x86_64__) && !defined(USE_SIMD_FALLBACK)
|
||||
|
||||
__m128i indices;
|
||||
memcpy(&indices, is, 16);
|
||||
indices = _mm_sub_epi8(indices, _mm_set1_epi8(begin));
|
||||
uint32_t mask = ~_mm_movemask_epi8(_mm_cmpeq_epi8(
|
||||
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
|
||||
uint32_t mask = mask_in_range_16(is, begin, end);
|
||||
|
||||
uint32_t compared = 0;
|
||||
if constexpr (kAVX512) {
|
||||
@@ -2153,12 +2119,14 @@ bool scan16(const InternalVersionT *vs, const uint8_t *is, int begin, int end,
|
||||
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
|
||||
|
||||
uint32_t compared = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
compared |= (vs[i] > readVersion) << i;
|
||||
}
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
mask |= inBounds(is[i]) << i;
|
||||
if (vs[i] > readVersion) {
|
||||
compared |= 1u << i;
|
||||
if (inBounds(is[i])) {
|
||||
mask |= 1u << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !(compared & mask);
|
||||
|
||||
@@ -2250,17 +2218,9 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
|
||||
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
|
||||
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
||||
for (int i = 0; i < self->numChildren; ++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) {
|
||||
return true;
|
||||
}
|
||||
@@ -2268,17 +2228,11 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
|
||||
const bool firstRangeOk =
|
||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||
uint32_t compared = 0;
|
||||
for (int i = 0; i < Node3::kMaxNodes; ++i) {
|
||||
for (int i = 0; i < self->numChildren; ++i) {
|
||||
compared |= (self->childMaxVersion[i] > readVersion) << i;
|
||||
}
|
||||
|
||||
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;
|
||||
return !(compared & mask) && firstRangeOk;
|
||||
}
|
||||
|
||||
template <bool kAVX512>
|
||||
@@ -2344,15 +2298,10 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
|
||||
|
||||
return !(compared & mask) && firstRangeOk;
|
||||
|
||||
#elif defined(HAS_AVX)
|
||||
#elif defined(__x86_64__) && !defined(USE_SIMD_FALLBACK)
|
||||
|
||||
__m128i indices;
|
||||
memcpy(&indices, self->index, 16);
|
||||
indices = _mm_sub_epi8(indices, _mm_set1_epi8(begin));
|
||||
uint32_t mask =
|
||||
0xffff & ~_mm_movemask_epi8(_mm_cmpeq_epi8(
|
||||
indices, _mm_max_epu8(indices, _mm_set1_epi8(end - begin))));
|
||||
mask &= (1 << self->numChildren) - 1;
|
||||
uint32_t mask = mask_in_range_16(self->index, begin, end) &
|
||||
((1 << self->numChildren) - 1);
|
||||
if (!mask) {
|
||||
return true;
|
||||
}
|
||||
@@ -2375,10 +2324,9 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
|
||||
auto inBounds = [&](unsigned c) { return c - shiftAmount < shiftUpperBound; };
|
||||
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int i = 0; i < self->numChildren; ++i) {
|
||||
mask |= inBounds(self->index[i]) << i;
|
||||
}
|
||||
mask &= (1 << self->numChildren) - 1;
|
||||
if (!mask) {
|
||||
return true;
|
||||
}
|
||||
@@ -2386,7 +2334,7 @@ bool checkMaxBetweenExclusiveImpl(Node16 *n, int begin, int end,
|
||||
const bool firstRangeOk =
|
||||
!child->entryPresent || child->entry.rangeVersion <= readVersion;
|
||||
uint32_t compared = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int i = 0; i < self->numChildren; ++i) {
|
||||
compared |= (self->childMaxVersion[i] > readVersion) << i;
|
||||
}
|
||||
return !(compared & mask) && firstRangeOk;
|
||||
@@ -3864,17 +3812,17 @@ PRESERVE_NONE void right_side_iter(Job *job, Context *context) {
|
||||
void Job::init(const ConflictSet::ReadRange *read, ConflictSet::Result *result,
|
||||
Node *root, int64_t oldestVersionFullPrecision) {
|
||||
auto begin = TrivialSpan(read->begin.p, read->begin.len);
|
||||
auto end = TrivialSpan(read->end.p, read->end.len);
|
||||
if (read->readVersion < oldestVersionFullPrecision) [[unlikely]] {
|
||||
*result = ConflictSet::TooOld;
|
||||
continuation = complete;
|
||||
} else if (end.size() == 0) {
|
||||
} else if (read->end.len == 0) {
|
||||
this->begin = begin;
|
||||
this->n = root;
|
||||
this->readVersion = InternalVersionT(read->readVersion);
|
||||
this->result = result;
|
||||
continuation = check::point_read_state_machine::begin;
|
||||
} else {
|
||||
auto end = TrivialSpan(read->end.p, read->end.len);
|
||||
this->begin = begin;
|
||||
this->end = end;
|
||||
this->n = root;
|
||||
@@ -5046,8 +4994,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto &w = writes[i];
|
||||
auto begin = TrivialSpan(w.begin.p, w.begin.len);
|
||||
auto end = TrivialSpan(w.end.p, w.end.len);
|
||||
if (w.end.len > 0) {
|
||||
auto end = TrivialSpan(w.end.p, w.end.len);
|
||||
addWriteRange(rootParent->children[0], begin, end,
|
||||
InternalVersionT(writeVersion), &writeContext);
|
||||
} else {
|
||||
@@ -5589,7 +5537,12 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
|
||||
: impl(std::exchange(other.impl, nullptr)) {}
|
||||
|
||||
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (impl) {
|
||||
internal_destroy(impl);
|
||||
}
|
||||
impl = std::exchange(other.impl, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -5674,13 +5627,13 @@ std::string getPartialKeyPrintable(Node *n) {
|
||||
}
|
||||
|
||||
std::string strinc(std::string_view str, bool &ok) {
|
||||
int index;
|
||||
for (index = str.size() - 1; index >= 0; index--)
|
||||
if ((uint8_t &)(str[index]) != 255)
|
||||
int index = static_cast<int>(str.size()) - 1;
|
||||
for (; index >= 0; index--)
|
||||
if (static_cast<uint8_t>(str[index]) != 255)
|
||||
break;
|
||||
|
||||
// Must not be called with a string that consists only of zero or more
|
||||
// '\xff' bytes.
|
||||
// '\xff' bytes, and the empty string has no successor.
|
||||
if (index < 0) {
|
||||
ok = false;
|
||||
return {};
|
||||
@@ -5688,7 +5641,8 @@ std::string strinc(std::string_view str, bool &ok) {
|
||||
ok = true;
|
||||
|
||||
auto r = std::string(str.substr(0, index + 1));
|
||||
((uint8_t &)r[r.size() - 1])++;
|
||||
auto &last = r[r.size() - 1];
|
||||
last = static_cast<char>(static_cast<uint8_t>(last) + 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
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
|
||||
+14
-3
@@ -96,7 +96,9 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
||||
return impl->setOldestVersion(oldestVersion);
|
||||
}
|
||||
|
||||
int64_t ConflictSet::getBytes() const { return -1; }
|
||||
// The hash_table implementation does not track memory usage, so return 0 to
|
||||
// satisfy the API contract that getBytes() returns a non-negative value.
|
||||
int64_t ConflictSet::getBytes() const { return 0; }
|
||||
|
||||
void ConflictSet::getMetricsV1(MetricsV1 **metrics, int *count) const {
|
||||
*metrics = nullptr;
|
||||
@@ -119,7 +121,13 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
|
||||
: impl(std::exchange(other.impl, nullptr)) {}
|
||||
|
||||
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (impl) {
|
||||
impl->~Impl();
|
||||
safe_free(impl, sizeof(Impl));
|
||||
}
|
||||
impl = std::exchange(other.impl, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -155,7 +163,10 @@ __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||
}
|
||||
__attribute__((__visibility__("default"))) int64_t
|
||||
ConflictSet_getBytes(void *cs) {
|
||||
using Impl = ConflictSet::Impl;
|
||||
return -1;
|
||||
(void)cs;
|
||||
// The hash_table implementation does not track memory usage, so return 0 to
|
||||
// satisfy the API contract that ConflictSet_getBytes returns a non-negative
|
||||
// value.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-3
|
||||
```
|
||||
$ clang++ --version
|
||||
|
||||
Ubuntu clang version 20.0.0 (++20241120082228+86734c857724-1~exp1~20241120202359.554)
|
||||
Ubuntu clang version 21.1.8 (6ubuntu1)
|
||||
Target: x86_64-pc-linux-gnu
|
||||
Thread model: posix
|
||||
InstalledDir: /usr/lib/llvm-20/bin
|
||||
InstalledDir: /usr/lib/llvm-21/bin
|
||||
```
|
||||
|
||||
# Microbenchmark
|
||||
@@ -19,30 +19,30 @@ InstalledDir: /usr/lib/llvm-20/bin
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 161.29 | 6,200,056.17 | 0.1% | 3,014.03 | 831.04 | 3.627 | 504.59 | 0.0% | 1.93 | `point reads`
|
||||
| 158.32 | 6,316,160.64 | 0.1% | 2,954.16 | 815.80 | 3.621 | 490.17 | 0.0% | 1.89 | `prefix 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`
|
||||
| 442.11 | 2,261,878.94 | 0.0% | 4,450.57 | 2,314.25 | 1.923 | 707.92 | 2.1% | 5.28 | `point 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`
|
||||
| 290.96 | 3,436,936.78 | 0.0% | 2,315.38 | 1,528.68 | 1.515 | 396.69 | 3.3% | 3.49 | `range 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`
|
||||
| 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`
|
||||
| 45.50 | 21,978,369.95 | 1.1% | 902.00 | 232.36 | 3.882 | 132.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 164.29 | 6,086,873.38 | 0.0% | 3,107.03 | 604.19 | 5.142 | 558.59 | 0.0% | 1.96 | `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`
|
||||
| 239.55 | 4,174,539.38 | 0.1% | 3,722.71 | 880.68 | 4.227 | 692.00 | 0.0% | 2.86 | `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`
|
||||
| 345.32 | 2,895,878.47 | 0.1% | 4,484.57 | 1,270.31 | 3.530 | 705.00 | 1.8% | 4.12 | `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`
|
||||
| 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`
|
||||
| 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`
|
||||
| 37.80 | 26,454,311.17 | 0.4% | 701.00 | 139.14 | 5.038 | 102.00 | 0.0% | 0.01 | `create and destroy`
|
||||
|
||||
## Radix tree (this implementation)
|
||||
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 12.36 | 80,885,626.43 | 0.2% | 243.56 | 63.62 | 3.828 | 31.07 | 0.6% | 0.15 | `point reads`
|
||||
| 14.18 | 70,502,196.81 | 0.1% | 297.72 | 73.13 | 4.071 | 40.31 | 0.5% | 0.17 | `prefix reads`
|
||||
| 33.44 | 29,901,623.04 | 0.1% | 767.90 | 172.42 | 4.454 | 101.32 | 0.2% | 0.40 | `range reads`
|
||||
| 19.48 | 51,342,564.70 | 0.3% | 374.45 | 100.43 | 3.728 | 48.92 | 0.5% | 0.23 | `point writes`
|
||||
| 37.46 | 26,694,471.44 | 0.1% | 672.00 | 193.14 | 3.479 | 101.28 | 0.3% | 0.45 | `prefix writes`
|
||||
| 38.78 | 25,784,784.34 | 0.0% | 738.26 | 199.93 | 3.693 | 111.59 | 0.1% | 0.47 | `range 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`
|
||||
| 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`
|
||||
| 95.66 | 10,453,798.72 | 0.5% | 1,986.00 | 495.04 | 4.012 | 315.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 12.89 | 77,565,115.56 | 0.1% | 244.55 | 47.43 | 5.155 | 34.21 | 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`
|
||||
| 36.29 | 27,559,358.29 | 0.1% | 783.16 | 133.44 | 5.869 | 109.52 | 0.2% | 0.43 | `range reads`
|
||||
| 20.53 | 48,719,405.55 | 0.1% | 381.81 | 75.51 | 5.057 | 51.04 | 0.5% | 0.25 | `point writes`
|
||||
| 39.37 | 25,402,042.40 | 0.1% | 685.00 | 144.83 | 4.730 | 106.72 | 0.3% | 0.47 | `prefix writes`
|
||||
| 43.78 | 22,843,841.63 | 0.1% | 800.40 | 161.06 | 4.970 | 127.36 | 0.1% | 0.53 | `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`
|
||||
| 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`
|
||||
| 99.99 | 10,000,718.79 | 0.4% | 1,775.00 | 367.93 | 4.824 | 288.00 | 0.0% | 0.01 | `create and destroy`
|
||||
|
||||
# "Real data" test
|
||||
|
||||
@@ -51,13 +51,13 @@ Point queries only. Gc ratio is the ratio of time spent doing garbage collection
|
||||
## skip list
|
||||
|
||||
```
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
## radix tree
|
||||
|
||||
```
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
## hash table
|
||||
@@ -65,6 +65,6 @@ Check: 0.957735 seconds, 1760.6 MB/s, Add: 1.19942 seconds, 479.678 MB/s, Gc rat
|
||||
(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.804598 seconds, 2095.69 MB/s, Add: 0.671221 seconds, 857.147 MB/s, Gc ratio: 35.0034%, Peak idle memory: 0
|
||||
Check: 0.854254 seconds, 1908.74 MB/s, Add: 0.632626 seconds, 886.232 MB/s, Gc ratio: 41.0827%, Peak idle memory: 0
|
||||
```
|
||||
|
||||
|
||||
+7
-6
@@ -1,5 +1,6 @@
|
||||
#include <ConflictSet.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
@@ -77,10 +78,10 @@ int main(int argc, const char **argv) {
|
||||
begin = end + 1;
|
||||
end = (uint8_t *)memchr(begin, '\n', size);
|
||||
|
||||
if (line.size() > 0 && line[0] == 'P') {
|
||||
write = line.subspan(2, line.size());
|
||||
} else if (line.size() > 0 && line[0] == 'L') {
|
||||
reads.push_back(line.subspan(2, line.size()));
|
||||
if (line.size() >= 2 && line[0] == 'P') {
|
||||
write = line.subspan(2, line.size() - 2);
|
||||
} else if (line.size() >= 2 && line[0] == 'L') {
|
||||
reads.push_back(line.subspan(2, line.size() - 2));
|
||||
} else if (line.empty()) {
|
||||
{
|
||||
readRanges.resize(reads.size());
|
||||
@@ -90,7 +91,7 @@ int main(int argc, const char **argv) {
|
||||
iter->begin.len = read.size();
|
||||
checkBytes += read.size();
|
||||
iter->end.len = 0;
|
||||
iter->readVersion = version - 100;
|
||||
iter->readVersion = std::max<int64_t>(0, version - 100);
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
@@ -121,7 +122,7 @@ int main(int argc, const char **argv) {
|
||||
}
|
||||
|
||||
timer = now();
|
||||
cs.setOldestVersion(version - 10000);
|
||||
cs.setOldestVersion(std::max<int64_t>(0, version - 10000));
|
||||
gcTime += now() - timer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <thread>
|
||||
|
||||
+13
-3
@@ -38,6 +38,9 @@ std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
||||
|
||||
std::span<const uint8_t> copyToArena(Arena &arena,
|
||||
std::span<const uint8_t> key) {
|
||||
if (key.size() == 0) {
|
||||
return std::span<const uint8_t>();
|
||||
}
|
||||
auto result = std::span<uint8_t>(new (arena) uint8_t[key.size()], key.size());
|
||||
memcpy(result.data(), key.data(), key.size());
|
||||
return result;
|
||||
@@ -97,7 +100,7 @@ force_inline bool getCharacter(const KeyInfo &ki, int character,
|
||||
|
||||
bool operator<(const KeyInfo &lhs, const KeyInfo &rhs) {
|
||||
int i = std::min(lhs.key.size(), rhs.key.size());
|
||||
int c = memcmp(lhs.key.data(), rhs.key.data(), i);
|
||||
int c = i > 0 ? memcmp(lhs.key.data(), rhs.key.data(), i) : 0;
|
||||
if (c != 0)
|
||||
return c < 0;
|
||||
|
||||
@@ -287,7 +290,8 @@ private:
|
||||
|
||||
static force_inline bool less(const uint8_t *a, int aLen, const uint8_t *b,
|
||||
int bLen) {
|
||||
int c = memcmp(a, b, std::min(aLen, bLen));
|
||||
int n = std::min(aLen, bLen);
|
||||
int c = n > 0 ? memcmp(a, b, n) : 0;
|
||||
if (c < 0)
|
||||
return true;
|
||||
if (c > 0)
|
||||
@@ -754,7 +758,8 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
|
||||
void addWrites(const ConflictSet::WriteRange *writes, int count,
|
||||
int64_t writeVersion) {
|
||||
auto points = std::vector<KeyInfo>(count * 2);
|
||||
auto points = std::vector<KeyInfo>();
|
||||
points.reserve(count * 2);
|
||||
Arena arena;
|
||||
|
||||
for (int r = 0; r < count; r++) {
|
||||
@@ -981,7 +986,12 @@ ConflictSet::ConflictSet(ConflictSet &&other) noexcept
|
||||
: impl(std::exchange(other.impl, nullptr)) {}
|
||||
|
||||
ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (impl) {
|
||||
internal_destroy(impl);
|
||||
}
|
||||
impl = std::exchange(other.impl, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/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}"
|
||||
+27
-9
@@ -27,23 +27,37 @@ class Result(enum.Enum):
|
||||
TOO_OLD = 2
|
||||
|
||||
|
||||
def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange:
|
||||
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||
def _make_key(buf: bytes) -> tuple[_Key, bytearray]:
|
||||
"""Create a _Key and a backing bytearray that must be kept alive."""
|
||||
backing = bytearray(buf)
|
||||
array = (ctypes.c_ubyte * len(backing)).from_buffer(backing)
|
||||
return _Key(array, len(array)), backing
|
||||
|
||||
|
||||
def write(begin: bytes, end: Optional[bytes] = None) -> WriteRange:
|
||||
begin_key, begin_buf = _make_key(begin)
|
||||
if end is None:
|
||||
e = (ctypes.c_ubyte * 0)()
|
||||
end_key = _Key((ctypes.c_ubyte * 0)(), 0)
|
||||
end_buf = None
|
||||
else:
|
||||
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||
return WriteRange(_Key(b, len(b)), _Key(e, len(e)))
|
||||
end_key, end_buf = _make_key(end)
|
||||
result = WriteRange(begin_key, end_key)
|
||||
result._begin_buf = begin_buf
|
||||
result._end_buf = end_buf
|
||||
return result
|
||||
|
||||
|
||||
def read(version: int, begin: bytes, end: Optional[bytes] = None) -> ReadRange:
|
||||
b = (ctypes.c_ubyte * len(begin)).from_buffer(bytearray(begin))
|
||||
begin_key, begin_buf = _make_key(begin)
|
||||
if end is None:
|
||||
e = (ctypes.c_ubyte * 0)()
|
||||
end_key = _Key((ctypes.c_ubyte * 0)(), 0)
|
||||
end_buf = None
|
||||
else:
|
||||
e = (ctypes.c_ubyte * len(end)).from_buffer(bytearray(end))
|
||||
return ReadRange(_Key(b, len(b)), _Key(e, len(e)), version)
|
||||
end_key, end_buf = _make_key(end)
|
||||
result = ReadRange(begin_key, end_key, version)
|
||||
result._begin_buf = begin_buf
|
||||
result._end_buf = end_buf
|
||||
return result
|
||||
|
||||
|
||||
class ConflictSet:
|
||||
@@ -88,6 +102,7 @@ class ConflictSet:
|
||||
ctypes.POINTER(ctypes.c_int),
|
||||
ctypes.c_int,
|
||||
)
|
||||
self._lib.ConflictSet_check.restype = None
|
||||
|
||||
self._lib.ConflictSet_addWrites.argtypes = (
|
||||
ctypes.c_void_p,
|
||||
@@ -95,13 +110,16 @@ class ConflictSet:
|
||||
ctypes.c_int,
|
||||
ctypes.c_int64,
|
||||
)
|
||||
self._lib.ConflictSet_addWrites.restype = None
|
||||
|
||||
self._lib.ConflictSet_setOldestVersion.argtypes = (
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_int64,
|
||||
)
|
||||
self._lib.ConflictSet_setOldestVersion.restype = None
|
||||
|
||||
self._lib.ConflictSet_destroy.argtypes = (ctypes.c_void_p,)
|
||||
self._lib.ConflictSet_destroy.restype = None
|
||||
|
||||
self._lib.ConflictSet_getBytes.argtypes = (ctypes.c_void_p,)
|
||||
self._lib.ConflictSet_getBytes.restype = ctypes.c_int64
|
||||
|
||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user