Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Unsafe.SizeOf to calculate object sizes #39

Merged
merged 7 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 0 additions & 12 deletions libs/client/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ namespace Garnet.client
/// </summary>
public static class Utility
{
/// <summary>
/// Get size of type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
internal static unsafe int GetSize<T>(this T value)
{
T[] arr = new T[2];
return (int)((long)Unsafe.AsPointer(ref arr[1]) - (long)Unsafe.AsPointer(ref arr[0]));
}

internal static bool IsBlittableType(Type t)
{
var mi = typeof(Utility).GetMethod("IsBlittable", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod);
Expand Down
31 changes: 12 additions & 19 deletions libs/storage/Tsavorite/cs/src/core/Allocator/BlittableAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ internal sealed unsafe class BlittableAllocator<Key, Value> : AllocatorBase<Key,
private readonly long[] pointers;
private readonly long* nativePointers;

// Record sizes
private static readonly int recordSize = Utility.GetSize(default(Record<Key, Value>));
private static readonly int recordInfoSize = Utility.GetSize(default(RecordInfo));
private static readonly int keySize = Utility.GetSize(default(Key));
private static readonly int valueSize = Utility.GetSize(default(Value));

internal static int RecordSize => recordSize;
internal static int KeySize => Unsafe.SizeOf<Key>();
internal static int ValueSize => Unsafe.SizeOf<Value>();
internal static int RecordSize => Unsafe.SizeOf<Record<Key, Value>>();

private readonly OverflowPool<PageUnit> overflowPagePool;

Expand Down Expand Up @@ -88,37 +84,34 @@ public override ref Key GetKey(long physicalAddress)

public override ref Value GetValue(long physicalAddress)
{
return ref Unsafe.AsRef<Value>((byte*)physicalAddress + RecordInfo.GetLength() + keySize);
return ref Unsafe.AsRef<Value>((byte*)physicalAddress + RecordInfo.GetLength() + KeySize);
}

public override (int actualSize, int allocatedSize) GetRecordSize(long physicalAddress)
{
return (recordSize, recordSize);
return (RecordSize, RecordSize);
}

public override (int actualSize, int allocatedSize, int keySize) GetRMWCopyDestinationRecordSize<Input, TsavoriteSession>(ref Key key, ref Input input, ref Value value, ref RecordInfo recordInfo, TsavoriteSession tsavoriteSession)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

public override int GetAverageRecordSize()
{
return recordSize;
}
public override int GetAverageRecordSize() => RecordSize;

public override int GetFixedRecordSize() => recordSize;
public override int GetFixedRecordSize() => RecordSize;

public override (int actualSize, int allocatedSize, int keySize) GetRMWInitialRecordSize<Input, TsavoriteSession>(ref Key key, ref Input input, TsavoriteSession tsavoriteSession)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

public override (int actualSize, int allocatedSize, int keySize) GetRecordSize(ref Key key, ref Value value)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

public override int GetValueLength(ref Value value) => valueSize;
public override int GetValueLength(ref Value value) => ValueSize;

/// <summary>
/// Dispose memory allocator
Expand All @@ -136,7 +129,7 @@ public override void Dispose()

public override AddressInfo* GetValueAddressInfo(long physicalAddress)
{
return (AddressInfo*)((byte*)physicalAddress + RecordInfo.GetLength() + keySize);
return (AddressInfo*)((byte*)physicalAddress + RecordInfo.GetLength() + KeySize);
}

/// <summary>
Expand Down
73 changes: 36 additions & 37 deletions libs/storage/Tsavorite/cs/src/core/Allocator/GenericAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,15 @@ internal sealed unsafe class GenericAllocator<Key, Value> : AllocatorBase<Key, V
// Tail offsets per segment, in object log
public readonly long[] segmentOffsets;
// Record sizes
private static readonly int recordSize = Utility.GetSize(default(Record<Key, Value>));
private readonly SerializerSettings<Key, Value> SerializerSettings;
private readonly bool keyBlittable = Utility.IsBlittable<Key>();
private readonly bool valueBlittable = Utility.IsBlittable<Value>();

internal static int RecordSize => recordSize;

// We do not support variable-length keys in GenericAllocator
private int keySize = Utility.GetSize(default(Key));
private int valueSize = Utility.GetSize(default(Value));
internal static int KeySize => Unsafe.SizeOf<Key>();
internal static int ValueSize => Unsafe.SizeOf<Value>();
internal static int RecordSize => Unsafe.SizeOf<Record<Key, Value>>();

private readonly OverflowPool<Record<Key, Value>[]> overflowPagePool;

Expand Down Expand Up @@ -136,7 +135,7 @@ void ReturnPage(int index)

public override void Initialize()
{
Initialize(recordSize);
Initialize(RecordSize);
}

/// <summary>
Expand All @@ -157,7 +156,7 @@ public override long GetStartLogicalAddress(long page)
public override long GetFirstValidLogicalAddress(long page)
{
if (page == 0)
return (page << LogPageSizeBits) + recordSize;
return (page << LogPageSizeBits) + RecordSize;

return page << LogPageSizeBits;
}
Expand All @@ -170,7 +169,7 @@ public override ref RecordInfo GetInfo(long physicalAddress)
// Index of page within the circular buffer
int pageIndex = (int)((physicalAddress >> LogPageSizeBits) & BufferSizeMask);

return ref values[pageIndex][offset / recordSize].info;
return ref values[pageIndex][offset / RecordSize].info;
}

public override ref RecordInfo GetInfoFromBytePointer(byte* ptr)
Expand All @@ -186,7 +185,7 @@ public override ref Key GetKey(long physicalAddress)
// Index of page within the circular buffer
int pageIndex = (int)((physicalAddress >> LogPageSizeBits) & BufferSizeMask);

return ref values[pageIndex][offset / recordSize].key;
return ref values[pageIndex][offset / RecordSize].key;
}

