Skip to content

Commit

Permalink
[C#] Use GC.AllocateArray (#683)
Browse files Browse the repository at this point in the history
* Use GC.AllocateArray where possible
* update benchmark as well.
  • Loading branch information
badrishc authored Mar 21, 2022
1 parent 0aa459f commit 4a4692b
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 228 deletions.
6 changes: 2 additions & 4 deletions cs/benchmark/ConcurrentDictionaryBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ internal ConcurrentDictionary_YcsbBenchmark(Key[] i_keys_, Key[] t_keys_, TestLo
writeStats = new bool[threadCount];
freq = Stopwatch.Frequency;
#endif
input_ = new Input[8];
input_ = GC.AllocateArray<Input>(8, true);
for (int i = 0; i < 8; i++)
input_[i].value = i;

Expand Down Expand Up @@ -177,8 +177,7 @@ internal unsafe (double, double) Run(TestLoader testLoader)
{
RandomGenerator rng = new ();

GCHandle handle = GCHandle.Alloc(input_, GCHandleType.Pinned);
input_ptr = (Input*)handle.AddrOfPinnedObject();
input_ptr = (Input*)Unsafe.AsPointer(ref input_[0]);

#if DASHBOARD
var dash = new Thread(() => DoContinuousMeasurements());
Expand Down Expand Up @@ -255,7 +254,6 @@ internal unsafe (double, double) Run(TestLoader testLoader)
dash.Abort();
#endif

handle.Free();
input_ptr = null;

double seconds = swatch.ElapsedMilliseconds / 1000.0;
Expand Down
135 changes: 66 additions & 69 deletions cs/benchmark/TestLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

Expand Down Expand Up @@ -158,9 +159,6 @@ private unsafe void LoadDataFromFile<TKey, TKeySetter>(string filePath, string d
{
Console.WriteLine($"loading subset of keys and txns from {txn_filename} into memory...");
using FileStream stream = File.Open(txn_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
GCHandle chunk_handle = GCHandle.Alloc(chunk, GCHandleType.Pinned);
byte* chunk_ptr = (byte*)chunk_handle.AddrOfPinnedObject();

var initValueSet = new HashSet<long>(init_keys.Length);

Expand All @@ -169,44 +167,47 @@ private unsafe void LoadDataFromFile<TKey, TKeySetter>(string filePath, string d

long offset = 0;

while (true)
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
fixed (byte* chunk_ptr = chunk)
{
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size && txn_count < txn_keys.Length; idx += 8)
while (true)
{
var value = *(long*)(chunk_ptr + idx);
if (!initValueSet.Contains(value))
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size && txn_count < txn_keys.Length; idx += 8)
{
if (init_count >= init_keys.Length)
var value = *(long*)(chunk_ptr + idx);
if (!initValueSet.Contains(value))
{
if (distribution == YcsbConstants.ZipfDist)
continue;

// Uniform distribution at current small-data counts is about a 1% hit rate, which is too slow here, so just modulo.
value %= init_keys.Length;
}
else
{
initValueSet.Add(value);
keySetter.Set(init_keys, init_count, value);
++init_count;
if (init_count >= init_keys.Length)
{
if (distribution == YcsbConstants.ZipfDist)
continue;

// Uniform distribution at current small-data counts is about a 1% hit rate, which is too slow here, so just modulo.
value %= init_keys.Length;
}
else
{
initValueSet.Add(value);
keySetter.Set(init_keys, init_count, value);
++init_count;
}
}
keySetter.Set(txn_keys, txn_count, value);
++txn_count;
}
keySetter.Set(txn_keys, txn_count, value);
++txn_count;
}
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;

if (txn_count == txn_keys.Length)
break;
if (txn_count == txn_keys.Length)
break;
}
}

sw.Stop();
chunk_handle.Free();

if (init_count != init_keys.Length)
throw new InvalidDataException($"Init file subset load fail! Expected {init_keys.Length} keys; found {init_count}");
Expand All @@ -222,34 +223,32 @@ private unsafe void LoadDataFromFile<TKey, TKeySetter>(string filePath, string d

using (FileStream stream = File.Open(init_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
GCHandle chunk_handle = GCHandle.Alloc(chunk, GCHandleType.Pinned);
byte* chunk_ptr = (byte*)chunk_handle.AddrOfPinnedObject();

long offset = 0;

while (true)
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
fixed (byte* chunk_ptr = chunk)
{
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size; idx += 8)
while (true)
{
keySetter.Set(init_keys, count, *(long*)(chunk_ptr + idx));
++count;
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size; idx += 8)
{
keySetter.Set(init_keys, count, *(long*)(chunk_ptr + idx));
++count;
if (count == init_keys.Length)
break;
}
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;

if (count == init_keys.Length)
break;
}
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;

if (count == init_keys.Length)
break;
}

chunk_handle.Free();

if (count != init_keys.Length)
throw new InvalidDataException($"Init file load fail! Expected {init_keys.Length} keys; found {count}");
}
Expand All @@ -262,35 +261,33 @@ private unsafe void LoadDataFromFile<TKey, TKeySetter>(string filePath, string d

using (FileStream stream = File.Open(txn_filename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
GCHandle chunk_handle = GCHandle.Alloc(chunk, GCHandleType.Pinned);
byte* chunk_ptr = (byte*)chunk_handle.AddrOfPinnedObject();

count = 0;
long offset = 0;

while (true)
byte[] chunk = new byte[YcsbConstants.kFileChunkSize];
fixed (byte* chunk_ptr = chunk)
{
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size; idx += 8)
while (true)
{
keySetter.Set(txn_keys, count, *(long*)(chunk_ptr + idx));
++count;
stream.Position = offset;
int size = stream.Read(chunk, 0, YcsbConstants.kFileChunkSize);
for (int idx = 0; idx < size; idx += 8)
{
keySetter.Set(txn_keys, count, *(long*)(chunk_ptr + idx));
++count;
if (count == txn_keys.Length)
break;
}
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;

if (count == txn_keys.Length)
break;
}
if (size == YcsbConstants.kFileChunkSize)
offset += YcsbConstants.kFileChunkSize;
else
break;

if (count == txn_keys.Length)
break;
}

chunk_handle.Free();

if (count != txn_keys.Length)
throw new InvalidDataException($"Txn file load fail! Expected {txn_keys.Length} keys; found {count}");
}
Expand Down
79 changes: 53 additions & 26 deletions cs/src/core/Allocator/BlittableAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ namespace FASTER.core
public unsafe sealed class BlittableAllocator<Key, Value> : AllocatorBase<Key, Value>
{
// Circular buffer definition
private byte[][] values;
private GCHandle[] handles;
private long[] pointers;
private readonly byte[][] values;
private readonly long[] pointers;
#if !NET5_0_OR_GREATER
private readonly GCHandle[] handles;
private readonly GCHandle ptrHandle;
#endif
private readonly long* nativePointers;

// Record sizes
Expand All @@ -30,14 +32,29 @@ public unsafe sealed class BlittableAllocator<Key, Value> : AllocatorBase<Key, V
public BlittableAllocator(LogSettings settings, IFasterEqualityComparer<Key> comparer, Action<long, long> evictCallback = null, LightEpoch epoch = null, Action<CommitInfo> flushCallback = null)
: base(settings, comparer, evictCallback, epoch, flushCallback)
{
overflowPagePool = new OverflowPool<PageUnit>(4, p => p.handle.Free());
overflowPagePool = new OverflowPool<PageUnit>(4, p =>
#if NET5_0_OR_GREATER
{ }
#else
p.handle.Free()
#endif
);

values = new byte[BufferSize][];
handles = new GCHandle[BufferSize];
pointers = new long[BufferSize];

ptrHandle = GCHandle.Alloc(pointers, GCHandleType.Pinned);
nativePointers = (long*)ptrHandle.AddrOfPinnedObject();
if (BufferSize > 0)
{
values = new byte[BufferSize][];

#if NET5_0_OR_GREATER
pointers = GC.AllocateArray<long>(BufferSize, true);
nativePointers = (long*)Unsafe.AsPointer(ref pointers[0]);
#else
pointers = new long[BufferSize];
handles = new GCHandle[BufferSize];
ptrHandle = GCHandle.Alloc(pointers, GCHandleType.Pinned);
nativePointers = (long*)ptrHandle.AddrOfPinnedObject();
#endif
}
}

public override void Initialize()
Expand Down Expand Up @@ -99,19 +116,17 @@ public override void Dispose()
{
base.Dispose();

if (values != null)
#if !NET5_0_OR_GREATER
if (BufferSize > 0)
{
for (int i = 0; i < values.Length; i++)
for (int i = 0; i < handles.Length; i++)
{
if (handles[i].IsAllocated)
handles[i].Free();
values[i] = null;
}
ptrHandle.Free();
}
handles = null;
ptrHandle.Free();
pointers = null;
values = null;
#endif
overflowPagePool.Dispose();
}

Expand All @@ -135,18 +150,25 @@ internal override void AllocatePage(int index)

if (overflowPagePool.TryGet(out var item))
{
#if !NET5_0_OR_GREATER
handles[index] = item.handle;
#endif
pointers[index] = item.pointer;
values[index] = item.value;
return;
}

var adjustedSize = PageSize + 2 * sectorSize;
var tmp = new byte[adjustedSize];
Array.Clear(tmp, 0, adjustedSize);

#if NET5_0_OR_GREATER
byte[] tmp = GC.AllocateArray<byte>(adjustedSize, true);
long p = (long)Unsafe.AsPointer(ref tmp[0]);
#else
byte[] tmp = new byte[adjustedSize];
handles[index] = GCHandle.Alloc(tmp, GCHandleType.Pinned);
long p = (long)handles[index].AddrOfPinnedObject();
#endif
Array.Clear(tmp, 0, adjustedSize);
pointers[index] = (p + (sectorSize - 1)) & ~((long)sectorSize - 1);
values[index] = tmp;
}
Expand Down Expand Up @@ -222,7 +244,7 @@ internal override void ClearPage(long page, int offset)
else
{
// Adjust array offset for cache alignment
offset += (int)(pointers[page % BufferSize] - (long)handles[page % BufferSize].AddrOfPinnedObject());
offset += (int)(pointers[page % BufferSize] - (long)Unsafe.AsPointer(ref values[page % BufferSize][0]));
Array.Clear(values[page % BufferSize], offset, values[page % BufferSize].Length - offset);
}
}
Expand All @@ -233,13 +255,19 @@ internal override void FreePage(long page)
if (EmptyPageCount > 0)
{
int index = (int)(page % BufferSize);
overflowPagePool.TryAdd(new PageUnit {
handle = handles[index],
pointer = pointers[index],
value = values[index] });
overflowPagePool.TryAdd(new PageUnit
{
#if !NET5_0_OR_GREATER
handle = handles[index],
#endif
pointer = pointers[index],
value = values[index]
});
values[index] = null;
pointers[index] = 0;
#if !NET5_0_OR_GREATER
handles[index] = default;
#endif
Interlocked.Decrement(ref AllocatedPageCount);
}
}
Expand All @@ -251,13 +279,12 @@ internal override void DeleteFromMemory()
{
for (int i = 0; i < values.Length; i++)
{
#if !NET5_0_OR_GREATER
if (handles[i].IsAllocated)
handles[i].Free();
#endif
values[i] = null;
}
handles = null;
pointers = null;
values = null;
}

protected override void ReadAsync<TContext>(
Expand Down
Loading

0 comments on commit 4a4692b

Please sign in to comment.