.NET: ThreadStatic против блокировки {}. Почему ThreadStaticAttribute снижает производительность? - PullRequest
10 голосов
/ 20 августа 2011

Я написал небольшую тестовую программу и был удивлен, почему решение lock {} работает быстрее, чем без блокировки, но с атрибутом [ThreadStatic] над статической переменной.

Фрагмент [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);
}

lock {} фрагмент кода:

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] в этом простом примере добавляет столько времени ко времени выполнения программы?

ОБНОВЛЕНИЕ : Iочень жаль, но эти результаты для DEBUG сборки.За RELEASE один я получил совершенно разные числа: (1,2; 2,4; 1,2).Для DEBUG числа были (4,2; 3,2; 1,2).

Таким образом, для RELEASE сборки, похоже, нет [ThreadStatic] снижения производительности.

Ответы [ 2 ]

8 голосов
/ 23 августа 2011

Для сборки RELEASE, похоже, почти нет потери производительности [ThreadStatic] (только небольшое наказание на современных процессорах).

Вот код разборки для 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 
0 голосов
/ 23 августа 2011

У вас есть две строки кода, которые обновляют ms_Acc. В случае lock у вас есть одна блокировка вокруг них обоих, тогда как в случае ThreadStatic это происходит один раз для каждого доступа к ms_Acc, то есть дважды для каждой итерации вашего цикла. Обычно это преимущество использования lock, вы можете выбрать желаемую степень детализации. Я предполагаю, что сборка RELEASE оптимизировала эту разницу.

Мне было бы интересно узнать, станет ли производительность очень похожей или идентичной, если вы измените цикл for на одиночный доступ к ms_Acc.

...