2017-05-30 6 views
6

मैं हाल ही में std::chrono एपीआई की कोशिश कर रहा हूं और मैंने पाया कि 64 बिट लिनक्स आर्किटेक्चर और जीसीसी कंपाइलर पर time_point और duration कक्षाएं अधिकतम रिज़ॉल्यूशन (नैनोसेकंड) पर ऑपरेटिंग सिस्टम की अधिकतम समय सीमा को संभालने में सक्षम नहीं हैं। वास्तव में ऐसा लगता है इन कक्षाओं के लिए भंडारण एक 64 बिट अभिन्न प्रकार है, जो आंतरिक रूप से दो 64 बिट पूर्णांकों, सेकंड के लिए एक और एक नैनोसेकंड के लिए उपयोग कर रहे हैं timespec और timeval की तुलना में:क्यों std :: chrono :: time_point संरचना timpec को स्टोर करने के लिए पर्याप्त नहीं है?

#include <iostream> 
#include <chrono> 
#include <typeinfo> 
#include <time.h> 

using namespace std; 
using namespace std::chrono; 

int main() 
{ 
    cout << sizeof(time_point<nanoseconds>) << endl;      // 8 
    cout << sizeof(time_point<nanoseconds>::duration) << endl;    // 8 
    cout << sizeof(time_point<nanoseconds>::duration::rep) << endl;  // 8 
    cout << typeid(time_point<nanoseconds>::duration::rep).name() << endl; // l 
    cout << sizeof(struct timespec) << endl;        // 16 
    cout << sizeof(struct timeval) << endl;        // 16 
    return 0; 
} 

पर 64 बिट विंडोज (MSVC2017) स्थिति बहुत समान है: भंडारण प्रकार भी 64 बिट पूर्णांक है। यह स्थिर (उर्फ मोनोटोनिक) घड़ियों से निपटने में कोई समस्या नहीं है, लेकिन भंडारण सीमाएं अलग-अलग एपीआई कार्यान्वयन को बड़ी तारीखों और व्यापक समय अवधि को स्टोर करने के लिए उपयुक्त नहीं बनाती हैं, जो वाई 2 के जैसी बग के लिए जमीन बनाती हैं। क्या समस्या स्वीकार की गई है? क्या बेहतर कार्यान्वयन या एपीआई सुधार की योजना है?

उत्तर

12

ऐसा किया गया था ताकि आपको कॉम्पैक्ट आकार के साथ अधिकतम लचीलापन मिल सके। अगर आपको अति-ठीक परिशुद्धता की आवश्यकता है, तो आपको आमतौर पर बहुत बड़ी रेंज की आवश्यकता नहीं होती है। और यदि आपको बहुत बड़ी रेंज की आवश्यकता है, तो आपको आमतौर पर बहुत अधिक सटीकता की आवश्यकता नहीं होती है।

उदाहरण के लिए, यदि आप नैनोसेकंड में तस्करी कर रहे हैं, तो क्या आपको नियमित रूप से +/- 2 9 2 साल से अधिक के बारे में सोचना चाहिए? और यदि आपको उस से अधिक की सीमा के बारे में सोचने की ज़रूरत है, तो अच्छी तरह से माइक्रोसेकंड आपको +/- 2 9 2 हजार वर्षों देता है।

मैकोज़ system_clock वास्तव में माइक्रोसॉन्ड लौटाता है, नैनोसेकंड नहीं। तो घड़ी 1 9 70 से 2 9 2 हजार साल तक चल सकती है जब तक कि यह बहती न हो।

विंडोज system_clock में 100-एनएस इकाइयों की सटीकता है, और इसकी श्रेणी +/- 2 9 .2 हजार वर्ष है।

यदि कुछ सौ हजार साल अभी भी पर्याप्त नहीं हैं, तो मिलीसेकंड को आजमाएं। अब आप +/- 2 9 2 मिलियन वर्षों की एक श्रृंखला तक हैं।

अंत में, यदि आप सिर्फ एक से अधिक कुछ सौ वर्षों के लिए सटीक बाहर nanosecond बने रहना होगा, <chrono> आप भी भंडारण अनुकूलित करने के लिए अनुमति देता है:

using dnano = duration<double, nano>; 

इससे आप एक double के रूप में जमा नैनोसेकंड। अपने मंच एक 128 बिट अभिन्न प्रकार का समर्थन करता है, तो आप वह भी उपयोग कर सकते हैं:

using big_nano = duration<__int128_t, nano>; 

ओह, अगर तुम timespec के लिए ऑपरेटरों अतिभारित लिखते हैं, आप भी कि भंडारण के लिए उपयोग कर सकते हैं (मैं इसकी सलाह नहीं देते हालांकि)।

आप नैनोसेकंड से बेहतर परिशुद्धता भी प्राप्त कर सकते हैं, लेकिन आप ऐसा करने में रेंज का त्याग करेंगे। उदाहरण के लिए:

using picoseconds = duration<int64_t, pico>; 

इसमें केवल +/-292 वर्ष (कुछ महीने) की एक श्रृंखला है। तो आप को इसके साथ सावधान रहना होगा। समय के लिए बढ़िया चीजें हालांकि आपके पास एक स्रोत घड़ी है जो आपको उप-नैनोसेकंद परिशुद्धता देता है।

