From 1ea998015e5fce3cb14885cfe09ec8ad96a8f203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 12 Nov 2025 15:23:29 -0800 Subject: [PATCH] optimize event emission only convert arguments, do not transfer (avoid unnecessary copies) --- bbq/compiler/compiler.go | 14 +++++++++- bbq/compiler/compiler_test.go | 2 +- interpreter/interpreter_statement.go | 2 +- interpreter/misc_test.go | 38 ++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index 596c563cc5..1c55013173 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -1619,7 +1619,19 @@ func (c *Compiler[_, _]) VisitEmitStatement(statement *ast.EmitStatement) (_ str invocationExpression := statement.InvocationExpression arguments := invocationExpression.Arguments invocationTypes := c.DesugaredElaboration.InvocationExpressionTypes(invocationExpression) - c.compileArguments(arguments, invocationTypes) + + // Instead of compiling arguments as usual (via compileArguments), + // only convert the arguments and don't transfer them. + + for index, argument := range arguments { + c.compileExpression(argument.Expression) + + parameterType := invocationTypes.ParameterTypes[index] + parameterTypeIndex := c.getOrAddType(parameterType) + c.emit(opcode.InstructionConvert{ + Type: parameterTypeIndex, + }) + } argCount := len(arguments) if argCount >= math.MaxUint16 { diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index 3ebbd96a1f..e8a3da2efc 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1336,7 +1336,7 @@ func TestCompileEmit(t *testing.T) { opcode.InstructionStatement{}, // x opcode.InstructionGetLocal{Local: xIndex}, - opcode.InstructionTransferAndConvert{Type: 1}, + opcode.InstructionConvert{Type: 1}, // emit opcode.InstructionEmitEvent{ Type: 2, diff --git a/interpreter/interpreter_statement.go b/interpreter/interpreter_statement.go index 66c3c1344c..573470bf7f 100644 --- a/interpreter/interpreter_statement.go +++ b/interpreter/interpreter_statement.go @@ -403,7 +403,7 @@ func (interpreter *Interpreter) VisitEmitStatement(statement *ast.EmitStatement) argumentType := argumentTypes[i] parameterType := parameterTypes[i] - eventFields[i] = TransferAndConvert( + eventFields[i] = ConvertAndBoxWithValidation( interpreter, value, argumentType, diff --git a/interpreter/misc_test.go b/interpreter/misc_test.go index 7fb33446bc..c1cded3705 100644 --- a/interpreter/misc_test.go +++ b/interpreter/misc_test.go @@ -7340,6 +7340,44 @@ func TestInterpretEmitEvent(t *testing.T) { ) } +func BenchmarkEmit(b *testing.B) { + + inter, err := parseCheckAndPrepareWithOptions( + b, + ` + event Foo(ints: [Int]) + + fun test() { + var i = 0 + while i < 1000 { + emit Foo(ints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + i = i + 1 + } + } + `, + ParseCheckAndInterpretOptions{ + InterpreterConfig: &interpreter.Config{ + OnEventEmitted: func( + _ interpreter.ValueExportContext, + _ *sema.CompositeType, + _ []interpreter.Value, + ) error { + return nil + }, + }, + }, + ) + require.NoError(b, err) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := inter.Invoke("test") + require.NoError(b, err) + } +} + func TestInterpretReferenceEventParameter(t *testing.T) { t.Parallel()