public override ref Value GetValue(long physicalAddress)
Expand All @@ -197,36 +196,36 @@ public override ref Value GetValue(long physicalAddress)
// Index of page within the circular buffer
int pageIndex = (int)((physicalAddress >> LogPageSizeBits) & BufferSizeMask);

return ref values[pageIndex][offset / recordSize].value;
return ref values[pageIndex][offset / RecordSize].value;
}

public override (int actualSize, int allocatedSize) GetRecordSize(long physicalAddress)
{
return (recordSize, recordSize);
return (RecordSize, RecordSize);
}

public override int GetValueLength(ref Value value) => valueSize;
public override int GetValueLength(ref Value value) => ValueSize;

public override (int actualSize, int allocatedSize, int keySize) GetRMWCopyDestinationRecordSize<Input, TsavoriteSession>(ref Key key, ref Input input, ref Value value, ref RecordInfo recordInfo, TsavoriteSession tsavoriteSession)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

public override int GetAverageRecordSize()
{
return recordSize;
return RecordSize;
}

public override int GetFixedRecordSize() => recordSize;
public override int GetFixedRecordSize() => RecordSize;

public override (int actualSize, int allocatedSize, int keySize) GetRMWInitialRecordSize<Input, TsavoriteSession>(ref Key key, ref Input input, TsavoriteSession tsavoriteSession)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

public override (int actualSize, int allocatedSize, int keySize) GetRecordSize(ref Key key, ref Value value)
{
return (recordSize, recordSize, keySize);
return (RecordSize, RecordSize, KeySize);
}

internal override bool TryComplete()
Expand Down Expand Up @@ -292,18 +291,18 @@ internal Record<Key, Value>[] AllocatePage()
return item;

Record<Key, Value>[] tmp;
if (PageSize % recordSize == 0)
tmp = new Record<Key, Value>[PageSize / recordSize];
if (PageSize % RecordSize == 0)
tmp = new Record<Key, Value>[PageSize / RecordSize];
else
tmp = new Record<Key, Value>[1 + (PageSize / recordSize)];
tmp = new Record<Key, Value>[1 + (PageSize / RecordSize)];
Array.Clear(tmp, 0, tmp.Length);
return tmp;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal long SnapToLogicalAddressBoundary(ref long logicalAddress)
{
return logicalAddress = ((logicalAddress - Constants.kFirstValidAddress) / recordSize) * recordSize + Constants.kFirstValidAddress;
return logicalAddress = ((logicalAddress - Constants.kFirstValidAddress) / RecordSize) * RecordSize + Constants.kFirstValidAddress;
}

