将float 转换为byte 的最快方法是什么?
我想尽可能快地从float[]
获得一个byte[]
,而不需要遍历整个数组(可能是通过cast)。 不安全的代码是好的。 谢谢!
我正在寻找比浮点数组长4倍的字节数组(由于每个浮点数由4个字节组成,所以字节数组的维将是浮点数组的4倍)。 我会把这个传给一个BinaryWriter。
编辑 :对那些批评尖叫“不成熟的优化”:我已经使用ant探查器基准化之前,我优化。 由于该文件具有直写caching,浮点数组的大小与磁盘上的扇区大小相匹配,因此速度显着提高。 二进制编写器包装一个用pinvoke
win32 API创build的文件句柄。 发生优化,因为这减less了函数调用的次数。
而且,就内存而言,这个应用程序创build了大量的使用大量内存的caching。 我可以分配一次字节缓冲区并多次重复使用它 – 这个特定实例中的双重内存使用量在应用程序的整个内存消耗中相当于一个舍入错误。
所以我想这里的教训是不要过早的假设;)
如果你不想进行任何转换,我会build议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);
过早的优化是万恶之源! @弗拉德的build议迭代每个浮点数是一个比切换到一个字节[]更合理的答案。 采取下面的运行时间表来增加元素数量(平均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); }
有一个肮脏的快速(而不是不安全的代码)这样做的方式:
[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; }
这将工作,但我不确定对单声道或更新版本的CLR的支持 。 唯一奇怪的是array.Length
是字节长度。 可以这样解释,因为它查看了与数组一起存储的数组长度,并且因为这个数组是字节数组,长度仍然是字节长度。 索引器确实认为Double是八个字节,所以在这里不需要计算。
我已经找了一些,它实际上描述了MSDN , 如何:通过使用属性(C#和Visual Basic)创build一个C / C + +的联盟 ,所以这个机会将在未来的版本中支持。 虽然我不确定Mono。
有一种避免内存复制和迭代的方法。
你可以使用一个非常丑陋的黑客来临时改变你的数组使用(不安全)内存操作的另一种types。
我在32位和64位操作系统上testing了这个破解,所以它应该是可移植的。
源代码+示例用法维护在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<byte[]> 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<float[]> 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<TSrc,TDst>(this TSrc[] array, Action<TDst[]> 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); } });
让你的BinaryWriter 为你做这个更好。 无论使用哪种方法,都会遍历整个数据集,因此在字节播放中没有任何意义。
虽然您可以使用unsafe
和fixed
方式获取byte*
指针,但是您无法将byte*
转换为byte[]
,以便写入程序在不执行数据复制的情况下将其作为参数接受。 这是你不想做的,因为它会使你的内存占用量增加一倍, 并且为了将数据输出到磁盘而需要执行的不可避免的迭代增加一个额外的迭代。
相反,使用Write(double)
方法可以更好地迭代浮点数组,并将每个float
单独 Write(double)
。 由于作者内部的缓冲,它仍然会很快。 看到sixlettervariables
的数字。
我们有一个名为LudicrousSpeedSerialization的类,它包含以下不安全的方法:
static public byte[] ConvertFloatsToBytes(float[] data) { int n = data.Length; byte[] ret = new byte[n * sizeof(float)]; if (n == 0) return ret; unsafe { fixed (byte* pByteArray = &ret[0]) { float* pFloatArray = (float*)pByteArray; for (int i = 0; i < n; i++) { pFloatArray[i] = data[i]; } } } return ret; }
虽然它基本上在幕后做了一个for循环,但它确实在一行中完成了这项工作
byte[] byteArray = floatArray.Select( f=>System.BitConverter.GetBytes(f)).Aggregate( (bytes, f) => {List<byte> temp = bytes.ToList(); temp.AddRange(f); return temp.ToArray(); });