Most efficient way in java to continually read small files into an object

Benjamin H :

tl/dr: I need to keep some values in my app up to date with the values in ~10 small files, but I'm worried reading the value over and over will have a lot of GC overhead. Do I create a bunch of unbuffered file readers and poll them, or is there any way to "map" the values in a file into a java Double that I can re-run a moment later when the value (maybe) changed?

Long version: I've got some physical sensors (Gyroscope, tachometer) which ev3dev helpfully exposes their current values as small files in a virtual filesystem. Like one file called "/sys/bus/lego/drivers/ev3-analog-sensor/angle" that contains 56.26712

Or the next moment it contains 58.9834

And I'd like a value in my app to keep as close in sync with that file as possible. I could have your standard loop containing MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); (from here) but that seems like a lot of allocation overhead if it put it in a fast loop.

Maybe something with a Scanner, or FileChannel inChannel = aFile.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while(inChannel.read(buffer) > 0)...

I haven't found a magic function of KeepInSyncWithFile(myFloatArray, File("./angle", MODE.FILE_TO_VALUE, 10, TimeUnits.MS)

Java 8+

apangin :

Singe you are talking about pseudofiles on /sys virtual filesystem, it's unlikely that the standard WatchService will work for them. In order to get updated values, you need to read these files.

The good news is that you can keep reading in a garbage-free manner, i.e. with no allocation at all. Open the file and allocate the buffer just once, and every time you want to read a value, seek to the beginning of the file and read to an existing preallocated buffer.

Here is the code:

public class DeviceReader implements Closeable {
    private final RandomAccessFile file;
    private final byte[] buf = new byte[512];

    public DeviceReader(String fileName) throws IOException {
        this.file = new RandomAccessFile(fileName, "r");
    }

    @Override
    public void close() throws IOException {
        file.close();
    }

    public synchronized double readDouble() throws IOException {
        file.seek(0);
        int length = file.read(buf);
        if (length <= 0) {
            throw new EOFException();
        }

        int sign = 1;
        long exp = 0;
        long value = 0;

        for (int i = 0; i < length; i++) {
            byte ch = buf[i];
            if (ch == '-') {
                sign = -1;
            } else if (ch == '.') {
                exp = 1;
            } else if (ch >= '0' && ch <= '9') {
                value = (value * 10) + (ch - '0');
                exp *= 10;
            } else if (ch < ' ') {
                break;
            }
        }

        return (double) (sign * value) / Math.max(1, exp);
    }
}

Note that I manually parse a floating point number from a byte[] buffer. It would be much easier to call Double.parseDouble, but in this case you'd have to convert a byte[] to a String, and the algorithm will no longer be allocation free.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=156934&siteId=1