2014-04-24 10 views
5

लेता है मैं एक सी ++/एसडीएल/ओपनजीएल आवेदन लिख रहा हूं, और मेरे पास सबसे असाधारण बग है। यह गेम एक साधारण परिवर्तनीय टाइमस्टेप के साथ ठीक काम कर रहा था। लेकिन फिर एफपीएस ने अजीब तरीके से व्यवहार करना शुरू कर दिया। मुझे पता चला कि दोनों नींद (1) और एसडीएल_Delay (1) पूरा करने के लिए 15 एमएस लेते हैं।नींद (1) और एसडीएल_Delay (1) 15 एमएस

किसी भी इनपुट उन कार्यों में 0-15 के बीच पूरा करने के लिए 15ms लेता है, 64 के बारे में में एफपीएस ताला लगा अगर मैं 16 के लिए सेट है, यह 30 एमएस o.o

मैं कोई विचार क्यों यह हो रहा है है लेता है। यह सबसे अजीब बग है जिसे मैंने कभी सामना किया है।

मेरे पाश इस तरह दिखता है:

while (1){ 
    GLuint t = SDL_GetTicks(); 
    Sleep(1); //or SDL_Delay(1) 
    cout << SDL_GetTicks() - t << endl; //outputs 15 
} 

यह बहुत मुश्किल से ही 1ms लेने के रूप में यह माना जाता है, लेकिन समय के बहुमत यह 15ms लेता है।

मेरा ओएस विंडोज 8.1 है। सीपीयू एक इंटेल i7 है। मैं एसडीएल 2 का उपयोग कर रहा हूँ। किसी भी विचार की सराहना की जाएगी, क्योंकि मैं अनजान हूं।

+2

संभावित डुप्लिकेट [WinAPI स्लीप() फ़ंक्शन कॉल अपेक्षित से अधिक समय तक सोता है] (http://stackoverflow.com/questions/9518106/winapi-sleep- कार्यक्षमता- कॉल-नींद-अपेक्षा से अधिक लंबे समय तक) –

+0

यदि आप वास्तविक समय में जागने की उम्मीद करते हैं तो आप सोने के लिए थ्रेड/प्रक्रिया नहीं डालना चाहते हैं। यदि आप शेड्यूलिंग के बारे में चिंता नहीं करना चाहते हैं तो स्पिनलॉक का उपयोग करें। –

उत्तर

8

टिकर 64 हर्ट्ज या 15.625 एमएस/टिक तक डिफ़ॉल्ट है। आपको समय के साथ 1000hz == 1ms में बदलने की आवश्यकता है BeginPeriod (1)। MSDN लेख:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx

लक्ष्य यहाँ एक निश्चित आवृत्ति अनुक्रम प्राप्त करने के लिए है, तो आप एक उच्च संकल्प टाइमर का उपयोग करना चाहिए, लेकिन दुर्भाग्य से इनमें से केवल सर्वेक्षण में शामिल किया जा सकता है, तो मतदान और नींद का एक संयोजन cpu कम करने के लिए ओवरहेड की जरूरत है। उदाहरण कोड, जो मानता है कि एक नींद (1) लगभग 2 एमएस तक ले सकती है (जो विंडोज एक्सपी के साथ होती है, लेकिन विंडोज के बाद के संस्करणों के साथ नहीं)।

/* code for a thread to run at fixed frequency */ 
#define FREQ 400      /* frequency */ 

typedef unsigned long long UI64;  /* unsigned 64 bit int */ 

LARGE_INTEGER liPerfTemp;    /* used for query */ 
UI64 uFreq = FREQ;      /* process frequency */ 
UI64 uOrig;        /* original tick */ 
UI64 uWait;        /* tick rate/freq */ 
UI64 uRem = 0;       /* tick rate % freq */ 
UI64 uPrev;        /* previous tick based on original tick */ 
UI64 uDelta;       /* current tick - previous */ 
UI64 u2ms;        /* 2ms of ticks */ 
#if 0         /* for optional error check */ 
static DWORD dwLateStep = 0; 
#endif 

    /* wait for some event to start this thread code */ 
    timeBeginPeriod(1);     /* set period to 1ms */ 
    Sleep(128);       /* wait for it to stabilize */ 

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499)/((UI64)500); 

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp); 
    uOrig = uPrev = liPerfTemp.QuadPart; 

    while(1){ 
     /* update uWait and uRem based on uRem */ 
     uWait = ((UI64)(liPerfFreq.QuadPart) + uRem)/uFreq; 
     uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq; 
     /* wait for uWait ticks */ 
     while(1){ 
      QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp); 
      uDelta = (UI64)(liPerfTemp.QuadPart - uPrev); 
      if(uDelta >= uWait) 
       break; 
      if((uWait - uDelta) > u2ms) 
       Sleep(1); 
     } 
     #if 0     /* optional error check */ 
     if(uDelta >= (uWait*2)) 
      dwLateStep += 1; 
     #endif 
     uPrev += uWait; 
     /* fixed frequency code goes here */ 
     /* along with some type of break when done */ 
    } 

    timeEndPeriod(1);     /* restore period */ 
