更好/更快的方式来填充C#中的大数组

时间:2011-09-08 09:25:50

标签: c# arrays initialization

我有3 * .dat文件(346KB,725KB,1762KB),这些文件填充了json-string“big”int-Arrays。

每次创建对象(多次)时,我都会使用这三个文件并使用JsonConvert.DeserializeObject将数组反序列化为对象。

我考虑过使用二进制文件而不是json-string,还是可以直接保存这些数组?我不需要使用这些文件,它只是当前保存数据的位置。我很乐意更快地切换到任何东西。

加快这些对象初始化的不同方法是什么?

3 个答案:

答案 0 :(得分:9)

最快的方法是手动序列化数据。

一种简单的方法是创建一个FileStream,然后将其包装在BinaryWriter / BinaryReader中。

您可以访问函数来编写基本数据结构(numbersstringcharbyte[]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确实效率很低。您可以使用BinaryReaderBinaryWriter有效地编写二进制文件...但是我不清楚为什么每次创建对象时都需要读取文件。为什么每个新对象都不能保留对已经读过一次的原始数组的引用?或者,如果他们需要改变数据,您可以保留一个“规范源”,并在每次创建对象时将该数组复制到内存中。

答案 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更快。