2013-10-11 11 views
5

Chúng tôi có một số mã muốn gọi localtime rất thường xuyên từ nhiều chủ đề. (Nền có liên quan: đó là máy chủ mà một trong những thứ bạn có thể yêu cầu là thời gian địa phương dưới dạng chuỗi và muốn có thể phân phát 100 nghìn yêu cầu mỗi giây.)API Linux/crossplatform cho các quy tắc múi giờ? (thay thế khóa localtime_r)

Chúng tôi đã phát hiện ra rằng trên Ubuntu Linux 12.04, hàm glibc localtime_r ("reentrant localtime") gọi __tz_convert, mà still takes a global lock!

(Ngoài ra, nó trông giống như FreeBSD làm localtime_r gọi tzset trên mỗi gọi duy nhất, bởi vì họ đang hoang tưởng rằng chương trình có thể đã làm một setenv("TZ") và/hoặc người dùng đã tải xuống phiên bản mới /etc/localtime từ giờ đến lần cuối cùng localtime_r được gọi. (Điều này ngược lại với việc ngồi uation được mô tả here; có vẻ như glibc gọi tzseton every invocation of localtime but not localtime_r, chỉ để gây nhầm lẫn.)

Rõ ràng, điều này là khủng khiếp đối với hiệu suất. Vì mục đích của chúng tôi, chúng tôi muốn "chụp nhanh" các quy tắc cơ bản cho múi giờ hiện tại của chúng tôi khi máy chủ bắt đầu chạy và sau đó sử dụng ảnh chụp đó mãi mãi sau đó. Vì vậy, chúng tôi sẽ tiếp tục tôn trọng quy tắc Tiết kiệm ánh sáng ban ngày (vì các quy tắc cho khi chuyển sang DST sẽ là một phần của ảnh chụp nhanh), nhưng chúng tôi sẽ không bao giờ quay lại đĩa, thực hiện mutexes hoặc làm bất kỳ điều gì khác để ngăn chặn. (Chúng tôi không sao khi không tôn trọng các cập nhật đã tải xuống cho tzinfo và không tôn trọng các thay đổi đối với /etc/localtime; chúng tôi không mong đợi máy chủ thay đổi múi giờ về mặt vật lý trong khi đang chạy.)

Tuy nhiên, tôi không thể tìm thấy bất kỳ thông tin nào trực tuyến về cách để xử lý các quy tắc múi giờ - cho dù có một API không gian người dùng để làm việc với chúng hay chúng tôi buộc phải thực hiện lại vài trăm dòng mã glibc để tự đọc dữ liệu múi giờ.

Chúng tôi có phải thực hiện lại mọi thứ ở hạ lưu của __tz_convert - bao gồm tzfile_read, vì dường như dường như không tiếp xúc với người dùng? Hoặc có một số giao diện POSIX và/hoặc thư viện của bên thứ ba mà chúng tôi có thể sử dụng để làm việc với các quy tắc múi giờ không?

(Tôi đã nhìn thấy http://www.iana.org/time-zones/repository/tz-link.html nhưng tôi không chắc chắn rằng nó rất hữu ích.)

+0

Nó không phải là tất cả rõ ràng rằng khóa toàn cầu trong 'tzset' là" khủng khiếp cho hiệu suất "- bạn đã thực sự chuẩn nó? Trừ khi bạn chuẩn bị nó, nó dường như là một trường hợp tối ưu hóa ngôn ngữ sớm. Theo như tôi có thể nói, 'tzset' có một con đường nhanh mà về cơ bản không làm gì nếu múi giờ không thực sự thay đổi. Trong một ứng dụng thực tế, tôi mong chờ khóa sẽ không bao giờ được tranh luận. – user4815162342

+0

Khóa được sử dụng bất cứ lúc nào hai chủ đề gọi '__tz_convert' cùng một lúc; kiểm tra mã. Và có, lý do duy nhất chúng tôi biết khóa này tồn tại là chúng tôi thấy nó là một nút cổ chai trong các bài kiểm tra perf (các bài kiểm tra cụ thể phục vụ rất nhiều yêu cầu địa phương). – Quuxplusone

+1

Bạn đã xem [Boost] (http://www.boost.org/doc/libs/1_54_0/doc/html/date_time.html) hoặc [ICU] (http://userguide.icu-project.org/ ngày giờ)? –

Trả lời

2

Sử dụng https://github.com/google/cctz

Vừa nhanh vừa phải làm tất cả mọi thứ bạn muốn với một API rất đơn giản.

Cụ thể, đối với một cctz tương đương với localtime, hãy sử dụng chức năng cctz::BreakTime(). Ví dụ: https://github.com/google/cctz/blob/master/examples/example3.cc

+2

Nói chung, liên kết đến công cụ hoặc thư viện [cần được đi kèm với ghi chú sử dụng, giải thích cụ thể về cách tài nguyên được liên kết có thể áp dụng cho vấn đề hoặc một số mã mẫu] (http://meta.stackoverflow.com/a/251605), hoặc nếu có thể tất cả những điều trên. – IKavanagh

+0

Cảm ơn bạn Greg. Thư viện đó trông rất tuyệt. Cũng có một khóa toàn cầu trong thư viện của bạn, xem https://github.com/google/cctz/blob/master/src/time_zone_impl.cc#L64. Tìm thấy bởi Cloudera như descirbed trong ý kiến ​​tại https://issues.cloudera.org/browse/IMPALA-3316. Đã gửi https://github.com/google/cctz/issues/23 – Tagar

0

Có lẽ điều này free, open-source timezone library sẽ phù hợp với nhu cầu.

Nó có cờ cấu hình được gọi là LAZY_INIT được ghi đầy đủ here. Theo mặc định nó được bật, và sẽ gây ra các cuộc gọi đến std::call_once lần đầu tiên mỗi múi giờ riêng lẻ được truy cập. Tuy nhiên, bạn có thể biên dịch với:

-DLAZY_INIT=0 

và sau đó gọi đến std::call_once biến mất. Mỗi múi giờ duy nhất được đọc từ đĩa và được khởi tạo đầy đủ trên lần truy cập đầu tiên (thông qua một hàm tĩnh cục bộ). Từ đó trở đi, mọi thứ ổn định và không có khóa, không có quyền truy cập đĩa. Đương nhiên điều này làm tăng thời gian khởi tạo lên phía trước, nhưng giảm thời gian "truy cập đầu tiên" cho từng múi giờ.

Thư viện này yêu cầu C++ 11/14 và do đó có thể không phù hợp với lý do đó. Nó dựa trên (và sử dụng nhiều) thư viện C++ 11 <chrono>. Đây là mẫu mã mà in ra giờ địa phương hiện tại:

#include "tz.h" 
#include <iostream> 

int 
main() 
{ 
    using namespace date; 
    auto local = make_zoned(current_zone(), std::chrono::system_clock::now()); 
    std::cout << local << '\n'; 
} 

mà chỉ ra cho tôi:

2016-04-12 10:13:14.585945 EDT 

Thư viện là một hiệu suất cao chủ đề thiết kế an toàn hiện đại. Nó cũng rất linh hoạt và đầy đủ tài liệu. Khả năng của nó vượt xa một sự thay thế đơn giản cho chiếc C localtime của C. Không giống như C API, bạn có thể chỉ định bất kỳ IANA timezone bạn cần, ví dụ:

auto local = make_zoned("Europe/London", std::chrono::system_clock::now()); 

này cho thời gian hiện tại ở London:

2016-04-12 15:19:59.035533 BST 

Lưu ý rằng theo mặc định độ chính xác của các dấu thời gian là của std::chrono::system_clock. Nếu bạn muốn có độ chính xác khác, có thể dễ dàng hoàn thành:

using namespace date; 
using namespace std::chrono; 
auto local = make_zoned("Europe/London", std::chrono::system_clock::now()); 
std::cout << format("%F %H:%M %Z", local) << '\n'; 

2016-04-12 15:22 BST 

Xem chi tiết docs để biết thêm chi tiết.

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