2011-05-01 36 views
5

Tôi có một từ điển có chứa từ điển thứ hai với 1000 mục nhập. Các mục nhập là tất cả NSStrings của loại khóa = key XXX và giá trị = element XXX trong đó XXX là số từ 0 - số lượng phần tử - 1. (Vài ngày trước, tôi đã hỏi về từ điển Objective-C có chứa từ điển. Vui lòng refer to that question nếu bạn muốn mã tạo từ điển.)Mục tiêu-c: Vấn đề với các khối và NSEnumerationConcurrent

Tổng chiều dài của tất cả các chuỗi trong từ điển phụ là 28.670 ký tự. tức là:

strlen("key 0")+strlen("element 0")+ 
//and so on up through 
strlen("key 999")+strlen("element 999") == 28670. 

Hãy xem xét giá trị băm rất đơn giản này như một chỉ báo nếu phương pháp đã liệt kê mọi cặp khóa + giá trị một lần và chỉ một lần.

tôi có một chương trình con mà làm việc một cách hoàn hảo (sử dụng khối) để truy cập phím từ điển cá nhân và các giá trị:

NSUInteger KVC_access3(NSMutableDictionary *dict){ 
    __block NSUInteger ll=0; 
    NSMutableDictionary *subDict=[dict objectForKey:@"dict_key"]; 

    [subDict 
     enumerateKeysAndObjectsUsingBlock: 
      ^(id key, id object, BOOL *stop) { 
       ll+=[object length]; 
       ll+=[key length]; 
    }]; 
    return ll; 
} 
// will correctly return the expected length... 

Nếu tôi thử các khối đồng thời cùng sử dụng (trên một máy tính đa xử lý), tôi nhận được một số gần nhưng không chính xác 28.670 mong đợi:

NSUInteger KVC_access4(NSMutableDictionary *dict){ 
    __block NSUInteger ll=0; 
    NSMutableDictionary *subDict=[dict objectForKey:@"dict_key"]; 

    [subDict 
     enumerateKeysAndObjectsWithOptions: 
      NSEnumerationConcurrent 
     usingBlock: 
      ^(id key, id object, BOOL *stop) { 
       ll+=[object length]; 
       ll+=[key length]; 
    }]; 
    return ll; 
} 
// will return correct value sometimes; a shortfall value most of the time... 

các tài liệu Apple cho NSEnumerationConcurrent nhà nước:

"the code of the Block must be safe against concurrent invocation." 

Tôi nghĩ rằng đó có thể là vấn đề, nhưng vấn đề với mã của tôi hoặc khối trong số KVC_access4 KHÔNG an toàn cho yêu cầu đồng thời là gì?

Sửa & Kết luận

Nhờ BJ Homer excellent solution, tôi đã NSEnumerationConcurrent làm việc. Tôi đã hẹn giờ cả hai phương pháp. Mã tôi có ở trên trong KVC_access3 nhanh hơn và dễ dàng hơn cho các từ điển vừa và nhỏ. Nó nhanh hơn rất nhiều trên nhiều từ điển. Tuy nhiên, nếu bạn có từ điển lớn mongo (hàng triệu hoặc hàng chục triệu cặp khóa/giá trị) thì mã này:

[subDict 
    enumerateKeysAndObjectsWithOptions: 
     NSEnumerationConcurrent 
    usingBlock: 
     ^(id key, id object, BOOL *stop) { 
     NSUInteger workingLength = [object length]; 
     workingLength += [key length]; 

     OSAtomicAdd64Barrier(workingLength, &ll); 
}]; 

là nhanh hơn đến 4x. Điểm chéo cho kích thước là khoảng 1 từ điển của 100.000 yếu tố thử nghiệm của tôi. Nhiều từ điển hơn và điểm giao nhau cao hơn có lẽ là do thời gian thiết lập.

Trả lời

13

Với điều tra đồng thời, bạn sẽ có khối đang chạy đồng thời trên nhiều chuỗi. Điều này có nghĩa là nhiều chủ đề đang truy cập ll cùng một lúc. Vì bạn không có đồng bộ hóa, bạn dễ bị các điều kiện chủng tộc.

Đây là vấn đề vì hoạt động += không phải là hoạt động nguyên tử. Hãy nhớ rằng, ll += x cũng giống như ll = ll + x. Điều này liên quan đến việc đọc ll, thêm x vào giá trị đó và sau đó lưu trữ giá trị mới trở lại trong ll. Trong khoảng thời gian ll được đọc trên Chủ đề X và khi được lưu trữ, bất kỳ thay đổi nào do các chủ đề khác gây ra sẽ bị mất khi Thread X quay trở lại để lưu trữ phép tính của nó.

Bạn cần thêm đồng bộ hóa để nhiều chủ đề không thể sửa đổi giá trị cùng một lúc.Các giải pháp ngây thơ là thế này:

__block NSUInteger ll=0; 
NSMutableDictionary *subDict=[dict objectForKey:@"dict_key"]; 

[subDict 
    enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent 
    usingBlock: 
     ^(id key, id object, BOOL *stop) { 
      @synchronized(subDict) { // <-- Only one thread can be in this block at a time. 
       ll+=[object length]; 
       ll+=[key length]; 
      } 
}]; 
return ll; 

Tuy nhiên, điều này loại bỏ tất cả những lợi ích bạn nhận được từ liệt kê đồng thời, kể từ khi toàn bộ cơ thể của khối hiện đang được bao bọc trong một đồng bộ khối có hiệu lực, chỉ có một thể hiện của khối này sẽ thực sự chạy cùng một lúc.

Nếu đồng thời thực sự là một yêu cầu hiệu suất đáng kể ở đây, tôi muốn đề nghị như sau:

__block uint64 ll = 0; // Note the change in type here; it needs to be a 64-bit type. 

^(id key, id object, BOOL *stop) { 
    NSUInteger workingLength = [object length]; 
    workingLength += [key length]; 

    OSAtomicAdd64Barrier(workingLength, &ll); 
} 

Lưu ý rằng tôi đang sử dụng OSAtomicAdd64Barrier, mà là một chức năng ở mức độ thấp khá được đảm bảo để tăng một giá trị nguyên tử. Bạn cũng có thể sử dụng @synchronized để kiểm soát quyền truy cập, nhưng nếu thao tác này thực sự là nút cổ chai hiệu suất đáng kể, thì có thể bạn sẽ muốn tùy chọn hiệu suất nhất, ngay cả với chi phí của một chút rõ ràng. Nếu điều này cảm thấy như quá mức cần thiết, thì tôi nghi ngờ cho phép điều tra đồng thời không thực sự ảnh hưởng đến hiệu suất của bạn nhiều như vậy.

Các vấn đề liên quan