2015-05-10 11 views
37

Tôi đã dành một năm qua để phát triển một thư viện đăng nhập bằng C++ với hiệu suất trong đầu. Để đánh giá hiệu suất, tôi đã phát triển a set of benchmarks để so sánh mã của tôi với các thư viện khác, bao gồm cả trường hợp cơ bản không thực hiện ghi nhật ký.Tại sao thư viện ghi nhật ký của tôi gây ra các thử nghiệm hiệu suất để chạy nhanh hơn?

Trong điểm chuẩn cuối cùng, tôi đo tổng thời gian chạy của tác vụ đòi hỏi nhiều CPU trong khi ghi nhật ký đang hoạt động và khi nó không hoạt động. Sau đó tôi có thể so sánh thời gian để xác định mức phí mà thư viện của tôi có. Biểu đồ thanh này cho thấy sự khác biệt so với trường hợp cơ sở không ghi nhật ký của tôi.

performance chart

Như bạn thấy, thư viện của tôi ("liều lĩnh") thêm tiêu cực overhead (trừ khi tất cả 4 lõi CPU đang bận rộn). Chương trình chạy nhanh hơn nửa giây khi tính năng ghi nhật ký được bật hơn khi nó bị tắt.

Tôi biết tôi nên cố gắng tách biệt điều này thành một trường hợp đơn giản hơn là hỏi về chương trình 4000 dòng. Nhưng có rất nhiều địa điểm để loại bỏ, và không có giả thuyết tôi sẽ chỉ làm cho vấn đề biến mất khi tôi cố gắng cô lập nó. Tôi có thể dành một năm nữa để làm điều này. Tôi hy vọng rằng chuyên môn tập thể của Stack Overflow sẽ làm cho vấn đề này trở nên nông cạn hơn nhiều hoặc nguyên nhân sẽ hiển nhiên đối với một người có nhiều kinh nghiệm hơn tôi.

Một số sự thật về thư viện của tôi và các tiêu chuẩn:

  • Thư viện bao gồm một API front-end đẩy các đối số đăng nhập vào một hàng đợi lockless (Boost.Lockless) và một sợi back-end mà thực hiện định dạng chuỗi và ghi các mục nhật ký vào đĩa.
  • Thời gian dựa trên chỉ cần gọi std::chrono::steady_clock::now() ở đầu và cuối chương trình và in sự khác biệt.
  • Điểm chuẩn được chạy trên CPU Intel 4 lõi (i7-3770K).
  • Chương trình điểm chuẩn tính toán số liệu thống kê fractal và log của 1024x1024 Mandelbrot về mỗi pixel, tức là nó viết khoảng một triệu mục nhập nhật ký.
  • Tổng thời gian chạy là khoảng 35 giây đối với trường hợp chỉ công nhân đơn lẻ. Vì vậy, tốc độ tăng khoảng 1,5%.
  • Điểm chuẩn tạo ra tệp đầu ra (đây không phải là một phần của mã thời gian) có chứa phân đoạn Mandelbrot được tạo ra. Tôi đã xác minh rằng cùng một đầu ra được tạo ra khi đăng nhập được bật và tắt.
  • Điểm chuẩn được chạy 100 lần (với tất cả các thư viện được đo điểm chuẩn, quá trình này mất khoảng 10 giờ). Biểu đồ thanh hiển thị thời gian trung bình và các thanh lỗi hiển thị phạm vi interquartile.
  • Source code for the Mandelbrot computation
  • Source code for the benchmark.
  • Root of the code repository and documentation.

Câu hỏi của tôi là, làm cách nào để giải thích tốc độ tăng rõ ràng khi thư viện đăng nhập của tôi được bật?

