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…
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:
Saji N Hameed,
ARC-ENV, Center for Advanced Information Science and Technology,
University of Aizu, Tsuruga, Ikki-machi,
Aizuwakamatsu-shi, Fukushima 965-8580,
Japan
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
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")));
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.
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.
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
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
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?
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.
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.
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!
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.
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!
On Tue, Mar 12, 2013 at 8:03 PM, tamouse mailing lists <
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.