【问题标题】:Fastest way to save/load enum[] (byte) to a file将枚举[](字节)保存/加载到文件的最快方法
【发布时间】:2020-06-02 03:53:59
【问题描述】:

数组有重复的元素,它们的顺序很重要(必须保持)。我必须不断地保存/加载数百个这样的文件,每个文件可能包含多达 100,000 个元素的数组。

下面的代码是我当前保存/加载文件的示例。由于 IO 很慢,我通过在序列化之前将枚举转换为字节(将文件大小减少 10 倍)显着提高了速度。我不确定我是否应该使用 BinaryFormatter。

我仍在寻求改进,因为一切都应该尽可能快,有没有比我目前正在做的更好的选择?你会怎么做?

enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }

DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };

public void Save(string path)
{
    BinaryFormatter formatter = new BinaryFormatter();
    FileStream stream = new FileStream(path, FileMode.Create);
    byte[] myDogsInByte = Array.ConvertAll(myDogs, new Converter<DogBreed, byte>(DogBreedToByte));
    formatter.Serialize(stream, myDogsInByte);
    stream.Close();
}

public bool Load(string path)
{
    if (!File.Exists(path))
    {
        return false;
    }

    BinaryFormatter formatter = new BinaryFormatter();
    FileStream stream = new FileStream(path, FileMode.Open);
    byte[] myDogsInByte = formatter.Deserialize(stream) as byte[];
    myDogs = Array.ConvertAll(myDogsInByte, new Converter<byte, DogBreed>(ByteToDogBreed));
    stream.Close();
    return true;
}

private byte DogBreedToByte(DogBreed db)
{
    return (byte)db;
}

private DogBreed ByteToDogBreed(byte bt)
{
    return (DogBreed)bt;
}

编辑:基于 Jeremy 建议的新代码,该代码正在运行,我将尝试测试它的性能并尽快在此处发布结果。

enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }

DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };

public void Save(string path)
{       
    byte[] myDogsInByte = new byte[myDogs.Length];
    Array.Copy(myDogs,myDogsInByte,myDogs.Length);
    File.WriteAllBytes(path, myDogsInByte);
}

public bool Load(string path)
{
    if (!File.Exists(path))
    {
        return false;
    }

    byte[] myDogsInByte = File.ReadAllBytes(path);
    myDogs = (DogBreed[])(object)myDogsInByte;
    return true;
}

【问题讨论】:

  • 你绝对不应该使用BinaryFormatter。只需像读取任何二进制文件一样读取字节,使用原始Stream,然后将读取的每个字节转换为枚举值。播放流的缓冲区大小以获得最佳性能。
  • 您能否详细说明为什么这是最好的方法?我在其他地方读过有关使用 File.WriteAllBytes 和 File.ReadAllBytes 的信息。我对序列化性能几乎一无所知。
  • 这个stackoverflow.com/a/30740756/4139809 建议你可以myDogs = (DogBreed[])(object)myDogsInByte; ?
  • “对象必须是基元数组”。嗯,所以文件流不喜欢被enum[] 欺骗。所以你可以使用; var bytes = new byte[myDogs.Length]; Array.Copy(myDogs, bytes, myDogs.Length); 从枚举复制到字节。
  • 抱歉回复晚了,我根据您的建议更新了问题。将尝试衡量性能并稍后在此处发布。谢谢你的一切!

标签: c# optimization serialization


【解决方案1】:

如果您尝试将byte[] 直接分配给枚举数组,C# 编译器会报错。运行时不在乎。

var bytes = File.ReadAllBytes(path);
myDogs = (DogBreed[])(object)bytes;

VS 调试器会显示 myDogs 确实是一个字节数组,但是从数组中访问一个元素就可以了。

更新;

ArgumentException: 对象必须是基元数组。

所以File.WriteAllBytes() 不喜欢被enum[] 欺骗。您应该能够使用Array.Copy 将枚举值快速复制到byte[] 中。

   var buffer = new byte[myDogs.Length];
   Array.Copy(myDogs, buffer, myDogs.Length);
   File.WriteAllBytes(path, buffer);

当然,这不是一个免费的操作,但即使对于大型数组,它也应该相当快。

【讨论】:

  • 对,数组元素。我就是这个意思。
  • 这只是将整个文件读取到字节数组的最简单的api。 stream.Read() 不必返回您要求的字节数。
  • 当然,如果您实际上并不需要一次将整个文件存储在内存中,您可以重构您的 api,以便您可以将元素导入和导出。
  • 如果文件长度不完全是数组长度,那么你做错了。
  • File.WriteAllBytes(myDogsInByte) 应该只写字节数组。但是您使用的是序列化程序,它使用反射来发现类型信息,将该信息嵌入文件中。我的答案中已经存在的另一个线索是,当您以这种方式投射时,数组的运行时类型信息不会改变。但是,它确实允许您只调用读/写函数。
猜你喜欢
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-22
相关资源
最近更新 更多