Skip to content

Commit 7427a1f

Browse files
committed
adding StatsDetail.Function option for measuring function time
1 parent 5b7bbc8 commit 7427a1f

6 files changed

Lines changed: 73 additions & 6 deletions

File tree

Wacs.Core/Runtime/ExecContext.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,19 @@ public void ResetStats()
399399
{
400400
Stats[(ushort)(ByteCode)opcode] = new ExecStat();
401401
}
402+
403+
for (int i = 0, l = Store.FunctionCount(); i < l; i++)
404+
{
405+
406+
if (Store[new FuncAddr(i)] is FunctionInstance inst)
407+
{
408+
inst.CallCount = 0;
409+
}
410+
if (!Stats.TryGetValue((ushort)i, out var stat))
411+
{
412+
Stats[(ushort)i] = new ExecStat();
413+
}
414+
}
402415
}
403416

404417
public OpCode GetEndFor() =>

Wacs.Core/Runtime/Frame.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Wacs.Core.Runtime
2626
{
2727
public sealed class Frame : IPoolable
2828
{
29+
public ushort FuncAddr;
2930
public ModuleInstance Module = null!;
3031
public Memory<Value> Locals;
3132
public Label ReturnLabel = new();

Wacs.Core/Runtime/InvokerOptions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public enum StatsDetail: int
3535
None = 0,
3636
Total = 1,
3737
Instruction = 2,
38+
Function = 4,
3839
}
3940

4041
public class InvokerOptions
@@ -59,7 +60,7 @@ public bool UseFastPath()
5960
return false;
6061
if (ShowPath)
6162
return false;
62-
if (CollectStats == StatsDetail.Instruction)
63+
if (CollectStats is StatsDetail.Instruction or StatsDetail.Function)
6364
return false;
6465
return true;
6566
}

Wacs.Core/Runtime/Store.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using System;
1616
using System.Collections.Generic;
17+
using System.Linq;
1718
using Wacs.Core.Runtime.Types;
1819
using Wacs.Core.Types;
1920

@@ -122,6 +123,7 @@ public FuncAddr AllocateWasmFunction(Module.Function func, ModuleInstance module
122123
{
123124
var funcInst = new FunctionInstance(moduleInst, func);
124125
var funcAddr = AddFunction(funcInst);
126+
funcInst.Address = funcAddr;
125127
return funcAddr;
126128
}
127129

@@ -270,5 +272,7 @@ public void DropElement(ElemAddr addr)
270272
Elems[addr.Value] = ElementInstance.Empty;
271273
}
272274
}
275+
276+
public int FunctionCount() => Funcs.Count;
273277
}
274278
}

Wacs.Core/Runtime/Types/FunctionInstance.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public class FunctionInstance : IFunctionInstance
4040

4141
public readonly FuncIdx Index;
4242

43+
public FuncAddr Address;
44+
public int CallCount;
45+
4346
public readonly ModuleInstance Module;
4447

4548
//Copied from the static Definition
@@ -120,6 +123,7 @@ public void Invoke(ExecContext context)
120123
//8.
121124
//Push the frame and operate on the frame on the stack.
122125
var frame = context.ReserveFrame(Module, funcType.ResultType.Arity);
126+
frame.FuncAddr = (ushort)Address.Value;
123127
frame.Locals = context.OpStack.ReserveLocals(ParameterCount, TotalCount);
124128
context.OpStack.GuardExhaust(MaxStack);
125129

@@ -143,6 +147,7 @@ public void Invoke(ExecContext context)
143147
frame.Head = LinkedOffset;
144148

145149
context.InstructionPointer = LinkedOffset - 1;
150+
CallCount++;
146151
}
147152

148153
public void TailInvoke(ExecContext context)
@@ -178,6 +183,8 @@ public void TailInvoke(ExecContext context)
178183
frame.Head = LinkedOffset;
179184

180185
context.InstructionPointer = LinkedOffset - 1;
186+
187+
CallCount++;
181188
}
182189

183190
public override string ToString() => $"FunctionInstance[{Id}] (Type: {Type}, IsExport: {IsExport})";

