3 Commits

Author SHA1 Message Date
a4d1f91670 Update README benchmark after adding getBytes
All checks were successful
Tests / Clang total: 1096, passed: 1096
Clang |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc] total: 1096, passed: 1096
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap:
Tests / Release [gcc,aarch64] total: 824, passed: 824
Tests / Coverage total: 823, passed: 823
weaselab/conflict-set/pipeline/head This commit looks good
2024-03-20 16:26:05 -07:00
b7cdecaf71 Document thread-safety in terms of constness 2024-03-20 16:16:15 -07:00
cda28643a6 Fix ConflictSet_getBytes 2024-03-20 16:15:43 -07:00
5 changed files with 79 additions and 57 deletions

View File

@@ -2607,16 +2607,25 @@ ConflictSet_check(void *cs, const ConflictSet_ReadRange *reads,
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count, ConflictSet_addWrites(void *cs, const ConflictSet_WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
((ConflictSet::Impl *)cs)->addWrites(writes, count, writeVersion); auto *impl = ((ConflictSet::Impl *)cs);
mallocBytesDelta = 0;
impl->addWrites(writes, count, writeVersion);
impl->totalBytes += mallocBytesDelta;
} }
__attribute__((__visibility__("default"))) void __attribute__((__visibility__("default"))) void
ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) { ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion); auto *impl = ((ConflictSet::Impl *)cs);
mallocBytesDelta = 0;
impl->setOldestVersion(oldestVersion);
impl->totalBytes += mallocBytesDelta;
} }
__attribute__((__visibility__("default"))) void * __attribute__((__visibility__("default"))) void *
ConflictSet_create(int64_t oldestVersion) { ConflictSet_create(int64_t oldestVersion) {
return new (safe_malloc(sizeof(ConflictSet::Impl))) mallocBytesDelta = 0;
auto *result = new (safe_malloc(sizeof(ConflictSet::Impl)))
ConflictSet::Impl{oldestVersion}; ConflictSet::Impl{oldestVersion};
result->totalBytes += mallocBytesDelta;
return result;
} }
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) { __attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
using Impl = ConflictSet::Impl; using Impl = ConflictSet::Impl;
@@ -2630,6 +2639,12 @@ ConflictSet_getBytes(void *cs) {
} }
} }
// Make sure abi is well-defined
static_assert(std::is_standard_layout_v<ConflictSet::Result>);
static_assert(std::is_standard_layout_v<ConflictSet::Key>);
static_assert(std::is_standard_layout_v<ConflictSet::ReadRange>);
static_assert(std::is_standard_layout_v<ConflictSet::WriteRange>);
namespace { namespace {
std::string getSearchPathPrintable(Node *n) { std::string getSearchPathPrintable(Node *n) {

100
README.md
View File

@@ -9,49 +9,49 @@ Hardware for all benchmarks is a mac m1 2020.
## Skip list ## Skip list
``` ```
New conflict set: 1.964 sec New conflict set: 1.962 sec
0.637 Mtransactions/sec 0.637 Mtransactions/sec
2.546 Mkeys/sec 2.548 Mkeys/sec
Detect only: 1.859 sec Detect only: 1.842 sec
0.672 Mtransactions/sec 0.679 Mtransactions/sec
2.690 Mkeys/sec 2.714 Mkeys/sec
Skiplist only: 1.275 sec Skiplist only: 1.261 sec
0.980 Mtransactions/sec 0.991 Mtransactions/sec
3.921 Mkeys/sec 3.964 Mkeys/sec
Performance counters: Performance counters:
Build: 0.0496 Build: 0.0597
Add: 0.0539 Add: 0.0587
Detect: 1.86 Detect: 1.84
D.Sort: 0.412 D.Sort: 0.411
D.Combine: 0.0139 D.Combine: 0.0136
D.CheckRead: 0.682 D.CheckRead: 0.67
D.CheckIntraBatch: 0.00673 D.CheckIntraBatch: 0.00671
D.MergeWrite: 0.593 D.MergeWrite: 0.592
D.RemoveBefore: 0.148 D.RemoveBefore: 0.146
``` ```
## Radix tree (this implementation) ## Radix tree (this implementation)
``` ```
New conflict set: 1.289 sec New conflict set: 1.285 sec
0.970 Mtransactions/sec 0.973 Mtransactions/sec
3.879 Mkeys/sec 3.892 Mkeys/sec
Detect only: 1.199 sec Detect only: 1.196 sec
1.043 Mtransactions/sec 1.045 Mtransactions/sec
4.170 Mkeys/sec 4.180 Mkeys/sec
Skiplist only: 0.542 sec Skiplist only: 0.539 sec
2.305 Mtransactions/sec 2.320 Mtransactions/sec
9.220 Mkeys/sec 9.281 Mkeys/sec
Performance counters: Performance counters:
Build: 0.0395 Build: 0.0377
Add: 0.0492 Add: 0.0493
Detect: 1.2 Detect: 1.2
D.Sort: 0.411 D.Sort: 0.41
D.Combine: 0.0135 D.Combine: 0.0135
D.CheckRead: 0.22 D.CheckRead: 0.219
D.CheckIntraBatch: 0.00652 D.CheckIntraBatch: 0.00654
D.MergeWrite: 0.322 D.MergeWrite: 0.319
D.RemoveBefore: 0.223 D.RemoveBefore: 0.224
``` ```
# Our benchmark # Our benchmark
@@ -60,25 +60,25 @@ Performance counters:
| ns/op | op/s | err% | total | benchmark | ns/op | op/s | err% | total | benchmark
|--------------------:|--------------------:|--------:|----------:|:---------- |--------------------:|--------------------:|--------:|----------:|:----------
| 249.47 | 4,008,533.72 | 0.5% | 0.01 | `point reads` | 246.99 | 4,048,700.59 | 0.2% | 0.01 | `point reads`
| 236.78 | 4,223,252.12 | 0.4% | 0.01 | `prefix reads` | 260.16 | 3,843,784.65 | 0.1% | 0.01 | `prefix reads`
| 432.76 | 2,310,737.74 | 0.1% | 0.01 | `range reads` | 493.35 | 2,026,953.19 | 0.1% | 0.01 | `range reads`
| 449.42 | 2,225,105.96 | 0.5% | 0.01 | `point writes` | 462.05 | 2,164,289.23 | 0.6% | 0.01 | `point writes`
| 438.47 | 2,280,674.39 | 0.3% | 0.01 | `prefix writes` | 448.19 | 2,231,205.25 | 0.9% | 0.01 | `prefix writes`
| 242.92 | 4,116,581.59 | 3.7% | 0.02 | `range writes` | 255.83 | 3,908,845.72 | 1.5% | 0.02 | `range writes`
| 553.31 | 1,807,319.60 | 0.7% | 0.01 | `monotonic increasing point writes` | 582.63 | 1,716,349.02 | 1.3% | 0.01 | `monotonic increasing point writes`
## Radix tree (this implementation) ## Radix tree (this implementation)
| ns/op | op/s | err% | total | benchmark | ns/op | op/s | err% | total | benchmark
|--------------------:|--------------------:|--------:|----------:|:---------- |--------------------:|--------------------:|--------:|----------:|:----------
| 19.35 | 51,669,510.11 | 0.2% | 0.01 | `point reads` | 19.42 | 51,483,206.67 | 0.3% | 0.01 | `point reads`
| 56.64 | 17,655,057.00 | 0.2% | 0.01 | `prefix reads` | 58.43 | 17,115,612.57 | 0.1% | 0.01 | `prefix reads`
| 217.04 | 4,607,450.05 | 0.2% | 0.01 | `range reads` | 216.09 | 4,627,766.60 | 0.2% | 0.01 | `range reads`
| 26.31 | 38,012,015.29 | 0.0% | 0.01 | `point writes` | 28.35 | 35,267,567.72 | 0.2% | 0.01 | `point writes`
| 41.45 | 24,124,003.19 | 0.2% | 0.01 | `prefix writes` | 43.43 | 23,026,226.17 | 0.2% | 0.01 | `prefix writes`
| 48.33 | 20,691,082.14 | 0.0% | 0.01 | `range writes` | 50.00 | 20,000,000.00 | 0.0% | 0.01 | `range writes`
| 84.92 | 11,775,856.81 | 4.0% | 0.01 | `monotonic increasing point writes` | 92.38 | 10,824,863.69 | 4.1% | 0.01 | `monotonic increasing point writes`
# "Real data" test # "Real data" test
@@ -87,13 +87,13 @@ Point queries only, best of three runs. Gc ratio is the ratio of time spent doin
## skip list ## skip list
``` ```
Check: 11.267 seconds, 331.812 MB/s, Add: 5.33323 seconds, 131.635 MB/s, Gc ratio: 45.4671% Check: 11.3385 seconds, 329.718 MB/s, Add: 5.35612 seconds, 131.072 MB/s, Gc ratio: 45.7173%
``` ```
## radix tree ## radix tree
``` ```
Check: 2.48508 seconds, 1504.38 MB/s, Add: 2.07295 seconds, 338.666 MB/s, Gc ratio: 42.2825% Check: 2.48583 seconds, 1503.93 MB/s, Add: 2.12768 seconds, 329.954 MB/s, Gc ratio: 41.7943%
``` ```
## hash table ## hash table
@@ -101,5 +101,5 @@ Check: 2.48508 seconds, 1504.38 MB/s, Add: 2.07295 seconds, 338.666 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.83931 seconds, 2032.56 MB/s, Add: 0.607861 seconds, 1154.93 MB/s, Gc ratio: 48.7142% Check: 1.83386 seconds, 2038.6 MB/s, Add: 0.601411 seconds, 1167.32 MB/s, Gc ratio: 48.9776%
``` ```

View File

@@ -7,6 +7,7 @@ int main(void) {
ConflictSet_WriteRange w; ConflictSet_WriteRange w;
ConflictSet_Result result; ConflictSet_Result result;
ConflictSet_ReadRange r; ConflictSet_ReadRange r;
int64_t bytes;
w.begin.p = (const uint8_t *)"0000"; w.begin.p = (const uint8_t *)"0000";
w.begin.len = 4; w.begin.len = 4;
w.end.len = 0; w.end.len = 0;
@@ -17,6 +18,8 @@ int main(void) {
r.readVersion = 0; r.readVersion = 0;
ConflictSet_check(cs, &r, &result, 1); ConflictSet_check(cs, &r, &result, 1);
assert(result == ConflictSet_Conflict); assert(result == ConflictSet_Conflict);
bytes = ConflictSet_getBytes(cs);
assert(bytes > 0);
ConflictSet_destroy(cs); ConflictSet_destroy(cs);
return 0; return 0;
} }

View File

@@ -17,4 +17,6 @@ int main(void) {
r.readVersion = 0; r.readVersion = 0;
cs.check(&r, &result, 1); cs.check(&r, &result, 1);
assert(result == ConflictSet::Conflict); assert(result == ConflictSet::Conflict);
int64_t bytes = cs.getBytes();
assert(bytes > 0);
} }

View File

@@ -26,7 +26,7 @@ limitations under the License.
* - It's safe to operate on two different ConflictSets in two different * - It's safe to operate on two different ConflictSets in two different
* threads concurrently * threads concurrently
* - It's safe to have multiple threads operating on the same ConflictSet * - It's safe to have multiple threads operating on the same ConflictSet
* concurrently if and only if all threads only call `check`. * concurrently if and only if all threads only call const methods
*/ */
struct __attribute__((__visibility__("default"))) ConflictSet { struct __attribute__((__visibility__("default"))) ConflictSet {
enum Result { enum Result {
@@ -110,7 +110,8 @@ private:
* - It's safe to operate on two different ConflictSets in two different * - It's safe to operate on two different ConflictSets in two different
* threads concurrently * threads concurrently
* - It's safe to have multiple threads operating on the same ConflictSet * - It's safe to have multiple threads operating on the same ConflictSet
* concurrently if and only if all threads only call `check`. * concurrently if and only if all threads only call functions that accept a
* const ConflictSet pointer
*/ */
typedef struct ConflictSet ConflictSet; typedef struct ConflictSet ConflictSet;
@@ -150,7 +151,8 @@ typedef struct {
} ConflictSet_WriteRange; } ConflictSet_WriteRange;
/** The result of checking reads[i] is written in results[i] */ /** The result of checking reads[i] is written in results[i] */
void ConflictSet_check(ConflictSet *cs, const ConflictSet_ReadRange *reads, void ConflictSet_check(const ConflictSet *cs,
const ConflictSet_ReadRange *reads,
ConflictSet_Result *results, int count); ConflictSet_Result *results, int count);
/** `writes` must be sorted ascending, and must not have adjacent or /** `writes` must be sorted ascending, and must not have adjacent or
@@ -173,6 +175,6 @@ ConflictSet *ConflictSet_create(int64_t oldestVersion);
void ConflictSet_destroy(ConflictSet *cs); void ConflictSet_destroy(ConflictSet *cs);
/** Returns the total bytes in use by this ConflictSet */ /** Returns the total bytes in use by this ConflictSet */
int64_t ConflictSet_getBytes(ConflictSet *cs); int64_t ConflictSet_getBytes(const ConflictSet *cs);
#endif #endif