How copying an int made my code 11 times faster
medium.com
medium.com
Why is it faster to process a sorted array than an unsorted array?
https://stackoverflow.com/questions/11227809/why-is-it-faste...
Edit: After running with a wider range of parameters, it seems that the difference is always roughly the same order of magnitude. To investigate further, I included the sort into the second timing to double check and for 3276800 elements it's still a bit faster overall when you sort the array.
#!/usr/bin/env python
import time
import numpy as np
def main(n=32768):
arr = np.random.randint(0, 256, n)
t0 = time.time()
sum1 = do_loop(arr)
t1 = time.time()
arr = np.sort(arr)
t2 = time.time()
sum2 = do_loop(arr)
t3 = time.time()
assert sum1 == sum2
print(" Unsorted execution time: {} seconds".format(t1-t0))
print(" Sorted execution time: {} seconds".format(t3-t2))
def run_many(func):
def wrapper(arg):
for t in range(1000):
func(arg)
return func(arg)
return wrapper
@run_many
def do_loop(arr):
tot = 0
for i in arr:
if i >= 128:
tot += i
return tot
if __name__ == '__main__':
main() import random
...
arr = [random.randint(0, 256) for x in range(n)]
...
arr = sorted(arr)
Here are my times: $ time python2.7 hackernews_13682929.py
Unsorted execution time: 4.33348608017 seconds
Sorted execution time: 4.09405398369 seconds
$ time python3.5 hackernews_13682929.py
Unsorted execution time: 4.4200146198272705 seconds
Sorted execution time: 4.188237905502319 seconds
$ time python2.7 hackernews_13682929_purepython.py
Unsorted execution time: 0.981621026993 seconds
Sorted execution time: 0.832424879074 seconds
$ time python3.5 hackernews_13682929_purepython.py
Unsorted execution time: 1.3005650043487549 seconds
Sorted execution time: 1.157465934753418 seconds
$ time pypy hackernews_13682929_purepython.py
Unsorted execution time: 0.239459037781 seconds
Sorted execution time: 0.0910339355469 seconds
As you can see, the pure Python version is faster than the Numpy version, and also has a larger margin between unsorted and sorted. PyPy is of course faster than both, and also has an even greater margin between unsorted and sorted (2.63x faster). Unsorted execution time: 0.2175428867340088 seconds
Sorted execution time: 1.133354663848877 seconds
And if I don't: Unsorted execution time: 0.21171283721923828 seconds
Sorted execution time: 0.08376479148864746 secondsJust for fun add "randomvariable = 1"(you know, 1 one cycle machine op) in a tight loop and watch tenths of a second added to your "benchmark".
Also, while I'll grant you that using Python implies that convenience, programmer time, and/or development speed is a higher priority than performance, that doesn't at all mean that people who use Python "don't care about performance".
Is there a bug filed to fix this?
Now that MIR has landed, Rust will eventually get much better optimizations from both rustc and the llvm optimization passes but other things are a much higher priority like non-lexical scoping.
False.
http://llvm.org/docs/LangRef.html#noalias-and-alias-scope-me...
Also: "Originally implemented for C and C++, the language-agnostic design of LLVM has since spawned a wide variety of front ends: languages with compilers that use LLVM include ActionScript, Ada, C#,[4][5][6] Common Lisp, Crystal, D, Delphi, Fortran, OpenGL Shading Language, Halide, Haskell, Java bytecode, Julia, Lua, Objective-C, Pony,[7] Python, R, Ruby, Rust, CUDA, Scala,[8] and Swift."
Most of these are nothing like C when it comes to pointers and memory layout.
Further, those other languages may be nothing like C, but they're even less like Rust. So because of its heritage, even given the information Rust knows, LLVM simply doesn't have the optimization passes to take full advantage of it.
In any case, compiler optimisations are usually easy to implement on specific styles of internal representations, and this is MIR for Rust, which was only introduced recently, after requiring a very large refactoring of the compiler's internal pipeline.
def foo(a, b, thing=util.thing):
... like all generic interfaces in Rust, printing takes arguments by reference,
regardless of whether they are Copy or not. The println! macro hides this from
you by implicitly borrowing the arguments,
What's the reason for this? Seems a bit inconsistent that for functions you must explicitly pass with &, but for print it's automatic.- it'd be annoying to have to write `println!("{}", &s);`,
- this is not an inconsistency one thinks about in practice: ownership is important in Rust, but the compiler does all the annoying checking, so IME one can just let it fade into the background and not think about it until the compiler tells you there's a problem,
- it's an "permissive" inconsistency: writing a & will still compile and give the same output,
- it's a syntax-level inconsistency created by the println! macro packaging up the normal syntax, not some special rules for special functions: there's still a literal & borrow in there (see the macro expansion),
- historical reasons, and no-one was annoyed enough (or even noticed it enough) to change it.
Beeing permissive makes it even worse as there are now 2 inconsistent ways to do the same thing.
Maybe I need to understand rust macros better. I guess this all boils down to: macros are a very bad idea, as in other programming languages.
Some are properly marked readonly in declarations, some are not[1]
But this is a common problem with unmarked logging. The compiler will infer attributes all the way through things it has bitcode for. (IE it can determine it to be readonly, readnone, whatever, as long as it can see calls to either functions marked readonly/readnone, or determine the called functions to be readonly/readnone)
But, usually, if you don't give it the whole program, it can't infer that your logging function does not modify its arguments.
[1] Fun fact: glibc allows printf handler registration: https://www.gnu.org/software/libc/manual/html_node/Customizi...
So sadly, it's printf is not readonly, because it can do anything it wants in these callbacks.
(nobody uses the printf registration, though, so it may be time to just add a compiler option required to make it work, and not screw everyone else using printf over)
So you wouldn't see this there.
The call to the print is
call void @_ZN3std2io5stdio6_print17h690779b3bd8114d5E(%"core::fmt::Arguments"* noalias nocapture nonnull dereferenceable(48) %_3)
The entire chunk of `main` preceding that call: %size = alloca i64, align 8
%_3 = alloca %"core::fmt::Arguments", align 8
%_8 = alloca [1 x %"core::fmt::ArgumentV1"], align 8
%0 = bitcast i64* %size to i8*
call void @llvm.lifetime.start(i64 8, i8* %0)
store i64 33554432, i64* %size, align 8
%1 = bitcast %"core::fmt::Arguments"* %_3 to i8*
call void @llvm.lifetime.start(i64 48, i8* %1)
%2 = bitcast [1 x %"core::fmt::ArgumentV1"]* %_8 to i8*
call void @llvm.lifetime.start(i64 16, i8* %2)
%3 = ptrtoint i64* %size to i64
%4 = bitcast [1 x %"core::fmt::ArgumentV1"]* %_8 to i64*
store i64 %3, i64* %4, align 8
%5 = getelementptr inbounds [1 x %"core::fmt::ArgumentV1"], [1 x %"core::fmt::ArgumentV1"]* %_8, i64 0, i64 0, i32 1
%6 = bitcast i8 (%"core::fmt::Void"*, %"core::fmt::Formatter"*)** %5 to i64*
store i64 ptrtoint (i8 (i64*, %"core::fmt::Formatter"*)* @"_ZN4core3fmt3num54_$LT$impl$u20$core..fmt..Display$u20$for$u20$usize$GT$3fmt17hb872170870cc06d9E" to i64), i64* %6, align 8
%7 = getelementptr inbounds [1 x %"core::fmt::ArgumentV1"], [1 x %"core::fmt::ArgumentV1"]* %_8, i64 0, i64 0
%8 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 0, i32 0
store %str_slice* getelementptr inbounds ([2 x %str_slice], [2 x %str_slice]* @ref.8, i64 0, i64 0), %str_slice** %8, align 8, !alias.scope !1, !noalias !4
%9 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 0, i32 1
store i64 2, i64* %9, align 8, !alias.scope !1, !noalias !4
%_6.sroa.0.0..sroa_idx.i = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 1, i32 0, i32 0
store %"core::fmt::rt::v1::Argument"* null, %"core::fmt::rt::v1::Argument"** %_6.sroa.0.0..sroa_idx.i, align 8, !alias.scope !1, !noalias !4
%10 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 2, i32 0
store %"core::fmt::ArgumentV1"* %7, %"core::fmt::ArgumentV1"** %10, align 8, !alias.scope !1, !noalias !4
%11 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 2, i32 1
store i64 1, i64* %11, align 8, !alias.scope !1, !noalias !4
call void @_ZN3std2io5stdio6_print17h690779b3bd8114d5E(%"core::fmt::Arguments"* noalias nocapture nonnull dereferenceable(48) %_3)
[1]: https://play.rust-lang.org/It's not the same issue but it's the same area of stupid stuff you have to find out about and roll your eyes.