Friday Oct 31, 2014

USB Device Access for Java SE and OSGi

One of the challenges in creating the content for the Java One 2014 Java SE Embedded Internet of Things Hands-on-Lab concerned interacting, via Java and OSGi, with a USB temperature sensor.   Unfortunately a USB communications API is not part of the Java SE standard (as of this post: Halloween 2014).  So the question is, how can Java/USB communication be established, and furthermore and how does this work within the OSGi framework?

In looking around at some of the available options, we chose the javahidapi as the basis for this connectivity.  As a Java/JNI wrapper around the C/C++ HID API for Linux, MacOS X and Windows, the appeal behind this API is that using it does not require the use of a custom driver for each device on each platform.

In order to operate within an OSGi framework (in this case Apache Felix 4.4), javahidapi's open source code has been slightly modified/enhanced. The end result is that an OSGi bundle is available that can be dropped into standard OSGi frameworks to support USB communication for HID devices.  It does contain a native component, and for the sake of simplicity, we've decided to include a separate jar file for each supported architecture.  For the OSGi enthusiast, here's what the generated MANIFEST.MF file looks like for the Linux/armhf (suitable for the Raspberry Pi) architecture:

Manifest-Version: 1.0
Bnd-LastModified: 1415889978962
Build-Jdk: 1.7.0_51
Built-By: jtconnor
Bundle-Activator: com.codeminders.hidapi.Activator
Bundle-ManifestVersion: 2
Bundle-Name: hidapi OSGi Bundle for Linux/armhf
Bundle-NativeCode: native/linux/armv6l/libhidapi-jni-32.so; osname=Linux; processor=armv6l
Bundle-SymbolicName: com.codeminders.hidapi-armhf
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.codeminders.hidapi;uses:="org.osgi.framework";version="1.0.0"
Import-Package: org.osgi.framework;version="[1.6,2)"
Tool: Bnd-1.50.0

Here are some pre-built hidapi OSGi bundles for popular Linux platforms:

To get a feel for the changes made to the original source, here's what was done:

  1. A NetBeans project was created under the Maven->OSGi Bundle category
  2. The Java source code for javahidapi was placed in the project's src/main/java/com/codeminders/hidapi directory
  3. The architecture specific native library was placed in the project's src/main/resources/native/linux/architecture directory.  For example, the Linux/x86 version of the project places the libhidapi-jni-32.so file in the src/main/resources/native/linux/x86 directory.
  4. An Activator.java class was added in the project's src/main/java/com/codeminders/hidapi directory.  In OSGi, the start() method in this class gets called when this bundle is activated.  It is specified in the bundle's MANIFEST.MF file.
  5. The original ClassPathLibraryLoader.java file was simplified and is currently only appropriate for Linux deployments.
  6. As this is a Maven-based project, the project's pom.xml file was edited (here's what the x86 version looks like), such that at build time it will generate a MANIFEST.MF file similar to the one referenced above.

And here are the associated NetBeans Projects which can be used to build the 4 bundles referenced above.

If you'd like to extend this template to include OSGi bundles for additional architectures, you can start with one of the projects above, clone it, and make the appropriate changes for your new environment.  If the native javahidapi component for your new platform is not available, you'll have to pull down the source for hidapi and build it for inclusion into your project.  If anyone is interested in going through this exercise, I'd be glad to post the fruits of their labor here.

Friday Jun 20, 2014

Java Serial Communications Revisited

Last touched upon in this article dated August 2011, it's high time to revisit the state of Java serial port communications options for the following reasons:

  • As the hype around Internet of Things continues to ratchet up, serial communications are a requirement for a certain class of IoT sensors and devices.
  • RxTx, the platform with arguably the most history, cannot be built with the recently released Java 8 JDK without some modifications.
  • For some time now the website hosting the RxTx project: http://rxtx.qbang.org/wiki/index.php/Main_Page has not been available.
  • Building support for additional specifications like RS-485 and parallel is available in RxTx but was never addressed in the previous serial communications article.
  • An alternative framework called jSSC is gaining in popularity and is worth further discussion.
  • Work in the OpenJDK Device I/O Project is progressing. Among the goals of this project is support for serial communications.

RxTx

In my customer experiences, RxTx, despite its age, still gets mentioned most when considering serial communications with Java.  For whatever reasons, the RxTx project has gone off line, and access to the project source code is not readily available.  Fortunately, we have a copy, and have made a few enhancements such that:

  • The source can now be compiled with a JDK versions 6, 7 and 8.
  • The original article discussed only enough modifications to build the  librxtxSerial.so shared object required for traditional serial communications.  The librxtxParallel.so, librxtxRaw.so, librxtxI2C.so and librxtxRS485.so shared objects could not be built.  With some very slight modifications, these can successfully be built too.  I make absolutely no promises as to their usefulness, but they do compile. :)

