Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55e23bafba | |||
| 235938b5aa | |||
| 8b71852495 | |||
| e5e6402b43 | |||
| 61f5612e1f | |||
| 406b27936c | |||
| 7972ed919b | |||
| 0619b6325c | |||
| 7b14c8f9d5 | |||
| 22632fc9f2 | |||
| 1fccb65bd8 | |||
| ee5972f946 | |||
| 7166811387 | |||
| d68f208d9b | |||
| 81323972aa | |||
| 8694ba8b6a | |||
| 0cea5565b5 | |||
| 972f16ed8f | |||
| 2412684316 | |||
| 8190d2f24e | |||
| 8251631087 | |||
| 90fb2a9542 | |||
| 7c01f8ba0f | |||
| 0df2db7f8a | |||
| 5e975f3b2b | |||
| bcbae026b2 | |||
| e125b599b5 | |||
| 3f4d3b685a | |||
| 4198b8b090 | |||
| 8757d2387c | |||
| 4a22b95d53 | |||
| 03d6c7e471 | |||
| ceecc62a63 | |||
| 80f0697e79 | |||
| ce23d3995c | |||
| 6f899e063b | |||
| e5b9c03e77 | |||
| a158d375f5 | |||
| ee5a84cd7b | |||
| 33f14e3d9b | |||
| 77262ee2d3 | |||
| 9945998e05 | |||
| 2777e016ff | |||
| 661ffcd843 | |||
| 3a34d3cecb | |||
| 189c73e3bd | |||
| 35987030fc | |||
| 0621741ec3 | |||
| f5ec9f726a | |||
| 552fc11c5d | |||
| 71ace9cc55 | |||
| bcf459304f | |||
| f403c78410 | |||
| 08958d4109 | |||
| dcc5275ec9 | |||
| c5ef843f9e | |||
| b78e817e24 | |||
| 9c82f17e20 | |||
| 665a9313a4 | |||
| 6e66202d5e | |||
| a92271a205 | |||
| 0dbfb4deae | |||
| 6e229b6b36 | |||
| 2200de11c8 | |||
| b37feb58dd | |||
| 94a4802824 | |||
| 707dbdb391 | |||
| bdd343bb57 | |||
| 7b31bd5efe | |||
| e255e1a926 | |||
| f85b92f8db | |||
| 3c44614311 | |||
| 9c1ac3702e | |||
| 224d21648a | |||
| 33f9c89328 | |||
| 12c2d5eb95 | |||
| db357e747d | |||
| 4494359ca2 | |||
| f079d84bda | |||
| 724ec09248 | |||
| 4eaad39294 | |||
| 891100e649 | |||
| 22e55309be | |||
| d6269c5b7c | |||
| faacdff2d9 | |||
| 821179b8de | |||
| 681a961289 | |||
| c73a3da14c | |||
| 5153d25cce | |||
| d2ec4e7fae | |||
| c7e2358746 | |||
| ec1c1cf43f | |||
| eaad0c69a7 | |||
| 309e6ab816 | |||
| 12b82c1be5 | |||
| 0cce9df8a8 | |||
| 0df09743da | |||
| c4b0aa1085 | |||
| 051bfb05fe | |||
| 7e1bcbf9be | |||
| 4e685bbc3b | |||
| b6bfc6f48d | |||
| 3b858551f3 | |||
| 2c1c26bc88 | |||
| 958ee15cfc | |||
| 9015b555de | |||
| 7aac73ee80 | |||
| c06afeb81e | |||
| b015711b7c | |||
| f27ca6d6af | |||
| c0bb175b7e | |||
| 6a6fe5738a | |||
| dc16eccf06 | |||
| 3f15db7e82 | |||
| e8a8b5aef1 | |||
| b8fefff3ba | |||
| 2706b2f65e | |||
| f1292efe41 | |||
| a2d3d269ec | |||
| 8ff7a112b7 | |||
| cf25b8626c | |||
| e025f934d8 | |||
| e5452c0f7d | |||
| 66fc526a55 | |||
| 21f08b9f88 | |||
| 10c2f06199 | |||
| 5cf04e9718 | |||
| 707b220fbc | |||
| fd39065498 | |||
| b963d481c9 | |||
| e7ed47e288 | |||
| 04f138109b | |||
| a0d07dd40c | |||
| 7fb408b466 | |||
| 6d265acfc7 |
@@ -17,26 +17,26 @@ constexpr int kPrefixLen = 0;
|
||||
|
||||
constexpr int kMvccWindow = 100000;
|
||||
|
||||
std::span<const uint8_t> makeKey(Arena &arena, int index) {
|
||||
TrivialSpan makeKey(Arena &arena, int index) {
|
||||
|
||||
auto result =
|
||||
std::span<uint8_t>{new (arena) uint8_t[4 + kPrefixLen], 4 + kPrefixLen};
|
||||
uint8_t *buf = new (arena) uint8_t[4 + kPrefixLen];
|
||||
auto result = TrivialSpan{buf, 4 + kPrefixLen};
|
||||
index = __builtin_bswap32(index);
|
||||
memset(result.data(), 0, kPrefixLen);
|
||||
memcpy(result.data() + kPrefixLen, &index, 4);
|
||||
memset(buf, 0, kPrefixLen);
|
||||
memcpy(buf, &index, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConflictSet::ReadRange singleton(Arena &arena, std::span<const uint8_t> key) {
|
||||
auto r =
|
||||
std::span<uint8_t>(new (arena) uint8_t[key.size() + 1], key.size() + 1);
|
||||
memcpy(r.data(), key.data(), key.size());
|
||||
r[key.size()] = 0;
|
||||
ConflictSet::ReadRange singleton(Arena &arena, TrivialSpan key) {
|
||||
uint8_t *buf = new (arena) uint8_t[key.size() + 1];
|
||||
auto r = TrivialSpan(buf, key.size() + 1);
|
||||
memcpy(buf, key.data(), key.size());
|
||||
buf[key.size()] = 0;
|
||||
return {{key.data(), int(key.size())}, {r.data(), int(r.size())}, 0};
|
||||
}
|
||||
|
||||
ConflictSet::ReadRange prefixRange(Arena &arena, std::span<const uint8_t> key) {
|
||||
ConflictSet::ReadRange prefixRange(Arena &arena, TrivialSpan key) {
|
||||
int index;
|
||||
for (index = key.size() - 1; index >= 0; index--)
|
||||
if ((key[index]) != 255)
|
||||
@@ -48,14 +48,16 @@ ConflictSet::ReadRange prefixRange(Arena &arena, std::span<const uint8_t> key) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
auto r = std::span<uint8_t>(new (arena) uint8_t[index + 1], index + 1);
|
||||
memcpy(r.data(), key.data(), index + 1);
|
||||
r[r.size() - 1]++;
|
||||
uint8_t *buf = new (arena) uint8_t[index + 1];
|
||||
auto r = TrivialSpan(buf, index + 1);
|
||||
memcpy(buf, key.data(), index + 1);
|
||||
buf[r.size() - 1]++;
|
||||
return {{key.data(), int(key.size())}, {r.data(), int(r.size())}, 0};
|
||||
}
|
||||
|
||||
void benchConflictSet() {
|
||||
ankerl::nanobench::Bench bench;
|
||||
bench.minEpochIterations(10000);
|
||||
ConflictSet cs{0};
|
||||
|
||||
bench.batch(kOpsPerTx);
|
||||
@@ -81,14 +83,7 @@ void benchConflictSet() {
|
||||
++version;
|
||||
}
|
||||
|
||||
// I don't know why std::less didn't work /shrug
|
||||
struct Less {
|
||||
bool operator()(const std::span<const uint8_t> &lhs,
|
||||
const std::span<const uint8_t> &rhs) const {
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
auto points = set<std::span<const uint8_t>, Less>(arena);
|
||||
auto points = set<TrivialSpan, std::less<>>(arena);
|
||||
|
||||
while (points.size() < kOpsPerTx * 2 + 1) {
|
||||
// TODO don't use rand?
|
||||
|
||||
+40
-16
@@ -31,11 +31,20 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||
-Werror=switch-enum -fPIC)
|
||||
add_compile_options(
|
||||
# -Werror=switch-enum
|
||||
-Wswitch-enum -Wunused-variable -fPIC -fdata-sections -ffunction-sections
|
||||
-fno-jump-tables # https://github.com/llvm/llvm-project/issues/54247
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_link_options("-Wno-unused-command-line-argument")
|
||||
find_program(LLVM_OBJCOPY llvm-objcopy)
|
||||
if(LLVM_OBJCOPY)
|
||||
set(CMAKE_OBJCOPY
|
||||
${LLVM_OBJCOPY}
|
||||
CACHE FILEPATH "path to objcopy binary" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
@@ -56,6 +65,22 @@ if(HAS_FULL_RELRO)
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL
|
||||
arm64)
|
||||
add_compile_options(-mbranch-protection=standard)
|
||||
else()
|
||||
add_compile_options(-fcf-protection)
|
||||
set(rewrite_endbr_flags "-fuse-ld=mold;LINKER:-z,rewrite-endbr")
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${rewrite_endbr_flags})
|
||||
check_cxx_source_compiles("int main(){}" HAS_REWRITE_ENDBR FAIL_REGEX
|
||||
"warning:")
|
||||
if(HAS_REWRITE_ENDBR)
|
||||
add_link_options(${rewrite_endbr_flags})
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
|
||||
set(version_script_flags
|
||||
LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.map)
|
||||
cmake_push_check_state()
|
||||
@@ -73,27 +98,17 @@ option(DISABLE_TSAN "Disable TSAN" OFF)
|
||||
# https://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq
|
||||
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/valgrind)
|
||||
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||
|
||||
if(APPLE)
|
||||
add_link_options(-Wl,-dead_strip)
|
||||
else()
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
endif()
|
||||
|
||||
if(NOT USE_SIMD_FALLBACK)
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||
if(HAS_AVX)
|
||||
if(USE_SIMD_FALLBACK)
|
||||
add_compile_definitions(USE_SIMD_FALLBACK)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
|
||||
add_compile_options(-mavx)
|
||||
add_compile_definitions(HAS_AVX)
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
|
||||
check_include_file_cxx("arm_neon.h" HAS_ARM_NEON)
|
||||
if(HAS_ARM_NEON)
|
||||
add_compile_definitions(HAS_ARM_NEON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -356,6 +371,15 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
||||
${symbol_imports})
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
find_program(HARDENING_CHECK hardening-check)
|
||||
if(HARDENING_CHECK)
|
||||
add_test(NAME hardening_check
|
||||
COMMAND ${HARDENING_CHECK} $<TARGET_FILE:${PROJECT_NAME}>
|
||||
--nofortify --nostackprotector)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# bench
|
||||
add_executable(conflict_set_bench Bench.cpp)
|
||||
target_link_libraries(conflict_set_bench PRIVATE ${PROJECT_NAME} nanobench)
|
||||
|
||||
+2495
-1267
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -13,12 +13,14 @@ RUN TZ=America/Los_Angeles DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
ccache \
|
||||
cmake \
|
||||
curl \
|
||||
devscripts \
|
||||
g++-aarch64-linux-gnu \
|
||||
gcovr \
|
||||
git \
|
||||
gnupg \
|
||||
libc6-dbg \
|
||||
lsb-release \
|
||||
mold \
|
||||
ninja-build \
|
||||
pre-commit \
|
||||
python3-requests \
|
||||
@@ -45,7 +47,7 @@ RUN curl -Ls https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2 -o valg
|
||||
# Recent clang
|
||||
RUN wget https://apt.llvm.org/llvm.sh && chmod +x ./llvm.sh && ./llvm.sh 20
|
||||
|
||||
RUN apt-get install clang
|
||||
RUN apt-get -y install clang llvm
|
||||
|
||||
# Set after building valgrind, which doesn't build with clang for some reason
|
||||
ENV CC=clang
|
||||
|
||||
+48
-28
@@ -18,7 +18,6 @@ using namespace weaselab;
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <callgrind.h>
|
||||
@@ -26,9 +25,38 @@ using namespace weaselab;
|
||||
#define DEBUG_VERBOSE 0
|
||||
#define SHOW_MEMORY 0
|
||||
|
||||
[[nodiscard]] inline auto
|
||||
operator<=>(const std::span<const uint8_t> &lhs,
|
||||
const std::span<const uint8_t> &rhs) noexcept {
|
||||
// std::span is not trivially constructible. We want a span that leaves its
|
||||
// members uninitialized for performance reasons.
|
||||
struct TrivialSpan {
|
||||
TrivialSpan() = default;
|
||||
TrivialSpan(const uint8_t *begin, int len) : begin(begin), len(len) {}
|
||||
|
||||
uint8_t back() const {
|
||||
assert(len > 0);
|
||||
return begin[len - 1];
|
||||
}
|
||||
uint8_t front() const {
|
||||
assert(len > 0);
|
||||
return begin[0];
|
||||
}
|
||||
uint8_t operator[](int i) const {
|
||||
assert(0 <= i);
|
||||
assert(i < len);
|
||||
return begin[i];
|
||||
}
|
||||
int size() const { return len; }
|
||||
TrivialSpan subspan(int offset, int len) { return {begin + offset, len}; }
|
||||
const uint8_t *data() const { return begin; }
|
||||
|
||||
private:
|
||||
const uint8_t *begin;
|
||||
int len;
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial_v<TrivialSpan>);
|
||||
|
||||
[[nodiscard]] inline auto operator<=>(const TrivialSpan &lhs,
|
||||
const TrivialSpan &rhs) noexcept {
|
||||
int cl = std::min<int>(lhs.size(), rhs.size());
|
||||
if (cl > 0) {
|
||||
if (auto c = memcmp(lhs.data(), rhs.data(), cl) <=> 0; c != 0) {
|
||||
@@ -38,7 +66,7 @@ operator<=>(const std::span<const uint8_t> &lhs,
|
||||
return lhs.size() <=> rhs.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto operator<=>(const std::span<const uint8_t> &lhs,
|
||||
[[nodiscard]] inline auto operator<=>(const TrivialSpan &lhs,
|
||||
const ConflictSet::Key &rhs) noexcept {
|
||||
int cl = std::min<int>(lhs.size(), rhs.len);
|
||||
if (cl > 0) {
|
||||
@@ -46,7 +74,18 @@ operator<=>(const std::span<const uint8_t> &lhs,
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return lhs.size() <=> size_t(rhs.len);
|
||||
return lhs.size() <=> rhs.len;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline auto operator<=>(const ConflictSet::Key &lhs,
|
||||
const ConflictSet::Key &rhs) noexcept {
|
||||
int cl = std::min<int>(lhs.len, rhs.len);
|
||||
if (cl > 0) {
|
||||
if (auto c = memcmp(lhs.p, rhs.p, cl) <=> 0; c != 0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return lhs.len <=> rhs.len;
|
||||
}
|
||||
|
||||
// This header contains code that we want to reuse outside of ConflictSet.cpp or
|
||||
@@ -328,23 +367,6 @@ template <class T, class C = std::less<T>> auto set(Arena &arena) {
|
||||
return Set<T, C>(ArenaAlloc<T>(&arena));
|
||||
}
|
||||
|
||||
template <class T> struct MyHash;
|
||||
|
||||
template <class T> struct MyHash<T *> {
|
||||
size_t operator()(const T *t) const noexcept {
|
||||
size_t result;
|
||||
memcpy(&result, &t, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using HashSet =
|
||||
std::unordered_set<T, MyHash<T>, std::equal_to<T>, ArenaAlloc<T>>;
|
||||
template <class T> auto hashSet(Arena &arena) {
|
||||
return HashSet<T>(ArenaAlloc<T>(&arena));
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const ArenaAlloc<T> &lhs, const ArenaAlloc<U> &rhs) {
|
||||
return lhs.arena == rhs.arena;
|
||||
@@ -569,7 +591,7 @@ inline std::string printable(const Key &key) {
|
||||
return printable(std::string_view((const char *)key.p, key.len));
|
||||
}
|
||||
|
||||
inline std::string printable(std::span<const uint8_t> key) {
|
||||
inline std::string printable(TrivialSpan key) {
|
||||
return printable(std::string_view((const char *)key.data(), key.size()));
|
||||
}
|
||||
|
||||
@@ -677,10 +699,8 @@ struct TestDriver {
|
||||
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||
writes[i].end.len = keyLen;
|
||||
writes[i].end.p = begin;
|
||||
auto c =
|
||||
std::span<const uint8_t>(writes[i].begin.p,
|
||||
writes[i].begin.len) <=>
|
||||
std::span<const uint8_t>(writes[i].end.p, writes[i].end.len);
|
||||
auto c = TrivialSpan(writes[i].begin.p, writes[i].begin.len) <=>
|
||||
TrivialSpan(writes[i].end.p, writes[i].end.len);
|
||||
if (c > 0) {
|
||||
using std::swap;
|
||||
swap(writes[i].begin, writes[i].end);
|
||||
|
||||
Vendored
+4
-4
@@ -11,11 +11,11 @@ def CleanBuildAndTest(String cmakeArgs) {
|
||||
catchError {
|
||||
sh '''
|
||||
cd build
|
||||
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j `nproc` --timeout 90
|
||||
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j `nproc` --timeout 90 > /dev/null
|
||||
zstd Testing/*/Test.xml
|
||||
'''
|
||||
}
|
||||
xunit tools: [CTest(pattern: 'build/Testing/*/Test.xml')], reduceLog: false, skipPublishingChecks: false
|
||||
xunit tools: [CTest(pattern: 'build/Testing/*/Test.xml')], skipPublishingChecks: false
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/Testing/*/Test.xml.zst', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ pipeline {
|
||||
minio bucket: 'jenkins', credentialsId: 'jenkins-minio', excludes: '', host: 'minio.weaselab.dev', includes: 'build/*.deb,build/*.rpm,paper/*.pdf', targetFolder: '${JOB_NAME}/${BUILD_NUMBER}/${STAGE_NAME}/'
|
||||
}
|
||||
}
|
||||
stage('Release [gcc]') {
|
||||
stage('gcc') {
|
||||
agent {
|
||||
dockerfile {
|
||||
args '-v /home/jenkins/ccache:/ccache'
|
||||
@@ -99,7 +99,7 @@ pipeline {
|
||||
}
|
||||
}
|
||||
steps {
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-DNVALGRIND")
|
||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++")
|
||||
recordIssues(tools: [gcc()])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,16 @@ A data structure for optimistic concurrency control on ranges of bitwise-lexicog
|
||||
|
||||
Intended as an alternative to FoundationDB's skip list.
|
||||
|
||||
Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-34-34-89 1.35V RAM
|
||||
Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-34-34-89 1.35V RAM.
|
||||
|
||||
```
|
||||
$ clang++ --version
|
||||
|
||||
Ubuntu clang version 20.0.0 (++20241120082228+86734c857724-1~exp1~20241120202359.554)
|
||||
Target: x86_64-pc-linux-gnu
|
||||
Thread model: posix
|
||||
InstalledDir: /usr/lib/llvm-20/bin
|
||||
```
|
||||
|
||||
# Microbenchmark
|
||||
|
||||
@@ -10,44 +19,45 @@ Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-3
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 172.03 | 5,812,791.77 | 0.4% | 3,130.62 | 879.00 | 3.562 | 509.23 | 0.0% | 0.01 | `point reads`
|
||||
| 167.44 | 5,972,130.71 | 0.2% | 3,065.14 | 862.27 | 3.555 | 494.30 | 0.0% | 0.01 | `prefix reads`
|
||||
| 238.77 | 4,188,130.84 | 0.9% | 3,589.93 | 1,259.30 | 2.851 | 637.12 | 0.0% | 0.01 | `range reads`
|
||||
| 424.01 | 2,358,426.70 | 0.2% | 5,620.05 | 2,242.35 | 2.506 | 854.80 | 1.7% | 0.01 | `point writes`
|
||||
| 418.45 | 2,389,780.56 | 0.4% | 5,525.07 | 2,211.05 | 2.499 | 831.71 | 1.7% | 0.01 | `prefix writes`
|
||||
| 254.87 | 3,923,568.88 | 2.6% | 3,187.01 | 1,366.50 | 2.332 | 529.11 | 2.7% | 0.02 | `range writes`
|
||||
| 675.96 | 1,479,374.50 | 3.3% | 7,735.41 | 3,468.60 | 2.230 | 1,386.02 | 1.8% | 0.01 | `monotonic increasing point writes`
|
||||
| 137,986.20 | 7,247.10 | 0.6% | 789,752.33 | 699,462.00 | 1.129 | 144,824.14 | 0.0% | 0.01 | `worst case for radix tree`
|
||||
| 21.63 | 46,231,564.03 | 1.0% | 448.00 | 107.14 | 4.181 | 84.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 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`
|
||||
|
||||
## Radix tree (this implementation)
|
||||
|
||||
|
||||
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||
| 12.88 | 77,653,350.77 | 0.5% | 185.37 | 64.45 | 2.876 | 41.51 | 0.4% | 0.01 | `point reads`
|
||||
| 14.67 | 68,179,354.49 | 0.1% | 271.44 | 73.40 | 3.698 | 53.70 | 0.3% | 0.01 | `prefix reads`
|
||||
| 34.84 | 28,701,444.36 | 0.3% | 715.74 | 175.27 | 4.084 | 127.30 | 0.2% | 0.01 | `range reads`
|
||||
| 17.12 | 58,422,988.28 | 0.2% | 314.30 | 86.11 | 3.650 | 39.82 | 0.4% | 0.01 | `point writes`
|
||||
| 31.42 | 31,830,804.65 | 0.1% | 591.06 | 158.07 | 3.739 | 82.67 | 0.2% | 0.01 | `prefix writes`
|
||||
| 37.37 | 26,759,432.70 | 2.2% | 681.98 | 188.95 | 3.609 | 96.10 | 0.1% | 0.01 | `range writes`
|
||||
| 76.72 | 13,035,140.63 | 2.3% | 1,421.28 | 387.17 | 3.671 | 257.76 | 0.1% | 0.01 | `monotonic increasing point writes`
|
||||
| 297,452.00 | 3,361.89 | 0.9% | 3,508,083.00 | 1,500,834.67 | 2.337 | 727,525.33 | 0.1% | 0.01 | `worst case for radix tree`
|
||||
| 87.70 | 11,402,490.60 | 1.0% | 1,795.00 | 442.09 | 4.060 | 297.00 | 0.0% | 0.01 | `create and destroy`
|
||||
| 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`
|
||||
|
||||
# "Real data" test
|
||||
|
||||
Point queries only, best of three runs. Gc ratio is the ratio of time spent doing garbage collection to time spent adding writes or doing garbage collection. Lower is better.
|
||||
Point queries only. Gc ratio is the ratio of time spent doing garbage collection to time spent adding writes or doing garbage collection. Lower is better.
|
||||
|
||||
## skip list
|
||||
|
||||
```
|
||||
Check: 4.47891 seconds, 364.05 MB/s, Add: 4.55599 seconds, 123.058 MB/s, Gc ratio: 37.1145%
|
||||
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
|
||||
|
||||
```
|
||||
Check: 0.953012 seconds, 1710.94 MB/s, Add: 1.30025 seconds, 431.188 MB/s, Gc ratio: 43.9816%, Peak idle memory: 2.28375e+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
|
||||
@@ -55,5 +65,6 @@ Check: 0.953012 seconds, 1710.94 MB/s, Add: 1.30025 seconds, 431.188 MB/s, Gc ra
|
||||
(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.804094 seconds, 2027.81 MB/s, Add: 0.652952 seconds, 858.645 MB/s, Gc ratio: 35.3885%
|
||||
Check: 0.804598 seconds, 2095.69 MB/s, Add: 0.671221 seconds, 857.147 MB/s, Gc ratio: 35.0034%, Peak idle memory: 0
|
||||
```
|
||||
|
||||
|
||||
+8
-8
@@ -5,7 +5,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -64,7 +64,7 @@ int main(int argc, const char **argv) {
|
||||
auto *const mapOriginal = begin;
|
||||
const auto sizeOriginal = size;
|
||||
|
||||
using StringView = std::basic_string_view<uint8_t>;
|
||||
using StringView = std::span<const uint8_t>;
|
||||
|
||||
StringView write;
|
||||
std::vector<StringView> reads;
|
||||
@@ -78,9 +78,9 @@ int main(int argc, const char **argv) {
|
||||
end = (uint8_t *)memchr(begin, '\n', size);
|
||||
|
||||
if (line.size() > 0 && line[0] == 'P') {
|
||||
write = line.substr(2, line.size());
|
||||
write = line.subspan(2, line.size());
|
||||
} else if (line.size() > 0 && line[0] == 'L') {
|
||||
reads.push_back(line.substr(2, line.size()));
|
||||
reads.push_back(line.subspan(2, line.size()));
|
||||
} else if (line.empty()) {
|
||||
{
|
||||
readRanges.resize(reads.size());
|
||||
@@ -133,10 +133,10 @@ int main(int argc, const char **argv) {
|
||||
int metricsCount;
|
||||
cs.getMetricsV1(&metrics, &metricsCount);
|
||||
for (int i = 0; i < metricsCount; ++i) {
|
||||
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||
printf("# TYPE %s %s\n", metrics[i].name,
|
||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||
fprintf(stderr, "# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||
fprintf(stderr, "# TYPE %s %s\n", metrics[i].name,
|
||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
||||
fprintf(stderr, "%s %g\n", metrics[i].name, metrics[i].getValue());
|
||||
}
|
||||
|
||||
printf("Check: %g seconds, %g MB/s, Add: %g seconds, %g MB/s, Gc ratio: "
|
||||
|
||||
+210
-44
@@ -1,5 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
@@ -18,62 +20,210 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ConflictSet.h"
|
||||
#include "Internal.h"
|
||||
#include "third_party/nadeau.h"
|
||||
|
||||
constexpr int kCacheLine = 64; // TODO mac m1 is 128
|
||||
|
||||
template <class T> struct TxQueue {
|
||||
|
||||
explicit TxQueue(int lgSlotCount)
|
||||
: slotCount(1 << lgSlotCount), slotCountMask(slotCount - 1),
|
||||
slots(new T[slotCount]) {
|
||||
// Otherwise we can't tell the difference between full and empty.
|
||||
assert(!(slotCountMask & 0x80000000));
|
||||
}
|
||||
|
||||
/// Call from producer thread, after ensuring consumer is no longer accessing
|
||||
/// it somehow
|
||||
~TxQueue() { delete[] slots; }
|
||||
|
||||
/// Must be called from the producer thread
|
||||
void push(T t) {
|
||||
if (wouldBlock()) {
|
||||
// Wait for pops to change and try again
|
||||
consumer.pops.wait(producer.lastPopRead, std::memory_order_relaxed);
|
||||
producer.lastPopRead = consumer.pops.load(std::memory_order_acquire);
|
||||
}
|
||||
slots[producer.pushesNonAtomic++ & slotCountMask] = std::move(t);
|
||||
// seq_cst so that the notify can't be ordered before the store
|
||||
producer.pushes.store(producer.pushesNonAtomic, std::memory_order_seq_cst);
|
||||
// We have to notify every time, since we don't know if this is the last
|
||||
// push ever
|
||||
producer.pushes.notify_one();
|
||||
}
|
||||
|
||||
/// Must be called from the producer thread
|
||||
uint32_t outstanding() {
|
||||
return producer.pushesNonAtomic -
|
||||
consumer.pops.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Returns true if a call to push might block. Must be called from the
|
||||
/// producer thread.
|
||||
bool wouldBlock() {
|
||||
// See if we can determine that overflow won't happen entirely from state
|
||||
// local to the producer
|
||||
if (producer.pushesNonAtomic - producer.lastPopRead == slotCount - 1) {
|
||||
// Re-read pops with memory order
|
||||
producer.lastPopRead = consumer.pops.load(std::memory_order_acquire);
|
||||
return producer.pushesNonAtomic - producer.lastPopRead == slotCount - 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Valid until the next pop, or until this queue is destroyed.
|
||||
T *pop() {
|
||||
// See if we can determine that there's an entry we can pop entirely from
|
||||
// state local to the consumer
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// Re-read pushes with memory order and try again
|
||||
consumer.lastPushRead = producer.pushes.load(std::memory_order_acquire);
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// Wait for pushes to change and try again
|
||||
producer.pushes.wait(consumer.lastPushRead, std::memory_order_relaxed);
|
||||
consumer.lastPushRead = producer.pushes.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
auto result = &slots[consumer.popsNonAtomic++ & slotCountMask];
|
||||
// We only have to write pops with memory order if we've run out of items.
|
||||
// We know that we'll eventually run out.
|
||||
if (consumer.lastPushRead - consumer.popsNonAtomic == 0) {
|
||||
// seq_cst so that the notify can't be ordered before the store
|
||||
consumer.pops.store(consumer.popsNonAtomic, std::memory_order_seq_cst);
|
||||
consumer.pops.notify_one();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t slotCount;
|
||||
const uint32_t slotCountMask;
|
||||
T *slots;
|
||||
struct alignas(kCacheLine) ProducerState {
|
||||
std::atomic<uint32_t> pushes{0};
|
||||
uint32_t pushesNonAtomic{0};
|
||||
uint32_t lastPopRead{0};
|
||||
};
|
||||
struct alignas(kCacheLine) ConsumerState {
|
||||
std::atomic<uint32_t> pops{0};
|
||||
uint32_t popsNonAtomic{0};
|
||||
uint32_t lastPushRead{0};
|
||||
};
|
||||
ProducerState producer;
|
||||
ConsumerState consumer;
|
||||
};
|
||||
|
||||
std::atomic<int64_t> transactions;
|
||||
|
||||
constexpr int kWindowSize = 10000000;
|
||||
int64_t safeUnaryMinus(int64_t x) {
|
||||
return x == std::numeric_limits<int64_t>::min() ? x : -x;
|
||||
}
|
||||
|
||||
constexpr int kNumPrefixes = 250000;
|
||||
void tupleAppend(std::string &output, int64_t value) {
|
||||
if (value == 0) {
|
||||
output.push_back(0x14);
|
||||
return;
|
||||
}
|
||||
uint32_t size = 8 - __builtin_clrsbll(value) / 8;
|
||||
int typeCode = 0x14 + (value < 0 ? -1 : 1) * size;
|
||||
output.push_back(typeCode);
|
||||
if (value < 0) {
|
||||
value = ~safeUnaryMinus(value);
|
||||
}
|
||||
uint64_t swap = __builtin_bswap64(value);
|
||||
output.insert(output.end(), (uint8_t *)&swap + 8 - size,
|
||||
(uint8_t *)&swap + 8);
|
||||
}
|
||||
|
||||
std::string makeKey(int64_t num, int suffixLen) {
|
||||
void tupleAppend(std::string &output, std::string_view value) {
|
||||
output.push_back('\x02');
|
||||
if (memchr(value.data(), '\x00', value.size()) != nullptr) {
|
||||
for (auto c : value) {
|
||||
if (c == '\x00') {
|
||||
output.push_back('\x00');
|
||||
output.push_back('\xff');
|
||||
} else {
|
||||
output.push_back(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output.insert(output.end(), value.begin(), value.end());
|
||||
}
|
||||
output.push_back('\x00');
|
||||
}
|
||||
|
||||
template <class... Ts> std::string tupleKey(const Ts &...ts) {
|
||||
std::string result;
|
||||
result.resize(sizeof(int64_t) + suffixLen);
|
||||
int64_t be = __builtin_bswap64(num);
|
||||
memcpy(result.data(), &be, sizeof(int64_t));
|
||||
memset(result.data() + sizeof(int64_t), 0, suffixLen);
|
||||
(tupleAppend(result, ts), ...);
|
||||
return result;
|
||||
}
|
||||
|
||||
void workload(weaselab::ConflictSet *cs) {
|
||||
int64_t version = kWindowSize;
|
||||
for (int i = 0; i < kNumPrefixes; ++i) {
|
||||
for (int j = 0; j < 50; ++j) {
|
||||
weaselab::ConflictSet::WriteRange wr;
|
||||
auto k = makeKey(i, j);
|
||||
wr.begin.p = (const uint8_t *)k.data();
|
||||
wr.begin.len = k.size();
|
||||
wr.end.len = 0;
|
||||
cs->addWrites(&wr, 1, version);
|
||||
constexpr int kTotalKeyRange = 1'000'000'000;
|
||||
constexpr int kWindowSize = 1'000'000;
|
||||
constexpr int kNumReadKeysPerTx = 5;
|
||||
constexpr int kNumWriteKeysPerTx = 10;
|
||||
|
||||
struct Transaction {
|
||||
std::vector<std::string> keys;
|
||||
std::vector<weaselab::ConflictSet::ReadRange> reads;
|
||||
std::vector<weaselab::ConflictSet::WriteRange> writes;
|
||||
int64_t version;
|
||||
int64_t oldestVersion;
|
||||
Transaction() = default;
|
||||
explicit Transaction(int64_t version)
|
||||
: version(version), oldestVersion(version - kWindowSize) {
|
||||
std::vector<int64_t> keyIndices;
|
||||
for (int i = 0; i < std::max(kNumReadKeysPerTx, kNumWriteKeysPerTx); ++i) {
|
||||
keyIndices.push_back(rand() % kTotalKeyRange);
|
||||
}
|
||||
std::sort(keyIndices.begin(), keyIndices.end());
|
||||
constexpr std::string_view fullString =
|
||||
"this is a string, where a prefix of it is used as an element of the "
|
||||
"tuple forming the key";
|
||||
for (int i = 0; i < int(keyIndices.size()); ++i) {
|
||||
keys.push_back(
|
||||
tupleKey(0x100, keyIndices[i] / fullString.size(),
|
||||
fullString.substr(0, keyIndices[i] % fullString.size())));
|
||||
// printf("%s\n", printable(keys.back()).c_str());
|
||||
}
|
||||
for (int i = 0; i < kNumWriteKeysPerTx; ++i) {
|
||||
writes.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||
{nullptr, 0}});
|
||||
}
|
||||
reads.push_back({{(const uint8_t *)keys[0].data(), int(keys[0].size())},
|
||||
{(const uint8_t *)keys[1].data(), int(keys[1].size())},
|
||||
version - std::min(10, kWindowSize)});
|
||||
static_assert(kNumReadKeysPerTx >= 3);
|
||||
for (int i = 2; i < kNumReadKeysPerTx; ++i) {
|
||||
reads.push_back({{(const uint8_t *)keys[i].data(), int(keys[i].size())},
|
||||
{nullptr, 0},
|
||||
version - kWindowSize});
|
||||
}
|
||||
}
|
||||
++version;
|
||||
for (int i = 0; i < kNumPrefixes; ++i) {
|
||||
weaselab::ConflictSet::WriteRange wr;
|
||||
auto k = makeKey(i, 50);
|
||||
wr.begin.p = (const uint8_t *)k.data();
|
||||
wr.begin.len = k.size();
|
||||
wr.end.len = 0;
|
||||
cs->addWrites(&wr, 1, version);
|
||||
}
|
||||
|
||||
constexpr int kNumReads = 1;
|
||||
std::vector<weaselab::ConflictSet::Result> results(kNumReads);
|
||||
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||
std::vector<std::string> keys(kNumReads);
|
||||
for (auto &k : keys) {
|
||||
k = makeKey(rand() % kNumPrefixes, 49);
|
||||
}
|
||||
std::vector<weaselab::ConflictSet::ReadRange> reads(kNumReads);
|
||||
for (int i = 0; i < reads.size(); ++i) {
|
||||
reads[i].begin.p = (const uint8_t *)(keys[i].data());
|
||||
reads[i].begin.len = keys[i].size();
|
||||
reads[i].end.len = 0;
|
||||
reads[i].readVersion = version - 1;
|
||||
}
|
||||
cs->check(reads.data(), results.data(), kNumReads);
|
||||
Transaction(Transaction &&) = default;
|
||||
Transaction &operator=(Transaction &&) = default;
|
||||
Transaction(Transaction const &) = delete;
|
||||
Transaction const &operator=(Transaction const &) = delete;
|
||||
};
|
||||
|
||||
struct Resolver {
|
||||
|
||||
void resolve(const weaselab::ConflictSet::ReadRange *reads, int readCount,
|
||||
const weaselab::ConflictSet::WriteRange *writes, int writeCount,
|
||||
int64_t newVersion, int64_t newOldestVersion) {
|
||||
results.resize(readCount);
|
||||
cs.check(reads, results.data(), readCount);
|
||||
cs.addWrites(writes, writeCount, newVersion);
|
||||
cs.setOldestVersion(newOldestVersion);
|
||||
}
|
||||
}
|
||||
|
||||
ConflictSet cs{0};
|
||||
|
||||
private:
|
||||
std::vector<weaselab::ConflictSet::Result> results;
|
||||
};
|
||||
|
||||
// Adapted from getaddrinfo man page
|
||||
int getListenFd(const char *node, const char *service) {
|
||||
@@ -216,7 +366,8 @@ int main(int argc, char **argv) {
|
||||
{
|
||||
int listenFd = getListenFd(argv[1], argv[2]);
|
||||
|
||||
weaselab::ConflictSet cs{0};
|
||||
Resolver resolver;
|
||||
auto &cs = resolver.cs;
|
||||
weaselab::ConflictSet::MetricsV1 *metrics;
|
||||
int metricsCount;
|
||||
cs.getMetricsV1(&metrics, &metricsCount);
|
||||
@@ -265,7 +416,22 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
#endif
|
||||
|
||||
auto w = std::thread{workload, &cs};
|
||||
TxQueue<Transaction> queue{10};
|
||||
|
||||
auto workloadThread = std::thread{[&]() {
|
||||
for (int64_t version = kWindowSize;;
|
||||
++version, transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||
queue.push(Transaction(version));
|
||||
}
|
||||
}};
|
||||
|
||||
auto resolverThread = std::thread{[&]() {
|
||||
for (;;) {
|
||||
auto tx = queue.pop();
|
||||
resolver.resolve(tx->reads.data(), tx->reads.size(), tx->writes.data(),
|
||||
tx->writes.size(), tx->version, tx->oldestVersion);
|
||||
}
|
||||
}};
|
||||
|
||||
for (;;) {
|
||||
struct sockaddr_storage peer_addr = {};
|
||||
|
||||
+3
-2
@@ -767,7 +767,9 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
false, true);
|
||||
}
|
||||
|
||||
sortPoints(points);
|
||||
if (!std::is_sorted(points.begin(), points.end())) {
|
||||
sortPoints(points);
|
||||
}
|
||||
|
||||
int activeWriteCount = 0;
|
||||
std::vector<std::pair<StringRef, StringRef>> combinedWriteConflictRanges;
|
||||
@@ -794,7 +796,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
int temp[stripeSize];
|
||||
int stripes = (stringCount + stripeSize - 1) / stripeSize;
|
||||
StringRef values[stripeSize];
|
||||
int64_t writeVersions[stripeSize / 2];
|
||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||
int64_t entryDelta = 0;
|
||||
for (int s = stripes - 1; s >= 0; s--) {
|
||||
|
||||
@@ -5,6 +5,7 @@ __stack_chk_guard@GLIBC_2.17
|
||||
abort@GLIBC_2.17
|
||||
free@GLIBC_2.17
|
||||
malloc@GLIBC_2.17
|
||||
memcmp@GLIBC_2.17
|
||||
memcpy@GLIBC_2.17
|
||||
memmove@GLIBC_2.17
|
||||
memset@GLIBC_2.17
|
||||
@@ -1,3 +1,4 @@
|
||||
___chkstk_darwin
|
||||
___stack_chk_fail
|
||||
___stack_chk_guard
|
||||
__tlv_bootstrap
|
||||
@@ -5,6 +6,7 @@ _abort
|
||||
_bzero
|
||||
_free
|
||||
_malloc
|
||||
_memcmp
|
||||
_memcpy
|
||||
_memmove
|
||||
dyld_stub_binder
|
||||
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.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user