【发布时间】:2020-09-21 22:46:03
【问题描述】:
问:在数组本身中存储大数组的长度有什么好处吗?
说明:
假设我们使用 System.IO.Compression 命名空间的 GZipStream 类来压缩一些大型二进制序列化对象。 输出将是一些压缩字节数组的 Base64 字符串。 在稍后的某个时间点,Base64 字符串被转换回字节数组,并且需要解压缩数据。
在压缩数据时,我们创建了一个新的字节数组,其大小为压缩字节数组的大小 + 4。 在前 4 个字节中,我们存储压缩字节数组的长度/大小,然后将长度和数据块复制到新数组中。这个新数组被转换为 Base64 字符串。
在解压缩时,我们将 Base64 字符串转换为字节数组。 现在我们可以使用 BitConverter 类提取实际压缩数据的长度,该类将从前 4 个字节中提取一个 Int32。 然后我们分配一个字节数组,其长度是我们从前 4 个字节中得到的长度,并让 Stream 将解压缩的字节写入字节数组。
我无法想象这样的事情实际上有什么好处。 它增加了代码的复杂性,需要执行更多的操作。 可读性也降低了。 单独的 BlockCopy 操作应该会消耗太多资源,以至于这根本没有好处,对吧?
压缩示例代码:
byte[] buffer = new byte[0xffff] // Some large binary serialized object
// Compress in-memory.
using (var mem = new MemoryStream())
{
// The actual compression takes place here.
using (var zipStream = new GZipStream(mem, CompressionMode.Compress, true)) {
zipStream.Write(buffer, 0, buffer.Length);
}
// Store compressed byte data here.
var compressedData = new byte[mem.Length];
mem.Position = 0;
mem.Read(compressedData, 0, compressedData.Length);
/* Increase the size by 4 to accommadate for an Int32 that
** will store the total length of the compressed data. */
var zipBuffer = new byte[compressedData.Length + 4];
// Store length of compressedData array in the first 4 bytes.
Buffer.BlockCopy(compressedData, 0, zipBuffer, 4, compressedData.Length);
// Store the compressedData array after the first 4 bytes which store the length.
Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, zipBuffer, 0, 4);
return Convert.ToBase64String(zipBuffer);
}
解压示例代码:
byte[] zipBuffer = Convert.FromBase64String("some base64 string");
using (var inStream = new MemoryStream())
{
// The length of the array that was stored in the first 4 bytes.
int dataLength = BitConverter.ToInt32(zipBuffer, 0);
// Allocate array with specific size.
byte[] buffer = new byte[dataLength];
// Writer data to buffer array.
inStream.Write(zipBuffer, 4, zipBuffer.Length - 4);
inStream.Position = 0;
// Decompress data.
using (var zipStream = new GZipStream(inStream, CompressionMode.Decompress)) {
zipStream.Read(buffer, 0, buffer.Length);
}
... code
... code
... code
}
【问题讨论】:
-
I can't image that something like this actually has any benefit at all.当涉及数据流时,它会受益。您读取了流的开头 - 它告诉您在这个数据“块”结束之前要读取多少字节(例如可变长度字符串)。我相信 protobuf 是这样工作的,例如,在处理Length-delimited时,你知道这个数据“块”何时结束。 -
mem.Read(compressedData, 0, compressedData.Length);这行代码让我很担心。看起来您假设如果您要求它读取一定数量的字节,那么它就会这样做。在处理流时,这是一个危险的(读作:愚蠢的)假设。你真的应该检查那个调用的返回值。 -
你所做的就像很多 Linux/Unix 操作系统创建结构一样。对于二进制数据,字节数组使用长度属性进行处理,因此当数据从一个应用程序移动/发送到另一个应用程序时,系统的接收端知道数据在哪里结束。流(管道)的接收端没有包含字节数的对象。
-
数据有长度吗?如果这是一个好主意,您应该询问任何网络协议。对于每个帧、包和数据报格式,答案都是“是”。如果不出意外,只是为了有办法避免对数据进行缓冲区溢出攻击。
标签: c# arrays performance compression large-data