-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Change Zip
data structure to a flat one
#29238
Conversation
@Keno you have implemented this elaborate dance: Lines 295 to 316 in 3a15e6b
IIUC, you first iterate those iterators where |
c137098
to
107d814
Compare
About the See also my attempt in #27511. This test covers this issue: https://github.com/JuliaLang/julia/blob/master/test/iterators.jl#L537-L542
Edit: I would be a big fan of finally getting this |
Stateful iterators! Of course! [hand against forehead] Thanks for the pointers! |
4f8e9e3
to
6663c8f
Compare
i did some benchmarking
The only regressions above measurement noise here are for However, I have no idea whether these benchmarks are relevant, and BaseBenchmarks do not include any direct benchmarks of That said, I think the code is now in a state where review makes sense (unless CI finds something). |
Zip
data structure to a flat oneZip
data structure to a flat one
I think the bench in the last row hits a type instability: f() = collect(zip(1:1000,1:1000,1:1000,1:1000))
@btime f() gives |
6139bcc
to
abb1e03
Compare
test/iterators.jl
Outdated
@@ -540,10 +540,73 @@ end | |||
@test isempty(collect(zip(b,a))) | |||
@test !isempty(a) | |||
end | |||
let a = Iterators.Stateful("a"), b = "", c = Iterators.Stateful("c") | |||
@test isempty(collect(zip(a,b,c))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect indentation for these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well spotted!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What can I say, I have an eye for pedantry :P
test/iterators.jl
Outdated
@test length(collect(zip(a,b,c))) == 1 | ||
@test !isempty(a) | ||
@test !isempty(c) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
AV 64bit says:
I think is due to a bug in
( |
Hm, master has improved its handling of the old
Inferability is still slightly better: julia> x = zip(1:1, ["a"], (1.0,), Base.OneTo(1), Iterators.repeated("a"), 1.0:0.2:2.0,
(1 for i in 1:1), Iterators.Stateful(["a"]), (1.0 for i in 1:2, j in 1:3), 1);
julia> z = Iterators.filter(x -> x[1] == 1, x);
julia> Base.return_types(first, Tuple{typeof(z)})
1-element Array{Any,1}: # this PR
Tuple{Int64,String,Float64,Int64,String,Float64,Int64,String,Float64,Int64}
1-element Array{Any,1}: # master and 1.0
Tuple{Int64,Any,Float64,Any,String,Any,Int64,String,Float64,Int64} But I'm not sure the balance struck by the PR in its current form is better than master. If anyone wants to take a stab at tweaking this further, welcome! I won't have time to work on this for at least one week. |
Or I might be doing something very wrong, because I now see significantly better timing on 1.0, too. Compare e.g. julia> f() = collect(zip(1:1000,1:1000,1:1000,1:1000))
f (generic function with 1 method)
julia> @btime f()
2.864 μs (2 allocations: 31.33 KiB) with #29238 (comment). |
Which version exactly? For reference: julia> versioninfo()
Julia Version 1.0.0
Commit 5d4eaca0c9 (2018-08-08 20:58 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Xeon(R) CPU E3-1230 v5 @ 3.40GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, skylake)
julia> f() = collect(zip(1:1000,1:1000,1:1000,1:1000));
julia> @benchmark f()
BenchmarkTools.Trial:
memory estimate: 678.52 KiB
allocs estimate: 16439
--------------
minimum time: 619.442 μs (0.00% GC)
median time: 625.912 μs (0.00% GC)
mean time: 681.834 μs (5.98% GC)
maximum time: 31.411 ms (97.53% GC)
--------------
samples: 7326
evals/sample: 1 |
Ah, it's a question of compilation (inference) order. I created the data for above tables with for N in (1,1000), M in (1,2,3,4)
@btime collect(zip($(Iterators.repeated(1:N, M)...,)...))
end That gives the very fast timings on 1.0, also for timing |
Why would performance be different for different orders? That sounds like a bug which should be fixed. |
Inference will limit its work on recursive functions (it has to to avoid working infinitely). However, if it hits a previously computed result before it reaches its limit, it will use that. That's why running inference on increasing problem sizes often produces better results than immediately running it on the largest problem size. |
So, what is left to do in this PR? Is the only issue that we have to figure out why this PR is somewhat slower than master when inference would work well? |
Instead of creating a `zip` of more than two iterators by forming a tree structure, store all iterators in a tuple.
(don't advance isdone=false iterators if any other has reached its end)
I think there is still an inference test missing for |
I'd still appreciate a review by another pair of eyes. (I've poked at this for long enough that I'm quite likely somewhat blind to stupid mistakes/obvious possible improvements.) That said, I'll nevertheless go ahead and merge in the next couple of days unless any objections are voiced (or CI hits something). |
True. |
Even though there are no |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan |
Ah, I had lost track of this one... I'll re-trigger CI and also @nanosoldier |
Both failures are the now-standard #29923. |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan |
#29923 again, otherwise looks fine. |
From the 👍 to the benchmark results, I take it that the few regressions are within the expected fluctuations? (I have not been looking at nanosoldier outputs that much recently.) In any case, they seem unrelated at least at first glance. Regarding the "benchmark wanted" label: The new |
If this is just fixing inference order, feel free to remove the label. I also want to point out that the "benchmark beneficial" label is not a requirement for merging nor is it even a request for the author to make a PR to BaseBenchmarks. It is just as a tag so when someone feels they want to add stuff to BaseBenchmarks (JuliaCI/BaseBenchmarks.jl#233) they can just filter on the label. |
The benchmarks cover |
Instead of creating a `zip` of more than two iterators by forming a tree structure, store all iterators in a tuple.
This broke the Bridge package, which was using |
I think the Bridge package needs to adjust then since |
OnlineStats should be an easy fix. I defined a method for some function on |
Great to get the heads up. No problem from my side, this PR is very welcome. |
Instead of creating a
zip
of more than two iterators by forming a tree structure, store all iterators in a tuple. It should be possible to get rid ofZip1
andZip2
, but I wasn't sure of a good benchmark to quickly check locally whether the more general code was sub-optimal in the short case, hence WIP—ideas welcome!Fixes #26765.