2011-08-20 18 views
9

Tôi đã viết chương trình thử nghiệm nhỏ và đã rất ngạc nhiên tại sao giải pháp lock {} hoạt động nhanh hơn khóa nhưng với thuộc tính [ThreadStatic] trên biến tĩnh..NET: ThreadStatic và lock {}. Tại sao ThreadStaticAttribute làm giảm hiệu suất?

[ThreadStatic] đoạn:

[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); 
} 

khóa {} đoạn mã:

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); 
} 

Trên máy tính của đoạn đầu tiên của tôi phải mất 4,2 giây; thứ hai - 3,2 giây, nhanh hơn 1 giây. Không có ThreadStatic và khóa - 1.2 giây.

Tôi tò mò tại sao thuộc tính [ThreadStatic] trong ví dụ đơn giản này thêm quá nhiều thời gian thực thi chương trình?

CẬP NHẬT: Tôi cảm thấy rất tiếc, nhưng các kết quả này dành cho việc xây dựng DEBUG. Đối với RELEASE, tôi có các số hoàn toàn khác nhau: (1.2; 2.4; 1.2). Đối với số DEBUG là (4.2; 3.2; 1.2).

Vì vậy, đối với RELEASE, có vẻ như không có hình phạt nào về hiệu suất là [ThreadStatic].

+0

Đó không phải là những gì tôi nhìn thấy trên quadcore của tôi. Nếu tôi biên dịch bản phát hành trên bất kỳ Cpu nào, tôi nhận được 0,81 giây cho đoạn đầu tiên và 4,5 giây cho đoạn mã thứ hai.Các trường hợp tầm thường mà không có an toàn thread mất 0,46s. – FuleSnabel

+0

Bạn nói đúng. Tôi chuyển từ 'Debug' sang chế độ' Release' và nhận được kết quả tương tự. – Roman

+0

Nó có thể giúp đọc mã tháo rời để hiểu tại sao bạn nhận được kết quả đáng ngạc nhiên. – FuleSnabel

Trả lời

6

Để xây dựng RELEASE dường như không có hình phạt hiệu suất [ThreadStatic] (chỉ phạt nhẹ trên CPU hiện đại).

Ở đây có mã lắp ráp cho ms_Acc += one; cho RELEASE tối ưu hóa được kích hoạt:

Không[ThreadStatic], DEBUG:

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

Không[ThreadStatic], RELEASE:

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

[ThreadStatic], DEBUG:

012.351.
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

Có vẻ như [ThreadStatic], DEBUG là 32bit và [ThreadStatic], RELEASE là 64bit. – kerem

-1

Bạn có hai dòng mã cập nhật ms_Acc. Trong trường hợp lock, bạn có một khóa duy nhất xung quanh cả hai trường hợp này, trong khi trong trường hợp ThreadStatic, nó xảy ra một lần cho mỗi lần truy cập vào ms_Acc, tức là hai lần cho mỗi lần lặp của vòng lặp của bạn. Đây thường là lợi ích của việc sử dụng lock, bạn có thể chọn mức độ chi tiết bạn muốn. Tôi đoán rằng RELEASE xây dựng tối ưu hóa sự khác biệt này đi.

Tôi sẽ quan tâm để xem liệu hiệu suất có trở nên rất giống nhau hay giống hệt nhau, nếu bạn thay đổi vòng lặp cho một truy cập duy nhất thành ms_Acc.

+0

Tôi không chắc chắn sự chậm lại là do hai dòng mã cập nhật 'ms_Acc'. Tôi đã thêm 'ms_Acc/= one' chỉ vì' ms_Acc + = one' cực kỳ nhanh và khó đo thời gian. Tôi không nghĩ rằng '[ThreadStatic]' bằng cách nào đó đồng bộ hóa quyền truy cập vào biến - mục đích của nó là tránh đồng bộ hóa (vì các luồng khác "không thể thấy" biến [ShreadStatic] ') và, thu, s tăng hiệu năng. – Roman