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

Posted by Cliff Rosson (beaon)
on 2013-03-03 02:32
(Received via mailing list)
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...
Posted by Marvin Gülker (quintus)
on 2013-03-03 12:08
Attachment: signature.asc (489 Bytes)
(Received via mailing list)
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
Posted by Cliff Rosson (beaon)
on 2013-03-03 17:53
(Received via mailing list)
Thanks Quintus. I'll get around to playing with this today.
Posted by Saji Hameed (Guest)
on 2013-03-04 04:35
(Received via mailing list)
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
Posted by Cliff Rosson (beaon)
on 2013-03-05 06:26
(Received via mailing list)
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?
Posted by Hans Mackowiak (hanmac)
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
Posted by Josh Cheek (josh-cheek)
on 2013-03-05 08:27
(Received via mailing list)
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.
Posted by Bartosz Dziewoński (matmarex)
on 2013-03-05 08:31
(Received via mailing list)
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.)
Posted by Cliff Rosson (beaon)
on 2013-03-05 17:32
(Received via mailing list)
> 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.
Posted by Cliff Rosson (beaon)
on 2013-03-05 17:33
(Received via mailing list)
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.
Posted by Cliff Rosson (beaon)
on 2013-03-10 04:42
(Received via mailing list)
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?
Posted by Bartosz Dziewoński (matmarex)
on 2013-03-10 07:06
(Received via mailing list)
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.
Posted by Cliff Rosson (beaon)
on 2013-03-11 16:44
(Received via mailing list)
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.
Posted by Josh Cheek (josh-cheek)
on 2013-03-12 06:29
(Received via mailing list)
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
Posted by tamouse mailing lists (Guest)
on 2013-03-12 22:05
(Received via mailing list)
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?
Posted by Josh Cheek (josh-cheek)
on 2013-03-12 22:36
(Received via mailing list)
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
Posted by Cliff Rosson (beaon)
on 2013-03-12 22:42
(Received via mailing list)
But for numbers that is ok. I'll give it a look. Thanks Josh!
Posted by tamouse mailing lists (Guest)
on 2013-03-13 04:04
(Received via mailing list)
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...
Posted by Cliff Rosson (beaon)
on 2013-03-13 04:50
(Received via mailing list)
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 <
Posted by Robert Klemme (robert_k78)
on 2013-03-13 08:17
(Received via mailing list)
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
No account? Register here.