# Optimizing ruby constant array data

**URL:** <https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655>\
**Category:** ruby-talk\
**Created:** [11 March 2006 02:33 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655 "2006-03-11T02:33:43Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mike\_Austin4](https://avatars.discourse-cdn.com/v4/letter/m/fbc32d/32.png) [@Mike\_Austin4](https://rubytalk.org/u/Mike_Austin4)\
**Post date:** [11 March 2006 02:33 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655/1 "2006-03-11T02:33:43Z")

</div>

I decided it was time to do a little profiling, and am so glad that it's built right into Ruby. My biggest problem seems to be array access:

&nbsp;&nbsp;% cumulative self self total  
time seconds seconds calls ms/call ms/call name  
36.02 3.35 3.35 350 9.57 15.10 String#each\_byte  
10.45 4.32 0.97 27230 0.04 0.04 Array#[]  
&nbsp;&nbsp;6.73 4.95 0.63 16100 0.04 0.04 GL.Vertex

I'll eventually go to display-lists or similar in OpenGL, but I was wondering if I could optimize this any further?

def draw\_string( string )  
&nbsp;&nbsp;&nbsp;size = font.height  
&nbsp;&nbsp;&nbsp;x = 0

&nbsp;&nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
&nbsp;&nbsp;&nbsp;GL::Begin( GL::QUADS )  
&nbsp;&nbsp;&nbsp;string.each\_byte do |char|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;offset = char - 32  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_top[offset] );  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, 0 )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_bottom[offset] );  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, size )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_bottom[offset] );  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, size )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_top[offset] );  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, 0 )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x += @sizes[char-32][0]  
&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;GL::End()  
&nbsp;&nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
end

Thanks,  
Mike

---

<div class="post-metadata">

**Author:** ![George\_Ogata1](https://avatars.discourse-cdn.com/v4/letter/g/d9b06d/32.png) [@George\_Ogata1](https://rubytalk.org/u/George_Ogata1)\
**Post date:** [11 March 2006 03:03 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655/2 "2006-03-11T03:03:45Z")

</div>

Mike Austin \<noone@nowhere.com\> writes:

> I decided it was time to do a little profiling, and am so glad that  
> it's built right into Ruby. My biggest problem seems to be array  
> access:
> 
> &nbsp;&nbsp;% cumulative self self total  
> time seconds seconds calls ms/call ms/call name  
> 36.02 3.35 3.35 350 9.57 15.10 String#each\_byte  
> 10.45 4.32 0.97 27230 0.04 0.04 Array#  
> &nbsp;&nbsp;6.73 4.95 0.63 16100 0.04 0.04 GL.Vertex
> 
> I'll eventually go to display-lists or similar in OpenGL, but I was  
> wondering if I could optimize this any further?
> 
> def draw\_string( string )  
> &nbsp;&nbsp;&nbsp;size = font.height  
> &nbsp;&nbsp;&nbsp;x = 0
> 
> &nbsp;&nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
> &nbsp;&nbsp;&nbsp;GL::Begin( GL::QUADS )  
> &nbsp;&nbsp;&nbsp;string.each\_byte do |char|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;offset = char - 32  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_top[offset] );  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, 0 )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_bottom[offset] );  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, size )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_bottom[offset] );  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, size )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_top[offset] );  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, 0 )  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x += @sizes[char-32][0]  
> &nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;GL::End()  
> &nbsp;&nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
> end

Perhaps use #at instead of # ? Using texture coord arrays might  
help too. But if that's really only 10% of the total time, I don't  
think it's going to make too much difference...

---

<div class="post-metadata">

