Mombu the Programming Forum

Go Back   Mombu the Programming Forum > Programming > Using RubyInline for Optimization
User Name
Password
REGISTER NOW! Mark Forums Read




Reply
1 6th October 08:20
eric hodel
External User
 
Posts: 1
Default Using RubyInline for Optimization



over 100 times faster.

http://segment7.net/projects/ruby/in...imization.html


--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
  Reply With Quote


 


2 22nd October 19:05
dominik bathon
External User
 
Posts: 1
Default Using RubyInline for Optimization



On Sat, 02 Sep 2006 05:26:04 +0200, Eric Hodel <drbrain@segment7.net> =

Nice article, but in this case it is possible to get almost the same =

speedup in pure Ruby:

Base version:

def to_blob
blob =3D []
blob << [137, 80, 78, 71, 13, 10, 26, 10].pack("C*") # PNG signatur=
e
blob << PNG.chunk('IHDR',
[ @height, @width, @bits, 6, 0, 0, 0 ].pack("N2C5=
"))
# 0 =3D=3D filter type code "none"
data =3D @data.map { |row| [0] + row.map { |p| p.values } }.flatten=

blob << PNG.chunk('IDAT', Zlib:eflate.deflate(data.pack("C*"), 9)=
)
blob << PNG.chunk('IEND', '')
blob.join
end

$ time ruby -Ilib profile.rb

real 0m15.504s
user 0m15.119s
sys 0m0.309s


Avoiding flatten (and using a literal for the signature):

def to_blob
blob =3D []
blob << "\211PNG\r\n\032\n" # PNG signature
blob << PNG.chunk('IHDR',
[ @height, @width, @bits, 6, 0, 0, 0 ].pack("N2C5=
"))
# 0 =3D=3D filter type code "none"
data =3D @data.map { |row| "\0" < row.map { |p| p.values.pack("C*")=
=

}.join }
blob << PNG.chunk('IDAT', Zlib:eflate.deflate(data.join, 9))
blob << PNG.chunk('IEND', '')
blob.join
end

$ time ruby -Ilib profile.rb

real 0m10.190s
user 0m10.081s
sys 0m0.043s


Using String#% instead of Array#pack:

format_str =3D "%c%c%c%c"
data =3D @data.map { |row| "\0" < row.map { |p| format_str % p.valu=
es =

}.join }

instead of

data =3D @data.map { |row| "\0" < row.map { |p| p.values.pack("C*")=
=

}.join }

$ time ruby -Ilib profile.rb

real 0m4.974s
user 0m4.911s
sys 0m0.031s


Caching the string representation of the values in PNG::Color (because =

each pixel is the same instance of color in this case):

Add to PNG::Color

def values_str
@values_str ||=3D "%c%c%c%c" % @values
end

Use

data =3D @data.map { |row| "\0" < row.map { |p| p.values_str }.join=
}

instead of

format_str =3D "%c%c%c%c"
data =3D @data.map { |row| "\0" < row.map { |p| format_str % p.valu=
es =

}.join }

$ time ruby -Ilib profile.rb

real 0m2.489s
user 0m2.463s
sys 0m0.013s


Improving PNG::Canvas#initialize:

Use

@data =3D Array.new(@width) { |x| Array.new(@height, background) }

instead of

@data =3D Array.new(@width) { |x| Array.new(@height) { background }=
}

$ time ruby -Ilib profile.rb

real 0m1.941s
user 0m1.914s
sys 0m0.014s


Representing the values in PNG::Color as String (instead of as Array) (s=
ee =

complete patch below):

$ time ruby -Ilib profile.rb

real 0m1.492s
user 0m1.445s
sys 0m0.015s


So, it is ten times faster in pure Ruby.

Dominik


--- png-1.0.0/lib/png.rb 2006-08-31 22:57:13.000000000 +0200
+++ png-1.0.0_opt/lib/png.rb 2006-09-02 19:26:46.000000000 +0200
@@ -71,15 +71,21 @@
##
# Writes the PNG to +path+.

- def save(path)
- File.open(path, "w") do |f|
- f.write [137, 80, 78, 71, 13, 10, 26, 10].pack("C*") # PNG signat=
ure
- f.write PNG.chunk('IHDR',
+ def to_blob
+ blob =3D []
+ blob << "\211PNG\r\n\032\n" # PNG signature
+ blob << PNG.chunk('IHDR',
[ @height, @width, @bits, 6, 0, 0, 0 =

].pack("N2C5"))
# 0 =3D=3D filter type code "none"
- data =3D @data.map { |row| [0] + row.map { |p| p.values } }.flatt=
en
- f.write PNG.chunk('IDAT', Zlib:eflate.deflate(data.pack("C*"), =
9))
- f.write PNG.chunk('IEND', '')
+ data =3D @data.map { |row| "\0" < row.map { |p| p.values }.join }
+ blob << PNG.chunk('IDAT', Zlib:eflate.deflate(data.join, 9))
+ blob << PNG.chunk('IEND', '')
+ blob.join
+ end
+
+ def save(path)
+ File.open(path, "w") do |f|
+ f.write to_blob
end
end

@@ -94,7 +100,7 @@
# Creates a new color with values +red+, +green+, +blue+, and +alp=
ha+.

def initialize(red, green, blue, alpha)
- @values =3D [red, green, blue, alpha]
+ @values =3D "%c%c%c%c" % [red, green, blue, alpha]
end

##
@@ -151,7 +157,7 @@
end

def inspect # :nodoc:
- "#<%s %02x %02x %02x %02x>" % [self.class, *@values]
+ "#<%s %02x %02x %02x %02x>" % [self.class, r, b, g, a]
end

end
@@ -179,7 +185,7 @@
def initialize(height, width, background =3D Color::White)
@height =3D height
@width =3D width
- @data =3D Array.new(@width) { |x| Array.new(@height) { background=
} }
+ @data =3D Array.new(@width) { |x| Array.new(@height, background) =
}
end

##
  Reply With Quote
3 22nd October 19:10
eric hodel
External User
 
Posts: 1
Default Using RubyInline for Optimization


This change gives a broken PNG. You meant to call #<<, not #<.

I see no significant speedup when using #<<.

These are good.

Not really worth optimizing, since the improvement is so small. In
the optimized RubyInline version only 20% of the time is spent here
with no image generation. Adding image generation makes this
optimization insignificant.

Using your changes from the pure-ruby version I went from about 30
seconds to about 4 seconds, or 7.5 times faster.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
  Reply With Quote
4 22nd October 19:11
dominik bathon
External User
 
Posts: 1
Default Using RubyInline for Optimization


On Sat, 02 Sep 2006 23:45:17 +0200, Eric Hodel <drbrain@segment7.net> =


Oops, of course I meant #<<. I actually had tested that my changes still=
=

produce correct results when I did the optimizations originally. Then I =
=

recreated them to get the times for each step and made that typo, oh wel=
l.

Just for reference, the time for the final version with #< changed to #<=
< =

on my machine:

$ time ruby -Ilib profile.rb

real 0m1.901s
user 0m1.841s sys 0m0.032s

Yes, it's not much for the total runtime, but it makes this one line abo=
ut =

14 times faster:

$ time ruby -e "40.times { Array.new(400) { Array.new(400) { 0 } } }"

real 0m2.430s
user 0m2.402s
sys 0m0.017s

$ time ruby -e "40.times { Array.new(400) { Array.new(400, 0) } }"

real 0m0.165s
user 0m0.147s
sys 0m0.016s
  Reply With Quote
Reply


Thread Tools
Display Modes




666