Skip to content

Commit

Permalink
AVM: Clearer cost benchmarks, and a nice optimization of b== and b< (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jannotti authored Jan 12, 2023
1 parent eddf773 commit 5d38a15
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 67 deletions.
24 changes: 18 additions & 6 deletions data/transactions/logic/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,15 @@ func opBytesSqrt(cx *EvalContext) error {
return nil
}

func nonzero(b []byte) []byte {
for i := range b {
if b[i] != 0 {
return b[i:]
}
}
return nil
}

func opBytesLt(cx *EvalContext) error {
last := len(cx.stack) - 1
prev := last - 1
Expand All @@ -1830,9 +1839,11 @@ func opBytesLt(cx *EvalContext) error {
return errors.New("math attempted on large byte-array")
}

rhs := new(big.Int).SetBytes(cx.stack[last].Bytes)
lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes)
cx.stack[prev] = boolToSV(lhs.Cmp(rhs) < 0)
rhs := nonzero(cx.stack[last].Bytes)
lhs := nonzero(cx.stack[prev].Bytes)

cx.stack[prev] = boolToSV(len(lhs) < len(rhs) || bytes.Compare(lhs, rhs) < 0)

cx.stack = cx.stack[:last]
return nil
}
Expand Down Expand Up @@ -1866,9 +1877,10 @@ func opBytesEq(cx *EvalContext) error {
return errors.New("math attempted on large byte-array")
}

rhs := new(big.Int).SetBytes(cx.stack[last].Bytes)
lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes)
cx.stack[prev] = boolToSV(lhs.Cmp(rhs) == 0)
rhs := nonzero(cx.stack[last].Bytes)
lhs := nonzero(cx.stack[prev].Bytes)