+1

बहुत बहुत धन्यवाद! मुझे इस बारे में नहीं पता था! – user3346893

+0

चूंकि यूप्रिव टाइमर की मूल रीडिंग के बाद टिकों की गणना की गई संख्या पर आधारित है, इसलिए टाइमर के वर्तमान और पिछले रीडिन के बीच डेल्टा पर भरोसा करने के विरोध में इस विधि का उपयोग करके समय के साथ कोई बहाव नहीं होगा। चूंकि नींद 2 एमएस देरी की अनुमति देने के लिए सेट है, उदाहरण कोड लगभग 400 हर्ट्ज तक अच्छा होना चाहिए। यदि Windows XP समर्थन की आवश्यकता नहीं है, तो देरी यह मान सकती है कि नींद (1) में लगभग 1 एमएस (शायद इसमें कुछ मार्जिन जोड़ना होगा), इस मामले में यह 800hz के लिए अच्छा होना चाहिए। – rcgldr

+0

यदि निश्चित अवधि 1ms का सटीक एकाधिक है, और Windows XP समर्थन की आवश्यकता नहीं है, तो मल्टीमीडिया टाइमर ईवेंट फ़ंक्शंस का उपयोग किया जा सकता है। एमएसडीएन लेख: http://msdn.microsoft.com/en-us/library/windows/desktop/dd742877(v=vs.85).aspx। विंडोज एक्सपी एक मुद्दा है, टिकर वास्तव में 1024hz पर चलाएगा, और 1000 हर्ट्ज 42, 42, उसके बाद 41 टिक के बाद एक अतिरिक्त टिक समेत अनुरूपित किया जाता है ताकि 128 वास्तविक टिक 125 छद्म टिकों पर समाप्त हो जाएं (1ms सीमा पर वापस पाने के लिए) । – rcgldr

0

एसडीएल_Delay()/नींद() 10-15 मिलीसेकंड से कम समय के साथ विश्वसनीय रूप से उपयोग नहीं किया जा सकता है। सीपीयू टिक 1 एमएस अंतर का पता लगाने के लिए पर्याप्त तेज़ी से पंजीकरण नहीं करते हैं।

SDL docs here देखें।

+0

आप सही हैं, धन्यवाद, मुझे इसके बारे में पता नहीं था! – user3346893

+1

सीपीयू टिक्स पर्याप्त तेज़ी से पंजीकरण करते हैं, वास्तविक समस्या तब होती है जब आप सोने के लिए चलने वाले धागे डालते हैं। यह एक तैयार कतार के पीछे जाता है, और फिर यह शेड्यूलिंग की दया पर है। अंतराल काफी छोटा है, लेकिन स्पष्ट रूप से यह उनमें से एक नहीं है, तो कुछ ढांचे इस व्यवहार से बचने के लिए पर्याप्त स्मार्ट हैं। वैकल्पिक रूप से, आप प्रक्रिया प्राथमिकता को रीयल-टाइम पर सेट कर सकते हैं, और प्रक्रिया अक्सर सामान्य 10-15 एमएस क्वांटम से पहले दूसरों को पसंद करेगी। –

1

ऐसा लगता है कि 15 एमएस ओएस आपको सबसे छोटा टुकड़ा देगा। मुझे आपके विशिष्ट ढांचे के बारे में निश्चित नहीं है लेकिन नींद आमतौर पर कम से कम सोने का समय गारंटी देती है। (यानी यह कम से कम 1ms के लिए सो जाएगा।)

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