cadence icon indicating copy to clipboard operation
cadence copied to clipboard

[Subtyping Generator] Switch to static-types for runtime subtype checking

Open SupunS opened this issue 3 months ago • 4 comments

⚠️ DO NOT MERGE: While this improves the performance of the VM, as a side effect, it degrades the performance of the interpreter. So we should only merge/deploy this once we fully switched to the VM. Unfortunately it's not trivial to put these changes behind a feature flag.

Closes #3691

Description

  • Use the generated static-type based subtyping check for the IsSubType() function (instead of the subtyping check based on sema-type)
  • Remove usages of MustConvertStaticToSemaType in VM, and rely on static-types.
  • Migrate all stdlib functions (except for a one or two that strictly needs composite type) to only use static-types (replace the usages of sema-type).

Results:

Vs VM in last released version (v1.8.3)

goos: darwin
goarch: arm64
pkg: github.com/onflow/cadence/runtime
cpu: Apple M1 Pro
                                 │ v1.8.3.txt  │             after.txt              │
                                 │   sec/op    │   sec/op     vs base               │
RuntimeFungibleTokenTransferVM-8   291.3µ ± 9%   273.0µ ± 4%  -6.27% (p=0.002 n=10)

                                 │  v1.8.3.txt  │              after.txt              │
                                 │     B/op     │     B/op      vs base               │
RuntimeFungibleTokenTransferVM-8   171.9Ki ± 0%   160.0Ki ± 0%  -6.92% (p=0.000 n=10)

                                 │ v1.8.3.txt  │              after.txt              │
                                 │  allocs/op  │  allocs/op   vs base                │
RuntimeFungibleTokenTransferVM-8   3.082k ± 0%   2.753k ± 0%  -10.67% (p=0.000 n=10)

Vs VM before switching to static-types (master)

goos: darwin
goarch: arm64
pkg: github.com/onflow/cadence/runtime
cpu: Apple M1 Pro
                                 │ before.txt  │             after.txt              │
                                 │   sec/op    │   sec/op     vs base               │
RuntimeFungibleTokenTransferVM-8   285.6µ ± 9%   273.0µ ± 4%  -4.40% (p=0.011 n=10)

                                 │  before.txt  │              after.txt              │
                                 │     B/op     │     B/op      vs base               │
RuntimeFungibleTokenTransferVM-8   170.6Ki ± 0%   160.0Ki ± 0%  -6.21% (p=0.000 n=10)

                                 │ before.txt  │             after.txt              │
                                 │  allocs/op  │  allocs/op   vs base               │
RuntimeFungibleTokenTransferVM-8   3.000k ± 0%   2.753k ± 0%  -8.23% (p=0.000 n=10)

**Note: The master branch also contains some optimizations that was ported over. So the above comparison only includes the improvement we get from switching to static-types only.

Vs Interpreter (master)

goos: darwin
goarch: arm64
pkg: github.com/onflow/cadence/runtime
cpu: Apple M1 Pro
                               │ interpreter.txt │           after.txt           │
                               │     sec/op      │   sec/op     vs base          │
RuntimeFungibleTokenTransfer-8       272.8µ ± 2%   273.0µ ± 4%  ~ (p=0.796 n=10)

                               │ interpreter.txt │              after.txt              │
                               │      B/op       │     B/op      vs base               │
RuntimeFungibleTokenTransfer-8      157.3Ki ± 0%   160.0Ki ± 0%  +1.70% (p=0.000 n=10)

                               │ interpreter.txt │             after.txt              │
                               │    allocs/op    │  allocs/op   vs base               │
RuntimeFungibleTokenTransfer-8       2.980k ± 0%   2.753k ± 0%  -7.62% (p=0.000 n=10)

