Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/turso-benchmark-portability.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ corresponding Rust internal API.
| `core/benches/fts_benchmark.rs` | Cold/warm search, selectivity, ingest, commit/merge churn | Direct Ahtola index-method port |
| `core/benches/fts_comparison_benchmark.rs` | Ahtola-style FTS versus SQLite FTS5 | Direct SQL port with storage-model labels |
| (Ahtola workload) `TursoFtsWorkloadBenchmarks` | Zipf 20,000-term corpus: rare/mid/common terms, AND, phrase, prefix, ranked top-10, write-then-search cycle, versus SQLite FTS5 | Managed workload complementing the upstream ports |
| (Ahtola workload) `DotNetHotspotBenchmarks` | .NET application access patterns through the `Microsoft.Data.Sqlite`-compatible facade on a file-backed WAL database: pooled connection-per-operation, PK/unique-key lookups (new and reused commands), COUNT probes, typed/`GetValue`/`GetFieldValue<T>` materialization, paging, keyset, `IN` lists, LIKE, JOIN + GROUP BY, multiple result sets, autocommit and batched INSERT, UPDATE, UPSERT, EF-style unit of work with RETURNING, BLOB round trip | Managed workload versus `Microsoft.Data.Sqlite` on the same file |
| `core/benches/graph_queries_benchmark.rs` | Analyzed/unanalyzed graph queries | Managed deterministic-fixture adaptation |
| `core/benches/tpc_h_benchmark.rs` | Supported TPC-H query execution | Managed asset and supported-query adaptation |
| `core/benches/json_benchmark.rs` | JSONB conversion and JSON Patch | Direct SQL port |
Expand Down
57 changes: 51 additions & 6 deletions src/Ahtola.Core/CowChunkedList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,37 @@ internal sealed class CowChunkedList<T> : IList<T>, IReadOnlyList<T>
private object _owner = new();
private int _version;

// Process-unique identity of this list's current contents; 0 while unassigned (see
// ContentStamp). Every mutation clears it and ShareFrom copies it, so two lists report the
// same stamp only while they hold exactly the same elements in the same order.
private long _contentStamp;
private static long s_contentStampSequence;

public int Count => _count;

public bool IsReadOnly => false;

/// <summary>
/// A process-unique token for this list's exact current contents. It changes on every
/// mutation and is carried by <see cref="ShareFrom"/>, so equal stamps on any two lists prove
/// identical contents; a working copy that diverges and is discarded can never alias another
/// copy's state, which a per-list counter such as a revision cannot guarantee. Assigned
/// lazily so mutations only clear a field.
/// </summary>
public long ContentStamp
{
get
{
var stamp = Volatile.Read(ref _contentStamp);
if (stamp != 0)
return stamp;

var fresh = Interlocked.Increment(ref s_contentStampSequence);
var existing = Interlocked.CompareExchange(ref _contentStamp, fresh, 0);
return existing == 0 ? fresh : existing;
}
}

public T this[int index]
{
get
Expand All @@ -46,10 +73,21 @@ public T this[int index]
if ((uint)index >= (uint)_count)
ThrowIndexOutOfRange();
WritableChunk(index >> Shift)[index & Mask] = value;
_version++;
Mutated();
}
}

/// <summary>
/// Whether chunk <paramref name="chunkIndex"/> (elements <c>chunkIndex * ChunkSize</c> onward)
/// is the same physical chunk in both lists. A chunk is copied before any write, so a shared
/// chunk holds identical elements in both; when the lists also have equal counts, every
/// element of that chunk below the count is equal without comparing them.
/// </summary>
internal bool SharesChunkWith(CowChunkedList<T> other, int chunkIndex)
=> chunkIndex < _chunkCount
&& chunkIndex < other._chunkCount
&& ReferenceEquals(_chunks[chunkIndex], other._chunks[chunkIndex]);

/// <summary>Makes this list an O(chunks) copy-on-write clone of <paramref name="source"/>.</summary>
public void ShareFrom(CowChunkedList<T> source)
{
Expand All @@ -62,6 +100,7 @@ public void ShareFrom(CowChunkedList<T> source)
_owner = new object();
source._owner = new object();
_version++;
_contentStamp = source.ContentStamp;
}

public void Add(T item)
Expand All @@ -72,7 +111,7 @@ public void Add(T item)
: WritableChunk(_count >> Shift);
items[offset] = item;
_count++;
_version++;
Mutated();
}

public void AddRange(IEnumerable<T> items)
Expand All @@ -88,7 +127,7 @@ public void Clear()
_chunks = [];
_chunkCount = 0;
_count = 0;
_version++;
Mutated();
}

public bool Contains(T item) => IndexOf(item) >= 0;
Expand Down Expand Up @@ -163,7 +202,7 @@ public void Insert(int index, T item)
}

WritableChunk(firstChunk)[index & Mask] = item;
_version++;
Mutated();
}

public bool Remove(T item)
Expand Down Expand Up @@ -199,7 +238,7 @@ public void RemoveAt(int index)
_count--;
if ((_count & Mask) == 0 && _chunkCount > (_count >> Shift))
_chunks[--_chunkCount] = null!;
_version++;
Mutated();
}

public void Sort()
Expand All @@ -208,7 +247,7 @@ public void Sort()
Array.Sort(items);
for (var index = 0; index < items.Length; index++)
WritableChunk(index >> Shift)[index & Mask] = items[index];
_version++;
Mutated();
}

public List<T> GetRange(int index, int count)
Expand Down Expand Up @@ -238,6 +277,12 @@ public IEnumerator<T> GetEnumerator()

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

private void Mutated()
{
_version++;
_contentStamp = 0;
}

private T[] WritableChunk(int chunk)
{
var current = _chunks[chunk];
Expand Down
Loading
Loading