cx.stack[prev] = boolToSV(bytes.Equal(lhs, rhs))
cx.stack = cx.stack[:last]
return nil
}
Expand Down
23 changes: 16 additions & 7 deletions data/transactions/logic/evalCrypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,27 @@ func BenchmarkVerify(b *testing.B) {
benches := [][]string{
{"pop", "", "int 1234576; int 6712; pop; pop", "int 1"},
{"add", "", "int 1234576; int 6712; +; pop", "int 1"},
/*
{"ed25519verify_bare", "", `byte 0x
byte 0x
addr
ed25519verify_bare
assert`, "int 1"},*/
{"ecdsa_verify", "", `byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
{"ed25519verify_bare", "", `
byte 0x62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd
byte 0xaab40a8b4f1f386504af2473804abbc03bbd94506e8e0c8db881fc2b2c3aee65b867b25caa47fa25ae2105bf1731398df336213707f2d25f9b1d31b3dc133307;
addr C7ZCK6N2AJQMVEP4FRTK2UW45UFR6DKPRJHJVWB5O4VQOZMFPK2KCMR7M4
ed25519verify_bare; assert
`, "int 1"},
{"ecdsa_verify k1", "", `
byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
byte 0x5eb27782eb1a5df8de9a5d51613ad5ca730840ddf4af919c6feb15cde14f9978
byte 0x0cb3c0d636ed991ee030d09c295de3121eb166cb9e1552cf0ef0fb2358f35f0f
byte 0x79de0699673571df1de8486718d06a3e7838f6831ec4ef3fb963788fbfb773b7
byte 0xd76446a3393af3e2eefada16df80cc6a881a56f4cf41fa2ab4769c5708ce878d
ecdsa_verify Secp256k1
assert`, "int 1"},
{"ecdsa_verify r1", "", `
byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
byte 0xc010fc83ea196d6f5ce8a44637060bdcfb5bf1199cfc5bb893684d450c4f160c
byte 0x8e391a7b9cd75a99e8ebfe703036caebd9e91ae8339bd7e2abfb0f273eb8e972
byte 0x13e49a19378bbfa8d55ac81a35b87d7bae456c79fcf04a78803d8eb45b253fab
byte 0xa2d237cd897ca70787abf04d2155c6dc2fbe26fd642e0472cd75c13dc919ef1a
ecdsa_verify Secp256r1
assert`, "int 1"},
{"vrf_verify", "", `byte 0x72
byte 0xae5b66bdf04b4c010bfe32b2fc126ead2107b697634f6f7337b9bff8785ee111200095ece87dde4dbe87343f6df3b107d91798c8a7eb1245d3bb9c5aafb093358c13e6ae1111a55717e895fd15f99f07
Expand Down
164 changes: 110 additions & 54 deletions data/transactions/logic/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3728,27 +3728,37 @@ main:
}

func BenchmarkByteLogic(b *testing.B) {
e64 := "byte 0x8090a0b0c0d0e0f0;"
o64 := "byte 0x1020304050607080;"
hex128e := "90a0b0c0d0e0f0001020304050607080"
hex128o := "102030405060708090a0b0c0d0e0f000"
e128 := "byte 0x" + strings.Repeat(hex128e, 1) + ";"
o128 := "byte 0x" + strings.Repeat(hex128o, 1) + ";"
e256 := "byte 0x" + strings.Repeat(hex128e, 2) + ";"
o256 := "byte 0x" + strings.Repeat(hex128o, 2) + ";"
e512 := "byte 0x" + strings.Repeat(hex128e, 4) + ";"
o512 := "byte 0x" + strings.Repeat(hex128o, 4) + ";"

benches := [][]string{
{"b&", "", "byte 0x012345678901feab; byte 0x01ffffffffffffff; b&; pop", "int 1"},
{"b|", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b|; pop", "int 1"},
{"b^", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b^; pop", "int 1"},
{"b~", "byte 0x0123457673624736", "b~", "pop; int 1"},

{"b&big",
"byte 0x0123457601234576012345760123457601234576012345760123457601234576",
"byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b&",
"pop; int 1"},
{"b|big",
"byte 0x0123457601234576012345760123457601234576012345760123457601234576",
"byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b|",
"pop; int 1"},
{"b^big", "", // u256^u256
`byte 0x123457601234576012345760123457601234576012345760123457601234576a
byte 0xf123457601234576012345760123457601234576012345760123457601234576; b^; pop`,
"int 1"},
{"b~big", "byte 0xa123457601234576012345760123457601234576012345760123457601234576",
"b~",
"pop; int 1"},
{"b& 8", "", e64 + o64 + "b&; pop", "int 1"},
{"b| 8", "", e64 + o64 + "b|; pop", "int 1"},
{"b^ 8", "", e64 + o64 + "b^; pop", "int 1"},
{"b~ 8", e64, "b~", "pop; int 1"},

{"b& 16", "", e128 + o128 + "b&; pop", "int 1"},
{"b| 16", "", e128 + o128 + "b|; pop", "int 1"},
{"b^ 16", "", e128 + o128 + "b^; pop", "int 1"},
{"b~ 16", e128, "b~", "pop; int 1"},

{"b& 32", "", e256 + o256 + "b&; pop", "int 1"},
{"b| 32", "", e256 + o256 + "b|; pop", "int 1"},
{"b^ 32", "", e256 + o256 + "b^; pop", "int 1"},
{"b~ 32", e256, "b~", "pop; int 1"},

{"b& 64", "", e512 + o512 + "b&; pop", "int 1"},
{"b| 64", "", e512 + o512 + "b|; pop", "int 1"},
{"b^ 64", "", e512 + o512 + "b^; pop", "int 1"},
{"b~ 64", e512, "b~", "pop; int 1"},
}
for _, bench := range benches {
b.Run(bench[0], func(b *testing.B) {
Expand All @@ -3759,44 +3769,79 @@ func BenchmarkByteLogic(b *testing.B) {
}

func BenchmarkByteMath(b *testing.B) {
u64 := "byte 0x8090a0b0c0d0e0f0;"
hex128 := "102030405060708090a0b0c0d0e0f000"
u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";"
u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";"
u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";"

benches := [][]string{
{"bpop", "", "byte 0x01ffffffffffffff; pop", "int 1"},

{"b+", "byte 0x01234576", "byte 0x01ffffffffffffff; b+", "pop; int 1"},
{"b-", "byte 0x0ffff1234576", "byte 0x1202; b-", "pop; int 1"},
{"b*", "", "byte 0x01234576; byte 0x0223627389; b*; pop", "int 1"},
{"b/", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"},
{"b%", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"},
{"bsqrt", "", "byte 0x0123457673624736; bsqrt; pop", "int 1"},

{"b+big", // u256 + u256
"byte 0x0123457601234576012345760123457601234576012345760123457601234576",
"byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b+",
"pop; int 1"},
{"b-big", // second is a bit small, so we can subtract it over and over
"byte 0x0123457601234576012345760123457601234576012345760123457601234576",
"byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b-",
"pop; int 1"},
{"b*big", "", // u256*u256
`byte 0xa123457601234576012345760123457601234576012345760123457601234576
byte 0xf123457601234576012345760123457601234576012345760123457601234576; b*; pop`,
"int 1"},
{"b/big", "", // u256 / u128 (half sized divisor seems pessimal)
`byte 0xa123457601234576012345760123457601234576012345760123457601234576
byte 0x34576012345760123457601234576312; b/; pop`,
"int 1"},
{"b%big", "", // u256 / u128 (half sized divisor seems pessimal)
`byte 0xa123457601234576012345760123457601234576012345760123457601234576
byte 0x34576012345760123457601234576312; b/; pop`,
"int 1"},
{"bsqrt-big", "",
`byte 0xa123457601234576012345760123457601234576012345760123457601234576
bsqrt; pop`,
"int 1"},
{"bytec", u128 + "pop"},

{"b+ 128", u128 + u128 + "b+; pop"},
{"b- 128", u128 + u128 + "b-; pop"},
{"b* 128", u128 + u128 + "b*; pop"},
// half sized divisor seems pessimal for / and %
{"b/ 128", u128 + u64 + "b/; pop"},
{"b% 128", u128 + u64 + "b%; pop"},
{"bsqrt 128", u128 + "bsqrt; pop"},

{"b+ 256", u256 + u256 + "b+; pop"},
{"b- 256", u256 + u256 + "b-; pop"},
{"b* 256", u256 + u256 + "b*; pop"},
{"b/ 256", u256 + u128 + "b/; pop"},
{"b% 256", u256 + u128 + "b%; pop"},
{"bsqrt 256", u256 + "bsqrt; pop"},

{"b+ 512", u512 + u512 + "b+; pop"},
{"b- 512", u512 + u512 + "b-; pop"},
{"b* 512", u512 + u512 + "b*; pop"},
{"b/ 512", u512 + u256 + "b/; pop"},
{"b% 512", u512 + u256 + "b%; pop"},
{"bsqrt 512", u512 + "bsqrt; pop"},

{"bytec recheck", u128 + "pop"},
}
for _, bench := range benches {
b.Run(bench[0], func(b *testing.B) {
benchmarkOperation(b, bench[1], bench[2], bench[3])
b.ReportAllocs()
benchmarkOperation(b, "", bench[1], "int 1")
})
}
}

func BenchmarkByteCompare(b *testing.B) {
u64 := "byte 0x8090a0b0c0d0e0f0;"
hex128 := "102030405060708090a0b0c0d0e0f000"
u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";"
u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";"
u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";"
//u4k := "byte 0x" + strings.Repeat(hex128, 256) + ";"

benches := [][]string{
{"b== 64", u64 + u64 + "b==; pop"},
{"b< 64", u64 + u64 + "b<; pop"},
{"b<= 64", u64 + u64 + "b<=; pop"},
{"b== 128", u128 + u128 + "b==; pop"},
{"b< 128", u128 + u128 + "b<; pop"},
{"b<= 128", u128 + u128 + "b<=; pop"},
{"b== 256", u256 + u256 + "b==; pop"},
{"b< 256", u256 + u256 + "b<; pop"},
{"b<= 256", u256 + u256 + "b<=; pop"},
{"b== 512", u512 + u512 + "b==; pop"},
{"b< 512", u512 + u512 + "b<; pop"},
{"b<= 512", u512 + u512 + "b<=; pop"},
// These can only be run with the maxByteMathSize check removed. They
// show that we can remove that check in a later AVM version, as there
// is no appreciable cost to even a 4k compare.
// {"b== 4k", u4k + u4k + "b==; pop"},
// {"b< 4k", u4k + u4k + "b<; pop"},
// {"b<= 4k", u4k + u4k + "b<=; pop"},
}
for _, bench := range benches {
b.Run(bench[0], func(b *testing.B) {
b.ReportAllocs()
benchmarkOperation(b, "", bench[1], "int 1")
})
}
}
Expand Down Expand Up @@ -4871,9 +4916,20 @@ func TestBytesCompare(t *testing.T) {
testPanics(t, "byte 0x10; int 65; bzero; b<=", 4)
testAccepts(t, "byte 0x10; int 64; bzero; b>", 4)
testPanics(t, "byte 0x10; int 65; bzero; b>", 4)
testAccepts(t, "byte 0x1010; byte 0x10; b<; !", 4)

// All zero input are interesting, because they lead to bytes.Compare being
// called with nils. Show that is correct.
testAccepts(t, "byte 0x10; byte 0x00; b<; !", 4)
testAccepts(t, "byte 0x10; byte 0x0000; b<; !", 4)
testAccepts(t, "byte 0x00; byte 0x10; b<", 4)
testAccepts(t, "byte 0x0000; byte 0x10; b<", 4)
testAccepts(t, "byte 0x0000; byte 0x00; b<; !", 4)
testAccepts(t, "byte 0x; byte 0x00; b==", 4)

testAccepts(t, "byte 0x11; byte 0x10; b>", 4)
testAccepts(t, "byte 0x11; byte 0x0010; b>", 4)
testAccepts(t, "byte 0x1010; byte 0x11; b>", 4)

testAccepts(t, "byte 0x11; byte 0x10; b>=", 4)
testAccepts(t, "byte 0x11; byte 0x0011; b>=", 4)
Expand Down

0 comments on commit 5d38a15

Please sign in to comment.