Future work:

  • Event emitting still need the sema types (hence the conversion exist). Should try to replace those as well.
  • Generated subtype check for static-types still converts to sema types in certain cases (for e.g: conformance information is not available in static-types, so need to convert to sema-type to get these information)

  • [x] Targeted PR against master branch
  • [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work
  • [x] Code follows the standards mentioned here
  • [x] Updated relevant documentation
  • [x] Re-reviewed Files changed in the Github PR explorer
  • [x] Added appropriate labels

SupunS avatar Nov 06 '25 21:11 SupunS

Benchstat comparison

  • Base branch: onflow:master
  • Base commit: ba29a406ffec57c3db8b4723344413f894fc242a
Results

old.txtnew.txt
time/opdelta
pkg:github.com/onflow/cadence/bbq/compiler goos:linux goarch:amd64
CompileFungibleTokenTransferTransaction-4110µs ± 0%110µs ± 0%~(p=1.000 n=1+1)
CompileTime-430.1µs ± 0%30.9µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ContractFunctionInvocation-4376µs ± 0%384µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ContractImport-4179µs ± 0%176µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
Emit-44.37ms ± 0%5.31ms ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ExportType/composite_type-4305ns ± 0%300ns ± 0%~(p=1.000 n=1+1)
ExportType/simple_type-466.0ns ± 0%67.4ns ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
FTTransfer-4115µs ± 0%104µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ImperativeFib-421.6µs ± 0%26.3µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ImperativeFib-414.2µs ± 0%12.1µs ± 0%~(p=1.000 n=1+1)
ImperativeFibNewCompilerNewVM-433.8µs ± 0%32.3µs ± 0%~(p=1.000 n=1+1)
ImperativeFibNewVM-416.2µs ± 0%13.6µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
InterpretRecursionFib-42.19ms ± 0%2.65ms ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
InterpreterFTTransfer-4103µs ± 0%126µs ± 0%~(p=1.000 n=1+1)
InterpreterImperativeFib-421.0µs ± 0%25.8µs ± 0%~(p=1.000 n=1+1)
InterpreterNewStruct-463.0µs ± 0%71.7µs ± 0%~(p=1.000 n=1+1)
MethodCall/concrete_type_method_call-427.6µs ± 0%26.3µs ± 0%~(p=1.000 n=1+1)
MethodCall/interface_method_call-449.0µs ± 0%45.2µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
NewInterpreter/new_interpreter-4910ns ± 0%857ns ± 0%~(p=1.000 n=1+1)
NewInterpreter/new_sub-interpreter-4326ns ± 0%325ns ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
NewResource-478.6µs ± 0%81.1µs ± 0%~(p=1.000 n=1+1)
NewStruct-433.5µs ± 0%31.9µs ± 0%~(p=1.000 n=1+1)
NewStructRaw-44.14µs ± 0%3.88µs ± 0%~(p=1.000 n=1+1)
RecursionFib-4821µs ± 0%691µs ± 0%~(p=1.000 n=1+1)
RuntimeFungibleTokenTransfer-4724µs ± 0%825µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
RuntimeFungibleTokenTransferInterpreter-4592µs ± 0%653µs ± 0%~(p=1.000 n=1+1)
RuntimeFungibleTokenTransferVM-4631µs ± 0%622µs ± 0%~(p=1.000 n=1+1)
RuntimeResourceDictionaryValues-42.71ms ± 0%2.77ms ± 0%~(p=1.000 n=1+1)
RuntimeResourceTracking-412.6ms ± 0%12.1ms ± 0%~(p=1.000 n=1+1)
RuntimeScriptNoop-414.8µs ± 0%14.1µs ± 0%~(p=1.000 n=1+1)
RuntimeVMInvokeContractImperativeFib-426.8µs ± 0%23.1µs ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ValueIsSubtypeOfSemaType-454.6ns ± 0%54.0ns ± 0%~(p=1.000 n=1+1)
 
alloc/opdelta
pkg:github.com/onflow/cadence/bbq/compiler goos:linux goarch:amd64
CompileFungibleTokenTransferTransaction-479.3kB ± 0%79.3kB ± 0%~(all equal)
CompileTime-416.3kB ± 0%16.3kB ± 0%~(all equal)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ContractFunctionInvocation-4152kB ± 0%155kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ContractImport-473.9kB ± 0%73.7kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
Emit-41.47MB ± 0%1.49MB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ExportType/composite_type-4120B ± 0%120B ± 0%~(all equal)
ExportType/simple_type-40.00B 0.00B ~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
FTTransfer-439.2kB ± 0%34.3kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ImperativeFib-48.30kB ± 0%8.30kB ± 0%~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ImperativeFib-41.94kB ± 0%1.94kB ± 0%~(all equal)
ImperativeFibNewCompilerNewVM-421.9kB ± 0%21.6kB ± 0%~(p=1.000 n=1+1)
ImperativeFibNewVM-44.70kB ± 0%4.36kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
InterpretRecursionFib-41.19MB ± 0%1.19MB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
InterpreterFTTransfer-439.6kB ± 0%49.2kB ± 0%~(p=1.000 n=1+1)
InterpreterImperativeFib-48.29kB ± 0%8.29kB ± 0%~(all equal)
InterpreterNewStruct-424.4kB ± 0%26.2kB ± 0%~(p=1.000 n=1+1)
MethodCall/concrete_type_method_call-47.00kB ± 0%6.97kB ± 0%~(p=1.000 n=1+1)
MethodCall/interface_method_call-414.4kB ± 0%14.4kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
NewInterpreter/new_interpreter-4976B ± 0%976B ± 0%~(all equal)
NewInterpreter/new_sub-interpreter-4232B ± 0%232B ± 0%~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
NewResource-440.4kB ± 0%40.1kB ± 0%~(p=1.000 n=1+1)
NewStruct-410.4kB ± 0%10.3kB ± 0%~(p=1.000 n=1+1)
NewStructRaw-42.58kB ± 0%2.59kB ± 0%~(p=1.000 n=1+1)
RecursionFib-488.1kB ± 0%88.1kB ± 0%~(all equal)
RuntimeFungibleTokenTransfer-4234kB ± 0%266kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
RuntimeFungibleTokenTransferInterpreter-4158kB ± 0%185kB ± 0%~(p=1.000 n=1+1)
RuntimeFungibleTokenTransferVM-4173kB ± 0%162kB ± 0%~(p=1.000 n=1+1)
RuntimeResourceDictionaryValues-41.76MB ± 0%1.77MB ± 0%~(p=1.000 n=1+1)
RuntimeResourceTracking-49.27MB ± 0%9.26MB ± 0%~(p=1.000 n=1+1)
RuntimeScriptNoop-47.99kB ± 0%7.98kB ± 0%~(p=1.000 n=1+1)
RuntimeVMInvokeContractImperativeFib-49.80kB ± 0%9.32kB ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ValueIsSubtypeOfSemaType-432.0B ± 0%32.0B ± 0%~(all equal)
 
allocs/opdelta
pkg:github.com/onflow/cadence/bbq/compiler goos:linux goarch:amd64
CompileFungibleTokenTransferTransaction-4424 ± 0%424 ± 0%~(all equal)
CompileTime-4182 ± 0%182 ± 0%~(all equal)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ContractFunctionInvocation-42.46k ± 0%2.53k ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ContractImport-4938 ± 0%936 ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
Emit-439.0k ± 0%40.0k ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
ExportType/composite_type-43.00 ± 0%3.00 ± 0%~(all equal)
ExportType/simple_type-40.00 0.00 ~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
FTTransfer-41.00k ± 0%0.85k ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ImperativeFib-4176 ± 0%176 ± 0%~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
ImperativeFib-449.0 ± 0%49.0 ± 0%~(all equal)
ImperativeFibNewCompilerNewVM-4214 ± 0%212 ± 0%~(p=1.000 n=1+1)
ImperativeFibNewVM-486.0 ± 0%84.0 ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
InterpretRecursionFib-417.7k ± 0%17.7k ± 0%~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
InterpreterFTTransfer-4833 ± 0%1044 ± 0%~(p=1.000 n=1+1)
InterpreterImperativeFib-4175 ± 0%175 ± 0%~(all equal)
InterpreterNewStruct-4418 ± 0%448 ± 0%~(p=1.000 n=1+1)
MethodCall/concrete_type_method_call-4135 ± 0%135 ± 0%~(all equal)
MethodCall/interface_method_call-4285 ± 0%285 ± 0%~(all equal)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
NewInterpreter/new_interpreter-415.0 ± 0%15.0 ± 0%~(all equal)
NewInterpreter/new_sub-interpreter-44.00 ± 0%4.00 ± 0%~(all equal)
pkg:github.com/onflow/cadence/bbq/vm/test goos:linux goarch:amd64
NewResource-4730 ± 0%728 ± 0%~(p=1.000 n=1+1)
NewStruct-4219 ± 0%219 ± 0%~(all equal)
NewStructRaw-438.0 ± 0%38.0 ± 0%~(all equal)
RecursionFib-43.65k ± 0%3.65k ± 0%~(all equal)
RuntimeFungibleTokenTransfer-43.99k ± 0%4.82k ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/runtime goos:linux goarch:amd64
RuntimeFungibleTokenTransferInterpreter-42.98k ± 0%3.65k ± 0%~(p=1.000 n=1+1)
RuntimeFungibleTokenTransferVM-43.04k ± 0%2.79k ± 0%~(p=1.000 n=1+1)
RuntimeResourceDictionaryValues-436.7k ± 0%36.7k ± 0%~(p=1.000 n=1+1)
RuntimeResourceTracking-4159k ± 0%159k ± 0%~(p=1.000 n=1+1)
RuntimeScriptNoop-4113 ± 0%113 ± 0%~(all equal)
RuntimeVMInvokeContractImperativeFib-4197 ± 0%191 ± 0%~(p=1.000 n=1+1)
pkg:github.com/onflow/cadence/interpreter goos:linux goarch:amd64
ValueIsSubtypeOfSemaType-41.00 ± 0%1.00 ± 0%~(all equal)
 

github-actions[bot] avatar Nov 06 '25 21:11 github-actions[bot]

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

github-actions[bot] avatar Dec 05 '25 23:12 github-actions[bot]

[...] it degrades the performance of the interpreter.

I missed this before. Is this because in the interpreter we get sema types from the elaboration, and now have to convert them to static types?

turbolent avatar Dec 05 '25 23:12 turbolent