Skip to content

Performance Overhead in Ternary Operator Due to Value Loading vs Constant Loading #127685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ashm-dev opened this issue Dec 6, 2024 · 6 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage type-feature A feature request or enhancement

Comments

@ashm-dev
Copy link

ashm-dev commented Dec 6, 2024

Bug report

Bug description:

A performance bottleneck has been identified in CPython's bytecode generation for ternary expressions compared to traditional if-else statements, specifically related to how constants are loaded.

Bytecode Comparison

Traditional If-Else (Optimized)

def test(var):
    if var:
        return True
    else:
        return False

Bytecode:

  8           0 RESUME                   0

  9           2 LOAD_FAST                0 (var)
              4 POP_JUMP_IF_FALSE        1 (to 8)

 10           6 RETURN_CONST             1 (True)

 12     >>    8 RETURN_CONST             2 (False)

Ternary Operator (Less Efficient)

def test2(var):
    return True if var else False

Bytecode:

 14           0 RESUME                   0

 15           2 LOAD_FAST                0 (var)
              4 POP_JUMP_IF_FALSE        2 (to 10)
              6 LOAD_CONST               1 (True)
              8 RETURN_VALUE
        >>   10 LOAD_CONST               2 (False)
             12 RETURN_VALUE

Problem
In the ternary operator version:

  • LOAD_CONST is used instead of the more efficient RETURN_CONST
  • This results in an additional RETURN_VALUE instruction
  • Leads to slightly increased bytecode complexity and potential performance overhead

Recommendation
Modify the bytecode generation for ternary expressions to:

  • Use RETURN_CONST when returning boolean constants
  • Minimize additional instructions
  • Align bytecode generation with the efficiency of traditional if-else statements

Additional Notes

  • This is a micro-optimization
  • Impact may vary across different Python versions
  • Primarily of interest to performance-critical code paths

CPython versions tested on:

3.12

Operating systems tested on:

Linux

@ashm-dev ashm-dev added the type-bug An unexpected behavior, bug, or error label Dec 6, 2024
@picnixz
Copy link
Member

picnixz commented Dec 6, 2024

cc @markshannon

@picnixz picnixz added performance Performance or resource usage type-feature A feature request or enhancement interpreter-core (Objects, Python, Grammar, and Parser dirs) and removed type-bug An unexpected behavior, bug, or error labels Dec 6, 2024
@picnixz
Copy link
Member

picnixz commented Dec 6, 2024

Question: is this the result with a DEBUG build? or PGO build?

@ashm-dev
Copy link
Author

ashm-dev commented Dec 6, 2024

@picnixz This is not a special build, but just python3. 12 from uv. Python version -- 3.12.7

@sobolevn
Copy link
Member

sobolevn commented Dec 6, 2024

I think that this is a known problem (but I cannot find the needed issue).

@Eclips4
Copy link
Member

Eclips4 commented Dec 6, 2024

Question: is this the result with a DEBUG build? or PGO build?

PGO/LTO/BOLT/Debug builds doesn't affect the actual bytecode construction.

There's no more a RETURN_CONST opcode, so I don't think this is an issue, and this in fact is a duplicate of #121246.
Nevertheless, thank you for the report, Shamil.

@Eclips4 Eclips4 closed this as not planned Won't fix, can't repro, duplicate, stale Dec 6, 2024
@Eclips4
Copy link
Member

Eclips4 commented Dec 6, 2024

FYI, here's the bytecode of test and test2 functions on 3.14 branch, which in fact are identical:

eclips4@nixos ~/p/p/cpython (remove-ast-optimizer)> ./python
Python 3.14.0a2+ (heads/remove-ast-optimizer-dirty:7ce21584115, Jan  1 1980, 00:00:00) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> def test(var):
...     if var:
...         return True
...     else:
...         return False
...
>>> dis.dis(test)
  1           RESUME                   0

  2           LOAD_FAST                0 (var)
              TO_BOOL
              POP_JUMP_IF_FALSE        2 (to L1)

  3           LOAD_CONST               0 (True)
              RETURN_VALUE

  5   L1:     LOAD_CONST               1 (False)
              RETURN_VALUE
>>> def test2(var):
...     return True if var else False
...
>>> dis.dis(test2)
  1           RESUME                   0

  2           LOAD_FAST                0 (var)
              TO_BOOL
              POP_JUMP_IF_FALSE        2 (to L1)
              LOAD_CONST               0 (True)
              RETURN_VALUE
      L1:     LOAD_CONST               1 (False)
              RETURN_VALUE
>>>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) performance Performance or resource usage type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants