4

我有一个解决方案,我需要非常快速地将对象读入内存,但是二进制流可能会缓存在内存中压缩以节省磁盘 io 上的时间。

我已经修改了不同的解决方案,显然 XmlTextWriter 和 XmlTextReader 不是那么好,内置的二进制序列化也不是。Protobuf-net 非常好,但还是有点太慢了。以下是一些统计数据:

文件大小 XML:217 kb

文件大小二进制:87 kb

压缩二进制:26 KB

压缩的 XML:26 KB

使用 XML (XmlTextReader) 反序列化:8.4 sek

使用二进制反序列化(Protobuf-net):6.2 sek

使用 Binary wo string.interning (Protobuf-net) 反序列化:5.2 sek

使用内存中的二进制反序列化:5.9 Sek

将二进制文件解压到内存的时间:1.8 sek

使用 Xml (XmlTextWriter) 序列化:11 sek

使用二进制序列化(Protobuf):4 sek

使用二进制长度前缀序列化(Protobuf-net):3.8 sek

这让我想到,似乎(如果我错了,请纠正我)反序列化的主要罪魁祸首是实际的字节转换而不是 IO。如果是这样,那么它应该是使用新的并行扩展的候选者。

由于我在二进制 IO 方面有点新手,但在我投入时间解决之前,我会很感激一些输入:)

为简单起见,假设我们要反序列化一个没有可选字段的对象列表。我的第一个想法是简单地存储每个长度前缀。将每个的 byte[] 读入 byte[] 列表并使用 PLINQ 进行 byte[] -> 对象反序列化。

但是,使用这种方法,我仍然需要单线程读取 byte[],所以也许可以将整个二进制流读入内存(顺便说一句,二进制文件有多大?)并在二进制文件的开头存储如何有许多对象,每个对象的长度和偏移量。然后我应该能够创建 ArraySegments 或其他东西并并行进行分块。

那你们怎么看,可行吗?

4

4 回答 4

2

我经常做这样的事情,没有什么比使用 BinaryReader 读取内容更好的了。据我所知,没有比使用 BinaryReader.ReadInt32 读取 32 位整数更快的方法了。

您可能还会发现将其并行化并重新连接在一起的开销太大了。如果您真的想走并行路线,我建议您使用多个线程读取多个文件,而不是使用多个线程读取多个块中的一个文件。

您还可以调整块大小以使其与磁盘块大小相匹配,但是在您的应用程序和磁盘之间存在如此多的抽象级别,这可能会浪费时间。

于 2009-12-23T10:48:09.757 回答
1

这让我想到,似乎(如果我错了,请纠正我)反序列化的主要罪魁祸首是实际的字节转换而不是 IO。

不要假设时间都花在了哪里,给自己找一个分析器并找出答案。

于 2009-12-23T09:52:57.527 回答
1

二进制文件可以被多个线程同时读取。为此,必须使用适当的访问/共享修饰符打开它。然后每个线程都可以在该文件中获得自己的偏移量和长度。因此,并行阅读不是问题。

让我们假设您将坚持简单的二进制格式:每个对象都以它的长度为前缀。知道您可以“滚动”文件并知道放置反序列化线程的偏移量。

反序列化算法可能如下所示:1)分析文件(将其分成几个相对较大的块,块边界应与对象边界一致) 2)产生必要数量的反序列化器线程并“指示”它们以适当的偏移量和长度读取 3)将所有反序列化器线程的结果合并到一个列表中

于 2009-12-22T13:29:33.767 回答
0

当我反序列化大于 1 MB xml 的对象列表时,我使用以下代码反序列化文件 2 秒:

public static List<T> FromXML<T>(this string s) where T : class
        {
            var ls = new List<T>();
            var xml = new XmlSerializer(typeof(List<T>));
            var sr = new StringReader(s);
            var xmltxt = new XmlTextReader(sr);
            if (xml.CanDeserialize(xmltxt))
            {
                ls = (List<T>)xml.Deserialize(xmltxt);
            }
            return ls;
        }

如果对 XML 案例更好,试试这个?

于 2009-12-20T11:53:20.217 回答