Skip to content

Commit

Permalink
Experimenting with encoding unmanged structs as blobs
Browse files Browse the repository at this point in the history
  • Loading branch information
pachanga committed Nov 21, 2024
1 parent 2557c55 commit 9947671
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/compile/bhl_front.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
<PackageReference Include="Antlr4.Runtime.Standard" Version="4.13.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="lz4net" Version="1.0.15.93" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions src/vm/bhl_runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
<TargetFrameworks>netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup>
</Project>
10 changes: 5 additions & 5 deletions src/vm/coro.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public interface IInspectableCoroutine

public abstract class Coroutine : ICoroutine
{
internal VM.Pool<Coroutine> pool;
internal Pool<Coroutine> pool;

public abstract void Tick(VM.Frame frm, VM.ExecState exec, ref BHS status);
public virtual void Cleanup(VM.Frame frm, VM.ExecState exec) {}
Expand All @@ -33,7 +33,7 @@ public class CoroutinePool
//TODO: add debug inspection for concrete types
static class PoolHolder<T> where T : Coroutine
{
//alternative implemenation
//TODO: alternative implemenation, check if it's simpler and faster?
//[ThreadStatic]
//static public VM.Pool<Coroutine> _pool;
//static public VM.Pool<Coroutine> pool {
Expand All @@ -44,10 +44,10 @@ static class PoolHolder<T> where T : Coroutine
// }
//}

public static System.Threading.ThreadLocal<VM.Pool<Coroutine>> pool =
new System.Threading.ThreadLocal<VM.Pool<Coroutine>>(() =>
public static System.Threading.ThreadLocal<Pool<Coroutine>> pool =
new System.Threading.ThreadLocal<Pool<Coroutine>>(() =>
{
return new VM.Pool<Coroutine>();
return new Pool<Coroutine>();
});
}

Expand Down
26 changes: 26 additions & 0 deletions src/vm/extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ static public VM.Fiber Start(this VM vm, FuncSymbolScript fs, StackList<Val> arg
var addr = new VM.FuncAddr() { module = fs._module, fs = fs, ip = fs.ip_addr };
return vm.Start(addr, args.Count, args, opts);
}

static public void Alloc(this Pool<Val> pool, VM vm, int num)
{
for(int i=0;i<num;++i)
{
++pool.miss;
var tmp = new Val(vm);
pool.stack.Push(tmp);
}
}

static public string Dump(this Pool<Val> pool)
{
string res = "=== Val POOL ===\n";
res += "busy:" + pool.BusyCount + " idle:" + pool.IdleCount + "\n";

var dvs = new Val[pool.stack.Count];
pool.stack.CopyTo(dvs, 0);
for(int i=dvs.Length;i-- > 0;)
{
var v = dvs[i];
res += v + " (refs:" + v._refs + ") " + v.GetHashCode() + "\n";
}

return res;
}
}

}
92 changes: 92 additions & 0 deletions src/vm/util/blob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace bhl {

public class Blob<T> : IValRefcounted where T : unmanaged
{
internal Pool<Blob<T>> pool;

//NOTE: -1 means it's in released state,
// public only for quick inspection
internal int _refs;

public int refs => _refs;

internal byte[] data;

static int Size = Marshal.SizeOf<T>();

static class PoolHolder<T1> where T1 : unmanaged
{
public static System.Threading.ThreadLocal<Pool<Blob<T1>>> pool =
new System.Threading.ThreadLocal<Pool<Blob<T1>>>(() =>
{
return new Pool<Blob<T1>>();
});
}

static public Blob<T> New(ref T val)
{
var pool = PoolHolder<T>.pool.Value;

Blob<T> blob = null;
if(pool.stack.Count == 0)
{
++pool.miss;
blob = new Blob<T>();
}
else
{
++pool.hits;
blob = pool.stack.Pop();
}

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

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

blob._refs = 1;
blob.data = data;
blob.pool = pool;

return blob;
}

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

public void Retain()
{
if(_refs == -1)
throw new Exception("Invalid state(-1)");
++_refs;
}

public void Release()
{
if(_refs == -1)
throw new Exception("Invalid state(-1)");
if(_refs == 0)
throw new Exception("Double free(0)");

--_refs;
if(_refs == 0)
Del(this);
}

static void Del(Blob<T> blob)
{
if(blob._refs != 0)
throw new Exception("Freeing invalid object, refs " + blob._refs);

blob._refs = -1;
ArrayPool<byte>.Shared.Return(blob.data);

blob.pool.stack.Push(blob);
}
}

}
28 changes: 28 additions & 0 deletions src/vm/util/pool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections.Generic;

