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

Conversation

@asterite
Copy link
Member

commented Aug 15, 2019

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

left a comment

Nice 🥇

Copy link
Member

left a comment

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
5 checks passed
5 checks passed
ci/circleci: check_format Your tests passed on CircleCI!
Details
ci/circleci: test_darwin Your tests passed on CircleCI!
Details
ci/circleci: test_linux Your tests passed on CircleCI!
Details
ci/circleci: test_linux32 Your tests passed on CircleCI!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@asterite asterite deleted the asterite:opt/array-mul branch Aug 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.