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

Optimize Array#* #8087

Merged
merged 1 commit into from
Aug 16, 2019
Merged

Optimize Array#* #8087

merged 1 commit into from
Aug 16, 2019

Conversation

asterite
Copy link
Member

We use the same trick of multiple memcpy like we do in String#* and also consider a few edge cases like 0 and 1.

Benchmark:

require "benchmark"

Benchmark.ips do |x|
  x.report("old (1*100)") do
    [1] * 100
  end
  x.report("new (1*100)") do
    [1].mul(100)
  end
end

Benchmark.ips do |x|
  x.report("old (3*100)") do
    [1, 2, 3] * 100
  end
  x.report("new (3*100)") do
    [1, 2, 3].mul(100)
  end
end

Benchmark.ips do |x|
  x.report("old (3*1)") do
    [1, 2, 3] * 1
  end
  x.report("new (3*1)") do
    [1, 2, 3].mul(1)
  end
end

Results:

old (1*100)   1.62M (616.99ns) (± 6.18%)  528B/op   3.12× slower
new (1*100)   5.06M (197.73ns) (±13.24%)  528B/op        fastest
old (3*100)   1.25M (802.79ns) (± 3.55%)  2.08kB/op   1.58× slower
new (3*100)   1.96M (509.59ns) (± 2.59%)  2.08kB/op        fastest
old (3*1)  14.50M ( 68.96ns) (±12.02%)  96.0B/op   1.03× slower
new (3*1)  14.88M ( 67.21ns) (± 8.74%)  96.0B/op        fastest

I also got curious about this:

Benchmark.ips do |x|
  x.report("old (1*100)") do
    [1] * 100
  end
  x.report("new (1*100)") do
    [1].mul(100)
  end
  x.report("Array.new(size, value)") do
    Array.new(100, 1)
  end
end

Because they are essentially the same except that [1] * 100 is shorter to read/write and fancier.

Results:

           old (1*100)   1.74M (574.33ns) (± 0.96%)  528B/op   3.36× slower
           new (1*100)   5.48M (182.38ns) (± 1.00%)  528B/op   1.07× slower
Array.new(size, value)   5.85M (170.95ns) (± 1.37%)  480B/op        fastest

So it's not that much worse so now it becomes kind of acceptable to do.

We use the same trick of multiple memcpy like we do in `String#*`
and also consider a few edge cases like 0 and 1.
Copy link
Member

@straight-shoota straight-shoota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🥇

Copy link
Member

@sdogruyol sdogruyol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More performance 😍

@asterite asterite added this to the 0.31.0 milestone Aug 16, 2019
@asterite asterite merged commit b42c5dd into crystal-lang:master Aug 16, 2019
@asterite asterite deleted the opt/array-mul branch August 16, 2019 11:53
dnamsons pushed a commit to dnamsons/crystal that referenced this pull request Jan 10, 2020
We use the same trick of multiple memcpy like we do in `String#*`
and also consider a few edge cases like 0 and 1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants