1

this question is derived from this post and this post

a C++ compiler is able to discard the following function while optimizing.

void function()
{
    long long number = 0;
    long long problemSize = 100;

    for( long long i = 0; i < problemSize; ++i )
    {
        number++;
        number--;
    }
}

is there an equivalent or similar mechanism in Python ecosystem?

work = 1
for x in xrange(problemSize):
    work += 1
    work -= 1
  • I'm not very familiar with PyPy but they do have an optimizing JIT compiler. And that may be doing this kind of optimizations: https://pypy.org/ – rdas Jun 12 '19 at 02:25

2 Answers2

0

The CPython reference interpreter has no such features; the optimizer it has is extremely limited, and generally can't perform inter-statement optimizations; by the the optimizer is looking at work -= 1, it's forgotten all about work += 1.

Cython may or may not be able to optimize this; I'd suspect it wouldn't on its own, but if the types were properly declared, the C compiler that compiles the code Cython generates might be able to eliminate the unused code.

For other interpreters (PyPy, Jython, IronPython), it's all going to depend on the quality of their JIT compilation engines. Odds are if the code is executed only once they won't bother to JIT it, but if it's hot code, executed many times, it might be able to eliminate the loop.

It's harder to do this in Python largely because even a loop as simple as the one you express can have unpredictable side-effects. Sure, on its face it's safe to drop the loop (if nothing reads from x later anyway), but it's always possible the loop could be entered with xrange replaced, either in module global or built-in scope, and the replacement might have observable side-effects. It makes it close to impossible to do static compilation of code without simply ignoring the possibility of xrange being replaced (as Cython does) or compiling code that executes conditionally on xrange not being replaced (as a careful JIT would have to).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
0

In general, no, as @ShadowRanger's answer has already explained.

For example, say we have the two following functions:

def f():
    for i in range(10):
        i += 1
        i -= 1

    return True

def g():
    return True

Using dis, we can examine their bytecode. We see that g's bytecode is extremely simple:

  2           0 LOAD_CONST               1 (True)
              2 RETURN_VALUE

On the other hand, even though f does exactly the same thing, the extraneous instructions are not omitted:

  2           0 SETUP_LOOP              32 (to 34)
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_CONST               1 (10)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                20 (to 32)
             12 STORE_FAST               0 (i)

  3          14 LOAD_FAST                0 (i)
             16 LOAD_CONST               2 (1)
             18 INPLACE_ADD
             20 STORE_FAST               0 (i)

  4          22 LOAD_FAST                0 (i)
             24 LOAD_CONST               2 (1)
             26 INPLACE_SUBTRACT
             28 STORE_FAST               0 (i)
             30 JUMP_ABSOLUTE           10
        >>   32 POP_BLOCK

  6     >>   34 LOAD_CONST               3 (True)
             36 RETURN_VALUE
gmds
  • 19,325
  • 4
  • 32
  • 58