【问题标题】:How to Convert Primitive[] to byte[]如何将 Primitive[] 转换为 byte[]
【发布时间】:2015-09-08 02:03:25
【问题描述】:

对于原始数组的序列化,我想知道如何将原始 [] 转换为相应的字节 []。 (即 int[128] 到 byte[512],或 ushort[] 到 byte[]...) 目标可以是内存流、网络消息、文件或任何东西。 目标是性能(序列化和反序列化时间),以便能够使用一些流一次性写入一个字节 [],而不是循环遍历所有值,或者使用某些转换器进行分配。

已经探索了一些解决方案:

常规循环写入/读取

//array = any int[];
myStreamWriter.WriteInt32(array.Length);
for(int i = 0; i < array.Length; ++i)
   myStreamWriter.WriteInt32(array[i]);

此解决方案适用于序列化和反序列化,并且比使用标准 System.Runtime.Serialization 与 BinaryFormater 组合来序列化/反序列化单个 int 或其中几个 int 快 100 倍。

但如果 array.Length 包含超过 200/300 个值(对于 Int32),此解决方案会变慢。

演员?

似乎 C# 不能直接将 Int[] 转换为 byte[],或将 bool[] 转换为 byte[]。

BitConverter.Getbytes()

此解决方案有效,但它通过我的 int[] 在每次调用循环时分配一个新字节 []。表演当然很烂

Marshal.Copy

是的,这个解决方案也有效,但问题与之前的 BitConverter 相同。

C++ 破解

因为在 C# 中不允许直接强制转换,我在看到内存中数组长度存储在数组数据开始前 4 个字节后尝试了一些 C++ hack

ARRAYCAST_API void Cast(int* input, unsigned char** output)
{
   // get the address of the input (this is a pointer to the data)
   int* count = input;
   // the size of the buffer is located just before the data (4 bytes before as this is an int)
   count--;
   // multiply the number of elements by 4 as an int is 4 bytes
   *count = *count * 4;
   // set the address of the byte array
   *output = (unsigned char*)(input);
}

以及调用的 C#:

byte[] arrayB = null;
int[] arrayI = new int[128];
for (int i = 0; i < 128; ++i)
   arrayI[i] = i;

// delegate call
fptr(arrayI, out arrayB);

我成功地将我的 int[128] 检索到 C++,切换数组长度,并将正确的地址影响到我的“输出”变量,但 C# 仅检索一个字节 [1] 作为返回。看来我不能轻易破解这样的托管变量。

所以我真的开始认为我想要实现的所有这些转换在 C# 中都是不可能的(int[] -> byte[]、bool[] -> byte[]、double[] -> byte[]。 ..) 无需分配/复制...

我错过了什么?

【问题讨论】:

  • 你能更具体地说明你想要做什么吗?您正在序列化数组?序列化在哪里?硬盘可能是您真正的瓶颈。也许你应该使用byte[] 来保存你的原始数据(那时不需要序列化,但检索数据很棘手)。
  • 我很惊讶常规循环的性能如此糟糕。让我感到震惊的是,它应该更好,仅循环两三百个值听起来不会导致性能问题。或许您应该对此进行进一步调查,而不是将其视为不可避免的性能影响。
  • 关于“C++ hack”的一个注释我会指出,如果你愿意,你可以在 c# 中弄乱指针和东西。我自己没有做过,但如果你想看看msdn.microsoft.com/en-us/library/f58wzh21(VS.80).aspx 可能是一个起点。我在快速处理图像中的位图数据的背景下遇到过它,但考虑到这也只是处理数组,如果你真的需要性能,它可能对你有用。
  • @Sinatr 我已经编辑了我的顶部消息,但目标只是二进制序列化原始值数组(可以是 byte、sbyte、short、ushort、int、sint、long、slong、double ,十进制,日期时间,时间跨度或布尔)尽快。目的地无关紧要,它可以是 MemoryStream、网络消息或文件。
  • @Chris 我忘了说,但我已经尝试过固定块不成功,我什至尝试直接使用 IL 代码进行转换,但结果总是出现 InvalidCastException

标签: c# binary-serialization


【解决方案1】:

Buffer.BlockCopy怎么样?

// serialize
var intArray = new[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var byteArray = new byte[intArray.Length * 4];
Buffer.BlockCopy(intArray, 0, byteArray, 0, byteArray.Length);

// deserialize and test
var intArray2 = new int[byteArray.Length / 4];
Buffer.BlockCopy(byteArray, 0, intArray2, 0, byteArray.Length);
Console.WriteLine(intArray.SequenceEqual(intArray2));    // true

请注意,BlockCopy 仍在幕后进行分配/复制。我相当肯定这在托管代码中是不可避免的,而BlockCopy 可能与它所获得的一样好。

【讨论】:

  • BlockCopy 是否适用于原始值类型数组? (我怀疑是这样)
  • 这可能是一个不错的选择。我今天会试试这个,使用某种静态锁定的 byte[] 缓冲区来避免多次分配并保持线程安全。感谢您的想法
  • 我用这个方法除了十进制类型。
猜你喜欢
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 2014-04-18
  • 2014-09-01
  • 2019-04-04
  • 2010-11-08
相关资源
最近更新 更多