当前位置:  开发笔记 > 编程语言 > 正文

将float []转换为byte []的最快方法是什么?

如何解决《将float[]转换为byte[]的最快方法是什么?》经验,为你挑选了4个好方法。

我想获得一个byte[]float[]尽可能快地,不通过整个数组循环(通过铸造,可能).不安全的代码很好.谢谢!

我正在寻找比浮点数组长4倍的字节数组(字节数组的维数将是浮点数组的4倍,因为每个浮点数由4个字节组成).我将它传递给BinaryWriter.

编辑:对那些尖叫"过早优化"的评论家:我在优化之前使用ANTS探查器对此进行了基准测试.速度显着提高,因为该文件具有直写缓存,并且浮点数组的大小恰好与磁盘上的扇区大小相匹配.二进制编写器包装使用pinvoke'd win32 API 创建的文件句柄.优化发生,因为这减少了函数调用的数量.

而且,关于内存,这个应用程序创建了大量使用大量内存的缓存.我可以分配一次字节缓冲区并重复使用它多次 - 在这个特定实例中的双内存使用量相当于应用程序整体内存消耗的舍入误差.

所以我想这里的教训不是做出过早的假设;)



1> Davy Landman..:

有一种脏的快速(不安全的代码)方式:

[StructLayout(LayoutKind.Explicit)]
struct BytetoDoubleConverter
{
    [FieldOffset(0)]
    public Byte[] Bytes;

    [FieldOffset(0)]
    public Double[] Doubles;
}
//...
static Double Sum(byte[] data)
{
    BytetoDoubleConverter convert = new BytetoDoubleConverter { Bytes = data };
    Double result = 0;
    for (int i = 0; i < convert.Doubles.Length / sizeof(Double); i++)
    {
        result += convert.Doubles[i];
    }
    return result;
}

这将有效,但我不确定对Mono或更新版本的CLR的支持.唯一奇怪的是,它array.Length是字节长度.这可以解释,因为它查看了与数组一起存储的数组长度,并且因为该数组是一个字节数组,其长度仍然是字节长度.索引器确实认为Double是八个字节大,因此不需要进行计算.

我已经查找了一些,它实际上是在MSDN上描述的,如何:使用属性(C#和Visual Basic)创建一个C/C++联盟,所以很有可能在将来的版本中支持它.我不确定Mono.



2> user7116..:

过早优化是万恶之源!@ Vlad建议迭代每个浮点数是一个比切换到byte []更合理的答案.使用下面的运行时表来增加元素数量(平均50次运行):

Elements      BinaryWriter(float)      BinaryWriter(byte[])
-----------------------------------------------------------
10               8.72ms                    8.76ms
100              8.94ms                    8.82ms
1000            10.32ms                    9.06ms
10000           32.56ms                   10.34ms
100000         213.28ms                  739.90ms
1000000       1955.92ms                10668.56ms

对于少量元素,两者之间几乎没有差别.一旦你进入了大量的元素范围,从float []复制到byte []所花费的时间远远超过了好处.

所以简单地说:

float[] data = new float[...];
foreach(float value in data)
{
    writer.Write(value);
}



3> Omer Mor..:

有一种方法可以避免内存复制和迭代.

你可以使用一个非常丑陋的黑客来使用(不安全的)内存操作临时将你的数组更改为另一种类型.

我在32位和64位操作系统中测试了这个hack,所以它应该是可移植的.

源代码+样本使用情况保留在https://gist.github.com/1050703,但为方便起见,我也将其粘贴在此处:

public static unsafe class FastArraySerializer
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)] public byte[] bytes;
        [FieldOffset(0)] public float[] floats;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    private static readonly UIntPtr BYTE_ARRAY_TYPE;
    private static readonly UIntPtr FLOAT_ARRAY_TYPE;

    static FastArraySerializer()
    {
        fixed (void* pBytes = new byte[1])
        fixed (void* pFloats = new float[1])
        {
            BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
            FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
        }
    }

    public static void AsByteArray(this float[] floats, Action action)
    {
        if (floats.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {floats = floats};
        union.floats.toByteArray();
        try
        {
            action(union.bytes);
        }
        finally
        {
            union.bytes.toFloatArray();
        }
    }

    public static void AsFloatArray(this byte[] bytes, Action action)
    {
        if (bytes.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {bytes = bytes};
        union.bytes.toFloatArray();
        try
        {
            action(union.floats);
        }
        finally
        {
            union.floats.toByteArray();
        }
    }

    public static bool handleNullOrEmptyArray(this TSrc[] array, Action action)
    {
        if (array == null)
        {
            action(null);
            return true;
        }

        if (array.Length == 0)
        {
            action(new TDst[0]);
            return true;
        }

        return false;
    }

    private static ArrayHeader* getHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }

    private static void toFloatArray(this byte[] bytes)
    {
        fixed (void* pArray = bytes)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = FLOAT_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
        }
    }

    private static void toByteArray(this float[] floats)
    {
        fixed(void* pArray = floats)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = BYTE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
        }
    }
}

用法是:

var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
    foreach (var b in bytes)
    {
        Console.WriteLine(b);
    }
});


这个hack正在破坏内部垃圾收集器数据结构.它将导致与C++中使用后免费使用相同类的间歇性崩溃,数据损坏和安全漏洞..NET运行时绝对不支持黑客攻击这样的内部垃圾收集器数据结构.https://github.com/HelloKitty/Reinterpret.Net/issues/1对这个黑客将导致的崩溃进行了长时间的讨论.

4> Jeremy..:

如果您不希望发生任何转换,我建议使用Buffer.BlockCopy().

public static void BlockCopy(
    Array src,
    int srcOffset,
    Array dst,
    int dstOffset,
    int count
)

例如:

float[] floatArray = new float[1000];
byte[] byteArray = new byte[floatArray.Length * 4];

Buffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);


除了遍历*两个*数组(一次复制,一次写入)之外,这还将使内存分配量增加一倍。在速度和内存方面都非常低效。不建议。
您最好只遍历float []数组并为每个float调用Write。该解决方案效率极低。
推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有