【问题标题】:CPU intensive application with Compiler optimization flag turned on/off打开/关闭编译器优化标志的 CPU 密集型应用程序
【发布时间】:2012-06-12 05:09:52
【问题描述】:

我写了一个小程序来比较我的笔记本电脑的性能。为了使程序占用大量 CPU,我使用一些多线程代码(通过 Parallel API 实现)实现了 Rabin-Karp 模式匹配算法。

我注意到,当程序在编译器优化标志关闭的情况下执行时,它所花费的时间比在优化标志打开的情况下执行所花费的时间要多得多。

例如:

  • 所用时间(优化标志关闭时):40 秒(大约)
  • 所用时间(优化标志开启时):18 秒(大约)

我很想知道编译器正在应用什么样的优化来显着提高性能。任何关于如何理解在打开/关闭此标志的情况下执行代码时发生了什么的指针都会非常有帮助。

代码

void Main()
{
    Dictionary<string,bool> collection = new Dictionary<string,bool>();
    IEnumerable<string> commonWords = File.ReadAllLines(@"G:\LINQPad4\words.txt")
        .Where(x => !string.IsNullOrEmpty(x)).Select(t => t.Trim());

    string magna_carta = File.ReadAllText(@"G:\LINQPad4\magna-carta.txt");

    Parallel.ForEach(commonWords,
    () => new Dictionary<string,bool>(),
    (word, loopState, localState) =>
    {
        RabinKarpAlgo rbAlgo = new RabinKarpAlgo(magna_carta,word);
        localState.Add(word,rbAlgo.Match());
        return localState;
    },
    (localState) =>
    {
        lock(collection){
            foreach(var item in localState)
            {
                collection.Add(item.Key, item.Value);
            }
        }
    });

    collection.Dump();
}

public class RabinKarpAlgo
{
    private readonly string inputString;
    private readonly string pattern;
    private ulong siga = 0;
    private ulong sigb = 0;
    private readonly ulong Q = 100007;
    private readonly ulong D = 256;

    public RabinKarpAlgo(string inputString, string pattern)
    {
        this.inputString = inputString;
        this.pattern = pattern;
    }

    public bool Match()
    {
        for (int i = 0; i < pattern.Length; i++)
        {
            siga = (siga * D + (ulong)inputString[i]) % Q;
            sigb = (sigb * D + (ulong)pattern[i]) % Q;
        }

        if(siga == sigb)
            return true;

        ulong pow = 1;
        for (int k = 1; k <= pattern.Length - 1; k++)
            pow = (pow * D) % Q;

        for (int j = 1; j <= inputString.Length - pattern.Length; j++)
        {
            siga = (siga + Q - pow * (ulong)inputString[j - 1] %Q) % Q;
            siga = (siga * D + (ulong)inputString[j + pattern.Length - 1]) % Q;

            if (siga == sigb)
            {
                if (inputString.Substring(j, pattern.Length) == pattern)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

您可以从以下 gitHub 仓库下载相关文件Rabin-Karp Test

文章Performance Testing

【问题讨论】:

  • 作为一种猜测,查看差异的 2 种方法是 1) 比较为这 2 种情况生成的 IL,然后 2) 对这两种情况进行 ngen 并比较生成的程序集。例如,启用调试后,JIT 会跳过许多优化。如果不查看生成的输出,我会认为您只是在猜测?
  • IL 代码在这两种情况下看起来几乎相似,除了在优化的代码中缺少“nop”和其他语句等一些差异。我将尝试“ngen”方法并比较生成的程序集。
  • @JamesManning 我想你不需要ngen,你可以编译代码,启动它然后附加调试器并查看汇编代码。

标签: c# .net multithreading task-parallel-library compiler-optimization


【解决方案1】:

在您的特殊情况下,有 3 个 for 循环,它们位于并行 foreach 中, 我坚信大多数优化都是通过动态循环转换完成的,当然还有数学部分。

这里有一些可以用循环完成的例子:Loop transformation

来自 C# 编译器团队的 Eric Lippert 对此有一篇博客文章: what does the optimize switch do

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 2018-10-07
    相关资源
    最近更新 更多