Pointer has a method clear that will set a number of elements to zero (as in zero in all bytes). But it’s in Pointer and “unsafe” because setting, say, a string or a class instance, or in general something that’s not an integer to zero will result in segfault or undefined behavior.
That said, for your program you could add a method for that in your code. And we could add it to the standard library, except that I don’t know how useful/frequent that is.
class Array
def zero!
{% unless T < Int::Primitive %}
{% raise "Can't zero Array(#{T}), only Array of integers can be zeroed" %}
{% end %}
to_unsafe.clear(size)
end
end
ary = (1..1_000).to_a
Benchmark.ips do |x|
x.report("[]=") do
ary.each_index do |i|
ary[i] = 0
end
end
x.report("zero!") do
ary.zero!
end
end
I won’t add zero!. I will optimize fill(0) to do that trick. For now you could patch Array#fill with this:
class Array(T)
def fill(value : T)
{% if Int::Primitive.union_types.includes?(T) %}
if value == 0
to_unsafe.clear(size)
else
fill { value }
end
{% else %}
fill { value }
{% end %}
end
end
I actually need the fastest way to do it (how it looks is irrelevant).
This operation takes place inside an inner loop in an algorithm.
I have to initialize the array to zero before its operated on.
Given the huge performance difference you showed between zero! and fill(0)
this will have a tangible performance effect on the total algorithm.
(I’m translating this particular algorithm from D|Nim|Rust into Crystal. Currently the
Rust version is fastest, and Nim second. This particular operation is a bottleneck,
because you can’t get around doing it, so doing it the fastest way possible is desirable.
When I finish the full translation, and benchmarking, I’ll release the results to the forum.)
Is zero in floats always represented as a stream of zero bytes?
Never mind, I was thinking of their values not about their physical representation.
However, I think its still worth it in stdlib if just for integer arrays, for the reasons given.
I benchmarked all your changes, in file zero-test.cr below
require "benchmark"
class Array
def zero!
{% unless T < Int::Primitive %}
{% raise "Can't zero Array(#{T}), only Array of integers can be zeroed" %}
{% end %}
to_unsafe.clear(size)
end
end
class Array(T)
def fill(value : T)
{% if Int::Primitive.union_types.includes?(T) %}
if value == 0
to_unsafe.clear(size)
else
fill { value }
end
{% else %}
fill { value }
{% end %}
end
end
ary = (1..1_000).to_a
Benchmark.ips do |x|
x.report("[]=") do
ary.each_index do |i|
ary[i] = 0
end
end
x.report("fill(0)") do
ary.fill(0)
end
x.report("zero!") do
ary.zero!
end
end
Yes, but what should happen if you want to fill an array with negative zero? The code as pasted above doesn’t work as it would fill with positive zero instead.
Then you could do arr.fill(-0.0f64). But zero! method will be faster and would work for most usecases, because usually people want to fill array with 0.0, that is positive zero.
Hey @asterite I just want to be clear that you’re still doing the PR for fill(0) for integer arrays, which is what I need. Will that be available in next release?
From the discussion on zeroing floating point arrays, I want to be sure on which method will exist for my usecase.
Sorry, I won’t have time to do this right now. Feel free to open a PR. Otherwise, if zero! or that patch are working in your code, maybe there’s no need to add this extra logic to the standard library.