Ruby "Speedup" hints?

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

So without further ado:

- Using << instead of += for Strings as += creates a new object
whereas << will simply work on the current object.

- Use Inline C for critical methods (had to include that ;> )

- Reusing variable names might be better than using a lot of
different variables

- for is faster than .each on Arrays

- .last is faster than [0]

- .zero? is faster than == 0

If you know a few more hints, please add!

···

--
Posted via http://www.ruby-forum.com/.

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

So without further ado:

- Using << instead of += for Strings as += creates a new object
whereas << will simply work on the current object.

I'd replace that with the general rule to avoid object creation because that will cover more cases (Array#concat vs. Array#+, String#gsub! vs. String#gsub etc.).

- Use Inline C for critical methods (had to include that ;> )

You could argue that this does not speed up Ruby code but replaces it with something else. So it's questionable whether this item should be on the list.

- Reusing variable names might be better than using a lot of
different variables

Are you talking about a fact or a guess here? You write "might" - which indicates to me that this is not a proven fact.

- for is faster than .each on Arrays

- .last is faster than [0]

I guess you meant Array#last is faster than Array#[-1] or Array#first is faster than Array#[0].

- .zero? is faster than == 0

Interesting, I did not know that. But the difference is really small:

robert@fussel ~
$ time ruby -e '1_000_000.times { 0.zero? }'

real 0m0.748s
user 0m0.468s
sys 0m0.108s

robert@fussel ~
$ time ruby -e '1_000_000.times { 1.zero? }'

real 0m0.748s
user 0m0.483s
sys 0m0.124s

robert@fussel ~
$ time ruby -e '1_000_000.times { 0 == 0 }'

real 0m0.869s
user 0m0.561s
sys 0m0.108s

robert@fussel ~
$ time ruby -e '1_000_000.times { 1 == 0 }'

real 0m0.857s
user 0m0.562s
sys 0m0.124s

robert@fussel ~
$

If you know a few more hints, please add!

- freeze Strings that you are going to use as Hash keys.

Often bad design makes programs slow. While these are valid points often the bigger effect can be achieved by proper designing an application (i.e. use a Hash for frequent lookups instead of traversing an Aarry).

Kind regards

  robert

···

On 17.03.2008 13:42, Marc Heiler wrote:

Marc Heiler wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

There is only 1 hint:

PROFILE IT!!!

everything else is BS..

hth

ilan

···

--
Posted via http://www.ruby-forum.com/\.

Marc Heiler wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

So without further ado:

- Using << instead of += for Strings as += creates a new object
whereas << will simply work on the current object.

- Use Inline C for critical methods (had to include that ;> )

- Reusing variable names might be better than using a lot of
different variables

- for is faster than .each on Arrays

- .last is faster than [0]

- .zero? is faster than == 0

If you know a few more hints, please add!

A whole book full of tips!

ParseDate#parsedate is expensive - it uses rationals, gcd and other
heavy stuff to convert from [D, M, Y, H, M, S] to timestamp.
Once we did a log merger and we ordered the entries by time. We saved
a lot of processing time by storing the timestamps in the logs along
with the formatted date.
(They were removed afterwards during formatting.)

ERB#new is expensive. Cache compiled templates if they are to be reused.

Use /o switch for Regexp literals that contain constant
#{substitutions} (i.e. that do not depend on function parameters)

···

On Mon, Mar 17, 2008 at 1:42 PM, Marc Heiler <shevegen@linuxmail.org> wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )
If you know a few more hints, please add!

When generating text output, using StringIO is faster than using puts

···

On Mon, Mar 17, 2008 at 12:42 PM, Marc Heiler <shevegen@linuxmail.org> wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

--
Gerardo Santana