**Author:** ![Mauricio\_Fernandez](https://avatars.discourse-cdn.com/v4/letter/m/73ab20/32.png) [@Mauricio\_Fernandez](https://rubytalk.org/u/Mauricio_Fernandez)\
**Post date:** [11 March 2006 09:51 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655/3 "2006-03-11T09:51:18Z")

</div>

This should be a bit faster:

def draw\_string( string )  
&nbsp;&nbsp;size = font.height  
&nbsp;&nbsp;x = 0

&nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
&nbsp;&nbsp;GL::Begin( GL::QUADS )  
&nbsp;&nbsp;  
&nbsp;&nbsp;# lvars are often faster than dvars ...  
&nbsp;&nbsp;char = offset = left = top = bottom = right = 0  
&nbsp;&nbsp;string.each\_byte do |char|  
&nbsp;&nbsp;&nbsp;&nbsp;offset = char - 32  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# ... and also faster than ivars (array access vs. hash lookup)  
&nbsp;&nbsp;&nbsp;&nbsp;# we save an extra method call too  
&nbsp;&nbsp;&nbsp;&nbsp;top = @tex\_coords\_top[offset]  
&nbsp;&nbsp;&nbsp;&nbsp;bottom = @tex\_coords\_bottom[offset]  
&nbsp;&nbsp;&nbsp;&nbsp;left = @tex\_coords\_left[offset]  
&nbsp;&nbsp;&nbsp;&nbsp;right = @tex\_coords\_right[offset]

&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( left, top );  
&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, 0 )  
&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( left, bottom );  
&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, size )  
&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( right, bottom );  
&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, size )  
&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( right, top );  
&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, 0 )  
&nbsp;&nbsp;&nbsp;&nbsp;x += @sizes[char-32][0]  
&nbsp;&nbsp;end  
&nbsp;&nbsp;GL::End()  
&nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
end

You can't expect much from such micro-optimizations, but they do help a bit:

RUBY\_VERSION # =\> "1.8.4"  
require 'benchmark'

puts "ivar vs. lvar"  
Benchmark.bm(10) do |bm|  
&nbsp;&nbsp;o = Class.new do  
&nbsp;&nbsp;&nbsp;&nbsp;def initialize(ivar); @iv = ivar end  
&nbsp;&nbsp;&nbsp;&nbsp;def using\_ivar; 1000000.times { @iv; @iv; @iv; @iv; @iv} end # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;def using\_lvar; iv = @iv; 1000000.times { iv; iv; iv; iv; iv} end # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;def using\_ivar2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1000000.times {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@iv; @iv; @iv; @iv; @iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@iv; @iv; @iv; @iv; @iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@iv; @iv; @iv; @iv; @iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@iv; @iv; @iv; @iv; @iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;def using\_lvar2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iv = @iv; 1000000.times {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iv; iv; iv; iv; iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iv; iv; iv; iv; iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iv; iv; iv; iv; iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iv; iv; iv; iv; iv # !\> useless use of a variable in void context  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end.new(1)  
&nbsp;&nbsp;bm.report("ivar"){ o.using\_ivar }  
&nbsp;&nbsp;bm.report("lvar"){ o.using\_lvar }  
&nbsp;&nbsp;bm.report("ivar (x4)"){ o.using\_ivar2 }  
&nbsp;&nbsp;bm.report("lvar (x4)"){ o.using\_lvar2 }  
end

puts "dvar vs. lvar"

Benchmark.bm(10) do |bm|  
&nbsp;&nbsp;bm.report("dvar"){ 1000000.times{|i| i = 1} }  
&nbsp;&nbsp;j = 0  
&nbsp;&nbsp;bm.report("lvar"){ 1000000.times{|j| j = 1} }  
end  
# \>\> ivar vs. lvar  
# \>\> user system total real  
# \>\> ivar 0.850000 0.000000 0.850000 ( 0.886111)  
# \>\> lvar 0.530000 0.000000 0.530000 ( 0.529777)  
# \>\> ivar (x4) 2.900000 0.010000 2.910000 ( 2.998297)  
# \>\> lvar (x4) 1.580000 0.000000 1.580000 ( 1.645420)  
# \>\> dvar vs. lvar  
# \>\> user system total real  
# \>\> dvar 0.380000 0.000000 0.380000 ( 0.390633)  
# \>\> lvar 0.360000 0.000000 0.360000 ( 0.374979)

Access and assignment to lvars will often be faster than to dvars,  
especially if:  
\* you're accessing a dynamic variable from an enclosing lexical scope  
\* there are lots of variables  
&nbsp;&nbsp;&nbsp;foo { a = 1; bar{ b1 = 2; ...; b100 = 100; baz{ c = 3; foobar{ a } } } }

