当使用内核对象同步运行在不同CPU上的线程时,相对于其他OS,使用Windows Server 2008 R2时是否可能会花费一些额外的运行时间?

编辑:并且从答案中发现,该问题还应包括短语“当以较低的CPU利用率运行时”。我在自己对这个问题的回答中包含了更多信息。

背景

我致力于使用共享内存和信号量在进程之间进行通信的产品(当两个进程在同一台计算机上运行时)。关于Windows Server 2008 R2的性能问题的报告(此后我简称为Win2008R2)使我发现,与其他操作系统相比,在Win2008R2的两个线程之间共享信号量相对较慢。

复制它

我能够通过在两个线程上同时运行以下代码来重现它:

for ( i = 0; i < N; i++ )
  {
  WaitForSingleObject( globalSem, INFINITE );
  ReleaseSemaphore( globalSem, 1, NULL );
  }

使用将双启动到Windows Server 2003 R2 SP2和Windows Server 2008 R2的计算机进行测试,以上代码段在Win2003R2计算机上的运行速度比Win2008R2快7倍(Win2003R2为3秒,Win2008R2为21秒)。

测试的简单版本

以下是上述测试的完整版本:
#include <windows.h>
#include <stdio.h>
#include <time.h>


HANDLE gSema4;
int    gIterations = 1000000;

DWORD WINAPI testthread( LPVOID tn )
{
   int count = gIterations;

   while ( count-- )
      {
      WaitForSingleObject( gSema4, INFINITE );
      ReleaseSemaphore( gSema4, 1, NULL );
      }

   return 0;
}


int main( int argc, char* argv[] )
{
   DWORD    threadId;
   clock_t  ct;
   HANDLE   threads[2];

   gSema4 = CreateSemaphore( NULL, 1, 1, NULL );

   ct = clock();
   threads[0] = CreateThread( NULL, 0, testthread, NULL, 0, &threadId );
   threads[1] = CreateThread( NULL, 0, testthread, NULL, 0, &threadId );

   WaitForMultipleObjects( 2, threads, TRUE, INFINITE );

   printf( "Total time = %d\n", clock() - ct );

   CloseHandle( gSema4 );
   return 0;
}

更多细节

我更新了测试,以强制线程运行一次迭代,并在每个循环处强制切换到下一个线程。每个线程发出信号通知下一个线程在每个循环的末尾运行(循环式)。我还更新了它,以使用自旋锁代替信号量(这是一个内核对象)。

我测试过的所有机器都是64位机器。我将测试大部分编译为32位。如果构建为64位,则总体运行速度会稍快一些,并且会更改一些比率,但最终结果是相同的。除了Win2008R2,我还尝试使用Windows 7 Enterprise SP 1,Windows Server 2003 R2 Standard SP 2,Windows Server 2008(非R2)和Windows Server 2012 Standard。
  • 在单个CPU上运行测试的速度明显更快(通过使用SetThreadAffinityMask设置线程亲和力并使用GetCurrentProcessorNumber进行“强制”测试)。毫不奇怪,在使用单个CPU的所有操作系统上,速度都更快,但是在Win2008R2上,具有内核对象同步功能的多CPU和单CPU之间的比率要高得多。除Win2008R2之外,所有计算机的典型比率是2倍至4倍(在多个CPU上运行要花2至4倍的时间)。但是在Win2008R2上,该比率是9倍。
  • 但是...我无法在所有Win2008R2机器上重现减速情况。我在4上进行了测试,并在其中3处进行了测试。因此,我不禁想知道是否存在某种可能会影响此设置的配置设置或性能调整选项。我已阅读了性能调整指南,仔细研究了各种设置,并更改了各种设置(例如,后台服务与前台应用程序),但行为没有差异。
  • 它似乎不一定与物理内核之间的切换有关。我最初怀疑这与重复访问不同内核上的全局数据的成本有关。但是,当运行使用简单自旋锁进行同步的测试版本(而不是内核对象)时,在所有OS类型上,在不同CPU上运行各个线程的速度都相当快。多CPU信号量同步测试与多CPU自旋锁测试的比率通常为10倍至15倍。但是对于Win2008R2 Standard Edition机器,该比率是30倍。

  • 以下是更新后的测试中的一些实际数字(时间以毫秒为单位):
    +----------------+-----------+---------------+----------------+
    |       OS       | 2 cpu sem |   1 cpu sem   | 2 cpu spinlock |
    +----------------+-----------+---------------+----------------+
    | Windows 7      | 7115 ms   | 1960 ms (3.6) | 504 ms (14.1)  |
    | Server 2008 R2 | 20640 ms  | 2263 ms (9.1) | 866 ms (23.8)  |
    | Server 2003    | 3570 ms   | 1766 ms (2.0) | 452 ms (7.9)   |
    +----------------+-----------+---------------+----------------+
    

    测试中的2个线程中的每一个都运行1百万次迭代。这些睾丸都在相同的机器上运行。 Win Server 2008和Server 2003编号来自双启动计算机。 Win 7机器具有完全相同的规范,但物理机器却不同。在这种情况下,该机器是具有Core i5-2520M 2.5GHz的Lenovo T420笔记本电脑。显然不是服务器类机器,但是在真正的服务器类硬件上我得到了类似的结果。括号中的数字是第一列与给定列的比率。

    是否有任何解释说明为什么这个OS似乎会为跨CPU的内核级同步引入额外的费用?还是您知道一些可能会影响此设置的配置/调整参数?

    尽管这样做会使冗长且冗长,但我可以发布增强版的测试代码,如果有人愿意的话,上述数字也来自此代码。这将显示循环逻辑的执行和测试的自旋锁版本。

    延伸背景

    试图回答有关为什么以这种方式完成事情的一些不可避免的问题。而且我是一样的...当我阅读帖子时,我常常想知道为什么我什至在问。因此,这里有一些尝试可以弄清楚:
  • 什么是应用程序?它是一个数据库服务器。在某些情况下,客户在与服务器相同的计算机上运行客户端应用程序。在这种情况下,使用共享内存进行通信(相对于套接字)更快。此问题与共享内存通讯有关。
  • 工作负载真的取决于事件吗?好吧...共享内存通信是使用命名信号量实现的。客户端发信号量,服务器读取数据,服务器在响应就绪时发信号给客户端。在其他平台上,它的速度很快。在Win2008R2上则不是。它还非常依赖于客户的应用程序。如果他们向服务器发送许多小的请求,那么两个进程之间将进行大量的通信。
  • 可以使用轻型锁吗?可能吧。我已经在看了。但这与原始问题无关。
  • 最佳答案

    从评论中拉出答案:

    也许服务器未设置为高性能电源计划? Win2k8可能具有不同的默认值。默认情况下,许多服务器不是默认的,这会严重影响性能。

    OP确认这是根本原因。

    这是造成这种现象的一个有趣原因。当我做完全不同的事情时,这个想法突然浮现在脑海中。

    09-03 17:14