Skip to content
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

Don't use _mapreduce for countnz #13860

Merged
merged 1 commit into from
Nov 6, 2015
Merged

Don't use _mapreduce for countnz #13860

merged 1 commit into from
Nov 6, 2015

Conversation

andreasnoack
Copy link
Member

This was a surprising bottleneck in some of my code. Before I got

julia> x = map(Float64, bitrand(10^8));

julia> @time countnz(x);
 0.458802 seconds (5 allocations: 176 bytes)

julia> @time countnz(x);
 0.505234 seconds (5 allocations: 176 bytes)

and with this change

julia> x = map(Float64, bitrand(10^8));

julia> @time countnz(x);
  0.104807 seconds (3.82 k allocations: 201.454 KB)

julia> @time countnz(x);
  0.064269 seconds (5 allocations: 176 bytes)

@timholy
Copy link
Sponsor Member

timholy commented Nov 3, 2015

Shouldn't we change count so the inner loop is written n += pred(a)? With that change they become almost, but not quite, equal in performance for me. There seems to be a vectorization difference if you use @code_llvm on

function count1(pred, A::AbstractArray)
    n = 0
    @inbounds for a in A
        n += pred(a)
    end
    return n
end

function countnz1(a)
    n = 0
    @inbounds for i in eachindex(a)
        ai = a[i]
        n = ifelse(ai == zero(ai), n, n + 1)
    end
    return n
end

with only the latter getting vectorized. (EDIT: with count1(NotEqZero(), x) and countnz1(x).)

@timholy
Copy link
Sponsor Member

timholy commented Nov 3, 2015

Ah, that's interesting: we only vectorize

for i in eachindex(A)
    @inbounds a = A[i]
    ...
end

and not

for a in A
    ...
end

Seems like that should be fixable? CC @ArchRobison.

@jakebolewski
Copy link
Member

Shouldn't the goal be to remove all functor object hacks?

@timholy
Copy link
Sponsor Member

timholy commented Nov 3, 2015

Shouldn't the goal be to remove all functor object hacks?

Isn't that what Jeff's doing in #13412? The count function is quite general, and it will become even more useful once Jeff finishes that work. In contrast, countnz is very specialized.

@simonster
Copy link
Member

We don't vectorize for a in A because #11350

@simonster
Copy link
Member

Ah, but I see in that case there is actually an @inbounds. In that case I'm not sure what's spooking the vectorizer.

@simonster
Copy link
Member

I think the problem is that done(a::Array,i) is defined as (i > length(a)), which would be an infinite loop if length(a) == typemax(Int). It vectorizes if done is instead defined as done(a::Array,i) = i == length(a)+1 like done(::UnitRange,::Any). Will open a PR.

@JeffBezanson
Copy link
Sponsor Member

Really good find @simonster .

@andreasnoack
Copy link
Member Author

I've just tried the change proposed by @timholy with the fix of @simonster and it is as fast as the version here so when the tests pass locally, I'll push @timholy's version.

Is it necessary to have both the iterator and array version of count?

@timholy
Copy link
Sponsor Member

timholy commented Nov 4, 2015

Is it necessary to have both the iterator and array version of count?

I'm not certain I see why, but I haven't looked at the code.

@tkelman
Copy link
Contributor

tkelman commented Nov 4, 2015

Should this be Bool(pred(x))? Otherwise could be misleading/incorrect for general predicate functions.

@andreasnoack
Copy link
Member Author

Maybe even n = ifelse(pred(x), n + 1, n) such that it fails unless pred(x) is Bool.

@simonster
Copy link
Member

Is it necessary to have count at all? As @StefanKarpinski noted in #3180, it does the same thing as sum.

@andreasnoack
Copy link
Member Author

I thought it was for something like

julia> count(t -> t == 3, rand(1:3, 10))
3

@simonster
Copy link
Member

Does that differ from sum(t -> t == 3, rand(1:3, 10))?

@andreasnoack
Copy link
Member Author

Ha. I didn't know about the map version of sum. Then I agree that we should eliminate one of them. I don't care as long as countnz becomes fast.

@andreasnoack
Copy link
Member Author

...but sum is more general so what about removing count and make countnz call sum? Any complains?

@andreasnoack
Copy link
Member Author

I tried to remove count but it allows empty arrays which sum doesn't so I'll postpone the deprecation of count.

andreasnoack added a commit that referenced this pull request Nov 6, 2015
Don't use _mapreduce for countnz
@andreasnoack andreasnoack merged commit 94f31d1 into master Nov 6, 2015
@andreasnoack andreasnoack deleted the anj/countnz branch November 6, 2015 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants