Skip to content

Commit

Permalink
Reduce memory usage with interning of common Pine values
Browse files Browse the repository at this point in the history
  • Loading branch information
Viir committed May 20, 2024
1 parent 98c3985 commit 8232d0c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 9 deletions.
83 changes: 76 additions & 7 deletions implement/Pine.Core/PineValue.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -24,23 +25,91 @@ public static PineValue Blob(byte[] bytes) =>
Blob((ReadOnlyMemory<byte>)bytes);

public static PineValue Blob(ReadOnlyMemory<byte> bytes) =>
bytes.Length == 0
bytes.Length is 0
?
EmptyBlob
:
new BlobValue(bytes);

public static PineValue List(IReadOnlyList<PineValue> elements) =>
elements.Count == 0
bytes.Length is 1
?
InternedBlobSingle[bytes.Span[0]]
:
bytes.Length is 2
?
EmptyList
InternedBlobDouble[bytes.Span[0] * 256 + bytes.Span[1]]
:
new ListValue(elements);
new BlobValue(bytes);

public static PineValue List(IReadOnlyList<PineValue> elements)
{
if (elements.Count is 0)
return EmptyList;

var newInstance = new ListValue(elements);

if (InternedLists?.TryGetValue(newInstance, out var interned) ?? false)
return interned;

return newInstance;
}

public static readonly PineValue EmptyList = new ListValue([]);

public static readonly PineValue EmptyBlob = new BlobValue(ReadOnlyMemory<byte>.Empty);

private static readonly PineValue[] InternedBlobSingle =
Enumerable.Range(0, 256)
.Select(i => new BlobValue(new byte[] { (byte)i }))
.ToArray();

private static readonly PineValue[] InternedBlobDouble =
Enumerable.Range(0, 256)
.SelectMany(i => Enumerable.Range(0, 256).Select(j => new BlobValue(new byte[] { (byte)i, (byte)j })))
.ToArray();

private static readonly IEnumerable<string> InternedStrings =
[
"String",
"Nothing",
"Just",

"EQ",
"LT",
"GT",

"list_head",
"skip",
"equal",

"Literal",
"List",
"ParseAndEval",
"Conditional",
"Environment",
"Function",
"KernelApplication",

"functionName",
"argument",
"condition",
"ifTrue",
"ifFalse",
"environment",
"function",
"expression"


];

private static IEnumerable<ListValue> BuildInternedLists() =>
[
..InternedStrings.Select(s => new ListValue(PineValueAsString.ListValueFromString(s)))
];

private static readonly FrozenDictionary<ListValue, ListValue> InternedLists =
BuildInternedLists().ToFrozenDictionary(
keySelector: value => value,
elementSelector: value => value);

/// <summary>
/// A <see cref="PineValue"/> that is a list of <see cref="PineValue"/>s.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions implement/pine/ElmInteractive/ElmValueInterop.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Pine;
using Pine;
using System;
using System.Linq;

Expand Down Expand Up @@ -42,7 +42,7 @@ public static Result<string, PineValue> ElmValueDecodedAsInElmCompiler(ElmValue
ElmValue.ElmInteger integer => Result<string, byte>.ok((byte)integer.Value),
_ => Result<string, byte>.err("Invalid element in BlobValue tag")
}))
.Map(bytes => (PineValue)new PineValue.BlobValue(bytes.ToArray())),
.Map(bytes => PineValue.Blob([.. bytes])),

_ =>
Result<string, PineValue>.err("Invalid arguments for BlobValue tag")
Expand Down

0 comments on commit 8232d0c

Please sign in to comment.