Add example augmented radix tree figure
All checks were successful
Tests / Release [gcc] total: 704, passed: 704
GNU C Compiler (gcc) |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |0|0|0|0|:clap: Reference build: <a href="https://jenkins.weaselab.dev/job/weaselab/job/conflict-set/job/main/45//gcc">weaselab » conflict-set » main #45</a>
Tests / Coverage total: 702, passed: 702
weaselab/conflict-set/pipeline/head This commit looks good

This commit is contained in:
2024-02-29 16:35:07 -08:00
parent 946694b8a5
commit a4b03bc216
4 changed files with 57 additions and 9 deletions

View File

@@ -2232,13 +2232,24 @@ void printTree() {
ConflictSet::Impl cs{writeVersion}; ConflictSet::Impl cs{writeVersion};
ReferenceImpl refImpl{writeVersion}; ReferenceImpl refImpl{writeVersion};
Arena arena; Arena arena;
constexpr int kNumKeys = 5; constexpr int kNumKeys = 4;
auto *write = new (arena) ConflictSet::WriteRange[kNumKeys]; auto *write = new (arena) ConflictSet::WriteRange[kNumKeys];
for (int i = 0; i < kNumKeys; ++i) { write[0].begin = "and"_s;
write[i].begin = toKey(arena, i); write[0].end = "ant"_s;
write[i].end.len = 0; write[0].writeVersion = 1;
write[i].writeVersion = ++writeVersion;
} write[1].begin = "any"_s;
write[1].end.len = 0;
write[1].writeVersion = 2;
write[2].begin = "are"_s;
write[2].end.len = 0;
write[2].writeVersion = 3;
write[3].begin = "art"_s;
write[3].end.len = 0;
write[3].writeVersion = 4;
cs.addWrites(write, kNumKeys); cs.addWrites(write, kNumKeys);
for (int i = 0; i < kNumKeys; ++i) { for (int i = 0; i < kNumKeys; ++i) {
write[i].writeVersion = ++writeVersion; write[i].writeVersion = ++writeVersion;
@@ -2251,6 +2262,8 @@ void printTree() {
#include "third_party/nanobench.h" #include "third_party/nanobench.h"
int main(void) { int main(void) {
printTree();
return 0;
ankerl::nanobench::Bench bench; ankerl::nanobench::Bench bench;
ConflictSet::Impl cs{0}; ConflictSet::Impl cs{0};
for (int j = 0; j < 256; ++j) { for (int j = 0; j < 256; ++j) {

View File

@@ -435,6 +435,10 @@ struct ReferenceImpl {
using Key = ConflictSet::Key; using Key = ConflictSet::Key;
inline Key operator"" _s(const char *str, size_t size) {
return {reinterpret_cast<const uint8_t *>(str), int(size)};
}
[[maybe_unused]] static Key toKey(Arena &arena, int n) { [[maybe_unused]] static Key toKey(Arena &arena, int n) {
uint8_t *buf = new (arena) uint8_t[sizeof(n)]; uint8_t *buf = new (arena) uint8_t[sizeof(n)];
memcpy(buf, &n, sizeof(n)); memcpy(buf, &n, sizeof(n));

View File

@@ -3,6 +3,9 @@
\usepackage{hyperref} \usepackage{hyperref}
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[angle=90, hpos=\leftmargin]{draftwatermark} \usepackage[angle=90, hpos=\leftmargin]{draftwatermark}
\usepackage{tikz}
\usepackage{tikzscale}
\usepackage[edges]{forest}
\title{ARTful Conflict Checking for FoundationDB} \title{ARTful Conflict Checking for FoundationDB}
\author{Andrew Noyes \thanks{\href{mailto:andrew@weaselab.dev}{andrew@weaselab.dev}}} \author{Andrew Noyes \thanks{\href{mailto:andrew@weaselab.dev}{andrew@weaselab.dev}}}
@@ -18,7 +21,7 @@
\section*{Abstract} \section*{Abstract}
FoundationDB \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21} provides serializability using a specialized data structure called \textit{lastCommit} \footnote{See Algorithm 1 referenced in \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21}} to implement optimistic concurrency control \cite{kung1981optimistic}. FoundationDB \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21} provides serializability using a specialized data structure called \textit{lastCommit} \footnote{See Algorithm 1 referenced in \cite{DBLP:conf/sigmod/ZhouXSNMTABSLRD21}} to implement optimistic concurrency control \cite{kung1981optimistic}.
This data structure encodes the write sets for recent transactions as a map from key ranges (represented as bitwise-lexicographically-ordered half-open intervals) to most recent write version, represented as a 64-bit integer. This data structure encodes the write sets for recent transactions as a map from key ranges (represented as bitwise-lexicographically-ordered half-open intervals) to most recent write versions.
FoundationDB implements \textit{lastCommit} as a version-augmented probabilistic skip list \cite{10.1145/78973.78977}. FoundationDB implements \textit{lastCommit} as a version-augmented probabilistic skip list \cite{10.1145/78973.78977}.
In this paper, we propose an alternative implementation of \textit{lastCommit} as a version-augmented Adaptive Radix Tree (ART) \cite{DBLP:conf/icde/LeisK013}, and evaluate its performance. In this paper, we propose an alternative implementation of \textit{lastCommit} as a version-augmented Adaptive Radix Tree (ART) \cite{DBLP:conf/icde/LeisK013}, and evaluate its performance.
@@ -44,16 +47,37 @@ For comparison-based trees, updating max version along the search path cannot be
We have no choice but to do a second, bottom-up pass to propagate max version changes. We have no choice but to do a second, bottom-up pass to propagate max version changes.
Furthermore, the change will always propagate all the way to the root, since inserts always use the highest-yet version. Furthermore, the change will always propagate all the way to the root, since inserts always use the highest-yet version.
For a radix tree, insertion does not affect the search path, and so max version can be updated on the top-down pass. For a radix tree, insertion does not affect the search path, and so max version can be updated on the top-down pass.
There's minimal overhead compared to the radix tree un-augmented. There's minimal overhead compared to the radix tree unaugmented.
For ``last less than or equal to'' queries (which comprise the core of our read workload), skip lists have the convenient property that no backtracking is necessary, since the bottommost level is a sorted linked list. For ``last less than or equal to'' queries (which comprise the core of our read workload), skip lists have the convenient property that no backtracking is necessary, since the bottommost level is a sorted linked list.
Binary search trees and radix trees both require backtracking up the search path when an equal element is not found. Binary search trees and radix trees both require backtracking up the search path when an equal element is not found.
It's possible to trade off the backtracking for the increased overhead of maintaining the elements in an auxiliary sorted linked list during insertion. It's possible to trade off the backtracking for the increased overhead of maintaining the elements in an auxiliary sorted linked list during insertion.
Our options also have various tradeoffs inherited from their un-augmented versions such as different worst-case and expected bounds on the length of search paths and the number of rotations performed upon insert. Our options also have various tradeoffs inherited from their unaugmented versions such as different worst-case and expected bounds on the length of search paths and the number of rotations performed upon insert.
ART has been shown \cite{DBLP:conf/icde/LeisK013} to offer superior performance to comparison-based data structures on modern hardware, which is on its own a compelling reason to consider it. ART has been shown \cite{DBLP:conf/icde/LeisK013} to offer superior performance to comparison-based data structures on modern hardware, which is on its own a compelling reason to consider it.
The Height Optimized Trie (HOT) \cite{binna2018hot} outperforms ART, but has a few practical disadvantages \footnote{Implementing HOT is more complex than the already-daunting ART, and requires AVX2 and BMI2 instructions. HOT also involves rebalancing operations during insertion. Even so, it's likely that a HOT-based \emph{lastCommit} implementation would be superior.} and will not be considered in this paper. The Height Optimized Trie (HOT) \cite{binna2018hot} outperforms ART, but has a few practical disadvantages \footnote{Implementing HOT is more complex than the already-daunting ART, and requires AVX2 and BMI2 instructions. HOT also involves rebalancing operations during insertion. Even so, it's likely that a HOT-based \emph{lastCommit} implementation would be superior.} and will not be considered in this paper.
\section{Augmented radix tree}
Each node in the tree is annotated with either one field \emph{max}, or three fields: \emph{max}, \emph{point}, and \emph{range}.
\emph{max} represents the maximum version among all keys starting with the prefix associated with the node's place in the tree (i.e. the search path from the root to this node).
\emph{point} represents the version of the exact key starting with this node's prefix.
\emph{range} represents the version of all keys $k$ such that this is the first node greater than or equal to $k$ with all three fields set.
See figure \ref{fig:tree} for an example of an augmented radix tree, after inserting
$[AND, ANT) \rightarrow 1$,
$\{ANY\} \rightarrow 2$,
$\{ARE\} \rightarrow 3$, and
$\{ART\} \rightarrow 4$.
Each node shows its partial prefix annotated with $(max,point,range)$.
\begin{figure}
\caption{}
\label{fig:tree}
\centering
\includegraphics{tree.tikz}
\end{figure}
\printbibliography \printbibliography
\end{document} \end{document}

7
paper/tree.tikz Normal file
View File

@@ -0,0 +1,7 @@
\begin{forest}
for tree={
draw,
math content,
}
[ \varepsilon_{4} [ A_{4} [ N_{2} [ D_{1,1,0} ] [ T_{0,0,1} ] [ Y_{2,2,0} ]] [ R_{4} [ E_{3,3,0} ] [ T_{4,4,0} ]] ] ]
\end{forest}