Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a132799a4 | |||
| 72469ebb6e | |||
| 6c79847a42 | |||
| 405a2ca161 | |||
| f93466316a | |||
| 5626cd09d9 | |||
| 41840220c3 | |||
| 7ff00e7846 | |||
| 6242f40d48 | |||
| 403d70a1d3 | |||
| 9763452713 | |||
| 73d0593fca | |||
| 23c2a3e1c6 | |||
| a64e792964 | |||
| 5e362d5330 | |||
| cc526cb6ba | |||
| 7e49888bec | |||
| e64ebabced | |||
| 1e34951a77 | |||
| baf64520d6 | |||
| 3499626127 | |||
| b7f9084694 | |||
| 4b82502946 | |||
| 68bbacb69a | |||
| 3078845673 | |||
| 43f6126cc4 | |||
| b911d87d55 | |||
| 0c65a82b78 | |||
| e024cb8291 | |||
| 0740dcad43 | |||
| 176df61321 | |||
| 0a850f22e9 | |||
| 479b39d055 | |||
| 482408d725 | |||
| 45995e3307 | |||
| 359b0b29ff | |||
| 54e47ebd40 | |||
| 1c9dda68a6 | |||
| 142455dd28 | |||
| 567d385fbd | |||
| 8a44055533 | |||
| 62516825d1 | |||
| 3d592bd6a9 | |||
| f5f5fb620b | |||
| e3d1b2e842 | |||
| 9f8800af16 | |||
| 182c065c8e | |||
| 2dba0d5be3 | |||
| a1dfdf355c | |||
| 15919cb1c4 | |||
| 5ed9003a83 | |||
| 84c6a2bfc2 | |||
| b5772a6aa0 | |||
| e6c39981b9 | |||
| c20c08f112 | |||
| ac98d4a443 | |||
| 1d9e8ab68b | |||
| 7d86beb14c | |||
| 2fa954ed36 | |||
| ded6e7fc2c | |||
| 781ba15cae | |||
| 9b56a74b2f | |||
| 6da9cbdec9 | |||
| 29c05187fb | |||
| d89028dd2f | |||
| 09cf807747 | |||
| 051eb5919d | |||
| ed5589e4ed | |||
| a7b3d8fe4c | |||
| c3a047fdf8 | |||
| b4b469a175 | |||
| 0201e27498 | |||
| 2010920a2c | |||
| 19af8da65c | |||
| 80785e3c3b | |||
| 4580ee44b4 | |||
| 2d3985ca40 | |||
| c8be68db40 | |||
| f5d021d6b6 | |||
| 1c41605b53 | |||
| 8f03a105bb | |||
| 0e574856be | |||
| 493a6572ad | |||
| abce4591d0 | |||
| d1dc1247e1 | |||
| f1ad68109a | |||
| c4443bc355 | |||
| 857b402fe2 | |||
| 9b3e1b219b | |||
| ab52c63935 | |||
| bad9d7ced8 | |||
| c8d9dc034d | |||
| 72168ef6a3 | |||
| 620a0afd2a | |||
| b0414969be | |||
| 1673e1c0dd | |||
| 7351b6e417 | |||
| 561ed45e3e | |||
| ca804f28c0 | |||
| 3898cb596a | |||
| b8edd92698 | |||
| 8e480528d5 | |||
| 4113183155 | |||
| adb8fdc5e9 | |||
| c86e407985 | |||
| 71a84057cb | |||
| 9c5e5863c2 | |||
| be67555756 | |||
| 988ec5ce69 | |||
| f5a0d81c52 | |||
| 3b2bd16cd1 | |||
| 4b3df0a426 | |||
| 4cdf6deb50 | |||
| f21dde06d3 | |||
| 2b11650589 | |||
| fce998460f | |||
| 6da3125719 | |||
| 79410d071f | |||
| 1fcca6450d | |||
| 55271ad06c | |||
| e675612599 | |||
| 42b5d50492 | |||
| 6394995def | |||
| c649bc7964 | |||
| ec85a06d01 | |||
| fb9f5ce6f4 |
@@ -361,7 +361,21 @@ void benchWorstCaseForRadixRangeRead() {
|
|||||||
void benchCreateAndDestroy() {
|
void benchCreateAndDestroy() {
|
||||||
ankerl::nanobench::Bench bench;
|
ankerl::nanobench::Bench bench;
|
||||||
|
|
||||||
bench.run("create and destroy", [&]() { ConflictSet cs{0}; });
|
bench.run("create and destroy", [&]() {
|
||||||
|
ConflictSet cs{0};
|
||||||
|
ConflictSet::WriteRange w;
|
||||||
|
uint8_t b[9];
|
||||||
|
b[8] = 0;
|
||||||
|
for (int64_t i = 0; i < 1000; i += 7) {
|
||||||
|
auto x = __builtin_bswap64(i);
|
||||||
|
memcpy(b, &x, 8);
|
||||||
|
w.begin.p = b;
|
||||||
|
w.begin.len = 8;
|
||||||
|
w.end.len = 0;
|
||||||
|
w.end.p = b;
|
||||||
|
cs.addWrites(&w, 1, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
+42
-55
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(
|
project(
|
||||||
conflict-set
|
conflict-set
|
||||||
VERSION 0.0.9
|
VERSION 0.0.12
|
||||||
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"
|
||||||
@@ -19,12 +19,6 @@ include(CheckCXXSourceCompiles)
|
|||||||
|
|
||||||
set(DEFAULT_BUILD_TYPE "Release")
|
set(DEFAULT_BUILD_TYPE "Release")
|
||||||
|
|
||||||
if(EMSCRIPTEN OR CMAKE_SYSTEM_NAME STREQUAL WASI)
|
|
||||||
set(WASM ON)
|
|
||||||
else()
|
|
||||||
set(WASM OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
message(
|
message(
|
||||||
STATUS
|
STATUS
|
||||||
@@ -39,6 +33,10 @@ endif()
|
|||||||
|
|
||||||
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
add_compile_options(-fdata-sections -ffunction-sections -Wswitch-enum
|
||||||
-Werror=switch-enum -fPIC)
|
-Werror=switch-enum -fPIC)
|
||||||
|
if(NOT APPLE)
|
||||||
|
# This causes some versions of clang to crash on macos
|
||||||
|
add_compile_options(-g -fno-omit-frame-pointer)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(full_relro_flags "-pie;LINKER:-z,relro,-z,now,-z,noexecstack")
|
set(full_relro_flags "-pie;LINKER:-z,relro,-z,now,-z,noexecstack")
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
@@ -81,15 +79,6 @@ if(EMSCRIPTEN)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT USE_SIMD_FALLBACK)
|
if(NOT USE_SIMD_FALLBACK)
|
||||||
cmake_push_check_state()
|
|
||||||
list(APPEND CMAKE_REQUIRED_FLAGS -msimd128)
|
|
||||||
check_include_file_cxx("wasm_simd128.h" HAS_WASM_SIMD)
|
|
||||||
if(HAS_WASM_SIMD)
|
|
||||||
add_compile_options(-msimd128)
|
|
||||||
add_compile_definitions(HAS_WASM_SIMD)
|
|
||||||
endif()
|
|
||||||
cmake_pop_check_state()
|
|
||||||
|
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
list(APPEND CMAKE_REQUIRED_FLAGS -mavx)
|
||||||
check_include_file_cxx("immintrin.h" HAS_AVX)
|
check_include_file_cxx("immintrin.h" HAS_AVX)
|
||||||
@@ -162,47 +151,40 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
|
|
||||||
file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
|
file(GLOB CORPUS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/corpus/*)
|
||||||
|
|
||||||
# extra testing that relies on shared libraries, which aren't available with
|
# Shared library version of FoundationDB's skip list implementation
|
||||||
# wasm
|
add_library(skip_list SHARED SkipList.cpp)
|
||||||
if(NOT WASM)
|
target_compile_options(skip_list PRIVATE -fno-exceptions -fvisibility=hidden)
|
||||||
# Shared library version of FoundationDB's skip list implementation
|
target_include_directories(skip_list
|
||||||
add_library(skip_list SHARED SkipList.cpp)
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
target_compile_options(skip_list PRIVATE -fno-exceptions
|
set_target_properties(
|
||||||
-fvisibility=hidden)
|
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
target_include_directories(skip_list
|
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(
|
set_target_properties(skip_list PROPERTIES VERSION ${PROJECT_VERSION}
|
||||||
skip_list PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/skip_list")
|
|
||||||
set_target_properties(skip_list PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
|
||||||
set_target_properties(
|
|
||||||
skip_list PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
|
||||||
${PROJECT_VERSION_MAJOR})
|
|
||||||
|
|
||||||
# Shared library version of a std::unordered_map-based conflict set (point
|
# Shared library version of a std::unordered_map-based conflict set (point
|
||||||
# queries only)
|
# queries only)
|
||||||
add_library(hash_table SHARED HashTable.cpp)
|
add_library(hash_table SHARED HashTable.cpp)
|
||||||
target_compile_options(hash_table PRIVATE -fno-exceptions
|
target_compile_options(hash_table PRIVATE -fno-exceptions -fvisibility=hidden)
|
||||||
-fvisibility=hidden)
|
target_include_directories(hash_table
|
||||||
target_include_directories(hash_table
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
set_target_properties(
|
||||||
set_target_properties(
|
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
||||||
hash_table PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
"${CMAKE_CURRENT_BINARY_DIR}/hash_table")
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/hash_table")
|
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
set_target_properties(hash_table PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
set_target_properties(
|
||||||
set_target_properties(
|
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
||||||
hash_table PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION
|
${PROJECT_VERSION_MAJOR})
|
||||||
${PROJECT_VERSION_MAJOR})
|
|
||||||
|
|
||||||
add_executable(driver_skip_list TestDriver.cpp)
|
add_executable(driver_skip_list TestDriver.cpp)
|
||||||
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
target_compile_options(driver_skip_list PRIVATE ${TEST_FLAGS})
|
||||||
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
target_link_libraries(driver_skip_list PRIVATE skip_list)
|
||||||
|
|
||||||
foreach(TEST ${CORPUS_TESTS})
|
foreach(TEST ${CORPUS_TESTS})
|
||||||
get_filename_component(hash ${TEST} NAME)
|
get_filename_component(hash ${TEST} NAME)
|
||||||
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
add_test(NAME skip_list_${hash} COMMAND driver_skip_list ${TEST})
|
||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
|
||||||
|
|
||||||
# ad hoc testing
|
# ad hoc testing
|
||||||
add_executable(conflict_set_main ConflictSet.cpp)
|
add_executable(conflict_set_main ConflictSet.cpp)
|
||||||
@@ -320,7 +302,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
add_test(NAME conflict_set_cxx_api_test COMMAND conflict_set_cxx_api_test)
|
||||||
|
|
||||||
# symbol visibility tests
|
# symbol visibility tests
|
||||||
if(NOT WASM AND NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
if(NOT CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
|
set(symbol_exports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-exports.txt)
|
||||||
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
|
set(symbol_imports ${CMAKE_CURRENT_SOURCE_DIR}/apple-symbol-imports.txt)
|
||||||
@@ -358,6 +340,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND BUILD_TESTING)
|
|||||||
add_executable(driver_perf TestDriver.cpp)
|
add_executable(driver_perf TestDriver.cpp)
|
||||||
target_compile_definitions(driver_perf PRIVATE PERF_TEST=1)
|
target_compile_definitions(driver_perf PRIVATE PERF_TEST=1)
|
||||||
target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME})
|
target_link_libraries(driver_perf PRIVATE ${PROJECT_NAME})
|
||||||
|
|
||||||
|
# server bench
|
||||||
|
add_executable(server_bench ServerBench.cpp)
|
||||||
|
target_link_libraries(server_bench PRIVATE ${PROJECT_NAME})
|
||||||
|
set_target_properties(server_bench PROPERTIES SKIP_BUILD_RPATH ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# packaging
|
# packaging
|
||||||
|
|||||||
+1246
-980
File diff suppressed because it is too large
Load Diff
+79
-57
@@ -273,6 +273,16 @@ template <class T> struct Vector {
|
|||||||
size_ += slice.size();
|
size_ += slice.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caller must write to the returned slice
|
||||||
|
std::span<T> unsafePrepareAppend(int appendSize) {
|
||||||
|
if (size_ + appendSize > capacity) {
|
||||||
|
grow(std::max<int>(size_ + appendSize, capacity * 2));
|
||||||
|
}
|
||||||
|
auto result = std::span<T>(t + size_, appendSize);
|
||||||
|
size_ += appendSize;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void push_back(const T &t) { append(std::span<const T>(&t, 1)); }
|
void push_back(const T &t) { append(std::span<const T>(&t, 1)); }
|
||||||
|
|
||||||
T *begin() { return t; }
|
T *begin() { return t; }
|
||||||
@@ -580,11 +590,14 @@ namespace {
|
|||||||
|
|
||||||
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
template <class ConflictSetImpl, bool kEnableAssertions = true>
|
||||||
struct TestDriver {
|
struct TestDriver {
|
||||||
Arbitrary arbitrary;
|
Arbitrary *arbitrary;
|
||||||
explicit TestDriver(const uint8_t *data, size_t size)
|
explicit TestDriver(Arbitrary &a) : arbitrary(&a) {
|
||||||
: arbitrary({data, size}) {}
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "%p Initial version: {%" PRId64 "}\n", this, writeVersion);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int64_t oldestVersion = arbitrary.next();
|
int64_t oldestVersion = arbitrary->next();
|
||||||
int64_t writeVersion = oldestVersion;
|
int64_t writeVersion = oldestVersion;
|
||||||
ConflictSetImpl cs{oldestVersion};
|
ConflictSetImpl cs{oldestVersion};
|
||||||
ReferenceImpl refImpl{oldestVersion};
|
ReferenceImpl refImpl{oldestVersion};
|
||||||
@@ -593,33 +606,34 @@ struct TestDriver {
|
|||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
||||||
const int prefixLen = arbitrary.bounded(512);
|
const int prefixLen = arbitrary->bounded(512);
|
||||||
const int prefixByte = arbitrary.randT<uint8_t>();
|
const int prefixByte = arbitrary->randT<uint8_t>();
|
||||||
|
|
||||||
// Call until it returns true, for "done". Check internal invariants etc
|
// Call until it returns true, for "done". Check internal invariants etc
|
||||||
// between calls to next.
|
// between calls to next.
|
||||||
bool next() {
|
bool next() {
|
||||||
assert(cs.getBytes() >= 0);
|
assert(cs.getBytes() >= 0);
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Arena arena;
|
Arena arena;
|
||||||
{
|
{
|
||||||
int numPointWrites = arbitrary.bounded(100);
|
int numPointWrites = arbitrary->bounded(100);
|
||||||
int numRangeWrites = arbitrary.bounded(100);
|
int numRangeWrites = arbitrary->bounded(100);
|
||||||
int64_t v = (writeVersion += arbitrary.bounded(10) ? arbitrary.bounded(10)
|
int64_t v =
|
||||||
: arbitrary.next());
|
(writeVersion +=
|
||||||
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next());
|
||||||
auto *writes =
|
auto *writes =
|
||||||
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
new (arena) ConflictSet::WriteRange[numPointWrites + numRangeWrites];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
while (int(keys.size()) < numPointWrites + numRangeWrites * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,7 +643,7 @@ struct TestDriver {
|
|||||||
rangesRemaining = numRangeWrites;
|
rangesRemaining = numRangeWrites;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -648,33 +662,20 @@ struct TestDriver {
|
|||||||
++iter;
|
++iter;
|
||||||
--rangesRemaining;
|
--rangesRemaining;
|
||||||
}
|
}
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
if (writes[i].end.len == 0) {
|
|
||||||
fprintf(stderr, "Write: {%s}\n", printable(writes[i].begin).c_str());
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Write: [%s, %s)\n",
|
|
||||||
printable(writes[i].begin).c_str(),
|
|
||||||
printable(writes[i].end).c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
assert(iter == keys.end());
|
assert(iter == keys.end());
|
||||||
assert(i == numPointWrites + numRangeWrites);
|
assert(i == numPointWrites + numRangeWrites);
|
||||||
|
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
|
||||||
fprintf(stderr, "Write @ %" PRId64 "\n", v);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Test non-canonical writes
|
// Test non-canonical writes
|
||||||
if (numPointWrites > 0) {
|
if (numPointWrites > 0) {
|
||||||
int overlaps = arbitrary.bounded(numPointWrites);
|
int overlaps = arbitrary->bounded(numPointWrites);
|
||||||
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
for (int i = 0; i < numPointWrites + numRangeWrites && overlaps > 0;
|
||||||
++i) {
|
++i) {
|
||||||
if (writes[i].end.len == 0) {
|
if (writes[i].end.len == 0) {
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
writes[i].end.len = keyLen;
|
writes[i].end.len = keyLen;
|
||||||
writes[i].end.p = begin;
|
writes[i].end.p = begin;
|
||||||
auto c =
|
auto c =
|
||||||
@@ -692,10 +693,10 @@ struct TestDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (arbitrary.bounded(2)) {
|
if (arbitrary->bounded(2)) {
|
||||||
// Shuffle writes
|
// Shuffle writes
|
||||||
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
for (int i = numPointWrites + numRangeWrites - 1; i > 0; --i) {
|
||||||
int j = arbitrary.bounded(i + 1);
|
int j = arbitrary->bounded(i + 1);
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(writes[i], writes[j]);
|
swap(writes[i], writes[j]);
|
||||||
@@ -704,7 +705,7 @@ struct TestDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldestVersion +=
|
oldestVersion +=
|
||||||
arbitrary.bounded(10) ? arbitrary.bounded(10) : arbitrary.next();
|
arbitrary->bounded(10) ? arbitrary->bounded(10) : arbitrary->next();
|
||||||
oldestVersion = std::min(oldestVersion, writeVersion);
|
oldestVersion = std::min(oldestVersion, writeVersion);
|
||||||
|
|
||||||
#ifdef THREAD_TEST
|
#ifdef THREAD_TEST
|
||||||
@@ -721,6 +722,20 @@ struct TestDriver {
|
|||||||
ready.wait();
|
ready.wait();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
for (int i = 0; i < numPointWrites + numRangeWrites; ++i) {
|
||||||
|
if (writes[i].end.len == 0) {
|
||||||
|
fprintf(stderr, "%p Write: {%s}\n", this,
|
||||||
|
printable(writes[i].begin).c_str());
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%p Write: [%s, %s)\n", this,
|
||||||
|
printable(writes[i].begin).c_str(),
|
||||||
|
printable(writes[i].end).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%p Write @ %" PRId64 "\n", this, v);
|
||||||
|
#endif
|
||||||
|
|
||||||
CALLGRIND_START_INSTRUMENTATION;
|
CALLGRIND_START_INSTRUMENTATION;
|
||||||
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
cs.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
CALLGRIND_STOP_INSTRUMENTATION;
|
CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
@@ -729,6 +744,10 @@ struct TestDriver {
|
|||||||
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
refImpl.addWrites(writes, numPointWrites + numRangeWrites, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
|
fprintf(stderr, "%p Set oldest version: %" PRId64 "\n", this,
|
||||||
|
oldestVersion);
|
||||||
|
#endif
|
||||||
cs.setOldestVersion(oldestVersion);
|
cs.setOldestVersion(oldestVersion);
|
||||||
if constexpr (kEnableAssertions) {
|
if constexpr (kEnableAssertions) {
|
||||||
refImpl.setOldestVersion(oldestVersion);
|
refImpl.setOldestVersion(oldestVersion);
|
||||||
@@ -739,24 +758,24 @@ struct TestDriver {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int numPointReads = arbitrary.bounded(100);
|
int numPointReads = arbitrary->bounded(100);
|
||||||
int numRangeReads = arbitrary.bounded(100);
|
int numRangeReads = arbitrary->bounded(100);
|
||||||
|
|
||||||
int64_t v = std::max<int64_t>(writeVersion - (arbitrary.bounded(10)
|
int64_t v = std::max<int64_t>(writeVersion - (arbitrary->bounded(10)
|
||||||
? arbitrary.bounded(10)
|
? arbitrary->bounded(10)
|
||||||
: arbitrary.next()),
|
: arbitrary->next()),
|
||||||
0);
|
0);
|
||||||
auto *reads =
|
auto *reads =
|
||||||
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
|
||||||
auto keys = set<std::string_view>(arena);
|
auto keys = set<std::string_view>(arena);
|
||||||
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
while (int(keys.size()) < numPointReads + numRangeReads * 2) {
|
||||||
if (!arbitrary.hasEntropy()) {
|
if (!arbitrary->hasEntropy()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int keyLen = prefixLen + arbitrary.bounded(kMaxKeySuffixLen);
|
int keyLen = prefixLen + arbitrary->bounded(kMaxKeySuffixLen);
|
||||||
auto *begin = new (arena) uint8_t[keyLen];
|
auto *begin = new (arena) uint8_t[keyLen];
|
||||||
memset(begin, prefixByte, prefixLen);
|
memset(begin, prefixByte, prefixLen);
|
||||||
arbitrary.randomBytes(begin + prefixLen, keyLen - prefixLen);
|
arbitrary->randomBytes(begin + prefixLen, keyLen - prefixLen);
|
||||||
keys.insert(std::string_view((const char *)begin, keyLen));
|
keys.insert(std::string_view((const char *)begin, keyLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,7 +784,7 @@ struct TestDriver {
|
|||||||
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
|
||||||
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
pointsRemaining > 0 || rangesRemaining > 0; ++i) {
|
||||||
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
|
||||||
? bool(arbitrary.bounded(2))
|
? bool(arbitrary->bounded(2))
|
||||||
: pointsRemaining > 0;
|
: pointsRemaining > 0;
|
||||||
if (pointRead) {
|
if (pointRead) {
|
||||||
assert(pointsRemaining > 0);
|
assert(pointsRemaining > 0);
|
||||||
@@ -787,10 +806,10 @@ struct TestDriver {
|
|||||||
reads[i].readVersion = v;
|
reads[i].readVersion = v;
|
||||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr, "Read: {%s} @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: {%s} @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Read: [%s, %s) @ %" PRId64 "\n",
|
fprintf(stderr, "%p Read: [%s, %s) @ %" PRId64 "\n", this,
|
||||||
printable(reads[i].begin).c_str(),
|
printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
@@ -839,24 +858,27 @@ struct TestDriver {
|
|||||||
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
refImpl.check(reads, results2, numPointReads + numRangeReads);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compareResults = [reads](ConflictSet::Result *results1,
|
auto compareResults = [reads, this](ConflictSet::Result *results1,
|
||||||
ConflictSet::Result *results2, int count) {
|
ConflictSet::Result *results2,
|
||||||
|
int count) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
if (results1[i] != results2[i]) {
|
if (results1[i] != results2[i]) {
|
||||||
if (reads[i].end.len == 0) {
|
if (reads[i].end.len == 0) {
|
||||||
fprintf(stderr,
|
|
||||||
"Expected %s, got %s for read of {%s} at version %" PRId64
|
|
||||||
"\n",
|
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
|
||||||
printable(reads[i].begin).c_str(), reads[i].readVersion);
|
|
||||||
} else {
|
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Expected %s, got %s for read of [%s, %s) at version %" PRId64
|
"%p Expected %s, got %s for read of {%s} at version %" PRId64
|
||||||
"\n",
|
"\n",
|
||||||
resultToStr(results2[i]), resultToStr(results1[i]),
|
(void *)this, resultToStr(results2[i]),
|
||||||
printable(reads[i].begin).c_str(),
|
resultToStr(results1[i]), printable(reads[i].begin).c_str(),
|
||||||
printable(reads[i].end).c_str(), reads[i].readVersion);
|
reads[i].readVersion);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%p Expected %s, got %s for read of [%s, %s) at version "
|
||||||
|
"%" PRId64 "\n",
|
||||||
|
(void *)this, resultToStr(results2[i]),
|
||||||
|
resultToStr(results1[i]),
|
||||||
|
printable(reads[i].begin).c_str(),
|
||||||
|
printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+10
-7
@@ -117,15 +117,18 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
filter_args = "-f ConflictSet.cpp -f LongestCommonPrefix.h"
|
||||||
|
}
|
||||||
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_BUILD_TYPE=Debug -DDISABLE_TSAN=ON")
|
CleanBuildAndTest("-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DCMAKE_BUILD_TYPE=Debug -DDISABLE_TSAN=ON")
|
||||||
sh '''
|
sh """
|
||||||
gcovr -f ConflictSet.cpp --cobertura > build/coverage.xml
|
gcovr ${filter_args} --cobertura > build/coverage.xml
|
||||||
'''
|
"""
|
||||||
recordCoverage qualityGates: [[criticality: 'NOTE', metric: 'MODULE']], tools: [[parser: 'COBERTURA', pattern: 'build/coverage.xml']]
|
recordCoverage qualityGates: [[criticality: 'NOTE', metric: 'MODULE']], tools: [[parser: 'COBERTURA', pattern: 'build/coverage.xml']]
|
||||||
sh '''
|
sh """
|
||||||
# Suppress again, because we haven't dealt with function multi-versioning for x86 yet
|
gcovr ${filter_args}
|
||||||
# gcovr -f ConflictSet.cpp --fail-under-line 100 > /dev/null
|
gcovr ${filter_args} --fail-under-line 100 > /dev/null
|
||||||
'''
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <bit>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef HAS_AVX
|
||||||
|
#include <immintrin.h>
|
||||||
|
#elif HAS_ARM_NEON
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __SANITIZE_THREAD__
|
||||||
|
#if defined(__has_feature)
|
||||||
|
#if __has_feature(thread_sanitizer)
|
||||||
|
#define __SANITIZE_THREAD__
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAS_AVX) || defined(HAS_ARM_NEON)
|
||||||
|
constexpr int kStride = 64;
|
||||||
|
#else
|
||||||
|
constexpr int kStride = 16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr int kUnrollFactor = 4;
|
||||||
|
|
||||||
|
inline bool compareStride(const uint8_t *ap, const uint8_t *bp) {
|
||||||
|
#if defined(HAS_ARM_NEON)
|
||||||
|
static_assert(kStride == 64);
|
||||||
|
uint8x16_t x[4]; // GCOVR_EXCL_LINE
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
x[i] = vceqq_u8(vld1q_u8(ap + i * 16), vld1q_u8(bp + i * 16));
|
||||||
|
}
|
||||||
|
auto results = vreinterpretq_u16_u8(
|
||||||
|
vandq_u8(vandq_u8(x[0], x[1]), vandq_u8(x[2], x[3])));
|
||||||
|
bool eq = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(results, 4)), 0) ==
|
||||||
|
uint64_t(-1);
|
||||||
|
#elif defined(HAS_AVX)
|
||||||
|
static_assert(kStride == 64);
|
||||||
|
__m128i x[4]; // GCOVR_EXCL_LINE
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
x[i] = _mm_cmpeq_epi8(_mm_loadu_si128((__m128i *)(ap + i * 16)),
|
||||||
|
_mm_loadu_si128((__m128i *)(bp + i * 16)));
|
||||||
|
}
|
||||||
|
auto eq =
|
||||||
|
_mm_movemask_epi8(_mm_and_si128(_mm_and_si128(x[0], x[1]),
|
||||||
|
_mm_and_si128(x[2], x[3]))) == 0xffff;
|
||||||
|
#else
|
||||||
|
// Hope it gets vectorized
|
||||||
|
auto eq = memcmp(ap, bp, kStride) == 0;
|
||||||
|
#endif
|
||||||
|
return eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: ap[:kStride] != bp[:kStride]
|
||||||
|
inline int firstNeqStride(const uint8_t *ap, const uint8_t *bp) {
|
||||||
|
#if defined(HAS_AVX)
|
||||||
|
static_assert(kStride == 64);
|
||||||
|
uint64_t c[kStride / 16]; // GCOVR_EXCL_LINE
|
||||||
|
for (int i = 0; i < kStride; i += 16) {
|
||||||
|
const auto a = _mm_loadu_si128((__m128i *)(ap + i));
|
||||||
|
const auto b = _mm_loadu_si128((__m128i *)(bp + i));
|
||||||
|
const auto compared = _mm_cmpeq_epi8(a, b);
|
||||||
|
c[i / 16] = _mm_movemask_epi8(compared) & 0xffff;
|
||||||
|
}
|
||||||
|
return std::countr_zero(~(c[0] | c[1] << 16 | c[2] << 32 | c[3] << 48));
|
||||||
|
#elif defined(HAS_ARM_NEON)
|
||||||
|
static_assert(kStride == 64);
|
||||||
|
for (int i = 0; i < kStride; i += 16) {
|
||||||
|
// 0xff for each match
|
||||||
|
uint16x8_t results =
|
||||||
|
vreinterpretq_u16_u8(vceqq_u8(vld1q_u8(ap + i), vld1q_u8(bp + i)));
|
||||||
|
// 0xf for each mismatch
|
||||||
|
uint64_t bitfield =
|
||||||
|
~vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(results, 4)), 0);
|
||||||
|
if (bitfield) {
|
||||||
|
return i + (std::countr_zero(bitfield) >> 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
#else
|
||||||
|
int i = 0;
|
||||||
|
for (; i < kStride - 1; ++i) {
|
||||||
|
if (*ap++ != *bp++) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets covered in local development
|
||||||
|
// GCOVR_EXCL_START
|
||||||
|
#if defined(HAS_AVX) && !defined(__SANITIZE_THREAD__)
|
||||||
|
__attribute__((target("avx512f,avx512bw"))) inline int
|
||||||
|
longestCommonPrefix(const uint8_t *ap, const uint8_t *bp, int cl) {
|
||||||
|
int i = 0;
|
||||||
|
int end = cl & ~63;
|
||||||
|
while (i < end) {
|
||||||
|
const uint64_t eq =
|
||||||
|
_mm512_cmpeq_epi8_mask(_mm512_loadu_epi8(ap), _mm512_loadu_epi8(bp));
|
||||||
|
if (eq != uint64_t(-1)) {
|
||||||
|
return i + std::countr_one(eq);
|
||||||
|
}
|
||||||
|
i += 64;
|
||||||
|
ap += 64;
|
||||||
|
bp += 64;
|
||||||
|
}
|
||||||
|
if (i < cl) {
|
||||||
|
const uint64_t mask = (uint64_t(1) << (cl - i)) - 1;
|
||||||
|
const uint64_t eq = _mm512_cmpeq_epi8_mask(
|
||||||
|
_mm512_maskz_loadu_epi8(mask, ap), _mm512_maskz_loadu_epi8(mask, bp));
|
||||||
|
return i + std::countr_one(eq & mask);
|
||||||
|
}
|
||||||
|
assert(i == cl);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
__attribute__((target("default")))
|
||||||
|
#endif
|
||||||
|
// GCOVR_EXCL_STOP
|
||||||
|
|
||||||
|
inline int
|
||||||
|
longestCommonPrefix(const uint8_t *ap, const uint8_t *bp, int cl) {
|
||||||
|
if (!(cl >= 0)) {
|
||||||
|
__builtin_unreachable(); // GCOVR_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int end;
|
||||||
|
|
||||||
|
// kStride * kUnrollCount at a time
|
||||||
|
end = cl & ~(kStride * kUnrollFactor - 1);
|
||||||
|
while (i < end) {
|
||||||
|
for (int j = 0; j < kUnrollFactor; ++j) {
|
||||||
|
if (!compareStride(ap, bp)) {
|
||||||
|
return i + firstNeqStride(ap, bp);
|
||||||
|
}
|
||||||
|
i += kStride;
|
||||||
|
ap += kStride;
|
||||||
|
bp += kStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kStride at a time
|
||||||
|
end = cl & ~(kStride - 1);
|
||||||
|
while (i < end) {
|
||||||
|
if (!compareStride(ap, bp)) {
|
||||||
|
return i + firstNeqStride(ap, bp);
|
||||||
|
}
|
||||||
|
i += kStride;
|
||||||
|
ap += kStride;
|
||||||
|
bp += kStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// word at a time
|
||||||
|
end = cl & ~(sizeof(uint64_t) - 1);
|
||||||
|
while (i < end) {
|
||||||
|
uint64_t a; // GCOVR_EXCL_LINE
|
||||||
|
uint64_t b; // GCOVR_EXCL_LINE
|
||||||
|
memcpy(&a, ap, 8);
|
||||||
|
memcpy(&b, bp, 8);
|
||||||
|
const auto mismatched = a ^ b;
|
||||||
|
if (mismatched) {
|
||||||
|
return i + std::countr_zero(mismatched) / 8;
|
||||||
|
}
|
||||||
|
i += 8;
|
||||||
|
ap += 8;
|
||||||
|
bp += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte at a time
|
||||||
|
while (i < cl) {
|
||||||
|
if (*ap != *bp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++ap;
|
||||||
|
++bp;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
@@ -1,86 +1,39 @@
|
|||||||
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.
|
||||||
|
|
||||||
Intended to replace FoundationDB's skip list.
|
Intended as an alternative to FoundationDB's skip list.
|
||||||
|
|
||||||
Hardware for all benchmarks is a mac m1 2020.
|
Hardware for all benchmarks is an AMD Ryzen 9 7900 with (2x32GB) 5600MT/s CL28-34-34-89 1.35V RAM
|
||||||
|
|
||||||
# FoundationDB's benchmark
|
# Microbenchmark
|
||||||
|
|
||||||
## Skip list
|
## Skip list
|
||||||
|
|
||||||
```
|
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||||
New conflict set: 1.957 sec
|
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||||
0.639 Mtransactions/sec
|
| 172.03 | 5,812,791.77 | 0.4% | 3,130.62 | 879.00 | 3.562 | 509.23 | 0.0% | 0.01 | `point reads`
|
||||||
2.555 Mkeys/sec
|
| 167.44 | 5,972,130.71 | 0.2% | 3,065.14 | 862.27 | 3.555 | 494.30 | 0.0% | 0.01 | `prefix reads`
|
||||||
Detect only: 1.845 sec
|
| 238.77 | 4,188,130.84 | 0.9% | 3,589.93 | 1,259.30 | 2.851 | 637.12 | 0.0% | 0.01 | `range reads`
|
||||||
0.678 Mtransactions/sec
|
| 424.01 | 2,358,426.70 | 0.2% | 5,620.05 | 2,242.35 | 2.506 | 854.80 | 1.7% | 0.01 | `point writes`
|
||||||
2.710 Mkeys/sec
|
| 418.45 | 2,389,780.56 | 0.4% | 5,525.07 | 2,211.05 | 2.499 | 831.71 | 1.7% | 0.01 | `prefix writes`
|
||||||
Skiplist only: 1.263 sec
|
| 254.87 | 3,923,568.88 | 2.6% | 3,187.01 | 1,366.50 | 2.332 | 529.11 | 2.7% | 0.02 | `range writes`
|
||||||
0.990 Mtransactions/sec
|
| 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`
|
||||||
3.960 Mkeys/sec
|
| 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`
|
||||||
Performance counters:
|
| 21.63 | 46,231,564.03 | 1.0% | 448.00 | 107.14 | 4.181 | 84.00 | 0.0% | 0.01 | `create and destroy`
|
||||||
Build: 0.0546
|
|
||||||
Add: 0.0563
|
|
||||||
Detect: 1.84
|
|
||||||
D.Sort: 0.412
|
|
||||||
D.Combine: 0.0141
|
|
||||||
D.CheckRead: 0.671
|
|
||||||
D.CheckIntraBatch: 0.0068
|
|
||||||
D.MergeWrite: 0.592
|
|
||||||
D.RemoveBefore: 0.146
|
|
||||||
```
|
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
## Radix tree (this implementation)
|
||||||
|
|
||||||
```
|
| ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark
|
||||||
New conflict set: 1.366 sec
|
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|
||||||
0.915 Mtransactions/sec
|
| 10.80 | 92,600,541.52 | 0.6% | 180.38 | 54.49 | 3.310 | 41.51 | 0.4% | 0.01 | `point reads`
|
||||||
3.660 Mkeys/sec
|
| 15.00 | 66,687,691.68 | 0.4% | 278.44 | 76.44 | 3.642 | 55.56 | 0.3% | 0.01 | `prefix reads`
|
||||||
Detect only: 1.248 sec
|
| 36.81 | 27,163,394.61 | 0.4% | 795.06 | 187.91 | 4.231 | 142.67 | 0.2% | 0.01 | `range reads`
|
||||||
1.002 Mtransactions/sec
|
| 18.14 | 55,137,674.01 | 1.2% | 338.19 | 92.86 | 3.642 | 42.81 | 0.4% | 0.01 | `point writes`
|
||||||
4.007 Mkeys/sec
|
| 33.19 | 30,127,119.71 | 0.1% | 681.03 | 170.05 | 4.005 | 98.68 | 0.2% | 0.01 | `prefix writes`
|
||||||
Skiplist only: 0.573 sec
|
| 37.37 | 26,759,432.70 | 1.9% | 779.70 | 195.45 | 3.989 | 114.21 | 0.0% | 0.01 | `range writes`
|
||||||
2.182 Mtransactions/sec
|
| 74.36 | 13,448,582.47 | 1.9% | 1,425.68 | 389.08 | 3.664 | 258.88 | 0.1% | 0.01 | `monotonic increasing point writes`
|
||||||
8.730 Mkeys/sec
|
| 316,928.00 | 3,155.29 | 1.5% | 3,992,986.00 | 1,699,813.00 | 2.349 | 806,226.50 | 0.0% | 0.01 | `worst case for radix tree`
|
||||||
Performance counters:
|
| 75.26 | 13,286,517.16 | 0.5% | 1,590.01 | 386.67 | 4.112 | 258.00 | 0.0% | 0.01 | `create and destroy`
|
||||||
Build: 0.0594
|
|
||||||
Add: 0.0572
|
|
||||||
Detect: 1.25
|
|
||||||
D.Sort: 0.418
|
|
||||||
D.Combine: 0.0149
|
|
||||||
D.CheckRead: 0.232
|
|
||||||
D.CheckIntraBatch: 0.0067
|
|
||||||
D.MergeWrite: 0.341
|
|
||||||
D.RemoveBefore: 0.232
|
|
||||||
```
|
|
||||||
|
|
||||||
# Our benchmark
|
|
||||||
|
|
||||||
## Skip list
|
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|
||||||
| 245.99 | 4,065,232.81 | 0.3% | 0.01 | `point reads`
|
|
||||||
| 265.93 | 3,760,430.49 | 0.2% | 0.01 | `prefix reads`
|
|
||||||
| 485.30 | 2,060,569.50 | 0.2% | 0.01 | `range reads`
|
|
||||||
| 449.60 | 2,224,195.17 | 0.4% | 0.01 | `point writes`
|
|
||||||
| 441.76 | 2,263,688.18 | 1.1% | 0.01 | `prefix writes`
|
|
||||||
| 245.42 | 4,074,647.54 | 2.4% | 0.02 | `range writes`
|
|
||||||
| 572.80 | 1,745,810.06 | 1.3% | 0.01 | `monotonic increasing point writes`
|
|
||||||
| 154,819.33 | 6,459.14 | 0.9% | 0.01 | `worst case for radix tree`
|
|
||||||
|
|
||||||
## Radix tree (this implementation)
|
|
||||||
|
|
||||||
| ns/op | op/s | err% | total | benchmark
|
|
||||||
|--------------------:|--------------------:|--------:|----------:|:----------
|
|
||||||
| 19.17 | 52,163,930.66 | 0.1% | 0.01 | `point reads`
|
|
||||||
| 23.68 | 42,224,388.21 | 0.7% | 0.01 | `prefix reads`
|
|
||||||
| 63.30 | 15,797,506.06 | 0.9% | 0.01 | `range reads`
|
|
||||||
| 29.66 | 33,720,994.74 | 0.3% | 0.01 | `point writes`
|
|
||||||
| 43.50 | 22,987,781.25 | 1.0% | 0.01 | `prefix writes`
|
|
||||||
| 50.00 | 20,000,000.00 | 0.8% | 0.01 | `range writes`
|
|
||||||
| 103.25 | 9,684,786.47 | 2.9% | 0.01 | `monotonic increasing point writes`
|
|
||||||
| 1,181,500.00 | 846.38 | 2.3% | 0.01 | `worst case for radix tree`
|
|
||||||
|
|
||||||
# "Real data" test
|
# "Real data" test
|
||||||
|
|
||||||
@@ -89,13 +42,13 @@ Point queries only, best of three runs. Gc ratio is the ratio of time spent doin
|
|||||||
## skip list
|
## skip list
|
||||||
|
|
||||||
```
|
```
|
||||||
Check: 11.3385 seconds, 329.718 MB/s, Add: 5.35612 seconds, 131.072 MB/s, Gc ratio: 45.7173%
|
Check: 4.47891 seconds, 364.05 MB/s, Add: 4.55599 seconds, 123.058 MB/s, Gc ratio: 37.1145%
|
||||||
```
|
```
|
||||||
|
|
||||||
## radix tree
|
## radix tree
|
||||||
|
|
||||||
```
|
```
|
||||||
Check: 2.48583 seconds, 1503.93 MB/s, Add: 2.12768 seconds, 329.954 MB/s, Gc ratio: 41.7943%
|
Check: 0.910234 seconds, 1791.35 MB/s, Add: 1.25908 seconds, 445.287 MB/s, Gc ratio: 44.0415%
|
||||||
```
|
```
|
||||||
|
|
||||||
## hash table
|
## hash table
|
||||||
@@ -103,5 +56,5 @@ Check: 2.48583 seconds, 1503.93 MB/s, Add: 2.12768 seconds, 329.954 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)
|
(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: 1.83386 seconds, 2038.6 MB/s, Add: 0.601411 seconds, 1167.32 MB/s, Gc ratio: 48.9776%
|
Check: 0.804094 seconds, 2027.81 MB/s, Add: 0.652952 seconds, 858.645 MB/s, Gc ratio: 35.3885%
|
||||||
```
|
```
|
||||||
|
|||||||
+310
@@ -0,0 +1,310 @@
|
|||||||
|
#include <atomic>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ConflictSet.h"
|
||||||
|
#include "third_party/nadeau.h"
|
||||||
|
|
||||||
|
std::atomic<int64_t> transactions;
|
||||||
|
|
||||||
|
constexpr int kBaseSearchDepth = 32;
|
||||||
|
constexpr int kWindowSize = 10000000;
|
||||||
|
|
||||||
|
std::basic_string<uint8_t> numToKey(int64_t num) {
|
||||||
|
std::basic_string<uint8_t> result;
|
||||||
|
result.resize(kBaseSearchDepth + sizeof(int64_t));
|
||||||
|
memset(result.data(), 0, kBaseSearchDepth);
|
||||||
|
int64_t be = __builtin_bswap64(num);
|
||||||
|
memcpy(result.data() + kBaseSearchDepth, &be, sizeof(int64_t));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void workload(weaselab::ConflictSet *cs) {
|
||||||
|
int64_t version = kWindowSize;
|
||||||
|
cs->addWrites(nullptr, 0, version);
|
||||||
|
for (;; transactions.fetch_add(1, std::memory_order_relaxed)) {
|
||||||
|
// Reads
|
||||||
|
{
|
||||||
|
auto beginK = numToKey(version - kWindowSize);
|
||||||
|
auto endK = numToKey(version - 1);
|
||||||
|
auto pointRv = version - kWindowSize + rand() % kWindowSize + 1;
|
||||||
|
auto pointK = numToKey(pointRv);
|
||||||
|
weaselab::ConflictSet::ReadRange reads[] = {
|
||||||
|
{
|
||||||
|
{pointK.data(), int(pointK.size())},
|
||||||
|
{nullptr, 0},
|
||||||
|
pointRv,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{beginK.data(), int(beginK.size())},
|
||||||
|
{endK.data(), int(endK.size())},
|
||||||
|
version - 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
weaselab::ConflictSet::Result result[sizeof(reads) / sizeof(reads[0])];
|
||||||
|
cs->check(reads, result, sizeof(reads) / sizeof(reads[0]));
|
||||||
|
// for (int i = 0; i < sizeof(reads) / sizeof(reads[0]); ++i) {
|
||||||
|
// if (result[i] != weaselab::ConflictSet::Commit) {
|
||||||
|
// fprintf(stderr, "Unexpected conflict: [%s, %s) @ %" PRId64 "\n",
|
||||||
|
// printable(reads[i].begin).c_str(),
|
||||||
|
// printable(reads[i].end).c_str(), reads[i].readVersion);
|
||||||
|
// abort();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// Writes
|
||||||
|
{
|
||||||
|
weaselab::ConflictSet::WriteRange w;
|
||||||
|
auto k = numToKey(version);
|
||||||
|
w.begin.p = k.data();
|
||||||
|
w.end.len = 0;
|
||||||
|
if (version % (kWindowSize / 2) == 0) {
|
||||||
|
for (int l = 0; l <= k.size(); ++l) {
|
||||||
|
w.begin.len = l;
|
||||||
|
cs->addWrites(&w, 1, version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.begin.len = k.size();
|
||||||
|
cs->addWrites(&w, 1, version);
|
||||||
|
int64_t beginN = version - kWindowSize + rand() % kWindowSize;
|
||||||
|
auto b = numToKey(beginN);
|
||||||
|
auto e = numToKey(beginN + 1000);
|
||||||
|
w.begin.p = b.data();
|
||||||
|
w.begin.len = b.size();
|
||||||
|
w.end.p = e.data();
|
||||||
|
w.end.len = e.size();
|
||||||
|
cs->addWrites(&w, 1, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// GC
|
||||||
|
cs->setOldestVersion(version - kWindowSize);
|
||||||
|
++version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from getaddrinfo man page
|
||||||
|
int getListenFd(const char *node, const char *service) {
|
||||||
|
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *result, *rp;
|
||||||
|
int sfd, s;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||||
|
hints.ai_socktype = SOCK_STREAM; /* stream socket */
|
||||||
|
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
|
||||||
|
hints.ai_protocol = 0; /* Any protocol */
|
||||||
|
hints.ai_canonname = nullptr;
|
||||||
|
hints.ai_addr = nullptr;
|
||||||
|
hints.ai_next = nullptr;
|
||||||
|
|
||||||
|
s = getaddrinfo(node, service, &hints, &result);
|
||||||
|
if (s != 0) {
|
||||||
|
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getaddrinfo() returns a list of address structures.
|
||||||
|
Try each address until we successfully bind(2).
|
||||||
|
If socket(2) (or bind(2)) fails, we (close the socket
|
||||||
|
and) try the next address. */
|
||||||
|
|
||||||
|
for (rp = result; rp != nullptr; rp = rp->ai_next) {
|
||||||
|
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
|
if (sfd == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = 1;
|
||||||
|
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
||||||
|
|
||||||
|
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
|
||||||
|
break; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result); /* No longer needed */
|
||||||
|
|
||||||
|
if (rp == nullptr) { /* No address succeeded */
|
||||||
|
fprintf(stderr, "Could not bind\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv = listen(sfd, SOMAXCONN);
|
||||||
|
if (rv) {
|
||||||
|
perror("listen()");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP response
|
||||||
|
//
|
||||||
|
std::string_view part1 =
|
||||||
|
"HTTP/1.1 200 OK \r\nContent-type: text/plain; version=0.0.4; "
|
||||||
|
"charset=utf-8; escaping=values\r\nContent-Length: ";
|
||||||
|
// Decimal content length
|
||||||
|
std::string_view part2 = "\r\n\r\n";
|
||||||
|
// Body
|
||||||
|
|
||||||
|
double toSeconds(timeval t) {
|
||||||
|
return double(t.tv_sec) + double(t.tv_usec) * 1e-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
struct PerfCounter {
|
||||||
|
explicit PerfCounter(int event) {
|
||||||
|
struct perf_event_attr pe;
|
||||||
|
|
||||||
|
memset(&pe, 0, sizeof(pe));
|
||||||
|
pe.type = PERF_TYPE_HARDWARE;
|
||||||
|
pe.size = sizeof(pe);
|
||||||
|
pe.config = event;
|
||||||
|
pe.inherit = 1;
|
||||||
|
pe.exclude_kernel = 1;
|
||||||
|
pe.exclude_hv = 1;
|
||||||
|
|
||||||
|
fd = perf_event_open(&pe, 0, -1, -1, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
fprintf(stderr, "Error opening leader %llx\n", pe.config);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t total() {
|
||||||
|
int64_t count;
|
||||||
|
if (read(fd, &count, sizeof(count)) != sizeof(count)) {
|
||||||
|
perror("read instructions from perf");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
~PerfCounter() { close(fd); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fd;
|
||||||
|
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||||
|
int cpu, int group_fd, unsigned long flags) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = syscall(SYS_perf_event_open, hw_event, pid, cpu, group_fd, flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
struct PerfCounter {
|
||||||
|
explicit PerPerfCounter(int) {}
|
||||||
|
int64_t total() { return 0; }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 3) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int listenFd = getListenFd(argv[1], argv[2]);
|
||||||
|
|
||||||
|
weaselab::ConflictSet cs{0};
|
||||||
|
weaselab::ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
|
||||||
|
PerfCounter instructions{PERF_COUNT_HW_INSTRUCTIONS};
|
||||||
|
PerfCounter cycles{PERF_COUNT_HW_CPU_CYCLES};
|
||||||
|
auto w = std::thread{workload, &cs};
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct sockaddr_storage peer_addr = {};
|
||||||
|
socklen_t peer_addr_len = sizeof(peer_addr);
|
||||||
|
const int connfd =
|
||||||
|
accept(listenFd, (struct sockaddr *)&peer_addr, &peer_addr_len);
|
||||||
|
|
||||||
|
std::string body;
|
||||||
|
|
||||||
|
rusage r;
|
||||||
|
getrusage(RUSAGE_SELF, &r);
|
||||||
|
body += "# HELP process_cpu_seconds_total Total user and system CPU time "
|
||||||
|
"spent in seconds.\n# TYPE process_cpu_seconds_total counter\n"
|
||||||
|
"process_cpu_seconds_total ";
|
||||||
|
body += std::to_string(toSeconds(r.ru_utime) + toSeconds(r.ru_stime));
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP process_resident_memory_bytes Resident memory size in "
|
||||||
|
"bytes.\n# TYPE process_resident_memory_bytes gauge\n"
|
||||||
|
"process_resident_memory_bytes ";
|
||||||
|
body += std::to_string(getCurrentRSS());
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP transactions_total Total number of transactions\n"
|
||||||
|
"# TYPE transactions_total counter\n"
|
||||||
|
"transactions_total ";
|
||||||
|
body += std::to_string(transactions.load(std::memory_order_relaxed));
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP instructions_total Total number of instructions\n"
|
||||||
|
"# TYPE instructions_total counter\n"
|
||||||
|
"instructions_total ";
|
||||||
|
body += std::to_string(instructions.total());
|
||||||
|
body += "\n";
|
||||||
|
body += "# HELP cycles_total Total number of cycles\n"
|
||||||
|
"# TYPE cycles_total counter\n"
|
||||||
|
"cycles_total ";
|
||||||
|
body += std::to_string(cycles.total());
|
||||||
|
body += "\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
|
body += "# HELP ";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += metrics[i].help;
|
||||||
|
body += "\n";
|
||||||
|
body += "# TYPE ";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += metrics[i].type == metrics[i].Counter ? "counter" : "gauge";
|
||||||
|
body += "\n";
|
||||||
|
body += metrics[i].name;
|
||||||
|
body += " ";
|
||||||
|
body += std::to_string(metrics[i].getValue());
|
||||||
|
body += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto len = std::to_string(body.size());
|
||||||
|
iovec iov[] = {
|
||||||
|
{(void *)part1.data(), part1.size()},
|
||||||
|
{(void *)len.data(), len.size()},
|
||||||
|
{(void *)part2.data(), part2.size()},
|
||||||
|
{(void *)body.data(), body.size()},
|
||||||
|
};
|
||||||
|
int written;
|
||||||
|
do {
|
||||||
|
written = writev(connfd, iov, sizeof(iov) / sizeof(iov[0]));
|
||||||
|
} while (written < 0 && errno == EINTR);
|
||||||
|
close(connfd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
fprintf(stderr, "Expected ./%s <host> <port>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -778,10 +778,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
for (int s = stripes - 1; s >= 0; s--) {
|
for (int s = stripes - 1; s >= 0; s--) {
|
||||||
for (int i = 0; i * 2 < ss; ++i) {
|
for (int i = 0; i * 2 < ss; ++i) {
|
||||||
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
const auto &w = combinedWriteConflictRanges[s * stripeSize / 2 + i];
|
||||||
#if DEBUG_VERBOSE
|
|
||||||
printf("Write begin: %s\n", printable(w.begin).c_str());
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
values[i * 2] = w.first;
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.second;
|
values[i * 2 + 1] = w.second;
|
||||||
keyUpdates += 3;
|
keyUpdates += 3;
|
||||||
|
|||||||
+50
-17
@@ -13,25 +13,58 @@ int main(int argc, char **argv) {
|
|||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << t.rdbuf();
|
buffer << t.rdbuf();
|
||||||
auto str = buffer.str();
|
auto str = buffer.str();
|
||||||
TestDriver<ConflictSet, !PERF_TEST> driver{(const uint8_t *)str.data(),
|
Arbitrary arbitrary({(const uint8_t *)str.data(), str.size()});
|
||||||
str.size()};
|
TestDriver<ConflictSet, !PERF_TEST> driver1{arbitrary};
|
||||||
while (!driver.next())
|
TestDriver<ConflictSet, !PERF_TEST> driver2{arbitrary};
|
||||||
;
|
bool done1 = false;
|
||||||
if (!driver.ok) {
|
bool done2 = false;
|
||||||
abort();
|
for (;;) {
|
||||||
|
if (!done1) {
|
||||||
|
done1 = driver1.next();
|
||||||
|
if (!driver1.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done2) {
|
||||||
|
done2 = driver2.next();
|
||||||
|
if (!driver2.ok) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done1 && done2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConflictSet::MetricsV1 *metrics;
|
{
|
||||||
int metricsCount;
|
ConflictSet::MetricsV1 *metrics;
|
||||||
driver.cs.getMetricsV1(&metrics, &metricsCount);
|
int metricsCount;
|
||||||
printf("#################### METRICS FOR %s ####################\n",
|
driver1.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
argv[i]);
|
printf("#################### METRICS for ConflictSet 1 for %s "
|
||||||
for (int i = 0; i < metricsCount; ++i) {
|
"####################\n",
|
||||||
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
argv[i]);
|
||||||
printf("# TYPE %s %s\n", metrics[i].name,
|
for (int i = 0; i < metricsCount; ++i) {
|
||||||
metrics[i].type == metrics[i].Counter ? "counter" : "gauge");
|
printf("# HELP %s %s\n", metrics[i].name, metrics[i].help);
|
||||||
printf("%s %g\n", metrics[i].name, metrics[i].getValue());
|
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());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ConflictSet::MetricsV1 *metrics;
|
||||||
|
int metricsCount;
|
||||||
|
driver2.cs.getMetricsV1(&metrics, &metricsCount);
|
||||||
|
printf("#################### METRICS for ConflictSet 2 for %s "
|
||||||
|
"####################\n",
|
||||||
|
argv[i]);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
}
|
}
|
||||||
puts("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__aarch64_cas8_relax
|
__aarch64_ldadd8_relax
|
||||||
__getauxval@GLIBC_2.17
|
__getauxval@GLIBC_2.17
|
||||||
__stack_chk_fail@GLIBC_2.17
|
__stack_chk_fail@GLIBC_2.17
|
||||||
__stack_chk_guard@GLIBC_2.17
|
__stack_chk_guard@GLIBC_2.17
|
||||||
|
|||||||
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.
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