2011-08-20 66 views
9

我写的小测试程序,并很惊讶为什么lock {}解决方案的性能比无锁但[ThreadStatic]属性在静态变量更快。.NET:ThreadStatic vs lock {}。为什么ThreadStaticAttribute会降低性能?

[ThreadStatic]片段:

[ThreadStatic] 
private static long ms_Acc; 
public static void RunTest() 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    int one = 1; 
    for (int i = 0; i < 100 * 1000 * 1000; ++i) { 
     ms_Acc += one; 
     ms_Acc /= one; 
    } 
    stopwatch.Stop(); 
    Console.WriteLine("Time taken: {0}", stopwatch.Elapsed.TotalSeconds); 
} 

锁{}片段:

private static long ms_Acc; 
private static object ms_Lock = new object(); 
public static void RunTest() 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    int one = 1; 
    for (int i = 0; i < 100 * 1000 * 1000; ++i) { 
     lock (ms_Lock) { 
      ms_Acc += one; 
      ms_Acc /= one; 
     } 
    } 
    stopwatch.Stop(); 
    Console.WriteLine("Time taken: {0}", stopwatch.Elapsed.TotalSeconds); 
} 

在我的机器第一片断接受4.2秒;第二 - 3.2秒,这是1秒快。没有ThreadStatic和锁 - 1.2秒。

我很好奇,为什么在这个简单的例子[ThreadStatic]属性增加了这么多的程序执行时间?

UPDATE:我感到非常抱歉,但这些结果是为DEBUG构建。对于RELEASE我有一个完全不同的数字:(1.2; 2.4; 1.2)。对于DEBUG号码分别是(4.2; 3.2; 1.2)。

因此,对于RELEASE构建似乎没有[ThreadStatic]性能损失。

+0

那不是我看到我的四核。如果我在任何Cpu上编译发布,我会在第一个片段中获得0.81s,在第二个片段中获得4.5s。没有线程安全的平凡情况需要0.46s。 – FuleSnabel

+0

你说得对。我从'Debug'切换到'Release'模式,并得到了类似于你的结果。 – Roman

+0

它可以帮助阅读反汇编的代码,以了解为什么你会得到惊人的结果。 – FuleSnabel

回答

6

对于RELEASE构建,似乎几乎没有[ThreadStatic]性能损失(现代CPU只有轻微的损失)。

这里谈到对于ms_Acc += one的拆卸代码;对于RELEASE已启用优化:

没有[ThreadStatic]DEBUG

00000060 mov   eax,dword ptr [ebp-40h] 
00000063 add   dword ptr ds:[00511718h],eax 

没有[ThreadStatic]RELEASE

00000051 mov   eax,dword ptr [00040750h] 
00000057 add   eax,dword ptr [rsp+20h] 
0000005b mov   dword ptr [00040750h],eax 

[ThreadStatic]DEBUG

00000066 mov   edx,1 
0000006b mov   ecx,4616E0h 
00000070 call  664F7450 
00000075 mov   edx,1 
0000007a mov   ecx,4616E0h 
0000007f mov   dword ptr [ebp-50h],eax 
00000082 call  664F7450 
00000087 mov   edx,dword ptr [eax+18h] 
0000008a add   edx,dword ptr [ebp-40h] 
0000008d mov   eax,dword ptr [ebp-50h] 
00000090 mov   dword ptr [eax+18h],edx 

[ThreadStatic]RELEASE

00000058 mov   edx,1 
0000005d mov   rcx,7FF001A3F28h 
00000067 call  FFFFFFFFF6F9F740 
0000006c mov   qword ptr [rsp+30h],rax 
00000071 mov   rbx,qword ptr [rsp+30h] 
00000076 mov   ebx,dword ptr [rbx+20h] 
00000079 add   ebx,dword ptr [rsp+20h] 
0000007d mov   edx,1 
00000082 mov   rcx,7FF001A3F28h 
0000008c call  FFFFFFFFF6F9F740 
00000091 mov   qword ptr [rsp+38h],rax 
00000096 mov   rax,qword ptr [rsp+38h] 
0000009b mov   dword ptr [rax+20h],ebx 
+2

似乎[ThreadStatic],DEBUG是32位和[ThreadStatic],RELEASE是64位。 – kerem

-1

你的代码更新ms_Acc两行。在lock的情况下,您对这两种情况都有一次锁定,而在ThreadStatic的情况下,每次访问ms_Acc时会发生一次,即对于您的循环的每次迭代都会发生一次。这通常是使用lock的好处,您可以选择所需的粒度。我猜测RELEASE构建优化了这种差异。

我很想看看性能变得非常相似,或相同的,如果你改变for循环的单一访问ms_Acc

+0

我不确定减速是因为更新'ms_Acc'的两行代码。我仅仅因为ms_Acc + = one'非常快速地添加了ms_Acc/= one',所以很难测量时间。我不认为'[ThreadStatic]'以某种方式同步对变量的访问 - 它的目的是为了避免同步(因为其他线程“看不到'[ShreadStatic]'变量),并且提高了性能。 – Roman