【问题标题】:Multiple threads slowing down overall dictionary access?多个线程减慢了整体字典访问速度?
【发布时间】:2010-02-06 04:02:24
【问题描述】:

我正在分析一个 C# 应用程序,它看起来像两个线程在两个单独但相同的字典(只有两个项目)上每个调用 Dictionary<>.ContainsKey() 5000 次,比在单个字典上调用 Dictionary<>.ContainsKey() 的线程慢两倍10000 次。

我正在使用名为 JetBrains dotTrace 的工具测量“线程时间”。我明确地使用了相同数据的副本,所以我没有使用同步原语。 .NET 是否有可能在幕后进行一些同步?

我有一台双核机器,正在运行三个线程:一个使用Semaphore.WaitAll() 阻塞,而工作在优先级设置为ThreadPriority.Highest 的两个新线程上完成。

已经排除了明显的罪魁祸首,例如,实际上没有并行运行代码,并且不使用发布版本。

编辑:

人们想要代码。那么好吧:

    private int ReduceArrayIteration(VM vm, HeronValue[] input, int begin, int cnt)
    {
        if (cnt <= 1)
            return cnt;

        int cur = begin;

        for (int i=0; i < cnt - 1; i += 2)
        {
            // The next two calls are effectively dominated by a call 
            // to dictionary ContainsKey
            vm.SetVar(a, input[begin + i]);
            vm.SetVar(b, input[begin + i + 1]);
            input[cur++] = vm.Eval(expr);
        }

        if (cnt % 2 == 1)
        {
            input[cur++] = input[begin + cnt - 1];
        }

        int r = cur - begin;
        Debug.Assert(r >= 1);
        Debug.Assert(r < cnt);
        return r;
    }

    // From VM
    public void SetVar(string s, HeronValue o)
    {
        Debug.Assert(o != null);
        frames.Peek().SetVar(s, o);
    }

    // From Frame
    public bool SetVar(string s, HeronValue o)
    {
        for (int i = scopes.Count; i > 0; --i)
        {
            // Scope is a derived class of Dictionary
            Scope tbl = scopes[i - 1];
            if (tbl.HasName(s))
            {
                tbl[s] = o;
                return false;
            }
        }
        return false;
    }

现在这里是线程生成代码,可能会延迟:

public static class WorkSplitter
{
    static WaitHandle[] signals;

    public static void ThreadStarter(Object o)
    {
        Task task = o as Task;
        task.Run();
    }

    public static void SplitWork(List<Task> tasks)
    {
        signals = new WaitHandle[tasks.Count];
        for (int i = 0; i < tasks.Count; ++i)
            signals[i] = tasks[i].done;
        for (int i = 0; i < tasks.Count; ++i)
        {
            Thread t = new Thread(ThreadStarter);
            t.Priority = ThreadPriority.Highest;
            t.Start(tasks[i]);
        }
        Semaphore.WaitAll(signals);
    }        
}

【问题讨论】:

  • 您能否解释一下您尝试使用这些线程和字典解决的实际问题...
  • 写一个解释器,我需要并行化某些向量操作。
  • 已发表,感谢您的建议。

标签: c# .net performance multithreading


【解决方案1】:

即使字典中有任何锁定(没有),它也不会影响您的测量,因为每个线程都使用单独的线程。运行这个测试 10,000 次不足以获得可靠的时序数据, ContainsKey() 只需要 20 纳秒左右。您至少需要数百万次才能避免计划工件。

【讨论】:

  • 问题是我需要它在 10,000 次迭代中快速。获得更多迭代的时间,将针对我不关心的情况进行优化。我只是在尝试充分利用第二个核心进行操作,我知道理论上应该是可并行的。
  • 当线程只执行 200 微秒时,我看不出你怎么能期望并行操作。您必须很幸运才能同时启动两个线程。
  • 线程运行的时间比这长得多,整个测试需要大约 100 毫秒才能在没有仪器的情况下运行。我的分析器说第二个最热门的热点是 Dictionary 内的“FindEntry”。它说在一个线程的测试中花费了 296 毫秒,在两个线程的测试中花费了 577 毫秒。这在数十次试验中确实是一致的,让我大吃一惊。
  • 我怀疑在您提供可识别和可运行的代码示例之前您会得到一个好的答案。
  • 您是否尝试过在相同线程上多次迭代运行相同的测试?例如,启动单线程并运行 10k 查询 100 次,然后启动两个线程并运行它们的 5k 查询 100 次。这将使您更好地了解初始化后的吞吐量。还有,这真的是多核机器吗?如果不是,那么我完全期待这个结果。
猜你喜欢
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多