Java GPIO Frequency Benchmarks


21 posts
by savageautomate » Wed Jan 09, 2013 3:15 pm
I posted an article on benchmarks of GPIO state change frequency running a java test program on different JVMs.
The Oracle JVM turned out to the be clear winner in this speed test.

See the full details here:
http://www.savagehomeautomation.com/projects/raspberry-pi-java-gpio-frequency-benchmarks.html

Image
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by trouch » Wed Jan 09, 2013 7:26 pm
I see Pi4J relies on WiringPi for handling GPIO.
As WiringPi can use both SoC registers and GPIO driver, can you tell me which mode is used for your tests ?

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by savageautomate » Wed Jan 09, 2013 7:38 pm
Pi4J initializes wiringPi using this method:
Code: Select all
wiringPiSetup()
Thus is it not using the "/sys/class/gpio" interface but rather SoC registers directly.

We choose this method in Pi4J because the "wiringPiSetupSys()" initializer has some pin export limitations and we wanted to provide a single and consistent method for working with GPIO from Java.

More details on these initializer methods at:
https://projects.drogon.net/raspberry-pi/wiringpi/functions/

Note: the native C frequency test in the article also uses this same wiringPi initializer.

Thanks, Robert
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by hinkmond » Wed Jan 09, 2013 8:17 pm
Hi Robert,

