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
Github<https://github.com/crosson/rpi/blob/master/weather...
on 2013-03-03 02:32
on 2013-03-03 12:08
Am Sun, 3 Mar 2013 10:31:25 +0900 schrieb Cliff Rosson <cliff.rosson@gmail.com>: > 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: * The official README.EXT by Matz: http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v... * Rubinius’ ruby.h header file, because it’s way better documented than Ruby’s: https://github.com/rubinius/rubinius/blob/master/v... For getting values out of hashes, you want rb_hash_aref(). Vale, Marvin
on 2013-03-04 04:35
Hi Cliff, Maybe my ruby binding to the SOMPAK code may serve as a small example... https://github.com/sajinh/SOMPAK_4R/tree/master/sompak_4r 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 Rosson <cliff.rosson@gmail.com> wrote: >> Hi Cliff, >> expect. >> http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v... >> > > > > -- > vizualize.me/cliffrosson > -- 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: saji@u-aizu.ac.jp url: http://www.u-aizu.ac.jp bib: http://www.researcherid.com/rid/B-9188-2009
on 2013-03-05 06:26
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 2013-03-05 06:37
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
on 2013-03-05 08:27
On Sat, Mar 2, 2013 at 7:31 PM, Cliff Rosson <cliff.rosson@gmail.com> 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.
on 2013-03-05 08:31
On Tue, 05 Mar 2013 06:25:50 +0100, Cliff Rosson <cliff.rosson@gmail.com> 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.)
on 2013-03-05 17:32
> 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 2013-03-05 17:33
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 2013-03-10 04:42
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?
on 2013-03-10 07:06
On Sun, 10 Mar 2013 04:41:32 +0100, Cliff Rosson
<cliff.rosson@gmail.com> 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.
on 2013-03-11 16:44
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.
on 2013-03-12 06:29
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 Rosson <cliff.rosson@gmail.com>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 2013-03-12 22:05
On Tue, Mar 12, 2013 at 12:28 AM, Josh Cheek <josh.cheek@gmail.com> 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?
on 2013-03-12 22:36
On Tue, Mar 12, 2013 at 4:05 PM, tamouse mailing lists < tamouse.lists@gmail.com> 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 2013-03-13 04:04
On Tue, Mar 12, 2013 at 4:35 PM, Josh Cheek <josh.cheek@gmail.com> 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 2013-03-13 04:50
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! :)
On Tue, Mar 12, 2013 at 8:03 PM, tamouse mailing lists <
on 2013-03-13 08:17
On Wed, Mar 13, 2013 at 4:49 AM, Cliff Rosson <cliff.rosson@gmail.com> 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! :) 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
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.