पर <chrono> पर अधिक जानकारी के लिए देखें।

वर्तमान ग्रेगोरियन कैलेंडर की वैधता से अधिक सीमा के साथ तारीखों को बनाने, व्यवस्थित करने और संग्रहीत करने के लिए, मैंने यह open-source date library बनाया है जो <chrono> कैलेंड्रिकल सेवाओं के साथ लाइब्रेरी को बढ़ाता है। यह लाइब्रेरी एक हस्ताक्षरित 16 बिट पूर्णांक में वर्ष संग्रहित करती है, और इसमें +/- 32K वर्षों की एक श्रृंखला है। यह इस तरह से इस्तेमाल किया जा सकता:

#include "date.h" 

int 
main() 
{ 
    using namespace std::chrono; 
    using namespace date; 
    system_clock::time_point now = sys_days{may/30/2017} + 19h + 40min + 10s; 
} 

अद्यतन

सवाल नीचे टिप्पणी में पूछा जाता है कि कैसे सेकंड और नैनोसेकंड में "सामान्य" के लिए duration<int32_t, nano> (और फिर एक time_point को सेकंड जोड़)।

सबसे पहले, मैं 32 बिट्स में नैनोसेकंड भरने से सावधान रहूंगा। रेंज +/- 2 सेकंड से थोड़ी अधिक है। लेकिन यहाँ कैसे मैं इस तरह इकाइयों को अलग है: यदि n सकारात्मक है

using ns = duration<int32_t, nano>; 
    auto n = ns::max(); 
    auto s = duration_cast<seconds>(n); 
    n -= s; 

ध्यान दें कि यह केवल काम करता है। सही ढंग से संभाल करने के लिए नकारात्मक n, तो सबसे अच्छा होगा है:

auto n = ns::max(); 
    auto s = floor<seconds>(n); 
    n -= s; 

std::floor साथ सी ++ 17 शुरू की है। यदि आप इसे पहले चाहते हैं, तो आप इसे here या here से पकड़ सकते हैं।

मैं उपरोक्त घटाव ऑपरेशन के आंशिक हूं, क्योंकि मुझे इसे और अधिक पठनीय लगता है। लेकिन यह भी काम करता है (यदि n नकारात्मक नहीं है):

auto s = duration_cast<seconds>(n); 
    n %= 1s; 

1s सी ++ 14 में शुरू की है। सी ++ 11 में, आपको इसके बजाय seconds{1} का उपयोग करना होगा।

एक बार आपके पास सेकंड (s) हो जाने के बाद, आप इसे अपने time_point पर जोड़ सकते हैं।

+0

आपका उत्तर बहुत समझ में आता है, धन्यवाद। क्या एपीआई में कुछ वस्तुओं को सामान्यीकृत करने के लिए कुछ उदाहरण हैं, उदाहरण के लिए, 'time_point ' और 'अवधि 'एक साथ उपयोग किया जाता है? तो उदाहरण के लिए अवधि में कुछ = = 1 सेकंड कभी नहीं है? – ceztko

+0

@ceztko: क्षमा करें, मैं सवाल पूरी तरह से समझ नहीं पा रहा हूं। यदि निम्नलिखित इसका उत्तर नहीं देता है, तो क्या आप फिर से बदल सकते हैं? 'time_point + अवधि 'आपको' time_point ' (जो 2s में बहती है) देगी। इसमें कोई अतिप्रवाह सुरक्षा नहीं है। हालांकि यदि आप "सुरक्षित" लाइब्रेरी लेते हैं, तो आप ओवरफ्लो सुरक्षा प्राप्त करने के लिए प्रतिनिधित्व के लिए इसका उपयोग कर सकते हैं। –

+0

क्षमा करें, उदाहरण वास्तव में गलत था।मैं वास्तव में 'time_point ' और एक अवधि 'सामान्य रूप से सामान्य करना चाहता था: इस मामले में मैं शायद 'अवधि ' से सेकेंड (मंजिल लेना) और 'time_point पर राशि को रूपांतरित कर दूंगा 'और मॉड्यूल एक सेकंड के रूप में अवधि को फिर से लिखें। बस एपीआई के साथ ऐसा करने का सबसे अधिक स्पष्ट तरीका क्या सोच रहा है। – ceztko

0

std::chrono::nanosecondsstd::chrono::duration<some_t, std::nano> के लिए एक प्रकार का उपनाम है जहां some_t कम से कम 64 बिट्स के संग्रहण के साथ एक हस्ताक्षरित int है। यह अभी भी कम से कम 2 9 2 साल की सीमा के लिए नैनोसेकंद परिशुद्धता के साथ अनुमति देता है। (| _fast | _least) 64_t परिवार

विशेष रूप से मानक से उल्लेख इस तरह विशेषताओं के साथ ही अभिन्न प्रकार int हैं।

यदि आपका कार्यान्वयन एक प्रदान करता है, तो आप अपने समय का प्रतिनिधित्व करने के लिए एक विस्तृत प्रकार चुनने के लिए स्वतंत्र हैं। आप टाइपपीफ के समूह के साथ नामस्थान प्रदान करने के लिए और अधिक स्वतंत्र हैं जो आपके व्यापक प्रकार के प्रतिनिधित्व के साथ std::chrono अनुपात दर्पण करते हैं।

संबंधित मुद्दे