Sounds like time to repost (yet again, can't we add this to a FAQ somewhere?)

Please add your hints (preferably with a benchmark to show the
magnitude of the improvement) to that page.

Thanks!

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

···

On Mon, 17 Mar 2008, Marc Heiler wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

Marc Heiler wrote:

(...)
If you know a few more hints, please add!

Date and DateTime are time consuming. Time however, is not.

require 'benchmark'

N = 10000
Benchmark.bmbm do |bm|

   bm.report 'empty' do
     N.times do 1 end
   end

   bm.report 'DateTime' do
     N.times {DateTime.new}
   end

   bm.report 'Date' do
     N.times {Date.new}
   end

   bm.report 'Time' do
     N.times {Time.new}
   end

end

               user system total real
empty 0.000000 0.000000 0.000000 ( 0.000000)
DateTime 3.687000 0.063000 3.750000 ( 3.813000)
Date 1.281000 0.000000 1.281000 ( 1.313000)
Time 0.016000 0.000000 0.016000 ( 0.015000)

Regards,

Siep

···

--
Posted via http://www.ruby-forum.com/\.

and it certainly will eventually lead to programmer confusion esp when dynamically scoped
blocks come into play.

···

On Mar 17, 2008, at 9:04 AM, Robert Klemme wrote:

- Reusing variable names might be better than using a lot of
different variables

Are you talking about a fact or a guess here? You write "might" - which indicates to me that this is not a proven fact.

+1

All discussions of performance tuning are meaningless without profiling numbers.

···

On Mon, Mar 17, 2008 at 9:22 AM, Ilan Berci <coder68@yahoo.com> wrote:

There is only 1 hint:

PROFILE IT!!!

--
Avdi

Is that in any way a speedup hint? or is it just a safety hint? What
causes the speedup?

--Ken

···

On Mon, 17 Mar 2008 14:01:51 +0100, Robert Klemme wrote:

- freeze Strings that you are going to use as Hash keys.

--
Ken (Chanoch) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

This doesn't make sense. One can easily call #puts on a StringIO
object.

Do you have a concrete example/benchmark that demonstrates the
difference?

Paul

···

On Tue, Mar 18, 2008 at 02:17:22AM +0900, Gerardo Santana G?mez Garrido wrote:

On Mon, Mar 17, 2008 at 12:42 PM, Marc Heiler <shevegen@linuxmail.org> wrote:
> Anyone of you has a few hints on how to speed up ruby code?
> (Note - not writing it, but running it :wink: )
>
> I only have a few hints, like 5... would love to extend it.

When generating text output, using StringIO is faster than using puts

Profiling only tells you where performance bottlenecks are. It does
nothing for letting you know how to fix those bottlenecks. That's where
benchmarks come into play.

Paul

···

On Mon, Mar 17, 2008 at 11:20:59PM +0900, Avdi Grimm wrote:

On Mon, Mar 17, 2008 at 9:22 AM, Ilan Berci <coder68@yahoo.com> wrote:
> There is only 1 hint:
>
> PROFILE IT!!!

+1

All discussions of performance tuning are meaningless without
profiling numbers.

Otherwise Hash will dup and freeze the String anyway.

class Hash - RDoc Documentation (What is not
mentioned there is that frozen strings will not be duplicated.)

···

On Mon, Mar 17, 2008 at 6:40 PM, Ken Bloom <kbloom@gmail.com> wrote:

On Mon, 17 Mar 2008 14:01:51 +0100, Robert Klemme wrote:
> - freeze Strings that you are going to use as Hash keys.

Is that in any way a speedup hint? or is it just a safety hint? What
causes the speedup?

$ time ruby a.rb 10_000_000 > /tmp/a && sleep 3 && time ruby b.rb
10_000_000 > /tmp/b; printf "\a"

real 1m45.305s
user 1m27.581s
sys 0m17.715s

real 0m59.049s
user 0m41.984s
sys 0m16.997s
$ cat a.rb
times = ARGV[0].to_i
times.times { puts "hola mundo" }
$ cat b.rb
require 'stringio'

times = ARGV[0].to_i
output = StringIO.new
times.times { output.write("hola mundo\n") }
output.rewind
print output.read

The difference increases in real world reports.

···

On Mon, Mar 17, 2008 at 6:08 PM, Paul Brannan <pbrannan@atdesk.com> wrote:

On Tue, Mar 18, 2008 at 02:17:22AM +0900, Gerardo Santana G?mez Garrido wrote:
> On Mon, Mar 17, 2008 at 12:42 PM, Marc Heiler <shevegen@linuxmail.org> wrote:
> > Anyone of you has a few hints on how to speed up ruby code?
> > (Note - not writing it, but running it :wink: )
> >
> > I only have a few hints, like 5... would love to extend it.
>
> When generating text output, using StringIO is faster than using puts

This doesn't make sense. One can easily call #puts on a StringIO
object.

Do you have a concrete example/benchmark that demonstrates the
difference?

Paul

--
Gerardo Santana

As Jano mentioned it's the skipped #dup for frozen Strings that makes
the speedup.

irb(main):002:0> s="foo"
=> "foo"
irb(main):003:0> h={s=>1}
=> {"foo"=>1}
irb(main):004:0> [s.object_id, h.keys.first.object_id]
=> [1073545320, 1073545340]
irb(main):005:0> s.freeze
=> "foo"
irb(main):006:0> h={s=>1}
=> {"foo"=>1}
irb(main):007:0> [s.object_id, h.keys.first.object_id]
=> [1073545320, 1073545320]
irb(main):008:0>

Kind regards

robert

···

2008/3/17, Ken Bloom <kbloom@gmail.com>:

On Mon, 17 Mar 2008 14:01:51 +0100, Robert Klemme wrote:
> - freeze Strings that you are going to use as Hash keys.

Is that in any way a speedup hint? or is it just a safety hint? What
causes the speedup?

--
use.inject do |as, often| as.you_can - without end

The difference increases in real world reports.

This and some other tips seem specific to either ruby 1.8 or 1.9.
While StringIO seems much faster with 1.9, in my world, the difference
is rather marginal with 1.8. Also, the following is slightly faster
with 1.8:

times = ARGV[0].to_i
times.times { STDOUT.puts "hola mundo" }

YMMV etc.

Hi there,

Thanks for the feedback so far. :slight_smile:

"PROFILE IT!!!

everything else is BS."

As for profiling, you can always do profiling of course,
but why copy the efforts others did on it already?

Example - General Statements done such as the one by Robert Klemme
seem to hold true no matter how much profiling one would do anyway:
  "I'd replace that with the general rule to avoid object creation
because
  that will cover more cases (Array#concat vs. Array#+, String#gsub! vs.
  String#gsub etc.)."

Personally I like such simple truths. :slight_smile:

···

--
Posted via http://www.ruby-forum.com/.

Do you not realize that this is fundamentally telling you your terminal is super-slow? This has nothing to do with ruby itself, really. Try it on some other platforms and tool stacks, for goodness sake.

And FYI, this is why context specific profiling is far more important than any of these strange idioms you're talking about.

Oh, and this is only really true for short strings and relatively small amounts of memory. Try doing that whilst say, stream editing a 1TB file.

This whole direction of thought is broken, honestly.

···

On 18 Mar 2008, at 05:40, Gerardo Santana Gómez Garrido wrote:

On Mon, Mar 17, 2008 at 6:08 PM, Paul Brannan <pbrannan@atdesk.com> > wrote:

On Tue, Mar 18, 2008 at 02:17:22AM +0900, Gerardo Santana G?mez >> Garrido wrote:

On Mon, Mar 17, 2008 at 12:42 PM, Marc Heiler <shevegen@linuxmail.org >>> > wrote:

Anyone of you has a few hints on how to speed up ruby code?
(Note - not writing it, but running it :wink: )

I only have a few hints, like 5... would love to extend it.

When generating text output, using StringIO is faster than using puts

This doesn't make sense. One can easily call #puts on a StringIO
object.

Do you have a concrete example/benchmark that demonstrates the
difference?

Paul

$ time ruby a.rb 10_000_000 > /tmp/a && sleep 3 && time ruby b.rb
10_000_000 > /tmp/b; printf "\a"

real 1m45.305s
user 1m27.581s
sys 0m17.715s

real 0m59.049s
user 0m41.984s
sys 0m16.997s
$ cat a.rb
times = ARGV[0].to_i
times.times { puts "hola mundo" }
$ cat b.rb
require 'stringio'

times = ARGV[0].to_i
output = StringIO.new
times.times { output.write("hola mundo\n") }
output.rewind
print output.read

The difference increases in real world reports.
--
Gerardo Santana

I think you are demonstrating the difference between IO#puts and
IO#write (which still surprised me).

cout@bean:~/tmp$ time ruby a.rb 1_000_000 > /dev/null

real 0m2.769s
user 0m2.660s
sys 0m0.044s
cout@bean:~/tmp$ time ruby b.rb 1_000_000 > /dev/null

real 0m1.315s
user 0m1.244s
sys 0m0.020s
cout@bean:~/tmp$ time ruby c.rb 1_000_000 > /dev/null

real 0m1.256s
user 0m1.160s
sys 0m0.036s
cout@bean:~/tmp$ cat c.rb
times = ARGV[0].to_i
times.times { $stdout.write "hola mundo\n" }

And on my machine with 10_000_000 iterations I starting digging into swap with
the stringio solution:

cout@bean:~/tmp$ time ruby a.rb 10_000_000 > /dev/null

real 0m27.841s
user 0m25.694s
sys 0m0.112s
cout@bean:~/tmp$ time ruby b.rb 10_000_000 > /dev/null

real 0m54.888s
user 0m11.333s
sys 0m0.504s
cout@bean:~/tmp$ time ruby c.rb 10_000_000 > /dev/null

real 0m13.141s
user 0m11.749s
sys 0m0.100s

Paul

···

On Tue, Mar 18, 2008 at 02:40:43PM +0900, Gerardo Santana G?mez Garrido wrote:

$ time ruby a.rb 10_000_000 > /tmp/a && sleep 3 && time ruby b.rb
10_000_000 > /tmp/b; printf "\a"

real 1m45.305s
user 1m27.581s
sys 0m17.715s

real 0m59.049s
user 0m41.984s
sys 0m16.997s
$ cat a.rb
times = ARGV[0].to_i
times.times { puts "hola mundo" }
$ cat b.rb
require 'stringio'

times = ARGV[0].to_i
output = StringIO.new
times.times { output.write("hola mundo\n") }
output.rewind
print output.read