【问题标题】:Why does the first access time for ConcurrentDictionary take so much longer than consequent accesses?为什么 ConcurrentDictionary 的第一次访问时间比后续访问时间长得多?
【发布时间】:2020-06-28 12:32:44
【问题描述】:

我有一个想法,尝试一下常见数据结构的访问时间,一个是Dictionary。因此,我做了一个小程序来测试访问数据需要多少滴答声,而不是直接计算值(我知道,这没什么用,只是一个实验)。

但是,我偶然发现了一个有趣的问题。我第一次访问ConcurrentDictionary 时,ElapsedTicks 总是比随后的访问时间长得多。

为什么会这样?

截图:

代码:

public static void Main() {
    var aLimit = 1000;
    var bLimit = aLimit;

    var precomputed = new ConcurrentDictionary<Tuple<int, int>, int>();
    Parallel.For(0, aLimit, a => {
        Parallel.For(0, bLimit, b => {
            while (!precomputed.TryAdd(new Tuple<int, int>(a, b), a * b));
        });
    });

    Console.Write("Press enter to begin.");
    Console.ReadLine();
    var repeats = 5;
    var stopwatch = new Stopwatch();
    var random = new Random();
    for (int i = 0; i < repeats; ++i) {
        stopwatch.Reset();
        var v1 = random.Next(0, aLimit);
        var v2 = random.Next(0, bLimit);

        // precomputed
        stopwatch.Start();
        var precomputedResult = precomputed[new Tuple<int, int>(v1, v2)];
        stopwatch.Stop();
        Console.WriteLine($"[{i}] V: {precomputedResult} | Precomp: {stopwatch.ElapsedTicks}");

        stopwatch.Reset();

        // actual
        stopwatch.Start();
        var actualResult = v1 * v2;
        stopwatch.Stop();
        Console.WriteLine($"[{i}] V: {actualResult} | Actual: {stopwatch.ElapsedTicks}");
    }
}

【问题讨论】:

  • 我认为这是字典访问器被 JIT 化的时候,即由 JIT 从 IL 代码编译为本机代码。
  • 可能是创建初始化 ConcurrentDictionary, int> 的抖动

标签: c# dictionary data-structures concurrency concurrentdictionary


【解决方案1】:

我会说这种行为的主要原因是索引器方法的 JIT 编译。如果您多次运行代码,例如将整个代码包装到另一个 for 循环中 - 您将看到随后的“第一次”访问将具有大致相同的测量值。在我的机器上,我有 4 个周期内第一次迭代的下一个输出:

[0] V: 912780 | Precomp: 6200
[0] V: 912780 | Actual: 1
[0] V: 508870 | Precomp: 78
[0] V: 508870 | Actual: 0
[0] V: 195570 | Precomp: 57
[0] V: 195570 | Actual: 5
[0] V: 67643 | Precomp: 156
[0] V: 67643 | Actual: 1
[0] V: 418966 | Precomp: 63
[0] V: 418966 | Actual: 1

另外,如果您对微基准测试感兴趣,我建议您使用 BenchmarkDotNet,它可以处理很多事情,例如计时器分辨率、预热等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-25
    • 2012-03-12
    • 1970-01-01
    相关资源
    最近更新 更多