-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mypyc] Inline increfs and decrefs in commonly executed blocks (#11540)
These operations are performance-critical, but inlining everywhere can slow down compilation a lot, so we only inline them outside error handlers (and other rarely executed code paths). This still can slow compilation by 10-15%, but I think that we just need to live with it, since the performance gains are impressive. We can perhaps claw back some of the loss by optimizing away redundant increfs/decrefs. Also parallel compilation would make this much less significant. This can speed up the richards benchmark by 65% (!). With this change: ``` running richards ........... interpreted: 0.181880s (avg of 6 iterations; stdev 0.91%) compiled: 0.005314s (avg of 6 iterations; stdev 1.2%) compiled is 34.229x faster ``` Using master: ``` running richards ........... interpreted: 0.182124s (avg of 6 iterations; stdev 2.1%) compiled: 0.008794s (avg of 6 iterations; stdev 1.9%) compiled is 20.710x faster ``` Also, this makes the int_list microbenchmark up to 80% faster. Compiled mypy was also around 3% faster.
- Loading branch information
Showing
8 changed files
with
247 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"""Find basic blocks that are likely to be executed frequently. | ||
For example, this would not include blocks that have exception handlers. | ||
We can use different optimization heuristics for common and rare code. For | ||
example, we can make IR fast to compile instead of fast to execute for rare | ||
code. | ||
""" | ||
|
||
from typing import Set | ||
|
||
from mypyc.ir.ops import BasicBlock, Goto, Branch | ||
|
||
|
||
def frequently_executed_blocks(entry_point: BasicBlock) -> Set[BasicBlock]: | ||
result: Set[BasicBlock] = set() | ||
worklist = [entry_point] | ||
while worklist: | ||
block = worklist.pop() | ||
if block in result: | ||
continue | ||
result.add(block) | ||
t = block.terminator | ||
if isinstance(t, Goto): | ||
worklist.append(t.label) | ||
elif isinstance(t, Branch): | ||
if t.rare or t.traceback_entry is not None: | ||
worklist.append(t.false) | ||
else: | ||
worklist.append(t.true) | ||
worklist.append(t.false) | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
-- Test cases for basic block execution frequency analysis. | ||
-- | ||
-- These test cases are using exception transform test machinery for convenience. | ||
-- | ||
-- NOTE: These must all have the _freq suffix | ||
|
||
[case testSimpleError_freq] | ||
from typing import List | ||
def f(x: List[int]) -> int: | ||
return x[0] | ||
[out] | ||
def f(x): | ||
x :: list | ||
r0 :: object | ||
r1, r2 :: int | ||
L0: | ||
r0 = CPyList_GetItemShort(x, 0) | ||
if is_error(r0) goto L3 (error at f:3) else goto L1 | ||
L1: | ||
r1 = unbox(int, r0) | ||
dec_ref r0 | ||
if is_error(r1) goto L3 (error at f:3) else goto L2 | ||
L2: | ||
return r1 | ||
L3: | ||
r2 = <error> :: int | ||
return r2 | ||
hot blocks: [0, 1, 2] | ||
|
||
[case testHotBranch_freq] | ||
from typing import List | ||
def f(x: bool) -> None: | ||
if x: | ||
y = 1 | ||
else: | ||
y = 2 | ||
[out] | ||
def f(x): | ||
x :: bool | ||
y :: int | ||
L0: | ||
if x goto L1 else goto L2 :: bool | ||
L1: | ||
y = 2 | ||
dec_ref y :: int | ||
goto L3 | ||
L2: | ||
y = 4 | ||
dec_ref y :: int | ||
L3: | ||
return 1 | ||
hot blocks: [0, 1, 2, 3] | ||
|
||
[case testGoto_freq] | ||
from typing import List | ||
def f(x: bool) -> int: | ||
if x: | ||
y = 1 | ||
else: | ||
return 2 | ||
return y | ||
[out] | ||
def f(x): | ||
x :: bool | ||
y :: int | ||
L0: | ||
if x goto L1 else goto L2 :: bool | ||
L1: | ||
y = 2 | ||
goto L3 | ||
L2: | ||
return 4 | ||
L3: | ||
return y | ||
hot blocks: [0, 1, 2, 3] | ||
|
||
[case testFalseOnError_freq] | ||
from typing import List | ||
def f(x: List[int]) -> None: | ||
x[0] = 1 | ||
[out] | ||
def f(x): | ||
x :: list | ||
r0 :: object | ||
r1 :: bit | ||
r2 :: None | ||
L0: | ||
r0 = box(short_int, 2) | ||
r1 = CPyList_SetItem(x, 0, r0) | ||
if not r1 goto L2 (error at f:3) else goto L1 :: bool | ||
L1: | ||
return 1 | ||
L2: | ||
r2 = <error> :: None | ||
return r2 | ||
hot blocks: [0, 1] | ||
|
||
[case testRareBranch_freq] | ||
from typing_extensions import Final | ||
|
||
x: Final = str() | ||
|
||
def f() -> str: | ||
return x | ||
[out] | ||
def f(): | ||
r0 :: str | ||
r1 :: bool | ||
r2 :: str | ||
L0: | ||
r0 = __main__.x :: static | ||
if is_error(r0) goto L1 else goto L3 | ||
L1: | ||
r1 = raise NameError('value for final name "x" was not set') | ||
if not r1 goto L4 (error at f:6) else goto L2 :: bool | ||
L2: | ||
unreachable | ||
L3: | ||
inc_ref r0 | ||
return r0 | ||
L4: | ||
r2 = <error> :: str | ||
return r2 | ||
hot blocks: [0, 3] |
Oops, something went wrong.