Nice blog post! Especially how you point out that the Oracle JDK 8 for ARM Early Access Java VM (see: http://jdk8.java.net/fxarmpreview/ ) is so much faster for Java to RPi GPIO access than other OpenJDK-based Java VMs currently out there.

One issue to point out (as you noted) is that the Pi4J library uses JNI to access the WiringPi native C library which incurs intrinsic JNI overhead when converting from Java Strings to Native C function calls as is done in JNI, that in turn slows down the Java GPIO benchmark measurement.

Another approach that may avoid this JNI overhead, is by using Java to directly access the Raspian Linux file handle drivers to the GPIO interface using the RPi /sys/classes/gpio/* and Java File I/O. See my blog post: https://blogs.oracle.com/hinkmond/entry ... dded_gpio4

If you modify the above code in my blog post to not to sleep between flipping the GPIO output on/off and use NIO2 instead of java.io.*, then you could potentially see the Oracle JDK 8 for ARM Early Access approach and even possibly match the Native C benchmark numbers, since the JNI overhead of Pi4J would be avoided, and instead you would allow the Hotspot VM in Oracle JDK 8 for ARM to use the faster JVMI to Native C I/O access that the VM uses instead of JNI that Pi4J uses, along with NIO2 to avoid all that JNI overhead slowness.

Just one idea if anyone out there is ever interested in having Java run even faster accessing GPIO on a RPi with your benchmark.

Great blog, BTW! Keep up the good work!

Thanks,
Hinkmond
Posts: 6
Joined: Wed Jan 09, 2013 7:49 pm
by trouch » Wed Jan 09, 2013 8:27 pm
thanks for precisions. I also choose to include native code in webiopi to get a full GPIO access through registers
I recently made some bench with java too, and I'm considering porting webiopi server to java.
But there is a big difference between 150KHz and 7Mhz !
How is compiled the native part ?
I'd like to see a native loop called from java to understand why.

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by trouch » Wed Jan 09, 2013 11:11 pm
hinkmond wrote:One issue to point out (as you noted) is that the Pi4J library uses JNI to access the WiringPi native C library which incurs intrinsic JNI overhead when converting from Java Strings to Native C function calls as is done in JNI, that in turn slows down the Java GPIO benchmark measurement.

thanks for this post I miss

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by savageautomate » Thu Jan 10, 2013 12:35 am
hinkmond wrote: ...
Another approach that may avoid this JNI overhead, is by using Java to directly access the Raspian Linux file handle drivers to the GPIO interface using the RPi /sys/classes/gpio/* and Java File I/O. ...
Hi Hinkmond,

Thank you for your post and suggestions. The overhead of JNI certainly makes sense to me so I went an re-tested using your suggested method of Java direct access to the "/sys/class/gpio/" interface. However, the results I am seeing seem to still favor the JNI approach ...

Using java.io with the Oracle JDK SE 8 (ARM preview) I am getting about 25kHz.
Image
(click here to see full size image)

Using java.nio with the Oracle JDK SE 8 (ARM preview) I am getting about 2.6kHz.
Image
(click here to see full size image)


So now I am left thinking ... what did I do wrong?

Here are the test programs I am using that are adapted from your code. Can you take a look and see if there is something wrong in my approach?

java.io test program @ http://pastebin.com/1upsTXev
java.nio test program @ http://pastebin.com/PWjx1PKD

Unless maybe accessing GPIO via the driver interface really slows it down more than the overhead of JNI?

Thanks, Robert
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by hinkmond » Thu Jan 10, 2013 12:56 am
savageautomate wrote:
Using java.nio with the Oracle JDK SE 8 (ARM preview) I am getting about 2.6kHz.
Image
(click here to see full size image)


So now I am left thinking ... what did I do wrong?

Here are the test programs I am using that are adapted from your code. Can you take a look and see if there is something wrong in my approach?

java.io test program @ http://pastebin.com/1upsTXev
java.nio test program @ http://pastebin.com/PWjx1PKD

Unless maybe accessing GPIO via the driver interface really slows it down more than the overhead of JNI?

Thanks, Robert


Hi Robert,

Sorry, I wasn't quite clear what I meant by NIO2 when I replied. I meant to suggest NIO Memory Mapped File I/O as the possible faster way to access GPIO. So something like this (not tested yet):

Code: Select all
          String fn = "/sys/class/gpio/gpio" + GpioChannel + "/value";
          RandomAccessFile memoryMappedFile = new RandomAccessFile(fn, "rw");
         
          //Mapping a file into memory
          MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 8);
       
          // Loop forever
          while (true) {
              //Writing into Memory Mapped File
              out.put((byte) '1');
              out.put((byte) '0');
          }


I'm not sure if you might need to flush it each time after the put, but in general the /sys/class/gpio/* file handle mapped directly to Java memory with NIO, should give you faster write access in Java than JNI to the WiringPi pin C library with Pi4J. This might need further investigation if it doesn't still show the correct results.

Unfortunately, I don't have a scope at Oracle. :( Go figure, but we are a software group. :D So, this theory might need some work to sort out.

Thanks for your interest in this! It's cool you are working with an oscilloscope, the RPi, and Java!
Hinkmond
Posts: 6
Joined: Wed Jan 09, 2013 7:49 pm
by savageautomate » Thu Jan 10, 2013 1:04 am
trouch wrote:thanks for precisions. I also choose to include native code in webiopi to get a full GPIO access through registers
I recently made some bench with java too, and I'm considering porting webiopi server to java.
But there is a big difference between 150KHz and 7Mhz !
How is compiled the native part ?
I'd like to see a native loop called from java to understand why.

Trouch,

If you are porting your project to Java, then you may just want to consume the Pi4J library to handle your GPIO implementation. This way you don't have to worry about compiling the native code. Also, Pi4J provides interrupt driven events on GPIO state changes so you don't have to poll for this. This interrupt monitoring takes place in the native part of Pi4J. If you are interested, here is the native C project that Pi4J uses to provide the JNI wrappers to the wiringPi library. https://github.com/Pi4J/pi4j/tree/develop/pi4j-native. I am using Maven to SCP/SSH files to actual Pi hardware to perform the compile on both hard-float and soft-float ABIs.

I bet if you invoke a native loop from Java you will see the same frequency results as my pure native test while the loop is running. You just have the pay for the initial overhead of Java and JNI to invoke the loop. Probably negligible.

If there any real need in your project for super hi-speed frequency on GPIO pins?

Thanks, Robert
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by trouch » Thu Jan 10, 2013 9:55 am
savageautomate wrote:I bet if you invoke a native loop from Java you will see the same frequency results as my pure native test while the loop is running. You just have the pay for the initial overhead of Java and JNI to invoke the loop. Probably negligible.

If there any real need in your project for super hi-speed frequency on GPIO pins?


I was just wondering where was the overhead to explain the difference between oracle jvm and native c code starting from the ground ;)
But Hinkmond added explaination about JNI I did not see.

Thanks for your post and blog article, it's a great complement to http://codeandlife.com/2012/07/03/bench ... pio-speed/
We can see Python is better than OpenJDK 7 but not Oracle JVM.
You will also see there is a difference between C/WiringPi and C/Direct register acces.
I think I will reuse my native code to wrap it with Java as I don't use wiringPi.

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by savageautomate » Thu Jan 10, 2013 10:38 am
trouch wrote:Thanks for your post and blog article, it's a great complement to http://codeandlife.com/2012/07/03/bench ... pio-speed/

Thanks for that link! I had not seen that article.

Regards, Robert
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by trouch » Sat Jan 12, 2013 2:40 pm
I've also made my own bench using matrix computation to see float performance
You may take on look on http://www.raspberrypi.org/phpBB3/viewt ... 31&t=29421
All detail on http://trouch.com/2013/01/12/raspberry-pi-benchmark/

WebIOPi - Raspberry Pi REST Framework to control your Pi from the web
http://store.raspberrypi.com/projects/webiopi
http://code.google.com/p/webiopi/
http://trouch.com
Posts: 308
Joined: Fri Aug 03, 2012 7:24 pm
Location: France
by neilf » Sat Jan 12, 2013 2:49 pm
I know it's not really relevant to a Java thread, but interpreted BBC Basic V, running under RISC OS, toggles an R-Pi GPIO line at >20MHz, so Java and C have a way to go yet.
Posts: 63
Joined: Sun Nov 11, 2012 8:14 am
by Sebioff » Fri Jan 25, 2013 11:47 am
Hi Hinkmond,
I'm interested in using the fastest GPIO speed possible, so I gave your NIO2 suggestion a try.
However, I'm getting an exception:
java.io.IOException: No matching device found
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:835)

at this line:
Code: Select all
memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 8);

Not quite sure what that's supposed to mean and what to do with this - any ideas?

Regards
Sebastian
Posts: 6
Joined: Mon Jan 14, 2013 9:43 pm
by hinkmond » Fri Jan 25, 2013 10:52 pm
Hi Sebastian,

Let me take a look and figure out the right Java code to use with NIO. I recently obtained an oscilloscope for the RPi here! So, I can now reproduce the benchmark.

I'll post when I have more info...

Thanks,
Hinkmond
Posts: 6
Joined: Wed Jan 09, 2013 7:49 pm
by Sebioff » Sun Feb 10, 2013 12:48 pm
That's awesome, thank you!
Posts: 6
Joined: Mon Jan 14, 2013 9:43 pm
by aTao » Fri Feb 22, 2013 10:57 pm
I am working on my own JNI library for GPIO access and have some timings I dont trust.

I first raised the question here: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=33&t=34742&p=294371#p294371

Here's the Java time measurement loops:

Code: Select all
         long now = 0;
         long then = 1;
         long onesies = 2;
         int loop = 1000000;
         setFuction( 16, PIN_OUTPUT);
         for(int rep = 0; rep <10; rep++){
         then = System.nanoTime();
         for(int i = 0; i<loop; i++){
            jpioOut(16, 1);
            jpioOut(16, 0);
         };
         now = System.nanoTime();
         onesies = now - then;
         then = System.nanoTime();
         for(int i = 0; i<loop; i++){
            jpioOut(16, 1);
            jpioOut(16, 0);
            jpioOut(16, 1);
            jpioOut(16, 0);
         };
         now = System.nanoTime();
         System.out.println("time:"+((now - then - onesies)/loop)+"ns / flash");
         };


and the native code just for good measure:
Code: Select all
JNIEXPORT void JNICALL Java_JPIO_jpioOut
  (JNIEnv *env, jobject mama, jint pin, jint data)
{
   if(data){
      *(jpio + 7) = 1 << pin;
   } else {
      *(jpio +10) = 1 << pin;
   };
} //jpioOutput


and here's some output:
Code: Select all
java.vm.vendor;Oracle Corporation
java.vm.version;1.8.0-ea
time:226ns / flash
time:349ns / flash
time:362ns / flash
time:365ns / flash
time:361ns / flash
time:384ns / flash
time:352ns / flash
time:364ns / flash
time:388ns / flash
time:348ns / flash



Now then, heres the bit thats bothering me a bit:, if the timings shown on Mr Savage's blog are all in the 5 to 160 kHz mark, how comes I seem to have ~3MHz ?
Did I miss something?
>)))'><'(((<
User avatar
Posts: 429
Joined: Wed Dec 12, 2012 10:41 am
Location: Swine Town UK
by savageautomate » Sat Feb 23, 2013 3:19 pm
This article (http://codeandlife.com/2012/07/03/benchmarking-raspberry-pi-gpio-speed/) shows that ~14-22 MHz is attainable using Native C. All my Java tests were conducted using JNI wrappers around the WiringPi C library which topped out around 7 MHz. It may be possible that your implementation is using a more efficient/optimized path. If you want to package up the sample test program, I would be happy to run it on my system and scope it to provide independent verification of the speeds you are getting.

WiringPi and Pi4J were not explicitly written to perform highly optimized GPIO switching speeds, but rather written to provide easy to use abstracted interfaces and an easily consumable API.

Thanks, Robert
Robert Savage | Follow me @savageautomate
http://www.pi4j.com | http://www.pislices.com
http://www.savagehomeautomation.com
User avatar
Posts: 184
Joined: Thu Aug 16, 2012 3:20 pm
Location: USA
by hinkmond » Wed Feb 27, 2013 12:15 am
Hi Sebastian,

I'm still working on finding the right Java code to use NIO memory mapped file I/O to the GPIO file handle on the RPi. I'm seeing the same "No device found" bug you are seeing. It works fine on the RPi for any other regular text file on the device, such as /tmp/test.txt that I create with sample text in it and can run my same NIO code to map it into memory. But NIO memory mapping does not like the /sys/class/gpio/* Linux file handles for some reason...

It might have something to do with the Linux driver for turning GPIO pins into a Linux file handle. Will let you know when I find a workaround. If anyone has suggestions of getting MappedByteBuffer to work with an NIO FileChannel to a Linux virtual file handle (pointing to the RPi GPIO pins), please let me know. I'm looking into just using the FileChannel directly without memory mapping it...

Code: Select all
            String filename = "/sys/class/gpio/gpio24/value";
// NOTE: Changing to a test text file works fine
//            String filename = "/tmp/test.txt";

            System.out.println("DEBUG: filename = "+filename);
            RandomAccessFile memoryMappedFile = new
                    RandomAccessFile(filename, "rw");
            memoryMappedFile.seek(0);
            // Map the GPIO file handle into memory
            FileChannel fileChannel = memoryMappedFile.getChannel();
            // ERROR occurs here: No device found
            MappedByteBuffer out = fileChannel.map(
                    FileChannel.MapMode.PRIVATE, 0, fileChannel.size());


hinkmond wrote:Hi Sebastian,

Let me take a look and figure out the right Java code to use with NIO. I recently obtained an oscilloscope for the RPi here! So, I can now reproduce the benchmark.

I'll post when I have more info...

Thanks,
Hinkmond
Posts: 6
Joined: Wed Jan 09, 2013 7:49 pm
by Williama1337 » Mon Aug 05, 2013 8:39 pm
hinkmond wrote:Hi Sebastian,

Let me take a look and figure out the right Java code to find a Malvern dentist to show your fillings. use with NIO. I recently obtained an oscilloscope for the RPi here! So, I can now reproduce the benchmark.

I'll post when I have more info...

Thanks,
Hinkmond

Just got a RPi oscilloscope as well, looking forward to posting my benchmarks!
Last edited by Williama1337 on Tue Aug 13, 2013 9:56 am, edited 1 time in total.
Posts: 1
Joined: Mon Aug 05, 2013 8:35 pm
by hinkmond » Mon Aug 05, 2013 10:30 pm
I posted an update at: https://blogs.oracle.com/hinkmond/entry ... scope_and7 More updates to come...
Posts: 6
Joined: Wed Jan 09, 2013 7:49 pm