> **···**
>
> On Sat, Mar 11, 2006 at 11:33:43AM +0900, Mike Austin wrote:
> 
> > I'll eventually go to display-lists or similar in OpenGL, but I was  
> > wondering if I could optimize this any further?
> > 
> > def draw\_string( string )  
> > &nbsp;&nbsp;size = font.height  
> > &nbsp;&nbsp;x = 0
> > 
> > &nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
> > &nbsp;&nbsp;GL::Begin( GL::QUADS )  
> > &nbsp;&nbsp;string.each\_byte do |char|  
> > &nbsp;&nbsp;&nbsp;&nbsp;offset = char - 32  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_top[offset] );  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, 0 )  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_bottom[offset] );  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, size )  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_bottom[offset] );  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, size )  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_top[offset] );  
> > &nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, 0 )  
> > &nbsp;&nbsp;&nbsp;&nbsp;x += @sizes[char-32][0]  
> > &nbsp;&nbsp;end  
> > &nbsp;&nbsp;GL::End()  
> > &nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
> > end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=====
> 
> Whereas lvars are subscripted directly from an array, dvars are looked up  
> linearly.
> 
> --  
> Mauricio Fernandez - [http://eigenclass.org](http://eigenclass.org) - singular Ruby

---

<div class="post-metadata">

**Author:** ![Robert](https://avatars.discourse-cdn.com/v4/letter/r/e95f7d/32.png) [@Robert](https://rubytalk.org/u/Robert)\
**Post date:** [11 March 2006 12:53 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655/4 "2006-03-11T12:53:43Z")

</div>

This looks rather like the code in each\_byte was the problem. Notice that self seconds for Array# is just 0.97 - while String#each\_byte consumes 3.35. Or did I miss something?

Kind regards

&nbsp;&nbsp;&nbsp;&nbsp;robert

> **···**
>
> Mike Austin \<noone@nowhere.com\> wrote:
> 
> > I decided it was time to do a little profiling, and am so glad that  
> > it's built right into Ruby. My biggest problem seems to be array  
> > access:  
> > % cumulative self self total  
> > time seconds seconds calls ms/call ms/call name  
> > 36.02 3.35 3.35 350 9.57 15.10 String#each\_byte  
> > 10.45 4.32 0.97 27230 0.04 0.04 Array#  
> > 6.73 4.95 0.63 16100 0.04 0.04 GL.Vertex

---

<div class="post-metadata">

**Author:** ![Mike\_Austin4](https://avatars.discourse-cdn.com/v4/letter/m/fbc32d/32.png) [@Mike\_Austin4](https://rubytalk.org/u/Mike_Austin4)\
**Post date:** [11 March 2006 04:13 UTC](https://rubytalk.org/t/optimizing-ruby-constant-array-data/25655/5 "2006-03-11T04:13:43Z")

</div>

Thanks for the feedback. at() didn't seem to do much, but I just remembered the glCallLists() trick:

def draw\_string( string )  
&nbsp;&nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::CallLists( string )  
&nbsp;&nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
end

Oh yea 🙂  
Mike

George Ogata wrote:

> **···**
>
> > Mike Austin \<noone@nowhere.com\> writes:
> > 
> > > I decided it was time to do a little profiling, and am so glad that  
> > > it's built right into Ruby. My biggest problem seems to be array  
> > > access:
> > > 
> > > &nbsp;&nbsp;% cumulative self self total  
> > > time seconds seconds calls ms/call ms/call name  
> > > 36.02 3.35 3.35 350 9.57 15.10 String#each\_byte  
> > > 10.45 4.32 0.97 27230 0.04 0.04 Array#  
> > > &nbsp;&nbsp;6.73 4.95 0.63 16100 0.04 0.04 GL.Vertex
> > > 
> > > I'll eventually go to display-lists or similar in OpenGL, but I was  
> > > wondering if I could optimize this any further?
> > > 
> > > def draw\_string( string )  
> > > &nbsp;&nbsp;&nbsp;size = font.height  
> > > &nbsp;&nbsp;&nbsp;x = 0
> > > 
> > > &nbsp;&nbsp;&nbsp;GL::Enable( GL::TEXTURE\_2D )  
> > > &nbsp;&nbsp;&nbsp;GL::Begin( GL::QUADS )  
> > > &nbsp;&nbsp;&nbsp;string.each\_byte do |char|  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;offset = char - 32  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_top[offset] );  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, 0 )  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_left[offset], @tex\_coords\_bottom[offset] );  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x, size )  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_bottom[offset] );  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, size )  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::TexCoord2f( @tex\_coords\_right[offset], @tex\_coords\_top[offset] );  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GL::Vertex( x + size, 0 )  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x += @sizes[char-32][0]  
> > > &nbsp;&nbsp;&nbsp;end  
> > > &nbsp;&nbsp;&nbsp;GL::End()  
> > > &nbsp;&nbsp;&nbsp;GL::Disable( GL::TEXTURE\_2D )  
> > > end
> > 
> > Perhaps use #at instead of # ? Using texture coord arrays might  
> > help too. But if that's really only 10% of the total time, I don't  
> > think it's going to make too much difference...
