【问题标题】:Speeding Up Conversion of Complex type to Another Complex type for Serialization in C#在 C# 中加速将复杂类型转换为另一种复杂类型以进行序列化
【发布时间】:2019-11-21 20:31:48
【问题描述】:

我正在使用 .Net Core 2.1Visual Studio 2017

问题 我正在与未实现 ISerializable 的第三方 dll class 对象合作。我正在尝试将这种类型的 4k+ 对象转换为另一种自定义类型,然后将它们序列化以从 API 传递回 Web 应用程序。将 1000 个对象转换为可序列化类型大约需要 55 秒,API 返回 JSON 的等待时间总计 2:30 分钟。

我的尝试 我尝试启动多个任务以将一批 1000 个 3rd 方对象转换为自定义可序列化类对象,然后将所有任务结果合并到一个列表中进行序列化。我尝试从使用列表切换到使用数组。我尝试使用Parallel.ForEachConcurrentBag 进行转换。无论我尝试哪种方式,似乎都需要大约 50 - 60 秒才能将 1000 个这些对象转换为自定义可序列化类型。我怀疑其中大部分来自于在第 3 方列表的每次迭代中对new 的调用。我还尝试使用 String Builder 进行字符串连接,通过迭代第 3 方列表并通过连接将属性放入字符串中来创建 JSON 数据结果,这对.ToString()的调用似乎也很慢@

如果有人可以在这方面给我一些帮助,我真的很想提高性能时间。我读到使用struct 可能是一个更好的解决方案,但我还没有尝试过。我希望有一种方法可以快速做到这一点并且仍然使用classes

代码示例

//OnBaseSearchDocument is my custom class which is serializable
OnBaseSearchDocument[] items = new OnBaseSearchDocument[sizeOfList];

//The class itself
public class OnBaseSearchDocument
{
    public string Name { get; set; }

    public long DocumentId { get; set; }

    public string DocumentType { get; set; }

    public string DocStorageDate { get; set; }

    public string DocRevisionDate { get; set; }

    public string DocumentDate { get; set; }

    //Switched to using long for dates so I didn't call .ToString() on DateTimes of 3rd party class
    //For the string props above
    public long DocDate { get; set; }

    public long DocReviseDate { get; set; }

    public long DocStoreDate { get; set; }

}

//Trying to convert
//items is an array of custom class type I created that are serializable
//Hyland.Unity.Document is a 3rd party class contained in a dll - Not Serializable
foreach(Hyland.Unity.Document d in dl)
{
    items[counter] = new OnBaseSearchDocument() { DocumentId = d.ID, Name = d.Name, DocumentType = d.DocumentType.Name, DocDate = d.DocumentDate.Ticks, DocReviseDate = d.LatestRevision.Date.Ticks, DocStoreDate = d.DateStored.Ticks };
    counter++;
 }

编辑

所以,我改用struct 而不是class

[Serializable]
public struct StructOnBaseSearchDocument
{
    public string Name { get; set; }

    public long DocumentId { get; set; }

    public string DocumentType { get; set; }

    public string DocStorageDate { get; set; }

    public string DocRevisionDate { get; set; }

    public string DocumentDate { get; set; }
}

for (int i = 0; i < dl.Count; i++)
{
    Structs.StructOnBaseSearchDocument st = new Structs.StructOnBaseSearchDocument()
    {
        DocumentId = dl[i].ID,
        Name = dl[i].Name,
        DocumentType = dl[i].DocumentType.Name,
        DocStorageDate = dl[i].DateStored.ToString(),
        DocRevisionDate = dl[i].LatestRevision.Date.ToString(),
        DocumentDate = dl[i].DocumentDate.ToString()
    };

    foundDocs[counter] = st;
    counter++;
}

我使用秒表来计时 - 在上述循环中将 1000 个 Hyland.Unity.Document 类型的对象转换为自定义 struct 需要 1 分钟。 切换到struct 根本没有提高性能。

