Extending Ruby. Little help or guidance if you are willing!

Hi Folk,

I’ve been playing around with 7 segment displays on my RPI. I noticed
that
multiplexing in ruby doesn’t perform that well. I get flickring on the
displays and the ruby process eats up 10-20% of the RPI CPU.

Ruby -rprofile myprogram.rb shows that my display_segments method is the
culprit. I’m told that re-writing this in C could speed things up a bit
so
I started playing around with ruby inline. Admittedly I seem to get lost
in
the Ruby C API.

I’m pretty much an amateur but I thought I would post here for some
guidance from any willing kind hearted soul.

The culprit method is

def display_segments(num, display, io, delay = 0.005)
power = display.power
seg_pins = display.segments
case num
when 0
pins = [:a, :b, :c, :d, :e, :f]
when 1
pins = [:b, :c]
when 2
pins = [:a, :b, :g, :e, :d]
when 3
end
seg_pins.each do |seg, pin|
pins.include?(seg) ? io.write(pin, LOW) : io.write(pin, HIGH)
end
io.write(power, HIGH)
sleep delay
io.write(power, LOW)
end

I think the logic in my C method would be the same as my ruby one. The
part
where I run into trouble is converting values. For example I can’t
figure
out how to decide which pins to light by hash key. In fact I can’t even
figure out how to get the hash keys…

Code on
Githubhttps://github.com/crosson/rpi/blob/master/weatherstation/weatherstation.rb

Am Sun, 3 Mar 2013 10:31:25 +0900
schrieb Cliff R. [email protected]:

Hi Folk,

Hi Cliff,

def display_segments(num, display, io, delay = 0.005)

sleep delay

end

I doubt Ruby’s sleep is exact enough to sleep such a small delay of
time, especially given the general Floating-Point precision problems.
I’d suspect that your #sleep call is actually taking longer than you
expect.

I think the logic in my C method would be the same as my ruby one.
The part where I run into trouble is converting values. For example I
can’t figure out how to decide which pins to light by hash key. In
fact I can’t even figure out how to get the hash keys…

There are two great sources I use when writing C extensions for Ruby:

For getting values out of hashes, you want rb_hash_aref().

Vale,
Marvin

Thanks Quintus. I’ll get around to playing with this today.

Hi Cliff,

Maybe my ruby binding to the SOMPAK code may serve as a small example…

However, I noted that you are already using a ruby binding to the
C-library
(WiringPi)!?

cheers,
saji

On Mon, Mar 4, 2013 at 1:52 AM, Cliff R. [email protected]
wrote:

Hi Cliff,
expect.

http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v2_0_0_0/README.EXT?view=markup


Secure & Reliable End-To-End Credentials Support | Parchment

Saji N Hameed,
ARC-ENV, Center for Advanced Information Science and Technology,
University of Aizu, Tsuruga, Ikki-machi,
Aizuwakamatsu-shi, Fukushima 965-8580,
Japan

Tel: +81242 37-2736
Fax:+81242 37-2760
email: [email protected]
url: http://www.u-aizu.ac.jp
bib: Web of Science

change rb_intern_str(rb_str_new2(“test”)); into rb_intern(“test”);
you get the sym as VALUE, but you need an ID … so do SYM2ID

return SYM2ID(sym) == id ? Qtrue : Qfalse;

PS: it would be good if you test if sym is really an symbol with
SYMBOL_P … otherwise you will get problems

So I am slowly stumbling my way through this. I know I’ve got quite a
bit
to learn. I’ve tried to do some reading but some of this is a bit over
my
head. I’m getting close. I built the below method by reading and copying
items from the ruby API docs. My ultimate goal is to extract keys from a
hash and compare them. Since I don’t know how to compare symbols in C I
thought I would start there.

require ‘inline’
module Whatever

inline(:C) do |builder|
builder.c ’
VALUE compare(VALUE sym) {

  ID id = rb_intern_str(rb_str_new2("test"));

  if (id == sym) return Qtrue;
  return Qfalse;
}
'

end
end

if FILE == $0
include Whatever
puts compare(:test)
end

This is my output

puts compare(“tes”)
=> false
puts compare(“test”)
=> false
puts compare(:test)
=> false

Any clues as to why :test fails?

On Tue, 05 Mar 2013 06:25:50 +0100, Cliff R.
[email protected] wrote:

puts compare(“tes”)
=> false
puts compare(“test”)
=> false
puts compare(:test)
=> false

Any clues as to why :test fails?

You’re comparing an ‘ID’ value with a ‘VALUE’ value; these are not the
same. VALUE represents a Ruby object (any object); ID is an internal
representation of a method or variable name.

You can convert an ID to a VALUE representing the corresponding symbol
using rb_id2name():

 ID id = rb_id2name(rb_intern_str(rb_str_new2("test")));

Then it should work. (Untested.)

It could possibly be faster to use a hash or a set than to use an array for
your pins, but not totally confident given the smallness of the arrays. In
general, you should benchmark to see if any of these have more merit.

Interesting point. I think you are right. I’ll try and experiment with
this
today.

On Sat, Mar 2, 2013 at 7:31 PM, Cliff R. [email protected]
wrote:

def display_segments(num, display, io, delay = 0.005)
end
seg_pins.each do |seg, pin|
pins.include?(seg) ? io.write(pin, LOW) : io.write(pin, HIGH)
end
io.write(power, HIGH)
sleep delay
io.write(power, LOW)
end

