【问题标题】:High Performance Multiplication/Evaluation in C#C# 中的高性能乘法/求值
【发布时间】:2011-07-21 19:39:09
【问题描述】:

对不起,含糊的主题标题;很难简洁地描述我的问题。

我有大量对象(几千个)的集合,定义为...

public class Item
{
    public int ID;
    public float A;
    public float B;
    public float C;
    public float D;
    public float E;
    public float F;
    public float G;
}

如果我为这些浮点字段中的每一个都提供了一个乘数,那么在我的大型集合中找到哪个项目具有最大的浮点数乘以它们的乘数的最快方法是什么。

例如,我目前有类似...

public Item FindLargest(float aMult, float bMult, float cMult, float dMult, float eMult, float fMult, float gMult)
{
    Item largest = null;
    float largestTotal = 0f;
    foreach(Item item in ItemsCollection)
    {
        float total = item.A * aMult + 
                      item.B * bMult + 
                      item.C * cMult + 
                      item.D * dMult + 
                      item.E * eMult + 
                      item.F * fMult + 
                      item.G * gMult;
        if (total > largestTotal)
        {
            largest = item;
            largestTotal = total;
        }
    }
    return largest;
}

缺乏这种性能,所以我想知道是否有什么办法可以提前以这种方式重组数据,以便 FindLargest 调用快得多。我已经这样做了一段时间,性能很好,ItemsCollection 中有大约 40-50 个项目,但现在我的应用程序的不同部分的设计已经改变,作为副产品,我需要处理更大的数据集(~2000ish 而不是~50ish),所以我有兴趣进一步优化它。感谢任何人提供的任何帮助!

编辑:我应该首先提到这一点:我已经在并行化这个,因为所谓的这个已经被高度并行化了。调用它确实是多次调用它,使用许多不同的参数,非常快速。每次在我的应用程序中打开文档中的值发生变化时,这需要调用大约一百次,并且应该感觉“响应”(已经在多个后台线程上进行了所有计算,所以我不是说 UI 锁定) .

编辑 2:在接受的答案中查看我的 cmets。

【问题讨论】:

  • 这对Code Review也有效。
  • 你在尝试实现矩阵乘法吗?
  • @Chadd -- 出于好奇,您现在需要多长时间?我们在谈论什么样的数字范围?
  • 想知道数学家是否会提出数学解决方案
  • @Chadd:为什么你需要调用它大约 100 次?每次的预期输出是否不同?

标签: c# performance matrix transform multiplication


【解决方案1】:

我认为问题不在于您的功能。我用不到 0.1 秒的时间完成了包含 500,000 个项目的功能。

您可能希望找到一种方法来优化调用此函数的代码部分。在该级别使用 PLINQ 应该会产生更好的结果。

【讨论】:

  • @Fun Mun Pieng - 是的,谢谢你这么说。我在 LinqPad 中对此进行了测试,它的运行速度非常快,大约有 1,000,000 个项目。我开始认为我做错了什么!
  • +1。对于该特定任务,该代码几乎与您所能获得的一样好-您必须遍历所有项目,因为无法为此类乘法定义项目之间的顺序,因此任何类型的二进制搜索方法都不起作用。它的调用次数可能比预期的要多,因此会减慢速度。
  • @Alexei Levenkov:谁说过二分搜索?什么叫做超出预期,什么变慢了?
  • @Steven Jeuris:同意我的评论不是最清楚的:)。我提到了二分搜索,因为它是查找 max IF 的最简单方法,可以以某种方式对项目进行排序,但在这种情况下,您不能提前排序,因为顺序本身取决于参数向量。我认为 FindLargest 的调用频率比预期的要高 - 验证/修复这个事实可以比并行执行更快地加快代码速度。
  • 它确实被调用了很多次,而且我已经证实它被调用的次数只是 当前 预期的。如果我找不到加快速度的方法,我会采取某种切角的方法;重新设计它,使其需要被调用的频率减半。
【解决方案2】:

一种选择是使用PLINQ 来利用多个内核。

        var result = (from item in ItemsCollection
                      let total = item.A * aMult + 
                                  item.B * bMult + 
                                  item.C * cMult + 
                                  item.D * dMult + 
                                  item.E * eMult + 
                                  item.F * fMult + 
                                  item.G * gMult
                      select new {item, total}).AsParallel().Max(i => i.total);

【讨论】:

  • @Steven - 似乎这会返回最大值,而不是最大的项目。没有?
  • @Naraen:你说得对,让我们看看我是否可以进一步linqify。 :)
  • 为什么会更快?就是想。无论如何,这不会做同样数量的乘法吗?
  • @jsoldi:PLINQ 应该足够聪明,可以在不同的内核上同时并行处理多个乘法。
  • @jsoldi - 当然可以,但现在多个 CPU 内核同时处理乘法运算,因此您分散了工作负载(而不是一次只做一个)。
【解决方案3】:

将您的数据集划分为 6 个连续范围。将每个范围分配给异步启动的不同线程以计算最大值。完成所有线程后,您将拥有 6 个不同的项目 - 每个范围中的一个。遍历这 6 个以找到整个数据集最大的那个。

您还可以进行进一步的优化。

您可以简单地使用 Microsoft 的 PLINQ 库进行编码,而不是自己启动 .NET 线程

【讨论】:

  • 多线程在这种情况下有何帮助?多线程不会神奇地使长时间运行的进程更快!只有与 CPU 内核一样多的线程才有用。
  • 是的。我假设有多个 CPU/内核。应该明确说明。
  • 对不起,刚刚更新了这个澄清的问题:我已经在并行化这个,因为所谓的这个已经被高度并行化了。
【解决方案4】:

在进行上述乘法运算时,请考虑使用Parallel.ForEach。您还可以考虑将查找表实现为包含 Item.ID 及其总数的 Dictionary。因此,当乘法完成后,您可以使用 LINQ 对总数最大的项目进行排序和提取。比如:

var sortedItems = from item in ItemsTotalsDictionary orderby item.Value descending select item.Key;

【讨论】:

  • 对不起,刚刚更新了这个澄清的问题:我已经在并行化这个,因为所谓的这个已经被高度并行化了。
猜你喜欢
  • 2017-11-06
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-04
  • 1970-01-01
  • 2018-03-24
  • 1970-01-01
相关资源
最近更新 更多