Chỉnh sửa: Điều này đã được giải quyết sau khi thử các đề xuất được đưa ra trong nhận xét. Đối tượng nhật ký của tôi được tạo trên line 24 of the benchmark test. Rõ ràng khi LOG_INIT() chạm vào đối tượng log nó gây ra lỗi trang khiến một số hoặc tất cả các trang của bộ đệm ảnh được ánh xạ tới bộ nhớ vật lý.Tôi vẫn không chắc tại sao điều này cải thiện hiệu suất của gần nửa giây; ngay cả khi không có đối tượng log, điều đầu tiên xảy ra trong hàm mandelbrot_thread() là ghi vào đáy của bộ đệm ảnh, nó sẽ có hiệu ứng tương tự. Tuy nhiên, trong mọi trường hợp, xóa bộ đệm bằng một memset() trước khi bắt đầu chuẩn làm cho mọi thứ trở nên lành mạnh hơn. các tiêu chuẩn hiện hành là here

Những thứ khác mà tôi đã cố gắng là:

  • Run nó với các hồ sơ oprofile. Tôi đã không bao giờ có thể để có được nó để đăng ký bất kỳ thời gian trong ổ khóa, ngay cả sau khi mở rộng công việc để làm cho nó chạy trong khoảng 10 phút. Hầu hết thời gian nằm trong vòng lặp bên trong của tính toán Mandelbrot. Nhưng có lẽ tôi sẽ có thể giải thích chúng khác nhau ngay bây giờ mà tôi biết về lỗi trang. Tôi không nghĩ rằng để kiểm tra xem việc ghi hình ảnh có chiếm một lượng thời gian không cân xứng hay không.
  • Tháo khóa. Điều này đã có ảnh hưởng đáng kể đến hiệu suất, nhưng kết quả vẫn còn kỳ lạ và dù sao tôi cũng không thể thay đổi bất kỳ biến thể đa luồng nào.
  • So sánh mã lắp ráp đã tạo. Có sự khác biệt nhưng việc xây dựng đăng nhập rõ ràng là làm nhiều việc hơn. Không có gì nổi bật như một kẻ giết người thực hiện rõ ràng.
+16

Trước tiên, bạn đã thử lược tả các thử nghiệm khác nhau và thấy sự khác biệt thực sự có thể ở đâu không? Đó là đề nghị đầu tiên của tôi. Thứ hai của tôi là một phỏng đoán được thừa nhận, nhưng thật dễ dàng: 'memset()' của bạn 'sample_buffer' trước khi bạn bắt đầu hẹn giờ. Tôi đã nhìn thấy các trường hợp lỗi trang tạo ra các vấn đề về thời gian khi lần đầu tiên truy cập bộ nhớ chưa được khởi tạo. –

+0

Thông thường, trước tiên tôi khuyên bạn nên tìm kiếm một trường hợp thực tế hơn khi bạn gặp phải các kết quả về hiệu suất khó hiểu, nhưng việc tạo một tập hợp mandelbrot đã khá thực tế, vì vậy bạn tốt ở đó. Tôi đã từng làm việc với một người rất hào hứng khi phát hiện ra rằng sử dụng 'std :: vector :: insert' làm cho mã của anh ta dường như luôn đi nhanh hơn bằng cách sử dụng' std :: vector :: push_back', chỉ sau đó được kích hoạt và thỉnh thoảng áp dụng 'chèn' ở mọi nơi và làm chậm mọi thứ ... –

+0

Với hiệu suất, có tất cả các loại yếu tố động có thể tham gia vào việc gây ra ít đột biến về hiệu suất. Một trong những điểm phổ biến nhất, như Andrew đã chỉ ra, là lỗi trang. Đôi khi một trình tối ưu hóa có thể thực hiện những điều kỳ lạ khi bạn cấu trúc lại mã của mình thậm chí hơi khác một chút. Để có được đáy của những điều này, nó luôn luôn có giá trị có một hồ sơ trong tay của bạn và một số kiến ​​thức cơ bản về cách giải thích việc tháo gỡ. –

Trả lời

2

Khi bộ nhớ chưa được khởi động được truy cập lần đầu tiên, lỗi trang sẽ ảnh hưởng đến thời gian.

Vì vậy, trước cuộc gọi đầu tiên của bạn, std::chrono::steady_clock::now(), khởi tạo bộ nhớ bằng cách chạy memset() trên sample_buffer của bạn.

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