Hello fellow noders,

This is a long post, jump to the end to see the final questions, but a 
context is provided for the curious.

*Introductory part*: I have some beaglebone black devices running Node (0.8 
atm, planning to upgrade for the streams api). *The OS is Angstrom 
GNU/Linux v2012.12 running on a 3.8.13 kernel using device trees. I need to 
send data to a device via bit banging. Ideally, the baud rate should be 
somewhere between 60000 and 120000*.

In order to benchmark the speed before writing the actual code, I wrote a 
test suite. Via micro-optimizations, I got close to an acceptable speed 
(approx 60kHz measured with an oscilloscope). Here is the path:

*[1.] Classic beaglebone code, from the example*

> var b = require('bonescript');
>
> var ledPin = "P8_10";
>
> b.pinMode(ledPin, b.OUTPUT);
>
> b.digitalWrite(ledPin, b.LOW);
>
> var fn = function(state) {
>
>     b.digitalWrite(ledPin, state);
>
> };
>
> var state = b.LOW;
>
> var getOppositeState = function() {
>
>     return state = state == b.LOW ? b.HIGH : b.LOW;
>
> };
>
> while(1 == 1) {
>
>     fn(getOppositeState());
>
> }
>
> With this function, the frequency measured by the oscilloscope was 
fluctuating between 0.9-1.3 kHz. So about 1100 bits per second (screenshot: 
http://cs.mene.ro/oscilloscope/NewFile1.bmp). This was too slow. I ended up 
looking through the beaglebone scripts, noticing that it was doing some 
validations and ended up writing to the pin's file. That's why I switch 
from the beaglebone scripts to pure Node FS calls:

*[2.] Classic FS calls*

> var fs = require('fs');
>
> var pin = {
>
>     "name": "TIMER6",
>
>     "gpio": 68
>
> };
>
> var gpioFile = '/sys/class/gpio/gpio' + pin.gpio + '/value';
>
> var fn = function() {
>
>     fs.writeFile(gpioFile, '' + 0, null, function() {
>
>         fs.writeFile(gpioFile, '' + 1, null, fn.bind(null));
>
>     });
>
> };
>
> fn();
>
> With this new approach, the oscilloscope measured the frequency aroung 
650Hz, so 50% slower. Something was wrong (screenshot: 
http://cs.mene.ro/oscilloscope/NewFile3.bmp). The problem was I was waiting 
for the callback. Switching from the async fs.writeFile to the 
fs.writeFileSync proved to be efficient, the frequency increased to about 
3.6kHz (screenshot: http://cs.mene.ro/oscilloscope/NewFile4.bmp), but this 
is still too slow. I decided to optimize a bit more, and since I am writing 
to the same file every time:

*[3.] FS calls with one descriptor*

> var fs = require('fs');
>
> var gpioFile = '/sys/class/gpio/gpio68/value';
>
> var fd = fs.openSync(gpioFile, 'a');
>
> while(1 == 1) {
>
>     fs.writeSync(fd, '1');
>
>     fs.writeSync(fd, '0');
>
> }
>
> Without requesting the file descriptor on each write, things really 
improved. I was now changing the pin at a rate of 12kHz (screenshot: 
http://cs.mene.ro/oscilloscope/NewFile5.bmp). But this is too slow to write 
an image, so I decided to hack the NodeJS core.

Node's FS functions are meant to be used by regular NodeJS users. The core 
expects users to make mistakes, and over-validates everything they send to 
it. After validating the buffer and creating a wrapper for the callback, 
the FS finally calls process.binding('fs').write(fd, buffer, offset, length, 
position, wrapper); (yes, this is a copy+paste from the node source code)
So i decided to go straight to the binding and write to it:

*[4.] process.binding('fs') calls*

> var fs = require('fs');
>
> var binding = process.binding('fs');
>
> var b0 = new Buffer('0', 0);
>
> var b1 = new Buffer('1', 0);
>
> var gpioFile = '/sys/class/gpio/gpio68/value';
>
> var fd = fs.openSync(gpioFile, 'a');
>
> while(1 == 1) {
>
>     binding.write(fd, b0, 0, 1, 0);
>
>     binding.write(fd, b1, 0, 1, 0);
>
> }
>
> The oscilloscope is now registering 57-62kHz. Yay! So after several 
micro-optimizations, I got close to the target values. I am not sure why 
process.binding should not be called directly by Node users (I assume this 
has something to do with validating stuff before calling), but this made 
things considerably faster (about 5 times faster).

At the moment, I am at the minimum acceptable baud rate. I can write a 
screen in about 2 seconds using Node and a 50+ kHz bit rate. But I know it 
can be faster, since I also tested a partially optimized C application 
which does the same benchmark at about 160kHz, so almost 3x faster than 
Node.

In order to simulate an SPI protocol with Node (eg: piping an image stream 
from a TCP socket to a screen on the embedded device), a higher 
pullup/pulldown frequency would be preferable.

Q1. Is there any way to optimize the calls from [4.] onward? For example, 
can I use malloc from process.binding('smalloc') to jump the buffer 
validation/creation other low-lever functions to make this faster?
Q2. Is there any way to use /dev/mem for gpio  (mmap bindings) in Node?
Q3. The OS and the GC keep interrupting the Node application, and that 
leads to noticeable fluctuations in the data flow (55kHz-62kHz), any 
suggestions to stabilize the flow would also be very welcomed


-- 
-- 
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

--- 
You received this message because you are subscribed to the Google Groups 
"nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to