C# 并行二进制反序列化?

C# 并行二进制反序列化?,c#,serialization,binary-data,C#,Serialization,Binary Data,我有一个解决方案,我需要非常快地将对象读入内存,但是二进制流可能会被缓存在内存中压缩以节省磁盘io上的时间 我尝试过不同的解决方案,显然XmlTextWriter和XmlTextReader都不太好,内置的二进制序列化也不太好。Protobuf网络非常出色,但仍然有点太慢。以下是一些统计数据: XML文件大小:217KB 二进制文件大小:87 kb 压缩二进制:26 KB 压缩XML:26KB 使用XML反序列化(XmlTextReader):8.4 sek 用二进制反序列化(Protobuf-

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

我尝试过不同的解决方案,显然XmlTextWriter和XmlTextReader都不太好,内置的二进制序列化也不太好。Protobuf网络非常出色,但仍然有点太慢。以下是一些统计数据:

XML文件大小:217KB

二进制文件大小:87 kb

压缩二进制:26 KB

压缩XML:26KB

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

用二进制反序列化(Protobuf-net):6.2瑞典克朗

使用二进制wo string.interning(Protobuf net)反序列化:5.2瑞典克朗

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

将二进制文件解压缩到内存中的时间:1.8瑞典克朗

用Xml序列化(XmlTextWriter):11瑞典克朗

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

使用二进制长度前缀序列化(Protobuf net):3.8瑞典克朗

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

由于我在二进制IO方面有点新手,所以在投入时间解决问题之前,我希望能得到一些意见:)

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

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


那个么,你们认为这可行吗?

当我反序列化大于1MB xml的对象列表时,我反序列化les,然后用以下代码执行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;
        }
publicstaticlist FromXML(这个字符串是s),其中T:class
{
var ls=新列表();
var xml=新的XmlSerializer(typeof(List));
var sr=新的StringReader;
var xmltxt=新的XmlTextReader(sr);
if(xml.CanDeserialize(xmltxt))
{
ls=(List)xml.Deserialize(xmltxt);
}
返回ls;
}

如果更适合XML情况,请尝试此方法。

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

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

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

这似乎让我思考 (如果我错了,请纠正我) 反序列化的主要罪魁祸首是 实际的字节转换,而不是 木卫一


不要假设时间花在哪里,给自己找一个剖析器,然后找出原因。

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

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


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

对不起,有些问题我没有得到确切的答案,但在一开始我只是没有意识到设置它。不过,我学到了教训,谢谢你指出这一点。似乎你不能在老问题上设置答案,或者我遗漏了什么?为了支持阿隆,你发布的java字符串比较问题有一些经过深思熟虑的答案…同意,我如何设置正确答案?不管怎样,我很愚蠢,我没意识到你可以点击v符号:)是“sek”秒吗?如果是这样的话,其他地方似乎出了可怕的问题。我们在几分之一秒内反序列化大小为数十MB的文档……xml序列化就是这样工作的,但部分开销可能是大量相对较小的对象,因此对象创建成为一个问题。尽管如此,XML序列化几乎从来没有比二进制更快,而且更详细,这会由于文件ioHi而导致更多时间。我正在研究这样的解决方案,但为了并行地从磁盘读取,我决定先将整个文件检索到字节缓冲区,然后对其进行反序列化/序列化。看起来要快得多,如果您并行地从磁盘读取数据,您将受到磁盘速度的限制。当我完成后,我会在这里发布一些关于我的解决方案的信息,谢谢。对于较小的数据量,将其缓存在内存中更为可行。我建议读取文件,因为这是一个更普遍的解决方案。内存缓存可以作为优化添加(永远不知道有多少数据需要反序列化:)