我有3 * .dat文件(346KB,725KB,1762KB),这些文件填充了json-string“big”int-Arrays。
每次创建对象(多次)时,我都会使用这三个文件并使用JsonConvert.DeserializeObject
将数组反序列化为对象。
我考虑过使用二进制文件而不是json-string,还是可以直接保存这些数组?我不需要使用这些文件,它只是当前保存数据的位置。我很乐意更快地切换到任何东西。
加快这些对象初始化的不同方法是什么?
答案 0 :(得分:9)
最快的方法是手动序列化数据。
一种简单的方法是创建一个FileStream,然后将其包装在BinaryWriter / BinaryReader中。
您可以访问函数来编写基本数据结构(numbers
,string
,char
,byte[]
和char[]
)。
编写int[]
的简单方法(如果它是固定大小则不需要)是通过在数组的长度前加上int / long(取决于大小,unsigned并没有真正给出任何优点,因为数组使用有符号数据类型进行长度存储)。然后写下所有的整数。
写下所有内容的两种方法是:
1.简单地遍历整个阵列
2.将其转换为byte[]
并使用BinaryWriter.Write(byte[])
这些是你如何实现它们的:
// Writing
BinaryWriter writer = new BinaryWriter(new FileStream(...));
int[] intArr = new int[1000];
writer.Write(intArr.Length);
for (int i = 0; i < intArr.Length; i++)
writer.Write(intArr[i]);
// Reading
BinaryReader reader = new BinaryReader(new FileStream(...));
int[] intArr = new int[reader.ReadInt32()];
for (int i = 0; i < intArr.Length; i++)
intArr[i] = reader.ReadInt32();
// Writing, method 2
BinaryWriter writer = new BinaryWriter(new FileStream(...));
int[] intArr = new int[1000];
byte[] byteArr = new byte[intArr.Length * sizeof(int)];
Buffer.BlockCopy(intArr, 0, byteArr, 0, intArr.Length * sizeof(int));
writer.Write(intArr.Length);
writer.Write(byteArr);
// Reading, method 2
BinaryReader reader = new BinaryReader(new FileStream(...));
int[] intArr = new int[reader.ReadInt32()];
byte[] byteArr = reader.ReadBytes(intArr.Length * sizeof(int));
Buffer.BlockCopy(byteArr, 0, intArr, 0, byteArr.Length);
我决定把这一切都用于测试,用10000个整数数组运行测试10000次。
导致方法一在我的系统上平均消耗888200ns(约0.89ms) 虽然方法2在我的系统上平均消耗568600ns(平均0.57ms)。
两次都包括垃圾收集者必须做的工作。
显然,方法2比方法1快,但可能性较差。
方法1可能比方法2更好的另一个原因是因为方法2需要的RAM量是您要写入的数据的两倍(原始int[]
和byte[]
已转换来自int[]
),当处理有限的RAM /超大文件(谈论512MB +)时,尽管如此,你总是可以制作一个混合解决方案,例如一次写掉128MB。 / p>
请注意,方法1也需要这个额外的空间,但因为它在int[]
的每个项目中按1次操作拆分,所以它可以更早地释放内存。
这样的话,一次会写出128MB的int[]
:
const int WRITECOUNT = 32 * 1024 * 1024; // 32 * sizeof(int)MB
int[] intArr = new int[140 * 1024 * 1024]; // 140 * sizeof(int)MB
for (int i = 0; i < intArr.Length; i++)
intArr[i] = i;
byte[] byteArr = new byte[WRITECOUNT * sizeof(int)]; // 128MB
int dataDone = 0;
using (Stream fileStream = new FileStream("data.dat", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
while (dataDone < intArr.Length)
{
int dataToWrite = intArr.Length - dataDone;
if (dataToWrite > WRITECOUNT) dataToWrite = WRITECOUNT;
Buffer.BlockCopy(intArr, dataDone, byteArr, 0, dataToWrite * sizeof(int));
writer.Write(byteArr);
dataDone += dataToWrite;
}
}
请注意,这仅适用于写作,阅读的工作方式也不同:P。 我希望这能让您在处理非常大的数据文件方面有更多的见解:)。
答案 1 :(得分:6)
如果你刚刚获得了一堆整数,那么在解析方面使用JSON确实效率很低。您可以使用BinaryReader
和BinaryWriter
有效地编写二进制文件...但是我不清楚为什么每次创建对象时都需要读取文件。为什么每个新对象都不能保留对已经读过一次的原始数组的引用?或者,如果他们需要改变数据,您可以保留一个“规范源”,并在每次创建对象时将该数组复制到内存中。
答案 2 :(得分:4)
从整数数组创建字节数组的最快方法是使用Buffer.BlockCopy
byte[] result = new byte[a.Length * sizeof(int)];
Buffer.BlockCopy(a, 0, result, 0, result.Length);
// write result to FileStream or wherever
如果将数组的大小存储在第一个元素中,则可以再次使用它来反序列化。确保所有内容都适合内存,但要查看文件大小。
var buffer = File.ReadAllBytes(@"...");
int size = BitConverter.ToInt32(buffer,0);
var result = new int[size];
Buffer.BlockCopy(buffer, 0, result, result.length);
Binary不是人类可读的,但比JSON更快。