namespace bhl {

public class Pool<T> where T : class
{
internal Stack<T> stack = new Stack<T>();
internal int hits;
internal int miss;

public int HitCount {
get { return hits; }
}

public int MissCount {
get { return miss; }
}

public int IdleCount {
get { return stack.Count; }
}

public int BusyCount {
get { return miss - IdleCount; }
}
}

}
12 changes: 6 additions & 6 deletions src/vm/util/refc_list.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

namespace bhl {

public class RefcList<T> : List<T>, IValRefcounted, IDisposable
{
internal VM.Pool<RefcList<T>> pool;
public class RefcList<T> : List<T>, IValRefcounted, IDisposable
{
internal Pool<RefcList<T>> pool;

//NOTE: -1 means it's in released state,
// public only for quick inspection
Expand All @@ -15,10 +15,10 @@ public class RefcList<T> : List<T>, IValRefcounted, IDisposable

static class PoolHolder<T1>
{
public static System.Threading.ThreadLocal<VM.Pool<RefcList<T1>>> pool =
new System.Threading.ThreadLocal<VM.Pool<RefcList<T1>>>(() =>
public static System.Threading.ThreadLocal<Pool<RefcList<T1>>> pool =
new System.Threading.ThreadLocal<Pool<RefcList<T1>>>(() =>
{
return new VM.Pool<RefcList<T1>>();
return new Pool<RefcList<T1>>();
});
}

Expand Down
8 changes: 4 additions & 4 deletions src/vm/util/val_list_adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace bhl {
//TODO: writing is not implemented
public class ValList<T> : IList<T>, IValRefcounted, IDisposable
{
internal VM.Pool<ValList<T>> pool;
internal Pool<ValList<T>> pool;

Func<Val, T> val2native;

Expand All @@ -26,10 +26,10 @@ public class ValList<T> : IList<T>, IValRefcounted, IDisposable

static class PoolHolder<T1>
{
public static System.Threading.ThreadLocal<VM.Pool<ValList<T1>>> pool =
new System.Threading.ThreadLocal<VM.Pool<ValList<T1>>>(() =>
public static System.Threading.ThreadLocal<Pool<ValList<T1>>> pool =
new System.Threading.ThreadLocal<Pool<ValList<T1>>>(() =>
{
return new VM.Pool<ValList<T1>>();
return new Pool<ValList<T1>>();
});
}

Expand Down
55 changes: 1 addition & 54 deletions src/vm/vm.pool.cs
Original file line number Diff line number Diff line change
@@ -1,62 +1,9 @@
using System.Collections.Generic;

namespace bhl {

public partial class VM : INamedResolver
{
public class Pool<T> where T : class
{
internal Stack<T> stack = new Stack<T>();
internal int hits;
internal int miss;

public int HitCount {
get { return hits; }
}

public int MissCount {
get { return miss; }
}

public int IdleCount {
get { return stack.Count; }
}

public int BusyCount {
get { return miss - IdleCount; }
}
}

public class ValPool : Pool<Val>
{
public void Alloc(VM vm, int num)
{
for(int i=0;i<num;++i)
{
++miss;
var tmp = new Val(vm);
stack.Push(tmp);
}
}

public string Dump()
{
string res = "=== Val POOL ===\n";
res += "busy:" + BusyCount + " idle:" + IdleCount + "\n";

var dvs = new Val[stack.Count];
stack.CopyTo(dvs, 0);
for(int i=dvs.Length;i-- > 0;)
{
var v = dvs[i];
res += v + " (refs:" + v._refs + ") " + v.GetHashCode() + "\n";
}

return res;
}
}

public ValPool vals_pool = new ValPool();
public Pool<Val> vals_pool = new Pool<Val>();
public Pool<ValList> vlsts_pool = new Pool<ValList>();
public Pool<ValMap> vmaps_pool = new Pool<ValMap>();
public Pool<Frame> frames_pool = new Pool<Frame>();
Expand Down
32 changes: 32 additions & 0 deletions tests/test_blobs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using bhl;
using Xunit;

public class TestBlobs : BHL_TestBase
{
public struct StructBlob
{
public int x;
public int y;
public int z;
}

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

var blob = Blob<StructBlob>.New(ref val);

AssertEqual(1, blob.Value.x);
AssertEqual(10, blob.Value.y);
AssertEqual(100, blob.Value.z);

blob.Value.y = 30;
AssertEqual(30, blob.Value.y);

blob.Release();
}
}

0 comments on commit 9947671

Please sign in to comment.