Compare commits
6
Commits
8a04e57353
...
c9d742b696
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9d742b696 | ||
|
|
795ae7cb01 | ||
|
|
849e2d3e5c | ||
|
|
1560037680 | ||
|
|
764c31bbc8 | ||
|
|
ee3361952a |
+4
-2
@@ -3149,6 +3149,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
tls.impl = this;
|
tls.impl = this;
|
||||||
int64_t check_byte_accum = 0;
|
int64_t check_byte_accum = 0;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
|
assert(reads[i].readVersion <= newestVersionFullPrecision);
|
||||||
const auto &r = reads[i];
|
const auto &r = reads[i];
|
||||||
check_byte_accum += r.begin.len + r.end.len;
|
check_byte_accum += r.begin.len + r.end.len;
|
||||||
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
auto begin = std::span<const uint8_t>(r.begin.p, r.begin.len);
|
||||||
@@ -3299,6 +3300,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setOldestVersion(int64_t o) {
|
void setOldestVersion(int64_t o) {
|
||||||
|
assert(o <= newestVersionFullPrecision);
|
||||||
if (o <= oldestVersionFullPrecision) {
|
if (o <= oldestVersionFullPrecision) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3758,13 +3760,13 @@ std::string getSearchPath(Node *n) {
|
|||||||
fprintf(file,
|
fprintf(file,
|
||||||
" k_%p [label=\"m=%" PRId64 " p=%" PRId64 " r=%" PRId64
|
" k_%p [label=\"m=%" PRId64 " p=%" PRId64 " r=%" PRId64
|
||||||
"\n%s\", pos=\"%d,%d!\"];\n",
|
"\n%s\", pos=\"%d,%d!\"];\n",
|
||||||
(void *)n, maxVersion(n).toInt64(),
|
(void *)n, n->parent == nullptr ? -1 : maxVersion(n).toInt64(),
|
||||||
n->entry.pointVersion.toInt64(),
|
n->entry.pointVersion.toInt64(),
|
||||||
n->entry.rangeVersion.toInt64(),
|
n->entry.rangeVersion.toInt64(),
|
||||||
getPartialKeyPrintable(n).c_str(), x, y);
|
getPartialKeyPrintable(n).c_str(), x, y);
|
||||||
} else {
|
} else {
|
||||||
fprintf(file, " k_%p [label=\"m=%" PRId64 "\n%s\", pos=\"%d,%d!\"];\n",
|
fprintf(file, " k_%p [label=\"m=%" PRId64 "\n%s\", pos=\"%d,%d!\"];\n",
|
||||||
(void *)n, maxVersion(n).toInt64(),
|
(void *)n, n->parent == nullptr ? -1 : maxVersion(n).toInt64(),
|
||||||
getPartialKeyPrintable(n).c_str(), x, y);
|
getPartialKeyPrintable(n).c_str(), x, y);
|
||||||
}
|
}
|
||||||
x += kSeparation;
|
x += kSeparation;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ using namespace weaselab;
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <callgrind.h>
|
#include <callgrind.h>
|
||||||
|
|
||||||
|
|||||||
+39
-40
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
std::span<const uint8_t> keyAfter(Arena &arena, std::span<const uint8_t> key) {
|
||||||
auto result =
|
auto result =
|
||||||
@@ -115,15 +116,6 @@ bool operator==(const KeyInfo &lhs, const KeyInfo &rhs) {
|
|||||||
return !(lhs < rhs || rhs < lhs);
|
return !(lhs < rhs || rhs < lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void swapSort(std::vector<KeyInfo> &points, int a, int b) {
|
|
||||||
if (points[b] < points[a]) {
|
|
||||||
KeyInfo temp;
|
|
||||||
temp = points[a];
|
|
||||||
points[a] = points[b];
|
|
||||||
points[b] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SortTask {
|
struct SortTask {
|
||||||
int begin;
|
int begin;
|
||||||
int size;
|
int size;
|
||||||
@@ -183,13 +175,6 @@ void sortPoints(std::vector<KeyInfo> &points) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static thread_local uint32_t g_seed = 0;
|
|
||||||
|
|
||||||
static inline int skfastrand() {
|
|
||||||
g_seed = g_seed * 1664525L + 1013904223L;
|
|
||||||
return g_seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare(const StringRef &a, const StringRef &b) {
|
static int compare(const StringRef &a, const StringRef &b) {
|
||||||
int c = memcmp(a.data(), b.data(), std::min(a.size(), b.size()));
|
int c = memcmp(a.data(), b.data(), std::min(a.size(), b.size()));
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
@@ -215,20 +200,24 @@ struct ReadConflictRange {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr int MaxLevels = 26;
|
||||||
|
|
||||||
|
struct RandomLevel {
|
||||||
|
explicit RandomLevel(uint32_t seed) : seed(seed) {}
|
||||||
|
|
||||||
|
int next() {
|
||||||
|
int result = __builtin_clz(seed | (uint32_t(-1) >> (MaxLevels - 1)));
|
||||||
|
seed = seed * 1664525L + 1013904223L;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t seed;
|
||||||
|
};
|
||||||
|
|
||||||
class SkipList {
|
class SkipList {
|
||||||
private:
|
private:
|
||||||
static constexpr int MaxLevels = 26;
|
RandomLevel randomLevel{0};
|
||||||
|
|
||||||
int randomLevel() const {
|
|
||||||
uint32_t i = uint32_t(skfastrand()) >> (32 - (MaxLevels - 1));
|
|
||||||
int level = 0;
|
|
||||||
while (i & 1) {
|
|
||||||
i >>= 1;
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
assert(level < MaxLevels);
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represent a node in the SkipList. The node has multiple (i.e., level)
|
// Represent a node in the SkipList. The node has multiple (i.e., level)
|
||||||
// pointers to other nodes, and keeps a record of the max versions for each
|
// pointers to other nodes, and keeps a record of the max versions for each
|
||||||
@@ -426,18 +415,23 @@ public:
|
|||||||
}
|
}
|
||||||
void swap(SkipList &other) { std::swap(header, other.header); }
|
void swap(SkipList &other) { std::swap(header, other.header); }
|
||||||
|
|
||||||
void addConflictRanges(const Finger *fingers, int rangeCount,
|
// Returns the change in the number of entries
|
||||||
|
int64_t addConflictRanges(const Finger *fingers, int rangeCount,
|
||||||
Version version) {
|
Version version) {
|
||||||
|
int64_t result = rangeCount;
|
||||||
for (int r = rangeCount - 1; r >= 0; r--) {
|
for (int r = rangeCount - 1; r >= 0; r--) {
|
||||||
const Finger &startF = fingers[r * 2];
|
const Finger &startF = fingers[r * 2];
|
||||||
const Finger &endF = fingers[r * 2 + 1];
|
const Finger &endF = fingers[r * 2 + 1];
|
||||||
|
|
||||||
if (endF.found() == nullptr)
|
if (endF.found() == nullptr) {
|
||||||
|
++result;
|
||||||
insert(endF, endF.finger[0]->getMaxVersion(0));
|
insert(endF, endF.finger[0]->getMaxVersion(0));
|
||||||
|
}
|
||||||
|
|
||||||
remove(startF, endF);
|
result -= remove(startF, endF);
|
||||||
insert(startF, version);
|
insert(startF, version);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detectConflicts(ReadConflictRange *ranges, int count,
|
void detectConflicts(ReadConflictRange *ranges, int count,
|
||||||
@@ -567,9 +561,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void remove(const Finger &start, const Finger &end) {
|
// Returns the number of entries removed
|
||||||
|
int64_t remove(const Finger &start, const Finger &end) {
|
||||||
if (start.finger[0] == end.finger[0])
|
if (start.finger[0] == end.finger[0])
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
Node *x = start.finger[0]->getNext(0);
|
Node *x = start.finger[0]->getNext(0);
|
||||||
|
|
||||||
@@ -578,17 +573,20 @@ private:
|
|||||||
if (start.finger[i] != end.finger[i])
|
if (start.finger[i] != end.finger[i])
|
||||||
start.finger[i]->setNext(i, end.finger[i]->getNext(i));
|
start.finger[i]->setNext(i, end.finger[i]->getNext(i));
|
||||||
|
|
||||||
|
int64_t result = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
Node *next = x->getNext(0);
|
Node *next = x->getNext(0);
|
||||||
x->destroy();
|
x->destroy();
|
||||||
|
++result;
|
||||||
if (x == end.finger[0])
|
if (x == end.finger[0])
|
||||||
break;
|
break;
|
||||||
x = next;
|
x = next;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(const Finger &f, Version version) {
|
void insert(const Finger &f, Version version) {
|
||||||
int level = randomLevel();
|
int level = randomLevel.next();
|
||||||
// std::cout << std::string((const char*)value,length) << " level: " <<
|
// std::cout << std::string((const char*)value,length) << " level: " <<
|
||||||
// level << std::endl;
|
// level << std::endl;
|
||||||
Node *x = Node::create(f.value, level);
|
Node *x = Node::create(f.value, level);
|
||||||
@@ -704,8 +702,6 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SkipListConflictSet {};
|
|
||||||
|
|
||||||
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||||
Impl(int64_t oldestVersion)
|
Impl(int64_t oldestVersion)
|
||||||
: oldestVersion(oldestVersion), newestVersion(oldestVersion),
|
: oldestVersion(oldestVersion), newestVersion(oldestVersion),
|
||||||
@@ -775,17 +771,20 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
StringRef values[stripeSize];
|
StringRef values[stripeSize];
|
||||||
int64_t writeVersions[stripeSize / 2];
|
int64_t writeVersions[stripeSize / 2];
|
||||||
int ss = stringCount - (stripes - 1) * stripeSize;
|
int ss = stringCount - (stripes - 1) * stripeSize;
|
||||||
|
int64_t entryDelta = 0;
|
||||||
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];
|
||||||
values[i * 2] = w.first;
|
values[i * 2] = w.first;
|
||||||
values[i * 2 + 1] = w.second;
|
values[i * 2 + 1] = w.second;
|
||||||
keyUpdates += 3;
|
|
||||||
}
|
}
|
||||||
skipList.find(values, fingers, temp, ss);
|
skipList.find(values, fingers, temp, ss);
|
||||||
skipList.addConflictRanges(fingers, ss / 2, writeVersion);
|
entryDelta += skipList.addConflictRanges(fingers, ss / 2, writeVersion);
|
||||||
ss = stripeSize;
|
ss = stripeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run gc at least 200% the rate we're inserting entries
|
||||||
|
keyUpdates += std::max<int64_t>(entryDelta, 0) * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOldestVersion(int64_t oldestVersion) {
|
void setOldestVersion(int64_t oldestVersion) {
|
||||||
@@ -795,7 +794,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int temp;
|
int temp;
|
||||||
std::span<const uint8_t> key = removalKey;
|
std::span<const uint8_t> key = removalKey;
|
||||||
skipList.find(&key, &finger, &temp, 1);
|
skipList.find(&key, &finger, &temp, 1);
|
||||||
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 10));
|
skipList.removeBefore(oldestVersion, finger, std::exchange(keyUpdates, 0));
|
||||||
removalArena = Arena();
|
removalArena = Arena();
|
||||||
removalKey = copyToArena(
|
removalKey = copyToArena(
|
||||||
removalArena, {finger.getValue().data(), finger.getValue().size()});
|
removalArena, {finger.getValue().data(), finger.getValue().size()});
|
||||||
@@ -804,7 +803,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
|||||||
int64_t totalBytes = 0;
|
int64_t totalBytes = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int64_t keyUpdates = 10;
|
int64_t keyUpdates = 0;
|
||||||
Arena removalArena;
|
Arena removalArena;
|
||||||
std::span<const uint8_t> removalKey;
|
std::span<const uint8_t> removalKey;
|
||||||
int64_t oldestVersion;
|
int64_t oldestVersion;
|
||||||
|
|||||||
+18
-12
@@ -54,8 +54,9 @@ struct __attribute__((__visibility__("default"))) ConflictSet {
|
|||||||
/** `end` having length 0 denotes that this range is the single key {begin}.
|
/** `end` having length 0 denotes that this range is the single key {begin}.
|
||||||
* Otherwise this denotes the range [begin, end), and begin must be < end */
|
* Otherwise this denotes the range [begin, end), and begin must be < end */
|
||||||
Key end;
|
Key end;
|
||||||
/** `readVersion` older than the the oldestVersion or the version of the
|
/** `readVersion` older than the oldestVersion or the version of the
|
||||||
* latest call to `addWrites` minus two billion will result in `TooOld` */
|
* latest call to `addWrites` minus two billion will result in `TooOld`.
|
||||||
|
* Must be <= the version of the latest call to `addWrites` */
|
||||||
int64_t readVersion;
|
int64_t readVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,11 +73,13 @@ struct __attribute__((__visibility__("default"))) ConflictSet {
|
|||||||
|
|
||||||
/** Reads intersecting writes where readVersion < `writeVersion` will result
|
/** Reads intersecting writes where readVersion < `writeVersion` will result
|
||||||
* in `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater
|
* in `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater
|
||||||
* than or equal to all previous write versions. */
|
* than or equal to all previous write versions. Call `addWrites` with `count`
|
||||||
|
* zero to only advance the version. */
|
||||||
void addWrites(const WriteRange *writes, int count, int64_t writeVersion);
|
void addWrites(const WriteRange *writes, int count, int64_t writeVersion);
|
||||||
|
|
||||||
/** Reads where readVersion < oldestVersion will result in `TooOld`. Must be
|
/** Reads where readVersion < `oldestVersion` will result in `TooOld`. Must be
|
||||||
* greater than or equal to all previous oldest versions. */
|
* greater than or equal to all previous oldest versions. Must be <= the
|
||||||
|
* version of the latest call to `addWrites` */
|
||||||
void setOldestVersion(int64_t oldestVersion);
|
void setOldestVersion(int64_t oldestVersion);
|
||||||
|
|
||||||
/** Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
/** Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
||||||
@@ -170,8 +173,9 @@ typedef struct {
|
|||||||
/** `end` having length 0 denotes that this range is the single key {begin}.
|
/** `end` having length 0 denotes that this range is the single key {begin}.
|
||||||
* Otherwise this denotes the range [begin, end), and begin must be < end */
|
* Otherwise this denotes the range [begin, end), and begin must be < end */
|
||||||
ConflictSet_Key end;
|
ConflictSet_Key end;
|
||||||
/** `readVersion` older than the the oldestVersion or the version of the
|
/** `readVersion` older than the oldestVersion or the version of the
|
||||||
* latest call to `addWrites` minus two billion will result in `TooOld` */
|
* latest call to `addWrites` minus two billion will result in `TooOld`.
|
||||||
|
* Must be <= the version of the latest call to `addWrites` */
|
||||||
int64_t readVersion;
|
int64_t readVersion;
|
||||||
} ConflictSet_ReadRange;
|
} ConflictSet_ReadRange;
|
||||||
|
|
||||||
@@ -188,15 +192,17 @@ void ConflictSet_check(const ConflictSet *cs,
|
|||||||
const ConflictSet_ReadRange *reads,
|
const ConflictSet_ReadRange *reads,
|
||||||
ConflictSet_Result *results, int count);
|
ConflictSet_Result *results, int count);
|
||||||
|
|
||||||
/** Reads intersecting writes where readVersion < `writeVersion` will result in
|
/** Reads intersecting writes where readVersion < `writeVersion` will result
|
||||||
* `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater than or
|
* in `Conflict` (or `TooOld`, eventually). `writeVersion` must be greater
|
||||||
* equal to all previous write versions. */
|
* than or equal to all previous write versions. Call `addWrites` with `count`
|
||||||
|
* zero to only advance the version. */
|
||||||
void ConflictSet_addWrites(ConflictSet *cs,
|
void ConflictSet_addWrites(ConflictSet *cs,
|
||||||
const ConflictSet_WriteRange *writes, int count,
|
const ConflictSet_WriteRange *writes, int count,
|
||||||
int64_t writeVersion);
|
int64_t writeVersion);
|
||||||
|
|
||||||
/** Reads where readVersion < oldestVersion will result in `TooOld`. Must be
|
/** Reads where readVersion < `oldestVersion` will result in `TooOld`. Must be
|
||||||
* greater than or equal to all previous oldest versions. */
|
* greater than or equal to all previous oldest versions. Must be <= the
|
||||||
|
* version of the latest call to `addWrites` */
|
||||||
void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion);
|
void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion);
|
||||||
|
|
||||||
/** Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
/** Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
||||||
|
|||||||
Reference in New Issue
Block a user