Incrementally improving the performance of a Python script
mycode.doesnot.run
mycode.doesnot.run
Allocating registers for all local vars statically means scopes have different sizes, which in turn complicates slab allocation and/or reuse. In return for being easier to reason about (except for the main scope issue) and saving space.
I opted for a fixed number of linearly assigned registers per scope in Snigl [0]; once the limit is reached, remaining variables are stored in a table. Which means I sort of get both, since additional scopes may be added using {} (it could make sense to add a scope: keyword to Python) if that becomes an issue.
It's all compromises, all the way down.
[A.b[i] for i in range(100)]
is a lot slower than:
B = A.b
[B[i] for i in range (100)]
It is something that compilers do for you, in this context, in most programming environments.
(though perhaps a JIT could perform this optimization for the usual, non-surprising path)
For example, in CPython 3.6,
n = 0
d = 100
for i in range(10**6):
n += i
if n >= d:
n %= d
is slower than n = (n + i) % d
This counter to lower level languages, where dividing by a variable is costly, and the CPU can predict the pipeline to be false most of the time in the conditional and thus skip it.I think there's a bug in the code then. It will be skipped a few times, but for i between 100 and 10^6, the condition is guaranteed true every time.
For [1, 2, 3, 4, 5] there are 5 pivots.
For [1, 2, 3, 5, 4] there are 3 pivots (1, 2, and 3)
For [2, 1, 3, 5, 4] there is only 1 (3).
Edit: in fact, 1 and 4 may be valid pivots too for this example, if empty subarrays are allowed (remember that we don't necessarily know A, so A = [4, 1, 2, 3] with a pivot of 4 will yield the same A' = [1, 2, 3, 4], as will A = [2, 3, 4, 1] with a pivot of 1).
I don't know in what ways they are differents, but these programs were not designed to work with duplicates in the input. This probably explains the results.