C# 将 float[] 转换为 byte[] 的最快方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/619041/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the fastest way to convert a float[] to a byte[]?
提问by Nick
I would like to get a byte[]from a float[]as quickly as possible, without looping through the whole array (via a cast, probably). Unsafe code is fine. Thanks!
我想尽快byte[]从 a获取 a float[],而不是遍历整个数组(可能是通过强制转换)。不安全的代码很好。谢谢!
I am looking for a byte array 4 time longer than the float array (the dimension of the byte array will be 4 times that of the float array, since each float is composed of 4 bytes). I'll pass this to a BinaryWriter.
我正在寻找比浮点数组长 4 倍的字节数组(字节数组的维度将是浮点数组的 4 倍,因为每个浮点数由 4 个字节组成)。我将把它传递给一个 BinaryWriter。
EDIT:
To those critics screaming "premature optimization":
I have benchmarked this using ANTS profiler before I optimized. There was a significant speed increase because the file has a write-through cache and the float array is exactly sized to match the sector size on the disk. The binary writer wraps a file handle created with pinvoke'd win32 API. The optimization occurs since this lessens the number of function calls.
编辑:对于那些尖叫“过早优化”的评论家:我在优化之前使用 ANTS 分析器对此进行了基准测试。由于文件具有直写缓存,并且浮点数组的大小与磁盘上的扇区大小完全匹配,因此速度显着提高。二进制编写器包装了使用pinvoke'd win32 API创建的文件句柄。进行优化是因为这减少了函数调用的数量。
And, with regard to memory, this application creates massive caches which use plenty of memory. I can allocate the byte buffer once and re-use it many times--the double memory usage in this particular instance amounts to a roundoff error in the overall memory consumption of the app.
并且,关于内存,该应用程序创建了大量使用大量内存的缓存。我可以分配字节缓冲区一次并重复使用它多次——在这个特定实例中双倍内存使用量相当于应用程序整体内存消耗的舍入误差。
So I guess the lesson here is not to make premature assumptions ;)
所以我想这里的教训不是做出过早的假设;)
采纳答案by Jeremy
If you do not want any conversion to happen, I would suggest Buffer.BlockCopy().
如果您不希望发生任何转换,我建议使用 Buffer.BlockCopy()。
public static void BlockCopy(
Array src,
int srcOffset,
Array dst,
int dstOffset,
int count
)
For example:
例如:
float[] floatArray = new float[1000];
byte[] byteArray = new byte[floatArray.Length * 4];
Buffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);
回答by vladr
Although you can obtain a byte*pointer using unsafeand fixed, you cannot convert the byte*to byte[]in order for the writer to accept it as a parameter without performing data copy. Which you do not want to do as it will double your memory footprint andadd an extra iteration over the inevitable iteration that needs to be performed in order to output the data to disk.
尽管您可以byte*使用unsafeand获取指针fixed,但您不能转换byte*tobyte[]以便编写者在不执行数据复制的情况下将其作为参数接受。你不想这样做,因为这将增加一倍,你的内存占用量,并在不可避免的迭代,要在顺序执行需要输出数据到磁盘添加一个额外的迭代。
Instead, you are still better off iterating over the array of floats and writing each floatto the writer individually, using the Write(double)method. It will still be fast because of buffering inside the writer. See sixlettervariables's numbers.
相反,您仍然最好使用方法迭代浮点数数组并将每个浮点数float单独写入writerWrite(double)。由于写入器内部的缓冲,它仍然会很快。查看sixlettervariables的数字。
回答by Jacob Adams
Although it basically does do a for loop behind the scenes, it does do the job in one line
尽管它基本上确实在幕后进行了 for 循环,但它确实在一行中完成了工作
byte[] byteArray = floatArray.Select(
f=>System.BitConverter.GetBytes(f)).Aggregate(
(bytes, f) => {List<byte> temp = bytes.ToList(); temp.AddRange(f); return temp.ToArray(); });
回答by Jacob Adams
You're better-off letting the BinaryWriter do this for you. There's going to be iteration over your entire set of data regardless of which method you use, so there's no point in playing with bytes.
你最好让 BinaryWriter为你做这件事。无论您使用哪种方法,都会对整个数据集进行迭代,因此使用字节是没有意义的。
回答by user7116
Premature optimization is the root of all evil! @Vlad's suggestion to iterate over each float is a much more reasonable answer than switching to a byte[]. Take the following table of runtimes for increasing numbers of elements (average of 50 runs):
过早优化是万恶之源!@Vlad 建议迭代每个浮点数是比切换到字节 [] 更合理的答案。对于越来越多的元素(平均 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
There is little difference between the two for small numbers of elements. Once you get into the huge number of elements range, the time spent copying from the float[] to the byte[] far outweighs the benefits.
对于少量元素,两者之间几乎没有区别。一旦进入大量元素范围,从 float[] 复制到 byte[] 所花费的时间远远超过好处。
So go with what is simple:
所以用简单的方法:
float[] data = new float[...];
foreach(float value in data)
{
writer.Write(value);
}
回答by Davy Landman
There is a dirty fast (not unsafe code)way of doing this:
有一种肮脏的快速(不是不安全的代码)方式来做到这一点:
[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;
}
This will work, but I'm not sure of the support on Monoor newer versions of the CLR. The only strange thing is that the array.Lengthis the bytes length. This can be explained because it looks at the array length stored with the array, and because this array was a byte array that length will still be in byte length. The indexer does think about the Double being eight bytes large so no calculation is necessary there.
这会起作用,但我不确定是否支持Mono或更新版本的CLR。唯一奇怪的是这array.Length是字节长度。这可以解释为,它查看与数组一起存储的数组长度,并且因为该数组是一个字节数组,因此长度仍为字节长度。索引器确实认为 Double 是 8 个字节大,因此不需要计算。
I've looked for it some more, and it's actually described on MSDN, How to: Create a C/C++ Union by Using Attributes (C# and Visual Basic), so chances are this will be supported in future versions. I am not sure about Mono though.
我已经寻找了更多,它实际上在MSDN 上进行了描述,如何:通过使用属性(C# 和 Visual Basic)创建 C/C++ 联合,因此很可能在未来版本中支持它。虽然我不确定 Mono。
回答by Davy Landman
We have a class called LudicrousSpeedSerialization and it contains the following unsafe method:
我们有一个名为 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;
}
回答by Omer Mor
There is a way which avoids memory copying and iteration.
有一种方法可以避免内存复制和迭代。
You can use a really ugly hack to temporary change your array to another type using (unsafe) memory manipulation.
您可以使用非常丑陋的 hack 使用(不安全的)内存操作将数组临时更改为另一种类型。
I tested this hack in both 32 & 64 bit OS, so it should be portable.
我在 32 位和 64 位操作系统中测试了这个 hack,所以它应该是可移植的。
The source + sample usage is maintained at https://gist.github.com/1050703, but for your convenience I'll paste it here as well:
源代码 + 示例用法在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));
}
}
}
And the usage is:
用法是:
var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
foreach (var b in bytes)
{
Console.WriteLine(b);
}
});

