代码之家  ›  专栏  ›  技术社区  ›  akappa

在并行计算机上启动应用程序时的性能问题

  •  0
  • akappa  · 技术社区  · 17 年前

    我有一个非常奇怪的问题:

    我有一个应用程序可以并行启动一些worker:

    for (it = jobList.begin(); it != jobList.end(); it++) {
        DWORD threadId;
        Job job = *it;
        Worker *worker = new Worker(job);
        workers[i] = worker;
        threads[i++] = CreateThread((LPSECURITY_ATTRIBUTES)NULL, (DWORD)0, &launchThread, worker, (DWORD)0, &threadId);
    }
    WaitForMultipleObjects((DWORD)jobList.size(), threads, (BOOL)true, (DWORD)INFINITE);
    

    它们分配了很多东西,所以我假设它们在新的平台上同步,但这是它们最终相互同步的唯一地方。

    当我在单核机器上运行应用程序时,一切正常;当我在多核机器上启动应用程序时,性能得到了提高 ,更糟的是:

    for (it = jobList.begin(); it != jobList.end(); it++) {
        DWORD threadId;
        Job job = *it;
        Worker *worker = new Worker(job);
        workers[i] = worker;
        threads[i++] = CreateThread((LPSECURITY_ATTRIBUTES)NULL, (DWORD)0, &launchThread, worker, (DWORD)0, &threadId);
        WaitForSingleObject(threads[i-1], (DWORD)INFINITE);
    }
    

    编辑 :

    我进行了一些测试,发现:

    1. 用最先进的并行分配器更改分配器没有帮助
    2. 在有内核的机器上,多线程应用程序的结果更好 与双xeon(具有不同缓存的两个处理器)相比,双xeon(具有共享二级缓存的两个内核)具有2个duo。

    我认为我手中有一个内存访问瓶颈的应用程序,但是。。。我如何检查这是否真的是问题所在,或者我应该看看其他地方?

    4 回复  |  直到 17 年前
        1
  •  1
  •   Eran    17 年前

    在寻找同步时,联锁*功能有时会被置于雷达之下。它们相当快,但它们确实会强制进行一些同步和CPU缓存更新,这会降低您的速度。话虽如此,人们可能不得不使用这些函数 很多 以获得您描述的影响。

    没有进一步的细节,我建议分析工作线程,深入到代码的冗长部分,并最终确定瓶颈。鉴于效果显著,瓶颈应该是显而易见的。如果您找到了位置,但没有找到原因,请使用导致问题的代码的实际部分更新您的问题。

        2
  •  1
  •   Max Lybbert    17 年前

    你的个人 Job s呼叫 new 刚出现的 几乎总是线程安全的,但通常 this safety comes at a huge performance penalty malloc ,但(1)同样的问题困扰着我们 刚出现的 ,及(2) 刚出现的 马洛克 ).

    new Worker(job) 如果没有清除代码,则失败。当然,为了发布示例,您可能已经删除了该代码,或者这可能是整个程序,您依赖于操作系统来清理。你可以考虑使用 Scope Guard -我喜欢这个解决方案。

    总的来说,我建议大家看看英特尔的产品 Threading Building Blocks 还是Windows的 thread pool 。这些应该处理许多其他棘手的细节(创建多少线程、如何公平地调度、为避免缓存未命中而提供哪些数据等)。

        3
  •  1
  •   Mukul Aggarwal Mukul Aggarwal    17 年前

    在创建任何工作线程之前,您可以尝试执行所有动态内存分配。动态内存分配访问需要关键段访问的堆。目前新的分配将做什么。当您有多个线程在运行时,这些线程将有很多时间执行,如果这些工作线程正在进行一些动态内存分配,那么您的主线程可能会有一点时间分配动态内存,因为它将不得不等待其他线程从新线程中出来。

        4
  •  0
  •   Goz    17 年前

    您正在创建多少线程,有多少内核?每个核心超过2个线程将显著降低性能。

    通常在处理类似这样的问题时,您会设置一个线程池(即双核机器最多4个线程),然后为每个线程分配一个“作业”。当该作业完成时,从作业列表中弹出下一个作业,线程继续处理新作业。在处理完所有作业之前,请一直执行此操作。请记住,池中的线程可以直接从作业列表中获取下一个作业,而不是让工作线程将下一个作业分配给它。

    工作流程如下。

    1) 主线程创建许多工作线程。
    2) 主线程填充作业列表。
    3) 辅助线程检查是否还有其他作业,并从列表中获取下一个作业。
    4) 如果还有工作,转到3。
    5) 完成(或根据您的要求返回到2)。

    解决这个问题还有其他方法,但上面的方法可能是最有效、最容易实现的方法。