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

Remove unnecessary allocation in BitSet() #49461

Merged
merged 1 commit into from
Apr 22, 2023
Merged

Remove unnecessary allocation in BitSet() #49461

merged 1 commit into from
Apr 22, 2023

Conversation

LilithHafner
Copy link
Member

Instead of allocating a vector of size 0 and then reallocating of size 4, skip straight to the larger allocation.

julia> @btime sizehint!(zeros(UInt64, 0), 4)
  30.276 ns (2 allocations: 144 bytes)
UInt64[]

julia> @btime resize!(Vector{UInt64}(undef, 4), 0)
  18.537 ns (1 allocation: 96 bytes)
UInt64[]

julia> @btime begin x = sizehint!(zeros(UInt64, 0), 4); push!(x, 1); push!(x, 5); push!(x, 3); push!(x, 6) end
  46.680 ns (2 allocations: 144 bytes)
4-element Vector{UInt64}:
 0x0000000000000001
 0x0000000000000005
 0x0000000000000003
 0x0000000000000006

julia> @btime begin x = resize!(Vector{UInt64}(undef, 4), 0); push!(x, 1); push!(x, 5); push!(x, 3); push!(x, 6) end
  34.954 ns (1 allocation: 96 bytes)
4-element Vector{UInt64}:
 0x0000000000000001
 0x0000000000000005
 0x0000000000000003
 0x0000000000000006

@LilithHafner LilithHafner added the performance Must go faster label Apr 22, 2023
@LilithHafner LilithHafner added the merge me PR is reviewed. Merge when all tests are passing label Apr 22, 2023
@LilithHafner
Copy link
Member Author

This works with _growbeg!, too, which is relevant for BitSet

julia> @btime begin x = sizehint!(zeros(UInt64, 0), 4); for _ in 1:3; Base._growbeg!(x, 1); end; end
  52.379 ns (2 allocations: 144 bytes)

julia> @btime begin x = resize!(Vector{UInt64}(undef, 4), 0); for _ in 1:3; Base._growbeg!(x, 1); end; end
  43.264 ns (1 allocation: 96 bytes)

@LilithHafner LilithHafner merged commit 5ea1a41 into master Apr 22, 2023
@LilithHafner LilithHafner deleted the bitset-opt-1 branch April 22, 2023 18:50
@LilithHafner LilithHafner removed the merge me PR is reviewed. Merge when all tests are passing label Apr 22, 2023
@vtjnash
Copy link
Member

vtjnash commented Apr 22, 2023

The benchmarks are unfair there, since you never use a large enough workload to show the higher wasted memory of this change. It is not large waste, so it might be fine if BitSet is normally less than 256. Otherwise this may be worse.

@LilithHafner
Copy link
Member Author

I did not realize that resize!(Vector{UInt64}(undef, 4), 0) allocates enough memory for 4 elements while sizehint!(zeros(UInt64, 0), 4) allocates enough memory for 8 elements.

In addition to the obvious change here (eliminating a never-used 0-size allocation) I also unwittingly changed the preallocation size from 8 (512) to 4 (256). FWIW, I imagine the intent of this code was to preallocate 4, not 8.

Here are some more comprehensive benchmarks in case we would like to put the preallocation size back to 8.

julia> for n in 1:17
           println(n)
           print("Old (0 + 8)"); @btime begin x = sizehint!(zeros(UInt64, 0), 4); for i in 1:$n; push!(x, i); end; end
           print("New (4)    "); @btime begin x = resize!(Vector{UInt64}(undef, 4), 0); for i in 1:$n; push!(x, i); end; end
           print("New (8)    "); @btime begin x = resize!(Vector{UInt64}(undef, 8), 0); for i in 1:$n; push!(x, i); end; end
       end
1
Old (0 + 8)  35.876 ns (2 allocations: 144 bytes)
New (4)      22.799 ns (1 allocation: 96 bytes)
New (8)      23.301 ns (1 allocation: 128 bytes)
2
Old (0 + 8)  41.708 ns (2 allocations: 144 bytes)
New (4)      29.397 ns (1 allocation: 96 bytes)
New (8)      30.025 ns (1 allocation: 128 bytes)
3
Old (0 + 8)  45.033 ns (2 allocations: 144 bytes)
New (4)      32.067 ns (1 allocation: 96 bytes)
New (8)      33.744 ns (1 allocation: 128 bytes)
4
Old (0 + 8)  48.415 ns (2 allocations: 144 bytes)
New (4)      35.373 ns (1 allocation: 96 bytes)
New (8)      36.631 ns (1 allocation: 128 bytes)
5
Old (0 + 8)  51.882 ns (2 allocations: 144 bytes)
New (4)      51.534 ns (2 allocations: 176 bytes)
New (8)      40.154 ns (1 allocation: 128 bytes)
6
Old (0 + 8)  54.894 ns (2 allocations: 144 bytes)
New (4)      55.415 ns (2 allocations: 176 bytes)
New (8)      42.717 ns (1 allocation: 128 bytes)
7
Old (0 + 8)  57.546 ns (2 allocations: 144 bytes)
New (4)      57.546 ns (2 allocations: 176 bytes)
New (8)      45.707 ns (1 allocation: 128 bytes)
8
Old (0 + 8)  60.572 ns (2 allocations: 144 bytes)
New (4)      60.213 ns (2 allocations: 176 bytes)
New (8)      48.745 ns (1 allocation: 128 bytes)
9
Old (0 + 8)  74.169 ns (3 allocations: 480 bytes)
New (4)      74.368 ns (3 allocations: 512 bytes)
New (8)      63.115 ns (2 allocations: 464 bytes)
10
Old (0 + 8)  78.727 ns (3 allocations: 480 bytes)
New (4)      77.809 ns (3 allocations: 512 bytes)
New (8)      66.973 ns (2 allocations: 464 bytes)
11
Old (0 + 8)  81.828 ns (3 allocations: 480 bytes)
New (4)      80.708 ns (3 allocations: 512 bytes)
New (8)      69.842 ns (2 allocations: 464 bytes)
12
Old (0 + 8)  84.540 ns (3 allocations: 480 bytes)
New (4)      84.410 ns (3 allocations: 512 bytes)
New (8)      72.522 ns (2 allocations: 464 bytes)
13
Old (0 + 8)  87.578 ns (3 allocations: 480 bytes)
New (4)      87.578 ns (3 allocations: 512 bytes)
New (8)      76.303 ns (2 allocations: 464 bytes)
14
Old (0 + 8)  90.133 ns (3 allocations: 480 bytes)
New (4)      90.234 ns (3 allocations: 512 bytes)
New (8)      78.866 ns (2 allocations: 464 bytes)
15
Old (0 + 8)  92.831 ns (3 allocations: 480 bytes)
New (4)      93.498 ns (3 allocations: 512 bytes)
New (8)      82.601 ns (2 allocations: 464 bytes)
16
Old (0 + 8)  96.373 ns (3 allocations: 480 bytes)
New (4)      96.228 ns (3 allocations: 512 bytes)
New (8)      85.019 ns (2 allocations: 464 bytes)
17
Old (0 + 8)  101.901 ns (3 allocations: 480 bytes)
New (4)      100.449 ns (3 allocations: 512 bytes)
New (8)      90.227 ns (2 allocations: 464 bytes)

@vtjnash
Copy link
Member

vtjnash commented Apr 22, 2023

resized memory can be freed later. The initial allocation cannot be freed later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Must go faster
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants