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 unchecked function callbacks for better performance #186

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/Caller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private IntPtr NativeHandle
/// <returns>Returns the fuel consumed by the executing WebAssembly code or 0 if fuel consumption was not enabled.</returns>
public ulong GetConsumedFuel() => ((IStore)this).Context.GetConsumedFuel();

private static class Native
internal static class Native
{
[DllImport(Engine.LibraryName)]
[return: MarshalAs(UnmanagedType.I1)]
Expand Down
6,027 changes: 3,502 additions & 2,525 deletions src/Function.FromCallback.cs

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions src/Function.FromCallback.tt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// Do not modify it directly.
// </auto-generated>

#nullable enable

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -64,12 +66,12 @@ foreach (var (hasCaller, resultCount, parameterCount, methodGenerics, delegateTy
#>
unsafe
{
Function.Native.WasmtimeFuncCallback func = (env, callerPtr, args, nargs, results, nresults) =>
Function.Native.WasmtimeFuncUncheckedCallback func = (env, callerPtr, args_and_results, num_args_and_results) =>
{
<# GenerateCallbackContent(hasCaller, resultCount, parameterCount); #>
};

Native.wasmtime_func_new(
Native.wasmtime_func_new_unchecked(
store.Context.handle,
funcType,
func,
Expand All @@ -81,6 +83,7 @@ foreach (var (hasCaller, resultCount, parameterCount, methodGenerics, delegateTy
return new Function(store, externFunc, parameterKinds, resultKinds);
}
}

<#
}
#>
Expand Down
17 changes: 14 additions & 3 deletions src/Function.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1999,17 +1999,28 @@ internal static class Native
{
public delegate void Finalizer(IntPtr data);

public unsafe delegate IntPtr WasmtimeFuncCallback(IntPtr env, IntPtr caller, Value* args, UIntPtr nargs, Value* results, UIntPtr nresults);
public unsafe delegate IntPtr WasmtimeFuncCallback(IntPtr env, IntPtr caller, Value* args, nuint nargs, Value* results, nuint nresults);

public unsafe delegate IntPtr WasmtimeFuncUncheckedCallback(IntPtr env, IntPtr caller, ValueRaw* args_and_results, nuint num_args_and_results);

[DllImport(Engine.LibraryName)]
public static extern void wasmtime_func_new(IntPtr context, TypeHandle type, WasmtimeFuncCallback callback, IntPtr env, Finalizer? finalizer, out ExternFunc func);

[DllImport(Engine.LibraryName)]
public static unsafe extern IntPtr wasmtime_func_call(IntPtr context, in ExternFunc func, Value* args, UIntPtr nargs, Value* results, UIntPtr nresults, out IntPtr trap);
public static extern void wasmtime_func_new_unchecked(IntPtr context, TypeHandle type, WasmtimeFuncUncheckedCallback callback, IntPtr env, Finalizer? finalizer, out ExternFunc func);

[DllImport(Engine.LibraryName)]
public static unsafe extern IntPtr wasmtime_func_call(IntPtr context, in ExternFunc func, Value* args, nuint nargs, Value* results, nuint nresults, out IntPtr trap);

[DllImport(Engine.LibraryName)]
public static unsafe extern IntPtr wasmtime_func_type(IntPtr context, in ExternFunc func);

[DllImport(Engine.LibraryName)]
public static unsafe extern void wasmtime_func_from_raw(IntPtr context, nuint raw, out ExternFunc func);

[DllImport(Engine.LibraryName)]
public static unsafe extern nuint wasmtime_func_to_raw(IntPtr context, in ExternFunc func);

[DllImport(Engine.LibraryName)]
public static extern IntPtr wasm_functype_new(in ValueTypeArray parameters, in ValueTypeArray results);

Expand All @@ -2027,7 +2038,7 @@ internal static class Native
public static unsafe extern IntPtr wasmtime_trap_new(byte* bytes, nuint len);
}

private readonly IStore? store;
internal readonly IStore? store;
internal readonly ExternFunc func;
internal readonly List<ValueKind> parameters = new List<ValueKind>();
internal readonly List<ValueKind> results = new List<ValueKind>();
Expand Down
44 changes: 38 additions & 6 deletions src/FunctionCallbackOverloadTemplates.t4
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

void GenerateCallbackContent(bool hasCaller, int resultCount, int parameterCount) {
#>
using var caller = new Caller(callerPtr);
var storeContext = new StoreContext(Caller.Native.wasmtime_caller_context(callerPtr));
using var caller = <#= hasCaller ? "" : "!converterRequiresStore ? null : " #>new Caller(callerPtr);

try
{
Expand All @@ -30,7 +31,7 @@ void GenerateCallbackContent(bool hasCaller, int resultCount, int parameterCount
genericType += (i + 1).ToString(CultureInfo.InvariantCulture);
}

#>conv<#= genericType #>.Unbox(caller, args[<#= i.ToString(CultureInfo.InvariantCulture) #>].ToValueBox())<#
#>conv<#= genericType #>.Unbox(storeContext, caller, args_and_results[<#= i.ToString(CultureInfo.InvariantCulture) #>])<#

if (i + 1 < parameterCount)
{
Expand All @@ -56,7 +57,7 @@ void GenerateCallbackContent(bool hasCaller, int resultCount, int parameterCount
tupleAccessor = ".Item" + (i + 1).ToString(CultureInfo.InvariantCulture);
}

#>results[<#= i.ToString(CultureInfo.InvariantCulture) #>] = Value.FromValueBox(conv<#= genericType #>.Box(result<#= tupleAccessor #>));
#>conv<#= genericType #>.Box(storeContext, caller, ref args_and_results[<#= i.ToString(CultureInfo.InvariantCulture) #>], result<#= tupleAccessor #>);
<#
}
#>
Expand Down Expand Up @@ -137,9 +138,9 @@ IEnumerable<(
}

methodGenerics.Append(genericType);
delegateType.Append(genericType);
delegateType.Append(genericType + '?');

parameterConverters.AppendLine($" var conv{genericType} = ValueBox.Converter<{genericType}>();");
parameterConverters.AppendLine($" var conv{genericType} = ValueRaw.Converter<{genericType}>();");
}

if (parameterCount > 0 && resultCount > 0)
Expand Down Expand Up @@ -170,7 +171,7 @@ IEnumerable<(
methodGenerics.Append(genericType);
delegateReturnType.Append(genericType);

parameterConverters.AppendLine($" var conv{genericType} = ValueBox.Converter<{genericType}>();");
parameterConverters.AppendLine($" var conv{genericType} = ValueRaw.Converter<{genericType}>();");
}

if (resultCount > 1)
Expand Down Expand Up @@ -211,6 +212,37 @@ IEnumerable<(
{
callbackReturnTypeExpression = $"typeof({delegateReturnType})";
}

// Generate a bool value that specifies whether any of the converters needs the IStore.
if (!hasCaller)
{
parameterConverters.AppendLine();
parameterConverters.AppendLine($" var converterRequiresStore =");

for (int x = 0; x < parameterCount; x++)
{
string genericType = "T";
if (parameterCount > 1)
{
genericType += (x + 1).ToString(CultureInfo.InvariantCulture);
}

parameterConverters.AppendLine($" conv{genericType}.RequiresStore(forBoxing: false) ||");
}

for (int x = 0; x < resultCount; x++)
{
string genericType = "TResult";
if (resultCount > 1)
{
genericType += (x + 1).ToString(CultureInfo.InvariantCulture);
}

parameterConverters.AppendLine($" conv{genericType}.RequiresStore(forBoxing: true) ||");
}

parameterConverters.AppendLine($" false;");
}

yield return (
hasCaller,
Expand Down
Loading