java 中持续将小文件读入对象的最有效方法
Most efficient way in java to continually read small files into an object
tl/dr:我需要让我的应用程序中的一些值与 ~10 个小文件中的值保持最新,但我担心一遍又一遍地读取这些值会产生大量 GC 开销.我是创建一堆无缓冲的文件读取器并轮询它们,还是有什么方法可以 "map" 将文件中的值转换为 java Double 以便稍后我可以重新 运行当值(可能)改变时?
长版本:我有一些物理传感器(陀螺仪、转速计),它们 ev3dev 有助于将它们的当前值公开为虚拟文件系统中的小文件。就像一个名为“/sys/bus/lego/drivers/ev3-analog-sensor/angle”的文件包含 56.26712
或者下一刻它包含58.9834
而且我希望我的应用程序中的值尽可能与该文件保持同步。我可以让你的标准循环包含 MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
(来自 here),但如果将其置于快速循环中,这似乎会产生大量分配开销。
也许有扫描仪,或者
FileChannel inChannel = aFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(inChannel.read(buffer) > 0)...
没找到KeepInSyncWithFile(myFloatArray, File("./angle", MODE.FILE_TO_VALUE, 10, TimeUnits.MS)
的神奇功能
Java8+
我不能保证这一点,但是 File Observer 可能值得研究。您可以在您的应用程序中缓存最新值并通过 FileObserver
观察文件以查找是否发生任何修改事件。我个人没有任何使用它的经验,所以我不能肯定地说它是否适用于系统文件。但如果确实如此,那么与在循环中重复查找文件相比,这是一个更好的解决方案。
你说的是 /sys
虚拟文件系统上的伪文件,标准 WatchService
不太可能适用于它们。为了获得更新的值,您需要阅读这些文件。
好消息是您可以 garbage-free 方式继续阅读,即完全没有分配。打开文件并只分配一次缓冲区,每次要读取一个值时,查找文件的开头并读取到现有的预分配缓冲区。
代码如下:
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);
}
}
请注意,我从 byte[]
缓冲区手动解析浮点数。调用 Double.parseDouble
会容易得多,但在这种情况下,您必须将 byte[]
转换为 String
,算法将不再是免费分配的。
tl/dr:我需要让我的应用程序中的一些值与 ~10 个小文件中的值保持最新,但我担心一遍又一遍地读取这些值会产生大量 GC 开销.我是创建一堆无缓冲的文件读取器并轮询它们,还是有什么方法可以 "map" 将文件中的值转换为 java Double 以便稍后我可以重新 运行当值(可能)改变时?
长版本:我有一些物理传感器(陀螺仪、转速计),它们 ev3dev 有助于将它们的当前值公开为虚拟文件系统中的小文件。就像一个名为“/sys/bus/lego/drivers/ev3-analog-sensor/angle”的文件包含 56.26712
或者下一刻它包含58.9834
而且我希望我的应用程序中的值尽可能与该文件保持同步。我可以让你的标准循环包含 MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
(来自 here),但如果将其置于快速循环中,这似乎会产生大量分配开销。
也许有扫描仪,或者
FileChannel inChannel = aFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(inChannel.read(buffer) > 0)...
没找到KeepInSyncWithFile(myFloatArray, File("./angle", MODE.FILE_TO_VALUE, 10, TimeUnits.MS)
Java8+
我不能保证这一点,但是 File Observer 可能值得研究。您可以在您的应用程序中缓存最新值并通过 FileObserver
观察文件以查找是否发生任何修改事件。我个人没有任何使用它的经验,所以我不能肯定地说它是否适用于系统文件。但如果确实如此,那么与在循环中重复查找文件相比,这是一个更好的解决方案。
你说的是 /sys
虚拟文件系统上的伪文件,标准 WatchService
不太可能适用于它们。为了获得更新的值,您需要阅读这些文件。
好消息是您可以 garbage-free 方式继续阅读,即完全没有分配。打开文件并只分配一次缓冲区,每次要读取一个值时,查找文件的开头并读取到现有的预分配缓冲区。
代码如下:
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);
}
}
请注意,我从 byte[]
缓冲区手动解析浮点数。调用 Double.parseDouble
会容易得多,但在这种情况下,您必须将 byte[]
转换为 String
,算法将不再是免费分配的。