Wacs.Core/Runtime/WasmRuntimeExecution.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,27 @@ public async Task ProcessThreadWithOptions(InvokerOptions options)
523523
st.duration += Context.InstructionTimer.ElapsedTicks;
524524
Context.Stats[(ushort)inst.Op] = st;
525525
}
526+
else if (options.CollectStats == StatsDetail.Function)
527+
{
528+
Context.InstructionTimer.Restart();
529+
if (inst.PointerAdvance > 0)
530+
Context.InstructionPointer += inst.PointerAdvance;
531+
if (inst.Nop)
532+
continue;
533+
534+
if (inst.IsAsync)
535+
await inst.ExecuteAsync(Context);
536+
else
537+
inst.Execute(Context);
538+
539+
Context.InstructionTimer.Stop();
540+
Context.steps += inst.Size;
541+
542+
var st = Context.Stats[Context.Frame.FuncAddr];
543+
st.count += inst.Size;
544+
st.duration += Context.InstructionTimer.ElapsedTicks;
545+
Context.Stats[Context.Frame.FuncAddr] = st;
546+
}
526547
else
527548
{
528549
Context.InstructionTimer.Start();
@@ -652,10 +673,10 @@ private void LogPostInstruction(InvokerOptions options, InstructionBase inst)
652673
private void PrintStats(InvokerOptions options)
653674
{
654675
long procTicks = Context.ProcessTimer.ElapsedTicks;
655-
long totalExecs = options.CollectStats == StatsDetail.Instruction
676+
long totalExecs = options.CollectStats is StatsDetail.Instruction or StatsDetail.Function
656677
? Context.Stats.Values.Sum(dc => dc.count)
657678
: Context.steps;
658-
long execTicks = options.CollectStats == StatsDetail.Instruction
679+
long execTicks = options.CollectStats is StatsDetail.Instruction or StatsDetail.Function
659680
? Context.Stats.Values.Sum(dc => dc.duration)
660681
: Context.InstructionTimer.ElapsedTicks;
661682
long overheadTicks = procTicks - execTicks;
@@ -690,12 +711,32 @@ private void PrintStats(InvokerOptions options)
690711

691712
foreach (var (opcode, st) in orderedStats)
692713
{
693-
string label = $"{((ByteCode)opcode).GetMnemonic()}".PadLeft(totalLabel.Length, ' ');
694714
TimeSpan instTime = new TimeSpan(st.duration/100); //100ns
695715
double percent = 100.0 * st.duration / execTicks;
696-
string execsLabel = $"{st.count}".PadLeft(totalInst.Length, ' ');
716+
int count = (int)st.count;
717+
697718
string percentLabel = $"{percent:#0.###}%e".PadLeft(8,' ');
698-
string instAve = $"{instTime.TotalMilliseconds * 1000000.0/st.count:#0.#}ns/i";
719+
string label;
720+
string instAve;
721+
if (options.CollectStats == StatsDetail.Function)
722+
{
723+
if (Context.Store[new FuncAddr(opcode)] is FunctionInstance func)
724+
{
725+
count = func.CallCount;
726+
label = $"{func.ModuleName}[{func.Index.Value}]".PadLeft(totalLabel.Length, ' ');
727+
instAve = $"{instTime.TotalMilliseconds * 1000000.0/count:#0.#}ns/call";
728+
}
729+
else
730+
{
731+
continue;
732+
}
733+
}
734+
else
735+
{
736+
label = $"{((ByteCode)opcode).GetMnemonic()}".PadLeft(totalLabel.Length, ' ');
737+
instAve = $"{instTime.TotalMilliseconds * 1000000.0/count:#0.#}ns/i";
738+
}
739+
string execsLabel = $"{count}".PadLeft(totalInst.Length, ' ');
699740
Console.Error.WriteLine($"{label}: {execsLabel}| ({percentLabel}) {instTime.TotalMilliseconds:#0.000}ms {instAve}");
700741
}
701742
}

0 commit comments

Comments
 (0)