【发布时间】: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