The source code is based upon the original 2.1-7r2 version and in this instance is now called 2.1.7r2-Java8.  You can download the source here.  If you want to get a feel for the changes made, take a look at this file:  JAVA8_MODS.txt which can be found in the main directory of the source code.  To build RxTx on your native platform:

   $ tar xvf 2.1.7r2-Java8.tar.gz
   $ cd 2.1.7r2-Java8/
   $ ./configure
   $ make

jSSC

The Java Simple Serial Connector, or jSSC for short, is another open source project that can be found here: http://code.google.com/p/java-simple-serial-connector/.  It is available for a host of processor/OS combinations and, in addition to source availability, comes bundled in binary form too.  Here's a list of supported platforms for its current 2.6.0 release:

Win32 Win64 Linux_x86 Linux_x86_64 Linux_ARM Solaris_x86 Solaris_x86_64 MacOSX_x86 MacOSX_x86_64 MacOSX_PPC MacOSX_PPC64

Like RxTx, it contains a native component but is packaged in a nice transparent fashion.  The one potential challenge here may be in trying to figure out how to support a new platform that isn't on this list.  I didn't have a whole lot of success finding out how to build the binary, and admittedly didn't spend an inordinate amount of time trying to figure it out either.  Nonetheless, this project is gaining in popularity and has a dedicated individual supporting the software.

OpenJDK Device I/O

Finally, a project is underway to treat serial communication and device I/O in general as a first class citizen for the Java SE standard. The wiki can be found here: https://wiki.openjdk.java.net/display/dio/Main. It is based on the work done to provide device I/O to the Java ME 8 platform.  Further solidifying the universality theme of Java 8, the ultimate goal would be to have a consistent device I/O API across both Java SE and Java ME.  If you want to further understand what those APIs look like you can view them here: http://docs.oracle.com/javame/8.0/api/dio/api/index.html.

In conclusion, support for serial communications in Java SE is -- albeit slowly -- progressing.  There are multiple open source projects and commercial alternatives too.  Ideally, it will be great to see a formal API supported by the Java SE Standard.

Monday Mar 17, 2014

An Embedded Java 8 Lambda Expression Microbenchmark

It's been a long road, but Java 8 has finally arrived.  Much has been written and said about all the new features contained in this release, perhaps the most important of these is the introduction of Lambda Expressions.  Lambdas are now intimately integrated into the Java platform and they have the potential to aid developers in the traditionally tricky realm of parallel programming.

Following closely behind, Compact Profiles promise to open up the tremendous benefits of Java Standard Edition compatibility to embedded platforms previously thought to be too small.  Can you see where this is heading?  It might be interesting to use these two technologies simultaneously and see how well they work together.  What follows is the description of a small program and its performance measurements -- a microbenchmark if you will -- that aims to highlight how programming with the new Lambda Expression paradigm can be beneficial not only for typical desktops and servers, but also for a growing number of embedded platforms too.

The Hardware/OS Platform(s)

Of primary interest for this article is the Boundary Devices BD-SL-i.MX6 single board computer.  It is a quad-core ARM® Cortex™-A9 based system with 1GB RAM running an armhf  Debian Linux distribution.  At the time of this article's publication, its list price is US $199.

imx6q_sabrelite_top1

What makes it more interesting is that we'll not only run Java 8 Lambda Expressions on device, we'll do it within the confines of the new Java 8 Compact1 profile.  The static footprint of this Java runtime environment is 10½ MB.

A second system, altogether different in capability and capacity from our embedded device will be used as a means to compare and contrast execution behavior across disparate hardware and OS environments.  The system in question is a Toshiba Tecra R840 laptop running Windows 7/64-bit.  It has a dual-core Intel® Core™ i5-2520M processor with 8GB RAM and will use the standard Java 8 Runtime Environment (JRE) for Windows 64-bit.

The Application

Looking for a sample dataset as the basis for our rudimentary application, this link provides an ideal (and fictional) database of employee records.  Among the available formats, a comma-delimited CSV file is supplied with approximately 300,000 entries.  Our sample application will read this file and store the employee records into a LinkedList<EmployeeRec>.  The EmployeeRec has the following  fields:

public class EmployeeRec {
    private String id;
    private String birthDate;
    private String lastName;
    private String firstName;
    private String gender;
    private String hireDate;
    ...
}

With this data structure initialized, our application is asked to perform one simple task:  calculate the average age of all male employees.

Old School

