c# - 读写二进制文件的最快方法

标签 c# file-io binary

我目前正在优化一个应用程序,经常进行的操作之一是读写二进制文件。我需要两种类型的函数:

Set(byte[] target, int index, int value);

int Get(byte[] source, int index);

按大端和小端顺序处理有符号和无符号的 short、int 和 long 需要这些函数。

下面是我做的一些例子,但我需要评估一下优缺点:

第一种方法是使用 Marshal 将值写入 byte[] 的内存,第二种方法是使用普通指针来完成此操作,第三种方法是使用 BitConverter 和 BlockCopy 来完成此操作

unsafe void Set(byte[] target, int index, int value)
{
    fixed (byte* p = &target[0])
    {
        Marshal.WriteInt32(new IntPtr(p), index, value);
    }
}

unsafe void Set(byte[] target, int index, int value)
{
    int* p = &value;
    for (int i = 0; i < 4; i++)
    {
        target[offset + i] = *((byte*)p + i);
    }
}

void Set(byte[] target, int index, int value)
{
    byte[] data = BitConverter.GetBytes(value);
    Buffer.BlockCopy(data, 0, target, index, data.Length);
}

下面是 Read/Get 方法:

第一个是使用 Marshal 从 byte[] 中读取值,第二个是使用普通指针,第三个是再次使用 BitConverter:

unsafe int Get(byte[] source, int index)
{
    fixed (byte* p = &source[0])
    {
        return Marshal.ReadInt32(new IntPtr(p), index);
    }
}

unsafe int Get(byte[] source, int index)
{
    fixed (byte* p = &source[0])
    {
        return *(int*)(p + index);
    }
}

unsafe int Get(byte[] source, int index)
{
    return BitConverter.ToInt32(source, index);
}

需要进行边界检查,但这不是我的问题的一部分......

如果有人能告诉我在这种情况下最好和最快的方法是什么,或者给我一些其他的解决方案,我会很高兴。一个通用的解决方案将是可取的


我刚刚做了一些性能测试,结果如下:

设置 Marshal:45 毫秒,设置指针:48 毫秒,设置 BitConverter:71 毫秒 获取 Marshal:45 ms,获取指针:26 ms,获取 BitConverter:30 ms

似乎使用指针是最快的方法,但我认为 Marshal 和 BitConverter 会进行一些内部检查...有人可以验证这一点吗?

最佳答案

重要:如果您只需要一个字节序,请参阅 wj32/dtb 的指针魔术


就我个人而言,我会直接写入 Stream(可能有一些缓冲),并重新使用我通常认为是干净的共享缓冲区。然后你可以做一些快捷方式并假设索引为 0/1/2/3。

当然不要使用 BitConverter,因为它不能同时用于您需要的小端/大端。我也倾向于只使用位移位而不是不安全等。它实际上是最快的,基于以下内容(所以我很高兴我已经这样做了我的代码 here ,寻找 EncodeInt32Fixed):

Set1: 371ms
Set2: 171ms
Set3: 993ms
Set4: 91ms <==== bit-shifting ;-p

代码:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
static class Program
{
    static void Main()
    {
        const int LOOP = 10000000, INDEX = 100, VALUE = 512;
        byte[] buffer = new byte[1024];
        Stopwatch watch;

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Set1(buffer, INDEX, VALUE);
        }
        watch.Stop();
        Console.WriteLine("Set1: " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Set2(buffer, INDEX, VALUE);
        }
        watch.Stop();
        Console.WriteLine("Set2: " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Set3(buffer, INDEX, VALUE);
        }
        watch.Stop();
        Console.WriteLine("Set3: " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Set4(buffer, INDEX, VALUE);
        }
        watch.Stop();
        Console.WriteLine("Set4: " + watch.ElapsedMilliseconds + "ms");

        Console.WriteLine("done");
        Console.ReadLine();
    }
    unsafe static void Set1(byte[] target, int index, int value)
    {
        fixed (byte* p = &target[0])
        {
            Marshal.WriteInt32(new IntPtr(p), index, value);
        }
    }

    unsafe static void Set2(byte[] target, int index, int value)
    {
        int* p = &value;
        for (int i = 0; i < 4; i++)
        {
            target[index + i] = *((byte*)p + i);
        }
    }

    static void Set3(byte[] target, int index, int value)
    {
        byte[] data = BitConverter.GetBytes(value);
        Buffer.BlockCopy(data, 0, target, index, data.Length);
    }
    static void Set4(byte[] target, int index, int value)
    {
        target[index++] = (byte)value;
        target[index++] = (byte)(value >> 8);
        target[index++] = (byte)(value >> 16);
        target[index] = (byte)(value >> 24);
    }
}

关于c# - 读写二进制文件的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2036718/

相关文章:

java - 在文件中添加员工

java默认Locale是en_US,它使用 ","作为十进制数

c# - 无法隐式转换字符串 - C#

c# - NSURLSession 下载任务 - Xamarin iOS F#

c# - 比较 EFCore 2.1.1 和 .NET Core 2.1 中的 DateTime 对象

C - fwrite - 无法打开文件,因为它包含无效字符

c++ - 二进制数与十进制数的转换

c++ - 给定一个 uint8_t,找出设置了多少位

macos - Mac App Store 二进制拒绝问题

c# - 在不触及 csproj 文件的情况下更改构建目标