2011-12-13 34 views
14

Bây giờ C++ được thêm thread_local lưu trữ như một tính năng ngôn ngữ, tôi đang tự hỏi một vài điều:Các chi phí của thread_local

  1. chi phí thead_local có thể sẽ là gì?
    • Trong bộ nhớ?
    • Đối với hoạt động đọc và ghi?
  2. Được liên kết với điều đó: Hệ điều hành thường triển khai điều này như thế nào? Nó có vẻ như bất cứ điều gì tuyên bố thread_local sẽ phải được cung cấp không gian lưu trữ thread cụ thể cho mỗi thread được tạo ra.
+3

Chi phí lớn nhất là bảo trì mã. –

Trả lời

8

Không gian lưu trữ: kích thước của biến * số lượng chủ đề hoặc có thể (sizeof (var) + sizeof (var *)) * số lượng chủ đề.

Có hai cách cơ bản để thực hiện lưu trữ thread-địa phương:

  1. Sử dụng một số loại cuộc gọi hệ thống mà được thông tin về các chủ đề hạt nhân hiện hành. Sloooow.

  2. Sử dụng một số con trỏ, có thể trong thanh ghi bộ xử lý, được đặt chính xác ở mọi công tắc ngữ cảnh luồng bởi hạt nhân - cùng lúc với tất cả các thanh ghi khác. Giá rẻ.

Trên nền tảng intel, biến thể 2 thường được thực hiện qua một số thanh ghi phân khúc (FS hoặc GS, tôi không nhớ). Cả GCC và MSVC đều hỗ trợ điều này. Do đó, thời gian truy cập nhanh như đối với các biến toàn cầu. Cũng có thể, nhưng tôi chưa thấy nó trong thực tế, để thực hiện điều này thông qua các chức năng thư viện hiện có như pthread_getspecific. Hiệu suất sau đó sẽ giống như 1. hoặc 2., cộng với phí gọi trên thư viện. Hãy nhớ rằng biến thể cuộc gọi trên thư viện phiên bản 2. + vẫn nhanh hơn rất nhiều so với cuộc gọi hạt nhân.

+0

Cần lưu ý rằng bộ nạp thực thi hệ điều hành và trình liên kết động (đối với thư viện/DLL được chia sẻ) cần hỗ trợ cụ thể cho Biến thể 2. Nhận TLS để làm việc thông qua thanh ghi phân đoạn là actaully rất nhiều công việc. Nhưng nó cũng là giá trị nó kể từ khi sử dụng các biến TLS sau đó là không đáng kể so với một toàn cầu bình thường. –

9

Một mô tả cho cách thức hoạt động trên Linux bằng Uli Drepper (duy trì glibc) có thể được tìm thấy ở đây: www.akkadia.org/drepper/tls.pdf

Yêu cầu để xử lý các module được nạp tự động, vv làm cho toàn bộ cơ chế một chút phức tạp, mà có lẽ một phần giải thích lý do tại sao tài liệu có trọng số ở 79 trang (!).

Bộ nhớ sử dụng khôn ngoan, mỗi biến cho mỗi luồng rõ ràng cần bộ nhớ riêng cho mỗi luồng (mặc dù trong một số trường hợp có thể được thực hiện một cách lười biếng sao cho không gian được phân bổ chỉ khi biến được truy cập đầu tiên), và sau đó có một số cơ sở dữ liệu bổ sung cần thiết cho các bảng bù trừ, v.v.

Hiệu suất cao, chi phí bổ sung để truy cập biến TLS chủ yếu xoay quanh việc lấy địa chỉ của biến. Trên x86 Linux, thanh ghi GS được sử dụng như là một khởi đầu để có được một id luồng, trên x86-64 FS. Thông thường có một vài tham số con trỏ, và một cuộc gọi hàm (__tls_get_addr) cho mã được nạp động. Ngoài ra còn có chi phí tạo ra một chuỗi mới chậm hơn vì việc triển khai cần phân bổ không gian và có thể khởi tạo tất cả các vars TLS (nếu không được thực hiện một cách lười biếng).

TLS là tốt đẹp để dễ dàng làm cho một số mẫu mã không an toàn chủ đề cũ an toàn (suy nghĩ errno), nhưng đối với mã mới được thiết kế từ đầu cho một thế giới đa luồng nó rất hiếm khi cần thiết.

+6

Tôi không đồng ý với điều đó _rất hiếm khi cần thiết_ bình luận. TLS là một cách rất đơn giản, gọn gàng và nhanh chóng để giảm ganh đua giữa các chủ đề và cải thiện hiệu suất bằng cách tránh tra cứu lặp lại trong một chuỗi. –

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