更新 因此,我删除了 Date 的字符串设置以及对 DateTimes 上的 .ToString() 的调用,性能提高了 1000 倍,与在 @ 上调用 .ToString() 相比,循环在不到 1 秒的时间内执行了 1 分钟。 987654342@。所以,我想我找到了瓶颈,但我可以在这方面使用一些帮助,因为我仍然需要将这些日期传递回网络应用程序。

更新代码

 //Removed the setting of the string representations of DateTime's in the `struct`
 //no calls to `DateTime.ToString()`
 for (int i = 0; i < dl.Count; i++)
    {
        Structs.StructOnBaseSearchDocument st = new Structs.StructOnBaseSearchDocument()
        {
            DocumentId = dl[i].ID,
            Name = dl[i].Name,
            DocumentType = dl[i].DocumentType.Name
            //DocStorageDate = dl[i].DateStored.ToString(),
            //DocRevisionDate = dl[i].LatestRevision.Date.ToString(),
            //DocumentDate = dl[i].DocumentDate.ToString()
        };

        foundDocs[counter] = st;
        counter++;
    }

【问题讨论】:

  • 您能否提供有关 DLL 的更多信息(可能会发布示例)以及序列化逻辑?
  • @Slothario 在 cmets 中添加了示例代码和解释。
  • 是否可以使用 protobuf 之类的东西,或者您是否坚持使用 JSON?
  • @Slothario 这就是我现在的想法,我只是希望有一种方法可以使用我不知道或在研究中发现的类。
  • 如果您取得了不错的成绩,请在此处再次发布并告诉我进展如何!

标签: c# asp.net-core


【解决方案1】:

(由于我无法发表评论,因此我必须发布建议作为答案)

我认为您需要确定的第一件事是您的瓶颈到底在哪里。猜测是在与dl 的交互中。也许创建它需要很长时间,或者它可能被懒惰地评估(也就是说,每次通过循环,可能会有一些非常昂贵的调用,这是花费时间的地方)。您可以通过以下几种方法确定缓慢的来源: 1. 将部分语句包装在Stopwatch 中。例如,您可能想要计算实现每个项目所需的时间。

var sw = Stopwatch();
foreach(Hyland.Unity.Document d in dl)
{
    // note: the stop is here intentionally to see how long 
    // it takes to materialize each item
    sw.Stop();
    // if this is taking a long time, the bottleneck is (likely) 
    // in whatever the 3rd party .dll is doing
    items[counter] = new OnBaseSearchDocument() { DocumentId = d.ID, Name = d.Name, DocumentType = d.DocumentType.Name, DocDate = d.DocumentDate.Ticks, DocReviseDate = d.LatestRevision.Date.Ticks, DocStoreDate = d.DateStored.Ticks };
    counter++;
    sw.Start();
 }

您也可以包装 OnBaseSearchDocument 的实例化,但如果这是问题所在,我会感到震惊(假设 Hyland.Unity.Document 对象上的属性访问速度很快) 2. 使用分析器(这应该是#1)

【讨论】:

    【解决方案2】:

    所以经过一番调查,我发现问题是调用此属性以获取修订版datetime 值。这非常奇怪,因为它是属性而不是方法,所以我真的不明白是什么导致了所有开销。:

    dl[i].LatestRevision.Date.Ticks, //This is the problem child - apparently calling .LatestRevision
                                     //causes massive overhead, the other two
                                     //DateTimes are no problem at all, loop executes in
                                     //under 1/10th of a second. 
    

    解决方案 如果用户想要查看修订日期,我不再费心返回最新的修订日期并将其设为单数调用,他们单击一个调用 API 的按钮并返回该特定项目的单数值。结案。谢谢大家的意见。

    【讨论】:

      【解决方案3】:

      如果您将其托管在云中,Azure 函数可能是理想的选择。将 DLL 存储在 Blob 存储中,将 DLL 转换任务放入队列中,然后让 Azure 函数从队列中使用。

      您可以轻松地让您的代码一次在数百台机器上运行,而一旦任务完成,您就会停止为它们付费。

      【讨论】:

      • 如果他不是?
      • @Slothario 我不在云端托管。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多