First off let's perform this calculation in a way that predates the availability of Lambda Expressions.  We'll call this version OldSchool.  The code performing the "average age of all male employees" calculation looks like this:

double sumAge = 0;
long numMales = 0;
for (EmployeeRec emp : employeeList) {
    if (emp.getGender().equals("M")) {
        sumAge += emp.getAge();
        numMales += 1;
    }
}
double avgAge = sumAge / numMales;

Lamba Expression Version 1

Our second variation will use a Lambda expression to perform the identical calculation.  We'll call this version Lamba stream().  The key statement in Java 8 looks like this:

double avgAge = employeeList.stream()
                .filter(s -> s.getGender().equals("M"))
                .mapToDouble(s -> s.getAge())
                .average()
                .getAsDouble();

Lambda Expression Version 2

Our final variation uses the preceding Lambda Expression with one slight modification: it replaces the stream() method call with the parallelStream() method, offering the potential to split the task into smaller units running on separate threads.  We'll call this version Lambda parallelStream(). The Java 8 statement looks as follows:

double avgAge = employeeList.parallelStream()
                .filter(s -> s.getGender().equals("M"))
                .mapToDouble(s -> s.getAge())
                .average()
                .getAsDouble();

Initial Test Results

The charts that follow display execution times of the sample problem solved via our three aforementioned variations.  The left chart represents times recorded on the ARM Cortex-A9 processor while the right chart shows recorded times for the Intel Core-i5.  The smaller the result, the faster, both examples indicate that there is some overhead to utilizing a serial Lambda stream() over and above the old school pre-Lambda solution.  As far as parallelStream() goes, it's a mixed bag.  For the Cortex-A9, the parallelStream() operation is negligibly faster than the old school solution, whereas for the Core-i5, the overhead incurred by parallelStream() actually makes the solution slower.

Without any further investigation, one might conclude that parallel streams may not be worth the effort. But what if performing a trivial calculation on a list of 300,000 employees simply isn't enough work to show the benefits of parallelization?  For this next series of tests, we'll increase the computational load to see how performance might be effected.

Adding More Work to the Test

For this version of the test, we'll solve the same problem, that is to say, calculate the average age of all males, but add a varying amount of intermediate computation.  We can variably increase the number of required compute cycles by introducing the following identity method to our programs:

/*
 * Rube Goldberg way of calculating identity of 'val',
 * assuming number is positive
 */
private static double identity(double val) {
    double result = 0;
    for (int i=0; i < loopCount; i++) {
        result += Math.sqrt(Math.abs(Math.pow(val, 2)));    
    }
    return result / loopCount;

}

As this method takes the square root of the square of a number, it is in essence an expensive identity function. By changing the value of loopCount (this is done via command-line option), we can change the number of times this loop executes per identity() invocation.  This method is inserted into our code, for example with the Lambda ParallelStream() version, as follows:

double avgAge = employeeList.parallelStream()
                .filter(s -> s.getGender().equals("M"))
                .mapToDouble(s -> identity(s.getAge()))
                .average()
                .getAsDouble();

A modification identical to what is highlighted in red above is also applied to both Old School and Lambda Stream() variations.  The charts that follow display execution times for three separate runs of our microbenchmark, each with a different value assigned to the internal loopCount variable in our Rube Goldberg identity() function.

For the Cortex-A9, you can clearly see the performance advantage of parallelStream() when the loop count is set to 100, and it becomes even more striking when the loop count is increased to 500.  For the Core-i5, it takes a lot more work to realize the benefits of parallelStream().  Not until the loop count is set to 50,000 do the performance advantages become apparent.  The Core-i5 is so much faster and only has two cores; consequently the amount of effort needed to overcome the initial overhead of parallelStream() is much more significant.

Downloads

The sample code used in this article is available as a NetBeans project.  As the project includes a CSV file with over 300,000 entries, it is larger than one might expect.  The blogs.oracle.com  site prohibits storing files larger than 2MB in size so this project source has been compressed and split into three parts.  Here are the links:

Just concatenate the three downloaded files together to recreate the original LambdaMicrobench.zip file.  In Linux, the command would look something like this:

$ cat LambdaMicrobench.zip.part? > LambdaMicrobench.zip

Conclusion

A great deal of effort has been put into making Java 8 a much more universal platform.  Our simple example here demonstrates that even an embedded Java runtime environment as small as 10½ MB can take advantage of the latest advances to the platform.  This is just the beginning.  There is lots more work to be done to further enhance the performance characteristics of parallel stream Lambda Expressions.  We look forward to future enhancements.

About

Jim Connors-Oracle

Search

Categories
Archives
« March 2015
SunMonTueWedThuFriSat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    
       
Today