Skip to content

Commit 0e8a03c

Browse files
authored
Implement boxed constant integers as literals (#12507)
This avoids creating a new int object every time we evaluate an integer literal in a context that requires a boxed value. This speeds up this microbenchmark by about 60%: ``` def f() -> None: for j in range(1000 * 1000): a = [] for i in range(10): a.append(10) ``` In more realistic workloads the impact is hard to measure and is likely below the noise floor.
1 parent ee0638f commit 0e8a03c

13 files changed

+130
-124
lines changed

mypyc/irbuild/ll_builder.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ def self(self) -> Register:
149149

150150
def box(self, src: Value) -> Value:
151151
if src.type.is_unboxed:
152+
if isinstance(src, Integer) and is_tagged(src.type):
153+
return self.add(LoadLiteral(src.value >> 1, rtype=object_rprimitive))
152154
return self.add(Box(src))
153155
else:
154156
return src

mypyc/test-data/exceptions-freq.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ def f(x):
8585
r1 :: bit
8686
r2 :: None
8787
L0:
88-
r0 = box(short_int, 2)
88+
r0 = object 1
89+
inc_ref r0
8990
r1 = CPyList_SetItem(x, 0, r0)
9091
if not r1 goto L2 (error at f:3) else goto L1 :: bool
9192
L1:

mypyc/test-data/irbuild-basic.test

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ L0:
720720
r0 = builtins :: module
721721
r1 = 'print'
722722
r2 = CPyObject_GetAttr(r0, r1)
723-
r3 = box(short_int, 10)
723+
r3 = object 5
724724
r4 = PyObject_CallFunctionObjArgs(r2, r3, 0)
725725
return 1
726726

@@ -738,7 +738,7 @@ L0:
738738
r0 = builtins :: module
739739
r1 = 'print'
740740
r2 = CPyObject_GetAttr(r0, r1)
741-
r3 = box(short_int, 10)
741+
r3 = object 5
742742
r4 = PyObject_CallFunctionObjArgs(r2, r3, 0)
743743
return 1
744744

@@ -810,7 +810,7 @@ def g(y):
810810
L0:
811811
r0 = g(y)
812812
r1 = PyList_New(1)
813-
r2 = box(short_int, 2)
813+
r2 = object 1
814814
r3 = get_element_ptr r1 ob_item :: PyListObject
815815
r4 = load_mem r3 :: ptr*
816816
set_mem r4, r2 :: builtins.object*
@@ -838,7 +838,7 @@ def g(y):
838838
r7 :: bit
839839
r8, r9 :: object
840840
L0:
841-
r0 = box(short_int, 2)
841+
r0 = object 1
842842
r1 = g(r0)
843843
r2 = PyList_New(1)
844844
r3 = get_element_ptr r2 ob_item :: PyListObject
@@ -851,7 +851,7 @@ L0:
851851
r7 = CPyList_SetItem(a, 0, r6)
852852
r8 = box(bool, 1)
853853
y = r8
854-
r9 = box(short_int, 6)
854+
r9 = object 3
855855
return r9
856856

857857
[case testCoerceToObject2]
@@ -869,7 +869,7 @@ def f(a, o):
869869
r2 :: int
870870
r3 :: object
871871
L0:
872-
r0 = box(short_int, 2)
872+
r0 = object 1
873873
a.x = r0; r1 = is_error
874874
r2 = a.n
875875
r3 = box(int, r2)
@@ -1204,7 +1204,7 @@ L0:
12041204
r0 = load_address PyLong_Type
12051205
r1 = 'base'
12061206
r2 = PyTuple_Pack(1, x)
1207-
r3 = box(short_int, 4)
1207+
r3 = object 2
12081208
r4 = CPyDict_Build(1, r1, r3)
12091209
r5 = PyObject_Call(r0, r2, r4)
12101210
r6 = unbox(int, r5)
@@ -1231,7 +1231,7 @@ L0:
12311231
r0 = 'insert'
12321232
r1 = CPyObject_GetAttr(xs, r0)
12331233
r2 = 'x'
1234-
r3 = box(short_int, 0)
1234+
r3 = object 0
12351235
r4 = PyTuple_Pack(1, r3)
12361236
r5 = box(int, first)
12371237
r6 = CPyDict_Build(1, r2, r5)
@@ -1242,7 +1242,7 @@ L0:
12421242
r11 = 'i'
12431243
r12 = PyTuple_Pack(0)
12441244
r13 = box(int, second)
1245-
r14 = box(short_int, 2)
1245+
r14 = object 1
12461246
r15 = CPyDict_Build(2, r10, r13, r11, r14)
12471247
r16 = PyObject_Call(r9, r12, r15)
12481248
return xs
@@ -1482,7 +1482,7 @@ L1:
14821482
L2:
14831483
r5 = __main__.globals :: static
14841484
r6 = 'x'
1485-
r7 = box(short_int, 2)
1485+
r7 = object 1
14861486
r8 = CPyDict_SetItem(r5, r6, r7)
14871487
r9 = r8 >= 0 :: signed
14881488
r10 = __main__.globals :: static
@@ -1516,7 +1516,7 @@ L0:
15161516
r0 = m :: module
15171517
r1 = 'f'
15181518
r2 = CPyObject_GetAttr(r0, r1)
1519-
r3 = box(short_int, 2)
1519+
r3 = object 1
15201520
r4 = PyObject_CallFunctionObjArgs(r2, r3, 0)
15211521
r5 = cast(str, r4)
15221522
return r5
@@ -1545,7 +1545,7 @@ def main():
15451545
r1 :: union[int, str]
15461546
r2, x :: int
15471547
L0:
1548-
r0 = box(short_int, 0)
1548+
r0 = object 0
15491549
r1 = foo(r0)
15501550
r2 = unbox(int, r1)
15511551
x = r2
@@ -1598,7 +1598,7 @@ def main():
15981598
r1 :: __main__.A
15991599
r2, x :: __main__.B
16001600
L0:
1601-
r0 = box(short_int, 0)
1601+
r0 = object 0
16021602
r1 = foo(r0)
16031603
r2 = cast(__main__.B, r1)
16041604
x = r2
@@ -1713,7 +1713,7 @@ L0:
17131713
r2 = 'f'
17141714
r3 = CPyDict_GetItem(r1, r2)
17151715
r4 = PyList_New(1)
1716-
r5 = box(short_int, 2)
1716+
r5 = object 1
17171717
r6 = get_element_ptr r4 ob_item :: PyListObject
17181718
r7 = load_mem r6 :: ptr*
17191719
set_mem r7, r5 :: builtins.object*
@@ -1757,9 +1757,9 @@ L0:
17571757
r0 = 'a'
17581758
r1 = 'b'
17591759
r2 = 'c'
1760-
r3 = box(short_int, 2)
1761-
r4 = box(short_int, 4)
1762-
r5 = box(short_int, 6)
1760+
r3 = object 1
1761+
r4 = object 2
1762+
r5 = object 3
17631763
r6 = CPyDict_Build(3, r0, r3, r1, r4, r2, r5)
17641764
r7 = __main__.globals :: static
17651765
r8 = 'f'
@@ -1787,16 +1787,16 @@ def h():
17871787
L0:
17881788
r0 = 'b'
17891789
r1 = 'c'
1790-
r2 = box(short_int, 4)
1791-
r3 = box(short_int, 6)
1790+
r2 = object 2
1791+
r3 = object 3
17921792
r4 = CPyDict_Build(2, r0, r2, r1, r3)
17931793
r5 = __main__.globals :: static
17941794
r6 = 'f'
17951795
r7 = CPyDict_GetItem(r5, r6)
17961796
r8 = PyDict_New()
17971797
r9 = CPyDict_UpdateInDisplay(r8, r4)
17981798
r10 = r9 >= 0 :: signed
1799-
r11 = box(short_int, 2)
1799+
r11 = object 1
18001800
r12 = PyTuple_Pack(1, r11)
18011801
r13 = PyObject_Call(r7, r12, r8)
18021802
r14 = unbox(tuple[int, int, int], r13)
@@ -1913,9 +1913,9 @@ def f():
19131913
L0:
19141914
r0 = PyList_New(0)
19151915
r1 = PyList_New(3)
1916-
r2 = box(short_int, 2)
1917-
r3 = box(short_int, 4)
1918-
r4 = box(short_int, 6)
1916+
r2 = object 1
1917+
r3 = object 2
1918+
r4 = object 3
19191919
r5 = get_element_ptr r1 ob_item :: PyListObject
19201920
r6 = load_mem r5 :: ptr*
19211921
set_mem r6, r2 :: builtins.object*
@@ -2012,9 +2012,9 @@ def f():
20122012
L0:
20132013
r0 = PyDict_New()
20142014
r1 = PyList_New(3)
2015-
r2 = box(short_int, 2)
2016-
r3 = box(short_int, 4)
2017-
r4 = box(short_int, 6)
2015+
r2 = object 1
2016+
r3 = object 2
2017+
r4 = object 3
20182018
r5 = get_element_ptr r1 ob_item :: PyListObject
20192019
r6 = load_mem r5 :: ptr*
20202020
set_mem r6, r2 :: builtins.object*
@@ -2429,7 +2429,7 @@ def SubclassedTrait.boxed(self):
24292429
self :: __main__.SubclassedTrait
24302430
r0 :: object
24312431
L0:
2432-
r0 = box(short_int, 6)
2432+
r0 = object 3
24332433
return r0
24342434
def DerivingObject.this(self):
24352435
self :: __main__.DerivingObject
@@ -2637,7 +2637,7 @@ L2:
26372637
r57 = __main__.globals :: static
26382638
r58 = 'Lol'
26392639
r59 = CPyDict_GetItem(r57, r58)
2640-
r60 = box(short_int, 2)
2640+
r60 = object 1
26412641
r61 = PyObject_CallFunctionObjArgs(r59, r60, r56, 0)
26422642
r62 = cast(tuple, r61)
26432643
r63 = __main__.globals :: static
@@ -2666,9 +2666,9 @@ L2:
26662666
r86 = CPyDict_SetItem(r84, r85, r83)
26672667
r87 = r86 >= 0 :: signed
26682668
r88 = PyList_New(3)
2669-
r89 = box(short_int, 2)
2670-
r90 = box(short_int, 4)
2671-
r91 = box(short_int, 6)
2669+
r89 = object 1
2670+
r90 = object 2
2671+
r91 = object 3
26722672
r92 = get_element_ptr r88 ob_item :: PyListObject
26732673
r93 = load_mem r92 :: ptr*
26742674
set_mem r93, r89 :: builtins.object*
@@ -3738,9 +3738,9 @@ def range_object():
37383738
r10 :: bit
37393739
L0:
37403740
r0 = load_address PyRange_Type
3741-
r1 = box(short_int, 8)
3742-
r2 = box(short_int, 24)
3743-
r3 = box(short_int, 4)
3741+
r1 = object 4
3742+
r2 = object 12
3743+
r3 = object 2
37443744
r4 = PyObject_CallFunctionObjArgs(r0, r1, r2, r3, 0)
37453745
r5 = cast(range, r4)
37463746
r = r5
@@ -3784,6 +3784,7 @@ L3:
37843784
goto L1
37853785
L4:
37863786
return 1
3787+
37873788
[case testLocalRedefinition]
37883789
# mypy: allow-redefinition
37893790
def f() -> None:

mypyc/test-data/irbuild-constant-fold.test

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,13 @@ def unsupported_div():
145145
r4, r5, r6 :: object
146146
r7, y :: float
147147
L0:
148-
r0 = box(short_int, 8)
149-
r1 = box(short_int, 12)
148+
r0 = object 4
149+
r1 = object 6
150150
r2 = PyNumber_TrueDivide(r0, r1)
151151
r3 = cast(float, r2)
152152
x = r3
153-
r4 = box(short_int, 20)
154-
r5 = box(short_int, 10)
153+
r4 = object 10
154+
r5 = object 5
155155
r6 = PyNumber_TrueDivide(r4, r5)
156156
r7 = cast(float, r6)
157157
y = r7
@@ -160,8 +160,8 @@ def unsupported_pow():
160160
r0, r1, r2 :: object
161161
r3, p :: float
162162
L0:
163-
r0 = box(short_int, 6)
164-
r1 = box(short_int, -2)
163+
r0 = object 3
164+
r1 = object -1
165165
r2 = CPyNumber_Power(r0, r1)
166166
r3 = cast(float, r2)
167167
p = r3

mypyc/test-data/irbuild-dict.test

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def f(d):
88
r0, r1 :: object
99
r2 :: bool
1010
L0:
11-
r0 = box(short_int, 0)
11+
r0 = object 0
1212
r1 = CPyDict_GetItem(d, r0)
1313
r2 = unbox(bool, r1)
1414
return r2
@@ -24,7 +24,7 @@ def f(d):
2424
r2 :: int32
2525
r3 :: bit
2626
L0:
27-
r0 = box(short_int, 0)
27+
r0 = object 0
2828
r1 = box(bool, 0)
2929
r2 = CPyDict_SetItem(d, r0, r1)
3030
r3 = r2 >= 0 :: signed
@@ -66,8 +66,8 @@ def f(x):
6666
r3, d :: dict
6767
L0:
6868
r0 = ''
69-
r1 = box(short_int, 2)
70-
r2 = box(short_int, 4)
69+
r1 = object 1
70+
r2 = object 2
7171
r3 = CPyDict_Build(2, r1, r2, r0, x)
7272
d = r3
7373
return 1
@@ -87,7 +87,7 @@ def f(d):
8787
r2 :: bit
8888
r3 :: bool
8989
L0:
90-
r0 = box(short_int, 8)
90+
r0 = object 4
9191
r1 = PyDict_Contains(d, r0)
9292
r2 = r1 >= 0 :: signed
9393
r3 = truncate r1: int32 to builtins.bool
@@ -114,7 +114,7 @@ def f(d):
114114
r2 :: bit
115115
r3, r4 :: bool
116116
L0:
117-
r0 = box(short_int, 8)
117+
r0 = object 4
118118
r1 = PyDict_Contains(d, r0)
119119
r2 = r1 >= 0 :: signed
120120
r3 = truncate r1: int32 to builtins.bool
@@ -178,7 +178,7 @@ L2:
178178
r8 = cast(str, r7)
179179
k = r8
180180
r9 = CPyDict_GetItem(d, k)
181-
r10 = box(short_int, 2)
181+
r10 = object 1
182182
r11 = PyNumber_InPlaceAdd(r9, r10)
183183
r12 = CPyDict_SetItem(d, k, r11)
184184
r13 = r12 >= 0 :: signed
@@ -208,11 +208,11 @@ def f(x, y):
208208
r7 :: bit
209209
L0:
210210
r0 = 'z'
211-
r1 = box(short_int, 4)
211+
r1 = object 2
212212
r2 = CPyDict_Build(1, x, r1)
213213
r3 = CPyDict_UpdateInDisplay(r2, y)
214214
r4 = r3 >= 0 :: signed
215-
r5 = box(short_int, 6)
215+
r5 = object 3
216216
r6 = CPyDict_SetItem(r2, r0, r5)
217217
r7 = r6 >= 0 :: signed
218218
return r2
@@ -423,7 +423,7 @@ L1:
423423
L2:
424424
r2 = 'a'
425425
r3 = PyList_New(1)
426-
r4 = box(short_int, 2)
426+
r4 = object 1
427427
r5 = get_element_ptr r3 ob_item :: PyListObject
428428
r6 = load_mem r5 :: ptr*
429429
set_mem r6, r4 :: builtins.object*
@@ -451,10 +451,11 @@ L1:
451451
L2:
452452
r2 = 'a'
453453
r3 = 'c'
454-
r4 = box(short_int, 2)
454+
r4 = object 1
455455
r5 = CPyDict_Build(1, r3, r4)
456456
r6 = CPyDict_SetDefault(d, r2, r5)
457457
return r6
458458
L3:
459459
r7 = box(None, 1)
460460
return r7
461+

mypyc/test-data/irbuild-generics.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def f():
6060
L0:
6161
r0 = C()
6262
c = r0
63-
r1 = box(short_int, 2)
63+
r1 = object 1
6464
c.x = r1; r2 = is_error
6565
r3 = c.x
6666
r4 = unbox(int, r3)
@@ -118,7 +118,7 @@ L0:
118118
r2 = CPyTagged_Add(y, 2)
119119
r3 = box(int, r2)
120120
r4 = x.set(r3)
121-
r5 = box(short_int, 4)
121+
r5 = object 2
122122
r6 = C(r5)
123123
x = r6
124124
return 1

0 commit comments

Comments
 (0)