Java高效地处理大型数据文件
我需要一种在内存和时间方面高效处理大型二进制数据文件的方法 我正在开发一个多线程Java应用程序,它处理由许多多维数据点组成的大型二进制数据文件。数据点具有相同的维度,每个点约为100KB。数据点的数量大约为10000到100000。在测试阶段,数据文件的大小为几GB,但在将来,它们的大小将达到很多GB 客户在运行应用程序时遇到了内存问题,因此我正在处理一系列数据点,这些数据点减少了处理所需的内存,同时仍提供了良好的性能 Java是项目的一个需求,我们受到客户机系统上当前内存的限制。客户端系统有许多内核,但它是一个共享系统,内存是目前的限制因素 数据点集在我们的应用程序中重复使用。有时点是按顺序处理的。在其他时间,处理点的子集,包括两个不同点的所有组合。在子集内,可以按任何顺序处理点,但子集中的点可以任意相距很远。数据文件是我通过解析文本数据文件并将值写入二进制文件而生成的简单二进制文件。目前,数据是双精度的,所以我将数据点作为一系列双精度连续写入二进制数据文件。(我解析每个文本文件数据点,并将其立即写入二进制文件,而不是将它们全部保存在内存中。)将来,我们可能会处理float、int等数据 我搜索过SO和其他网站。到目前为止,我已经尝试了几种方法,包括根据需要从二进制文件中读取点,但与同时在内存中保留所有数据点的列表相比,性能较差。这对于具有较小维度的较小数量点的测试非常有效,但这些测试的数据集比实际数据集小几个数量级。到目前为止,我尝试过的方法比将所有点都保存在内存中要慢数百倍或数千倍 我已经尝试过直接的ByteBuffers和MappedByteBuffers。最好的方法是我提取了下面相关部分的类。它将二进制数据读入mappedbytebuffer数组。然后,当通过下面的get(int index)方法请求数据点时,该方法加载相关缓冲区,将相关字节读入字节数组,将字节转换为双数组,并创建一个DataPoint对象。我使用了MappedBytebuffer数组,因为无法将整个数据文件放入物理内存。我使用了一个字节数组数组,这样线程就可以有单独的字节数组来读取数据。然后我只同步了对MappedByteBuffer的实际访问,以最小化阻塞。据我对Java类库的理解,缓冲区不是线程安全的,尽管我最近读到一篇文章,声称MappedBytebuffer不需要同步 欢迎任何反馈。特别是,我对MappedBytebuffer的同步感到好奇Java高效地处理大型数据文件,java,io,large-files,binary-data,memory-mapped-files,Java,Io,Large Files,Binary Data,Memory Mapped Files,我需要一种在内存和时间方面高效处理大型二进制数据文件的方法 我正在开发一个多线程Java应用程序,它处理由许多多维数据点组成的大型二进制数据文件。数据点具有相同的维度,每个点约为100KB。数据点的数量大约为10000到100000。在测试阶段,数据文件的大小为几GB,但在将来,它们的大小将达到很多GB 客户在运行应用程序时遇到了内存问题,因此我正在处理一系列数据点,这些数据点减少了处理所需的内存,同时仍提供了良好的性能 Java是项目的一个需求,我们受到客户机系统上当前内存的限制。客户端系统有
final static private int DOUBLE_BYTE_SIZE = Double.SIZE / Byte.SIZE;
public enum DataType {
CHAR,
DOUBLE,
FLOAT,
INT,
LONG,
SHORT;
}
final static private int numberOfBuffers = 8;
private MappedByteBuffer[] buffers = null;
private int bufferSize = -1;
private byte[][] readArray = null;
private DataType dataType;
private int sizeOfVector;
private int byteSizeOfVector;
private File binFile;
private int size = -1;
private int makeList(File binaryFile, DataType argDataType, int numberOfComponents) {
FileInputStream fis = null;
FileChannel fc = null;
try {
dataType = argDataType;
sizeOfVector = numberOfComponents;
fis = new FileInputStream(binaryFile);
fc = fis.getChannel();
long fileSize = fc.size();
switch (dataType) {
case DOUBLE:
byteSizeOfVector = DOUBLE_BYTE_SIZE * sizeOfVector;
break;
default:
break;
}
size = (int) fileSize / byteSizeOfVector;
bufferSize = size / numberOfBuffers;
buffers = new MappedByteBuffer[numberOfBuffers];
long remaining = fileSize;
long position = 0;
int bufferNumber = 0;
while(remaining > 0) {
long length = Math.min(remaining, bufferSize * byteSizeOfVector);
buffers[bufferNumber] = fc.map(MapMode.READ_ONLY, position, length);
position += length;
remaining -= length;
bufferNumber++;
}
readArray = new byte[numberOfBuffers][byteSizeOfVector];
} catch (IOException ex) {
return -1;
} finally {
try {
if(fis != null) {
fis.close();
}
if(fc != null) {
fc.close();
}
} catch (IOException exClose) {
return -1;
}
}
return 0;
}
private static long makeLong(byte[] data) {
if (data == null || data.length != 8) return 0x0;
return (long)(
(long) (0xFF & data[0]) << 56 |
(long) (0xFF & data[1]) << 48 |
(long) (0xFF & data[2]) << 40 |
(long) (0xFF & data[3]) << 32 |
(long) (0xFF & data[4]) << 24 |
(long) (0xFF & data[5]) << 16 |
(long) (0xFF & data[6]) << 8 |
(long) (0xFF & data[7]) << 0
);
}
private static double makeDouble(byte[] data) {
if (data == null || data.length != 8) return 0x0;
return Double.longBitsToDouble(makeLong(data));
}
private static double[] makeDoubleArray(byte[] data) {
if (data == null) return null;
if (data.length % 8 != 0) return null;
double[] doubleArray = new double[data.length / 8];
for (int index = 0; index < dbls.length; index++) {
doubleArray[index] = makeDouble(new byte[] {
data[(index*8)],
data[(index*8)+1],
data[(index*8)+2],
data[(index*8)+3],
data[(index*8)+4],
data[(index*8)+5],
data[(index*8)+6],
data[(index*8)+7],
}
);
}
return doubleArray;
}
@Override
public DataPoint get(int index) {
if(index > size() - 1) {
throw new IndexOutOfBoundsException("Index exceeds length of list.");
} else if(index < 0) {
throw new IndexOutOfBoundsException("Index is less than zero.");
}
int bufferNumber = index / bufferSize;
int bufferPosition = index % bufferSize;
MappedByteBuffer buffer = buffers[bufferNumber];
synchronized (buffer) {
buffer.load();
buffer.position(bufferPosition * sizeOfVector);
buffer.get(readArray[bufferNumber]);
}
switch(dataType) {
case DOUBLE:
return new DoublePoint(makeDoubleArray(readArray[bufferNumber]));
default:
return null;
}
}
final static private int DOUBLE\u BYTE\u SIZE=DOUBLE.SIZE/BYTE.SIZE;
公共枚举数据类型{
烧焦
双重的
浮动
INT,
长的
短的
}
最终静态私有int numberOfBuffers=8;
私有MappedByteBuffer[]缓冲区=null;
私有int bufferSize=-1;
专用字节[][]readArray=null;
私有数据类型数据类型;
私用的国际尺寸显示器;
私有int字节数;
私有文件;
私有整数大小=-1;
私有int生成列表(文件二进制文件、数据类型argDataType、int numberOfComponents){
FileInputStream fis=null;
FileChannel fc=null;
试一试{
数据类型=argDataType;
sizeOfVector=部件数量;
fis=新文件输入流(二进制文件);
fc=fis.getChannel();
long fileSize=fc.size();
交换机(数据类型){
双格:
byteSizeOfVector=双字节大小*sizeOfVector;
打破
违约:
打破
}
size=(int)fileSize/byteSizeOfVector;
bufferSize=大小/缓冲区数量;
缓冲区=新映射的缓冲区[numberOfBuffers];
长剩余=文件大小;
长位置=0;
int bufferNumber=0;
而(剩余>0){
长长度=数学最小值(剩余,缓冲区大小*字节数);
缓冲区[bufferNumber]=fc.map(仅MapMode.READ_,位置,长度);
位置+=长度;
剩余-=长度;
bufferNumber++;
}
readArray=新字节[numberOfBuffers][byteSizeOfVector];
}捕获(IOEX异常){
返回-1;
}最后{
试一试{
如果(fis!=null){
fis.close();
}
如果(fc!=null){
fc.close();
}
}捕获(IOExcepose除外){
返回-1;
}
}
返回0;
}
私有静态long makeLong(字节[]数据){
if(data==null | | data.length!=8)返回0x0;
返回(长)(
(long)(0xFF&data[0])我的2美分:如果它不适合内存,请读取数据点并将其存储在溢出到存储区(文件系统等)的缓存中。是其中之一。缓存维护“fast”的索引检索并将为您管理内存。我在这里看到的唯一问题是ByteBuffer
是否需要同步。答案是“是,”如果您愿意依赖于实现细节并将自己限制为绝对定位和只读访问。但这似乎不是您想要回答的问题。如果我不得不猜测,您真正的问题是“如果我没有足够的RAM来保存数据,我是否可以避免分页的成本”,答案应该是显而易见的。