From 5636ccd000df8f612d86b588b103186641ee192d Mon Sep 17 00:00:00 2001 From: algoidurovic <91566643+algoidurovic@users.noreply.github.com> Date: Fri, 15 Oct 2021 16:33:45 -0400 Subject: [PATCH] Optimization for constant assembly (#128) * Optimization added for repeated int constants under 2**7 w/ tests * fixed type problem and formatted * Expanded test and added comment for clarification --- pyteal/compiler/constants.py | 13 ++++-- pyteal/compiler/constants_test.py | 74 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/pyteal/compiler/constants.py b/pyteal/compiler/constants.py index 5152122a4..6d54e6607 100644 --- a/pyteal/compiler/constants.py +++ b/pyteal/compiler/constants.py @@ -132,7 +132,14 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]: sortedInts = sorted(intFreqs, key=lambda x: intFreqs[x], reverse=True) sortedBytes = sorted(byteFreqs, key=lambda x: byteFreqs[x], reverse=True) - intBlock = [i for i in sortedInts if intFreqs[i] > 1] + # Use Op.pushint if the constant does not occur in the top 4 most frequent and is smaller than + # 2 ** 7 to improve performance and save block space. + intBlock = [ + val + for i, val in enumerate(sortedInts) + if intFreqs[val] > 1 and (i < 4 or isinstance(val, str) or val >= 2 ** 7) + ] + byteBlock = [ ("0x" + b.hex()) if type(b) is bytes else cast(str, b) for b in sortedBytes @@ -151,13 +158,13 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]: if basicOp == Op.int: intValue = extractIntValue(op) - if intFreqs[intValue] == 1: + if intValue not in intBlock: assembled.append( TealOp(op.expr, Op.pushint, intValue, "//", *op.args) ) continue - index = sortedInts.index(intValue) + index = intBlock.index(intValue) if index == 0: assembled.append(TealOp(op.expr, Op.intc_0, "//", *op.args)) elif index == 1: diff --git a/pyteal/compiler/constants_test.py b/pyteal/compiler/constants_test.py index 8c169dac9..12d97b328 100644 --- a/pyteal/compiler/constants_test.py +++ b/pyteal/compiler/constants_test.py @@ -545,3 +545,77 @@ def test_createConstantBlocks_tmpl_all(): actual = createConstantBlocks(ops) assert actual == expected + + +def test_createConstantBlocks_intc(): + """Test scenario where there are more than 4 constants in the intcblock. + If the 4th constant can't fit in one varuint byte (more than 2**7) it + should be referenced with the Op.intc 4 command. + """ + + ops = [ + TealOp(None, Op.int, 0), + TealOp(None, Op.int, 0), + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, 2 ** 7), + TealOp(None, Op.int, 2 ** 7), + ] + + expected = [ + TealOp(None, Op.intcblock, 0, 1, 2, 3, 2 ** 7), + TealOp(None, Op.intc_0, "//", 0), + TealOp(None, Op.intc_0, "//", 0), + TealOp(None, Op.intc_1, "//", 1), + TealOp(None, Op.intc_1, "//", 1), + TealOp(None, Op.intc_2, "//", 2), + TealOp(None, Op.intc_2, "//", 2), + TealOp(None, Op.intc_3, "//", 3), + TealOp(None, Op.intc_3, "//", 3), + TealOp(None, Op.intc, 4, "//", 2 ** 7), + TealOp(None, Op.intc, 4, "//", 2 ** 7), + ] + + actual = createConstantBlocks(ops) + assert actual == expected + + +def test_createConstantBlocks_small_constant(): + """If a constant cannot be referenced using the intc_[0..3] commands + and it can be stored in one varuint it byte then Op.pushint is used. + """ + + for cur in range(4, 2 ** 7): + ops = [ + TealOp(None, Op.int, 0), + TealOp(None, Op.int, 0), + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, cur), + TealOp(None, Op.int, cur), + ] + + expected = [ + TealOp(None, Op.intcblock, 0, 1, 2, 3), + TealOp(None, Op.intc_0, "//", 0), + TealOp(None, Op.intc_0, "//", 0), + TealOp(None, Op.intc_1, "//", 1), + TealOp(None, Op.intc_1, "//", 1), + TealOp(None, Op.intc_2, "//", 2), + TealOp(None, Op.intc_2, "//", 2), + TealOp(None, Op.intc_3, "//", 3), + TealOp(None, Op.intc_3, "//", 3), + TealOp(None, Op.pushint, cur, "//", cur), + TealOp(None, Op.pushint, cur, "//", cur), + ] + + actual = createConstantBlocks(ops) + assert actual == expected