Skip to content

Commit

Permalink
Experimenting with new blobs implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pachanga committed Nov 22, 2024
1 parent c444bc1 commit cd9f24f
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 18 deletions.
118 changes: 105 additions & 13 deletions src/vm/val.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//#define DEBUG_REFS
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace bhl {

Expand All @@ -27,19 +29,24 @@ public class Val

//NOTE: below members are semi-public, one can use them for
// fast access in case you know what you are doing

//NOTE: -1 means it's in released state
public int _refs;
public double _num;
public object _obj;
//it's a cached version of _obj cast to IValRefcounted for less casting
//in refcounting routines
//NOTE: it's a cached version of _obj cast to IValRefcounted for
// less casting in refcounting routines
public IValRefcounted _refc;
//NOTE: extra values below are for efficient encoding of small structs,
// e.g Vector, Color, etc
// e.g Vector3, Color, etc
public double _num2;
public double _num3;
public double _num4;

//NOTE: indicates that _obj is byte[] rented from pool and must be copied
// and properly released
public int _blob_size;

public double num {
get {
return _num;
Expand Down Expand Up @@ -147,21 +154,81 @@ void Reset()
_num2 = 0;
_num3 = 0;
_num4 = 0;
_obj = null;
_refc = null;

if(_blob_size > 0)
{
ArrayPool<byte>.Shared.Return((byte[])_obj);
_blob_size = 0;
}

_obj = null;
}

//NOTE: doesn't affect refcounting
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ValueCopyFrom(Val dv)
public void ValueCopyFrom(Val o)
{
type = o.type;
_num = o._num;
_num2 = o._num2;
_num3 = o._num3;
_num4 = o._num4;
_refc = o._refc;

if(o._blob_size > 0)
{
CopyBlobDataFrom(o);
}
else if(_blob_size > 0)
{
ArrayPool<byte>.Shared.Return((byte[])_obj);
_obj = o._obj;
_blob_size = 0;
}
else
_obj = o._obj;
}

void CopyBlobDataFrom(Val src)
{
var src_data = (byte[])src._obj;

if(_blob_size > 0)
{
var data = (byte[])_obj;

//let's check if our current buffer has enough capacity
if(data.Length >= src._blob_size)
Array.Copy(src_data, data, src._blob_size);
else
{
ArrayPool<byte>.Shared.Return(data);
var new_data = ArrayPool<byte>.Shared.Rent(src._blob_size);
Array.Copy(src_data, new_data, src._blob_size);
_obj = new_data;
}
}
else
{
var new_data = ArrayPool<byte>.Shared.Rent(src._blob_size);
Array.Copy(src_data, new_data, src._blob_size);
_obj = new_data;
}

_blob_size = src._blob_size;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool IsBlobEqual(Val dv)
{
type = dv.type;
_num = dv._num;
_num2 = dv._num2;
_num3 = dv._num3;
_num4 = dv._num4;
_obj = dv._obj;
_refc = dv._refc;
if(_blob_size != dv._blob_size)
return false;

ReadOnlySpan<byte> a = (byte[])_obj;
ReadOnlySpan<byte> b = (byte[])dv._obj;

return a.SequenceEqual(b);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -336,6 +403,29 @@ public void SetObj(IValRefcounted o, IType type)
_obj = o;
_refc = o;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetBlob<T>(ref T val, IType type) where T : unmanaged
{
Reset();

int size = Marshal.SizeOf<T>();

var data = ArrayPool<byte>.Shared.Rent(size);

Unsafe.As<byte, T>(ref data[0]) = val;

this.type = type;
_blob_size = size;
_obj = data;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T GetBlob<T>() where T : unmanaged
{
byte[] data = (byte[])_obj;
return ref Unsafe.As<byte, T>(ref data[0]);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsValueEqual(Val o)
Expand All @@ -345,7 +435,9 @@ public bool IsValueEqual(Val o)
_num2 == o._num2 &&
_num3 == o._num3 &&
_num4 == o._num4 &&
(_obj != null ? _obj.Equals(o._obj) : _obj == o._obj)
((_blob_size > 0 || o._blob_size > 0) ?
IsBlobEqual(o) :
(_obj != null ? _obj.Equals(o._obj) : _obj == o._obj))
;

return res;
Expand Down
177 changes: 172 additions & 5 deletions tests/test_blobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,178 @@ public struct StructBlob
}

[Fact]
public void TestBlob()
public void TestSimpleBlob()
{
//var val = new StructBlob();
//val.x = 1;
//val.y = 10;
//val.z = 100;
var orig = new StructBlob();
orig.x = 1;
orig.y = 10;
orig.z = 100;

var vm = new VM();

var val = Val.New(vm);
val.SetBlob(ref orig, null);

ref var b = ref val.GetBlob<StructBlob>();
AssertEqual(b.x, orig.x);
AssertEqual(b.y, orig.y);
AssertEqual(b.z, orig.z);

b.x = 20;
AssertEqual(b.x, 20);
AssertEqual(orig.x, 1);
}

[Fact]
public void TestBlobCopyOverEmptyValue()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

val1.GetBlob<StructBlob>().x = 20;

var val2 = Val.New(vm);
val2.ValueCopyFrom(val1);
//let's check if it was copied
AssertEqual(val2.GetBlob<StructBlob>().x, 20);

val2.GetBlob<StructBlob>().x = 30;
//original value is intact
AssertEqual(val1.GetBlob<StructBlob>().x, 20);
}

[Fact]
public void TestBlobCopyOverAnotherBlob()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

val1.GetBlob<StructBlob>().x = 20;

var val2 = Val.New(vm);
val2.SetBlob(ref orig, null);

val2.ValueCopyFrom(val1);
//let's check if it was copied
AssertEqual(val2.GetBlob<StructBlob>().x, 20);
}

[Fact]
public void TestBlobCopyOverNumber()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

val1.GetBlob<StructBlob>().x = 20;

var val2 = Val.NewInt(vm, 10);
val2.SetBlob(ref orig, null);

val2.ValueCopyFrom(val1);
//let's check if it was copied
AssertEqual(val2.GetBlob<StructBlob>().x, 20);
}

[Fact]
public void TestBlobEquality()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

var val2 = Val.New(vm);
val2.SetBlob(ref orig, null);

AssertTrue(val1.IsValueEqual(val2));
}

[Fact]
public void TestBlobEqualityAfterChanges()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

var val2 = Val.New(vm);
val2.SetBlob(ref orig, null);

val1.GetBlob<StructBlob>().x = 20;
val2.GetBlob<StructBlob>().x = 20;

AssertTrue(val1.IsValueEqual(val2));
}

[Fact]
public void TestBlobInEquality()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

var val2 = Val.New(vm);
val2.SetBlob(ref orig, null);

val1.GetBlob<StructBlob>().x = 20;

AssertFalse(val1.IsValueEqual(val2));
}

[Fact]
public void TestBlobInEqualityWithNumber()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

var val2 = Val.NewInt(vm, 10);

AssertFalse(val1.IsValueEqual(val2));
}

[Fact]
public void TestBlobInEqualityWithRandomObj()
{
var orig = new StructBlob();
orig.x = 1;

var vm = new VM();

var val1 = Val.New(vm);
val1.SetBlob(ref orig, null);

var val2 = Val.NewObj(vm, /*using orig!*/ orig, null);

AssertFalse(val1.IsValueEqual(val2));
}
}

0 comments on commit cd9f24f

Please sign in to comment.