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.