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

Sve*_*ven 11 c# arrays initialization

我有3*.dat文件(346KB,725KB,1762KB),它们填充了一个json-string"big"int-Arrays.

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

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

加快这些对象初始化的不同方法有哪些?

Aid*_*api 9

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

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

您可以访问功能,写入基本数据结构(numbers,string,char,byte[]char[]).

编写一个简单的方法int[](如果是固定大小则不必要)是通过在数组的长度前加上int/long(取决于大小,unsigned实际上没有任何优势,因为数组使用signed数据类型作为它们的长度)存储).然后写下所有的整数.

编写所有整数的两种方法是:
1.简单地遍历整个数组.
2.将其转换为a 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);
Run Code Online (Sandbox Code Playgroud)

我决定将这一切都用于测试,使用10000个整数数组运行10000次测试.

这导致方法一在我的系统上平均消耗888200ns(约0.89ms).
虽然方法2在我的系统上平均消耗568600ns(平均0.57ms).

两次都包括垃圾收集器必须完成的工作.

显然,方法2比方法1快,但可能性较差.

方法1可能比方法2更好的另一个原因是,当处理有限的RAM /极大文件时,方法2需要的RAM数量比您要写入的数据(原始数据int[]byte[]从中转换的数据)少两倍.int[](谈论512MB +),但如果是这种情况,你总是可以制作一个混合解决方案,例如一次写掉128MB.

请注意,方法1也需要这个额外的空间,但是由于它在每个项目的1个操作中被拆分int[],所以它可以更早地释放内存.

像这样的东西,一次写入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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这只是为了写作,阅读的工作方式也不同:P.我希望这能让你在处理非常大的数据文件方面有更多的见解:).


Jon*_*eet 6

如果你只有一堆整数,那么在解析方面使用JSON确实效率很低.您可以有效地使用BinaryReaderBinaryWriter编写二进制文件...但是我不清楚为什么每次创建对象时都需要读取文件.为什么每个新对象都不能保留对已经读过一次的原始数组的引用?或者,如果他们需要改变数据,您可以保留一个"规范源",并在每次创建对象时将该数组复制到内存中.


Joh*_*lph 5

从整数数组创建字节数组的最快方法是使用Buffer.BlockCopy

byte[] result = new byte[a.Length * sizeof(int)];
Buffer.BlockCopy(a, 0, result, 0, result.Length);
// write result to FileStream or wherever
Run Code Online (Sandbox Code Playgroud)

如果将数组的大小存储在第一个元素中,则可以再次使用它进行反序列化。确保所有内容都适合内存,但应查看文件大小。

var buffer = File.ReadAllBytes(@"...");
int size = BitConverter.ToInt32(buffer,0);
var result = new int[size];
Buffer.BlockCopy(buffer, 0, result, result.length);
Run Code Online (Sandbox Code Playgroud)

二进制不是人类可读的,但是绝对比JSON快。