Honestly, you could probably get really far by changing a few things
about
the implementation here. I’m assuming you’re calling this frequently, in
which case, you’re creating these arrays often, it might be faster to
set
them into a constant, which you can then return from the case statement.
Also, the pins.include?(seg) is going to do a sequential search through
the
arrays. You could possibly run some tests to see which keys are most
frequent and place them at the beginning so they are found first. If you
have control over the display object, you might be able to swap out the
symbols with integers, allowing for you to be clever with bit
mathematics.
It could possibly be faster to use a hash or a set than to use an array
for
your pins, but not totally confident given the smallness of the arrays.
In
general, you should benchmark to see if any of these have more merit.

Haven’t been able to touch this in a week but I had a few minutes to
look
at this tonight. I had a few questions. My original ruby method looks
similar to this.

method(number, pin_hash)

case number
when 1
on_pins = [:b, :c]
end

pin_hash.each do |seg, pin|
pins.include?(seg) ? io.write(pin, LOW) : io.write(pin, HIGH)
end

So I need to convert everything in my C method to something
comparable. In my C code how do I create the on_pins array of symbols?

Also based on previous posts. What is the difference between a symbol
and an ID?

Although I think the exercise of extending ruby in C is a good one so
I’ll
probably proceed as is. Still there is something to be said for being
aware
of the performance different calls in ruby have so I’ll probably just
play
around with both ways.

On Sun, 10 Mar 2013 04:41:32 +0100, Cliff R.
[email protected] wrote:

Also based on previous posts. What is the difference between a symbol and an ID?

A symbol is an object of the Ruby class Symbol. It is essentially an
integer that also has a textual human-readable representation - sort of
like a magic numeric bconstant in other languages.

An ID is an internal representation for a method/class name used in the
C Ruby implementation. Under-the-hood it uses the same machinery as a
Symbol, and thus can be converted to and from it easily.

Got ya. So it doesn’t really matter if I convert things to ID and
compare
on that basis or keep things a symbol. Thanks Bartosz.

Rereading this, I suspect that you might have mis-analyzed the issue. I
don’t know how the profiler works, but if it’s just looking at total
time,
then it should see you spend a lot in this method, because it has both
IO,
and a sleep statement inside of it. The 10-20% could just be what it
takes
to run the Ruby runtime. The flickering could be due to

io.write(power, HIGH)
sleep delay
io.write(power, LOW)

I don’t know about your device, but is there actually value to writing
low
at all? Could that cause it to flicker?

On Mon, Mar 11, 2013 at 10:44 AM, Cliff R.
[email protected]wrote:

Got ya. So it doesn’t really matter if I convert things to ID and compare
on that basis or keep things a symbol. Thanks Bartosz.

That’s true if you mean to just swap them straight over, to avoid
creating
lots of throw-away arrays and iterating over them.

There isn’t enough code here to know what you can do (e.g. do you have
control over the representation shown by display.segments), or to play
with
it (or, for me, to even really understand how the pieces fit together)
but
if your low-level lib takes something like a number where each digit or
each bit represents the value of a pin in that position, then you could
just deal with ints like this:

case num.to_s
when '0' then 0b1111110
when '1' then 0b0110000
when '2' then 0b1101101
when '3' then 0b1111001
when '4' then 0b0110011
when '5' then 0b1011011
when '6' then 0b1011111
when '7' then 0b1110000
when '8' then 0b1111111
when '9' then 0b1110011
end

On Tue, Mar 12, 2013 at 12:28 AM, Josh C. [email protected]
wrote:

I don’t know about your device, but is there actually value to writing low
That’s true if you mean to just swap them straight over, to avoid creating
when ‘0’ then 0b1111110

Why case (plus num.to_s) instead of just an array?

But for numbers that is ok. I’ll give it a look. Thanks Josh!

On Tue, Mar 12, 2013 at 4:05 PM, tamouse mailing lists <
[email protected]> wrote:

io.write(power, HIGH)

control over the representation shown by display.segments), or to play
when ‘0’ then 0b1111110

Why case (plus num.to_s) instead of just an array?

Oh, I was playing with the idea
https://gist.github.com/JoshCheek/5144076and at one point had started
to implement characters, too, so this just
allowed me to use digits or character representations of digits. But
then I
realized characters like “B” look like “8”, and didn’t feel like adding
more pins to make it all work, so I took it back out.

-Josh

On Tue, Mar 12, 2013 at 4:35 PM, Josh C. [email protected]
wrote:

takes

lots of throw-away arrays and iterating over them.
with ints like this:
when ‘8’ then 0b1111111
allowed me to use digits or character representations of digits. But then I
realized characters like “B” look like “8”, and didn’t feel like adding more
pins to make it all work, so I took it back out.

-Josh

Ah, no worries, just wondering…

On Wed, Mar 13, 2013 at 4:49 AM, Cliff R. [email protected]
wrote:

Maybe I just need to revisit this. Having to extend in C slowed me down
quite a bit. If possible I just like to stay in ruby since I am comfortable
there. After all I am not developing guru! :slight_smile:

Did you post your profiler output in this thread? I can’t seem to
find it. Maybe it helps if everybody else is seeing what you are.

Kind regards

robert

Josh you added quite a bit of good information.

I don’t know about your device, but is there actually value to writing
low

at all? Could that cause it to flicker?

Maybe I just need to revisit this. Having to extend in C slowed me down
quite a bit. If possible I just like to stay in ruby since I am
comfortable
there. After all I am not developing guru! :slight_smile:

On Tue, Mar 12, 2013 at 8:03 PM, tamouse mailing lists <