【问题标题】:C# Optimized Merge AlgorithmC# 优化的合并算法
【发布时间】:2019-02-24 20:46:04
【问题描述】:

我有一个数据拉取服务,我的 C# 应用程序通过该服务拉取数据。使用多个作业拉入数据,一旦数据请求完成,数据拉取服务将调用我在应用程序类中实现的notify 方法。

以下是notify方法代码。它只是检查结果是否为非空,然后在新线程中调用mergeResults

public override void notify(List<IFields> results)
{
     if (!results.IsNullOrEmpty())
     {
         Task.Run(() => { mergeResults(results); });
     }
}

我正在使用列表来存储最终合并结果。

List<IFields> mergedResults;

我使用object mergeLock 进行互斥。

这是我正在使用的合并逻辑:

    public void mergeResults(List<IFieldsByPrePost> results)
    {
        lock (mergeLock)
        {
            foreach (var result in results)
            {
                if (mergedResults.Count > 0)
                {
                    var properties = mergedResults.First().getDiffProperties();
                    bool isMatch = false;
                    foreach (var mergedResult in mergedResults)
                    {
                        isMatch = true;
                        foreach (var property in properties)
                        {
                            var value1 = mergedResult.GetType().GetProperty(property).GetValue(mergedResult).ToString();
                            var value2 = result.GetType().GetProperty(property).GetValue(result).ToString();
                            if (value1 != value2) { isMatch = false; break; }
                        }
                        if (isMatch)
                        {
                            mergedResult.Count += result.Count;
                            break;
                        }
                    }
                    if (!isMatch)
                    {
                        mergedResults.Add(result);
                    }
                }
                else
                {
                    mergedResults.Add(result);
                }
            }
        }
    }

上述逻辑可以工作,但是很慢,只要将大量结果传递给方法。

另外,notify 方法被数据拉取服务多次调用,结果集不同,进一步减慢了它的速度。

我正在寻找更好的方法来解决这个问题。

TLDR;这个算法很慢,谁能教我一个让它跑得更快的方法吗?

【问题讨论】:

  • 如果您可以提供minimal reproducible example 并提供示例输入和基于这些示例输入的预期结果,那就太棒了。
  • 首先,您需要使用断点查看哪一行速度较慢,然后重新创建该函数或使用其他函数。
  • 如果您能比very slowlarge set of results 更具体,那就太好了。
  • 我很确定反射方法是导致性能问题的原因之一。为什么你需要它?长操作开头的lock (mergeLock)会阻塞整个程序,所以Task.Run似乎没用
  • 在你的IFieldsByPrePost 类中写一个类似IsMatch(IFieldsByPrePost post){...} 的函数而不是getDiffProperties(); 是一种替代方法吗?

标签: c# algorithm merge


【解决方案1】:

我建议,IFields 和/或 IFieldsByPrePost 派生自

IEquatable<IFields> and/or IEquatable<IFieldsByPrePost>. 

所以你可以用

来测试相等性
IFields fields1;
IFieldsByPrePost fields2;
bool equal = fields1.Equals(fields2);

这样您就可以绕过反射,这会减慢您的代码速度。 那么它只是

foreach (var result in results)
{
  if (!mergedResults.Any(x => x.Equals(result))
  {
    mergedResults.Add(result);
  }
}

我不知道,你在做什么

mergedResult.Count,

所以我省略了。

【讨论】:

  • 感谢您对 IEquatable 的投入。我会试一试并发布结果。
  • 当然,你必须在 Equals(other) 方法中比较项目。而且,如果您只是将您的反射代码放在那里,那将无济于事。但是你应该可以访问所有领域(甚至是同一班级的私人领域)。根据实现 IField 的类的数量,这可能是一项相当大的工作。
  • 它就像一个魅力。随着所有线程完成数据拉取,同时合并也完成。它只是从代码中消除了第三个循环。非常感谢。
  • 考虑将mergedResults 设为HashSet 并使用Contains 而不是Any 来提高大型集的性能。
【解决方案2】:

首先让我印象深刻的是mergeResults 方法不是通用的,所以我不确定为什么需要反射。删除线:

var value1 = mergedResult.GetType().GetProperty(property).GetValue(mergedResult).ToString();
var value2 = result.GetType().GetProperty(property).GetValue(result).ToString();
if (value1 != value2) { isMatch = false; break; }

并使用直接属性:

if(mergedResult.Property1 == result.Property1) { isMatch = false; break; }

可以帮忙。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2011-05-24
    • 2021-01-20
    • 1970-01-01
    相关资源
    最近更新 更多