public override long GetPhysicalAddress(long logicalAddress)
Expand Down Expand Up @@ -381,7 +380,7 @@ protected override void WriteAsyncToDevice<TContext>

internal override void ClearPage(long page, int offset)
{
Array.Clear(values[page % BufferSize], offset / recordSize, values[page % BufferSize].Length - offset / recordSize);
Array.Clear(values[page % BufferSize], offset / RecordSize, values[page % BufferSize].Length - offset / RecordSize);
}

internal override void FreePage(long page)
Expand Down Expand Up @@ -495,12 +494,12 @@ private void WriteAsync<TContext>(long flushPage, ulong alignedDestinationAddres
// Track the size to be written to the object log.
long endPosition = 0;

for (int i = start / recordSize; i < end / recordSize; i++)
for (int i = start / RecordSize; i < end / RecordSize; i++)
{
if (!src[i].info.Invalid)
{
// Calculate the logical address of the 'values' page currently being written.
var address = (flushPage << LogPageSizeBits) + i * recordSize;
var address = (flushPage << LogPageSizeBits) + i * RecordSize;

// Do not write v+1 records (e.g. during a checkpoint)
if (address < fuzzyStartLogicalAddress || !src[i].info.IsInNewVersion)
Expand All @@ -511,7 +510,7 @@ private void WriteAsync<TContext>(long flushPage, ulong alignedDestinationAddres
keySerializer.Serialize(ref src[i].key);

// Store the key address into the 'buffer' AddressInfo image as an offset into 'ms'.
var key_address = GetKeyAddressInfo((long)(buffer.aligned_pointer + i * recordSize));
var key_address = GetKeyAddressInfo((long)(buffer.aligned_pointer + i * RecordSize));
key_address->Address = pos;
key_address->Size = (int)(ms.Position - pos);
addr.Add((long)key_address);
Expand All @@ -524,7 +523,7 @@ private void WriteAsync<TContext>(long flushPage, ulong alignedDestinationAddres
valueSerializer.Serialize(ref src[i].value);

// Store the value address into the 'buffer' AddressInfo image as an offset into 'ms'.
var value_address = GetValueAddressInfo((long)(buffer.aligned_pointer + i * recordSize));
var value_address = GetValueAddressInfo((long)(buffer.aligned_pointer + i * RecordSize));
value_address->Address = pos;
value_address->Size = (int)(ms.Position - pos);
addr.Add((long)value_address);
Expand All @@ -534,13 +533,13 @@ private void WriteAsync<TContext>(long flushPage, ulong alignedDestinationAddres
else
{
// Mark v+1 records as invalid to avoid deserializing them on recovery
ref var record = ref Unsafe.AsRef<Record<Key, Value>>(buffer.aligned_pointer + i * recordSize);
ref var record = ref Unsafe.AsRef<Record<Key, Value>>(buffer.aligned_pointer + i * RecordSize);
record.info.SetInvalid();
}
}

// If this record's serialized size surpassed ObjectBlockSize or it's the last record to be written, write to the object log.
if (endPosition > ObjectBlockSize || i == (end / recordSize) - 1)
if (endPosition > ObjectBlockSize || i == (end / RecordSize) - 1)
{
var memoryStreamActualLength = ms.Position;
var memoryStreamTotalLength = (int)endPosition;
Expand Down Expand Up @@ -573,7 +572,7 @@ private void WriteAsync<TContext>(long flushPage, ulong alignedDestinationAddres
((AddressInfo*)address)->Address += _objAddr;

// If we have not written all records, prepare for the next chunk of records to be written.
if (i < (end / recordSize) - 1)
if (i < (end / RecordSize) - 1)
{
// Create a new MemoryStream for the next chunk of records to be written.
ms = new MemoryStream();
Expand Down Expand Up @@ -892,7 +891,7 @@ public void Deserialize(byte* raw, long ptr, long untilptr, Record<Key, Value>[]
while (ptr < untilptr)
{
ref Record<Key, Value> record = ref Unsafe.AsRef<Record<Key, Value>>(raw + ptr);
src[ptr / recordSize].info = record.info;
src[ptr / RecordSize].info = record.info;

if (!record.info.Invalid)
{
Expand All @@ -905,11 +904,11 @@ public void Deserialize(byte* raw, long ptr, long untilptr, Record<Key, Value>[]
stream.Seek(streamStartPos + key_addr->Address - start_addr, SeekOrigin.Begin);
}

keySerializer.Deserialize(out src[ptr / recordSize].key);
keySerializer.Deserialize(out src[ptr / RecordSize].key);
}
else
{
src[ptr / recordSize].key = record.key;
src[ptr / RecordSize].key = record.key;
}

if (!record.info.Tombstone)
Expand All @@ -923,11 +922,11 @@ public void Deserialize(byte* raw, long ptr, long untilptr, Record<Key, Value>[]
stream.Seek(streamStartPos + value_addr->Address - start_addr, SeekOrigin.Begin);
}

valueSerializer.Deserialize(out src[ptr / recordSize].value);
valueSerializer.Deserialize(out src[ptr / RecordSize].value);
}
else
{
src[ptr / recordSize].value = record.value;
src[ptr / RecordSize].value = record.value;
}
}
}
Expand Down Expand Up @@ -1129,7 +1128,7 @@ internal void PopulatePage(byte* src, int required_bytes, ref Record<Key, Value>
{
fixed (RecordInfo* pin = &destinationPage[0].info)
{
Debug.Assert(required_bytes <= recordSize * destinationPage.Length);
Debug.Assert(required_bytes <= RecordSize * destinationPage.Length);

Buffer.MemoryCopy(src, Unsafe.AsPointer(ref destinationPage[0]), required_bytes, required_bytes);
}
Expand Down Expand Up @@ -1174,10 +1173,10 @@ internal override void MemoryPageScan(long beginAddress, long endAddress, IObser
{
var page = (beginAddress >> LogPageSizeBits) % BufferSize;
long pageStartAddress = beginAddress & ~PageSizeMask;
int start = (int)(beginAddress & PageSizeMask) / recordSize;
int count = (int)(endAddress - beginAddress) / recordSize;
int start = (int)(beginAddress & PageSizeMask) / RecordSize;
int count = (int)(endAddress - beginAddress) / RecordSize;
int end = start + count;
using var iter = new MemoryPageScanIterator<Key, Value>(values[page], start, end, pageStartAddress, recordSize);
using var iter = new MemoryPageScanIterator<Key, Value>(values[page], start, end, pageStartAddress, RecordSize);
Debug.Assert(epoch.ThisInstanceProtected());
try
{
Expand Down
3 changes: 2 additions & 1 deletion libs/storage/Tsavorite/cs/src/core/Allocator/GenericFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

using System;
using System.Runtime.CompilerServices;

namespace Tsavorite.core
{
Expand All @@ -12,7 +13,7 @@ internal sealed class GenericFrame<Key, Value> : IDisposable
{
private readonly Record<Key, Value>[][] frame;
public readonly int frameSize, pageSize;
private readonly int recordSize = Utility.GetSize(default(Record<Key, Value>));
private readonly int recordSize = Unsafe.SizeOf<Record<Key, Value>>();

public GenericFrame(int frameSize, int pageSize)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,17 @@ internal struct RevivificationManager<Key, Value>
internal RevivificationStats stats = new();

internal readonly bool IsEnabled = false;
internal readonly int FixedValueLength;
internal readonly int FixedValueLength => Unsafe.SizeOf<Value>();
internal bool restoreDeletedRecordsIfBinIsFull;
internal bool useFreeRecordPoolForCTT;

internal readonly bool IsFixedLength => FixedValueLength != 0;
internal readonly bool IsFixedLength { get; }
PaulusParssinen marked this conversation as resolved.
Show resolved Hide resolved

internal double revivifiableFraction;

public RevivificationManager(TsavoriteKV<Key, Value> store, bool isFixedLen, RevivificationSettings revivSettings, LogSettings logSettings)
{
// Set these first in case revivification is not enabled; they still tell us not to expect fixed-length.
if (isFixedLen)
FixedValueLength = Utility.GetSize(default(Value));

IsFixedLength = isFixedLen;
revivifiableFraction = revivSettings is null || revivSettings.RevivifiableFraction == RevivificationSettings.DefaultRevivifiableFraction
? logSettings.MutableFraction
: revivSettings.RevivifiableFraction;
Expand Down
Loading