8.4 Better lang. support for shorts and bytes Source language memory usage,
source maintainability 8.5 Simple type definitions Source language source maintainability 8.6 Explicit and efficient inlining Source language performance
8.7 An optimising compiler Compiler performance
8.8 Allocating objects on stack Source language, VM (predictable) performance 8.9 Reconsidering adv. language features Source language, VM VM size, complexity,
threads, exceptions, OO, garbage collection and performance
turn these suggestions into working solutions, many of the points raised here could be improved with minor changes to Java, leading to a ’sensor node Java’, much like nesC [30] is a sensor node version of C, but some require more drastic changes.
8.1 A tailored standard library
A minimum Java API for resource-constrained devices, the Connected Limited Device Configuration (CLDC) specification, was proposed by Sun Microsystems [69]. The CLDC was primarily intended for devices larger than typical sensor nodes, and not tailored to the characteristics of typical sensor node code. Providing support for the full CLDC specification would require a substantial amount of memory and programme space for features that are rarely required by sensor node applications. Table 8.2 shows the code size of library support as implemented in the original Darjeeling VM.
The largest mismatch comes from the CLDC’s string support, which takes up over 10 KB. While string support is one of the most basic features one would expect to find in the standard library of any general purpose language, it is rarely required within sensor node applications that usually do not have a UI and only communicate with the outside world through radio messages.
On the other hand, the standard library should include abstractions for typical sensor node operations that are missing from the CLDC. The CLDC Stream abstraction is in-tended to facilitate file, network and memory operations. The abstraction is not well suited
Table 8.2: Size of Darjeeling VM components
Component std.lib VM total
(bytes) (bytes) (bytes)
Core VM 3529 7006 10535
Strings 8467 1942 10409
Interpreter loop 0 10370 10370
Garbage collection 80 3442 3522
Threads 909 2472 3381
Exceptions 1338 818 2156
Math 222 1274 1496
IO 530 680 1210
Total 15075 28004 43079
for communication protocols required by sensor node applications, such as I2C and SPI.
In CLDC, connections between devices can be initiated by specifying URI-like strings.
However, processing these is relatively expensive, and sensor nodes often identify each other using a 16 or 32-bit identifier.
Aslam [7] discusses a method for dead code removal that could be used to remove unused code from a library. The remaining library code becomes part of the application that is uploaded to a device as a whole. While this can be useful to allow developers to use a large library of seldom used functions that will only be included when needed, this is much less efficient compared to a natively implemented standard library, and not possible for library functions to access the hardware.
Therefore we argue a minimal tailored library is necessary that may be efficiently implemented in native code and present on all devices, and that this library should be designed from the ground-up specifically for sensor node applications. Such a library should include functionality for: (i) basic math; (ii) array operations; (iii) a communication API that encapsulates the low-level protocols typically used (e.g. I2C); and (iv) a higher-level generic radio and sensor API abstraction.
Table 8.3: Quantitative impact of Java/JVM issues
Section Measurea B.sort H.sort Bin.Search XXTEA MD5 RC5 FFT Outlier LEC CoreMark MoteTrack HeatCalib HeatDetect
8.2 Size of constant data 200 2,048 51 20,560
Constant array RAM overhead 208 2,056 67 too big
Constant array flash overhead 1,998 26,714 930 too big
8.3 Size of main data structures in C 512 512 200 144 174 256 256 860 1024 1633b 606 644 1088
Size of main data structures in Java 520 520 208 160 214 288 272 884 1058 1996 1387c 676 1158
Size increase 1.6% 1.6% 4.0% 11.1% 23.0% 12.5% 6.3% 2.8% 3.3% 22.2% 128.9% 5.0% 6.4%
8.4 Casts 1 6 5 8 8 8 16 3 10 70 33 4 64
Lines of coded 11 24 16 38 165 27 73 44 77 849 475 51 266
Casts per 100 LOC 9 25 31 21 5 30 22 7 13 8 7 8 24
8.6 Slowdown non-inlined version 69% 57% 25% 37% 20% 13%
Size difference non-inlined version +42 -224 -1502 -94 -20 +48
8.7 Slowdown w/o optimisations 91% 52% 544% 3% 3% 23% 117% 76% 2%
8.8 Slowdown from heap allocations 330% 6% 65%
aA blank entry indicates the benchmark was not affected. Highlights indicate a significant impact.
bActual amount of memory used. CoreMark’s C version allocates 2047 bytes, but the remaining space is not used.
cAfter replacing MoteTrack’s 2-byte RSSI array with two variables.
dCounted as the number of actual code lines, excluding blanks lines, comments, and brackets.
8.2 Support for constant arrays
Constant data is relatively common in sensor node code. In our benchmarks, they appear as the key schedule in the RC5 cipher, a table of precomputed sine wave values for the FFT benchmark, a dictionary of codes in the LEC benchmark, and a database of RSSI signatures in MoteTrack.
Sensor node CPUs differ from desktop systems in the fact that memory is split in a small amount of RAM for volatile data, and a relatively large amount of flash memory for code and constant data. Because Java was not designed for such systems, it has no way to distinguish between the two, and both constant and variable data are always placed in RAM.
There are two problems with Java’s approach: (i) an array of constant data will take up RAM, which is a scarce resource, and (ii) the data is not stored as raw data, but as a sequence of bytecode instructions that initialise each element of an array individually. In the worst case, an array of bytes, this means 7 byte of bytecode are needed for each byte of data, which increases even further after AOT compilation.
An extension that allows developers to place arrays of constant data in flash memory was presented in Section 5.3.5.