الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

طريقة سريعة و دقيقة لحساب الوقت

مغلق
بدأه hasan_aljudy في 24 أبريل 2005 · 4 رد · 1,373 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم

اليوم كنت أقرا عن الـ D Language http://www.digitalmars.com/d و بالصدفة وجدت معلومة فريدة اجدها لأول مرة: معالجات البنتيوم فيها امر لمعرفة الوقت! الأمر عبارة عن سطر اسمبلي واحد يخزن القيمة في الرجستر eax تلقائيا, و هذا الأمر (حسب ما فهمت) يعطيك عدد دقات ال.. كيف يقولوها .. processor cycles اظن دقات ساعة المعالج, يعني ادق شي مكن تحصل عليه.

هذا الأمر هو rdtsc و لتسهيل استخدامه نستطيع بكل بساطة كتابة فنكشن صغير هكذا:

inline unsigned int tick()
{
    __asm
    {
        rdtsc;
    }
    //no need for a return statement, 
    //time is already stored in eax, which is what return does
}

و عند ترجمة هذا الكود فستصبح مجرد سطر واحد في الأسمبلي, يعني عند استدعاء هذا الفنكشن فكل ما سيحصل هو تنفيذ الأمر rdtsc, فهو inline يعني لا يوجد حتى استدعاء لأي فنكشن, اللهم الا اذا كان الكومبايلر معد عالـ debug, في الفجول سي بلص بلص مثلا, اذا كنت تعمل في الـdebug configuration فسيكون هناك استدعاء لفنكشن حتى مع وجود كلمة inline, اما لو كنت في الـ release configuration, فسيتم تحويل الفنكشن الى inline حتى لو تكن هناك كلمة inline.

طبعا انا ضعيف في الأسمبلي, فلا أعرف إذا ما كان هذا السطر مثلا سينفذ كأمر واحد ام كعدة اوامر .. ولا اعرف شي عن طبيعة عمل بنتيوم ولا غيره من هذا الكلام.

تستطيع استخدام التوقيت لحساب الفرق بين زمنين هكذا:
 

   unsigned int cpu = tick();
    //do something here ..
    //basically what you want to time
    cpu = tick() - cpu;

    cout << "CPU time: " << cpu << endl;

اعتقد لا توجد طريقة اسرع او ادق من هذه لحساب الوقت المتغرق لتنفيذ كمية معينة من الكود, فهي اولا تعطيك الوقت نسبة الى المعالج, و ثانيا الكود اللذي يحضر الوقت ليس سوى سطر واحد في الأسمبلي, يعني لا يوجد احتمال ان الكود اللذي ياخذ الوقت هو نفسه يستغرق وقتا اضافيا.

الحكاية شفتها اليوم بالصدفة و استفاديت منها, و بصراحة لم اسمع بها ابدا من قبل مع اني بحثت طويلا عن أساليب لاسترجاع قيمة دقيقة لوقت, الا انني لم اجد ابدا اي اشارة لهذا الأمر في البنتيوم.

المشكلة الوحيدة هي ان هذا الأمر خاص بالبنتيوم, و قد لا يعمل على اي معالج آخر.

ان شاء الله تستفيدون منه.

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 10 أبريل 2014 في 19:26 — السبب: add some tags

#2

وعليكم السلام..

دايما مواضيعك قوية

..

لكن

tick();

هل هي دالة من دوال سي... ام ماذا .. اول مرة اشاهدها

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#3

لا, مجرد دالة انا توني كتبتها ووضعت فيها الأمر rdtsc كما هو مبين في الموضوع.

اول شي ردت اسميها time بس صار تعارض بين اسمها و اسم متغير كنت استخدمه, فغيرت الاسم الى tick و عموما تقدر تسميها ما تشاء.

#4

مشكور أخي hasan_aljudy على هذه الدالة.

كنت أستعملها سابقا في البرمجة بلغة ديلفي باسكال. تعليمة خاصة فقط بمعالجات Pentium فقط ولا تعمل على i486 و ما سبقه.

فمعالجات البانتيوم تحتوي عى عداد من حجم 64 بيت يحسب بسرعة نبضات المعالج, وينطلق كل مرة تشغل فيها الحاسب ويستمر في العد حتى يصل إلى قيمة (2 أس 64 ) -1, ثم يعاود العد من جديد انطلاقا من قيمة 0. وعند إعادة تشغيل الحاسب فأنه يعاود العد من 0.

يعني يمكنك تعمل عملية حسابية لتر كم سيستغرق هذا العداد لكي يصل إلى القيمة القصوى في عده وهي كما قلت = 1- 64^2؟

بالنسبة لي لدي معالج P4 سرعته 3GHz يعني في كل نبضة تستغرق 1/3e9 ثانية.

نضربها في 2^64 ستحصل على رقم خيالي لو تحوله للسنوات (وليس للدقائق او الساعات) سيصل ل 194 سنة :o .

هذا العداد يسمى ب MSR ولقرائته نستعمل هذه التعليمة في لغة التجميع rdtsc , غير أن بعض المصرفات لا تعتمدها كمصرف Borland, لهذا يمكنك كتابتها مباشرة بنظام العد الستعشري 0x310F.

تعليمة rdtsc تقوم بقرائة هذا العداد ثم ترجع قيمته في EDX:EAX حيث أن قيمته ستكون هي :

The high-order 32 bits of the MSR are loaded into the EDX register, and the low-order 32 bits are loaded into the EAX register
MSR= EDX * 2^32 + EAX

لقد قمت بترجمة وحدة مكتوبة بلغة الديلفي باسكال إلى سي++. ستجدون فيها مجموعة من الدوال التي تقوم بقراءة الفرق ما بين حدثين بطريقة جد دقيقة للوقت والراجع من هذه الدوال سيكون بالثواني وليس بعدد نبضات المعالج:

/*-----------------------------------------------------------------------------}
{ Copyright 2000-2001, Zloba Alexander.  All Rights Reserved.                  }
{ This unit can be freely used and distributed in commercial and private       }
{ environments, provided this notice is not modified in any way.               }
{ -----------------------------------------------------------------------------}
{ Feel free to contact me if you have any questions, comments or suggestions at}
{     zal@specosoft.com (Zloba Alexander)                                      }
{ You can always find the latest version of this unit at:                      }
{   http://www.specosoft.com                                                   }
{ -----------------------------------------------------------------------------}
{ Date last modified:  29/11/2001                                              }
{ -----------------------------------------------------------------------------}
{ Description:                                                                 }
{   This unit include function to work with RDTSC instruction of pentium       }
{    processors                                                                }
{  Example 1:                                                                  }
{     var ticks:int64;                                                         }
{     ...                                                                      }
{     ticks := cpugettick;                                                     }
{      dosomething;                                                            }
{     label1.caption := format('"dosomething" time = %d ms',[cpugetms(ticks)]) }
{  Example 2:                                                                  }
{     var tick1,tick2:int64;                                                   }
{     ...                                                                      }
{     tick1 := cpugettick;                                                     }
{      dosomething;                                                            }
{     tick2 := cpugettick;                                                     }
{      ... // do other work                                                    }
{     label1.caption := format('"dosomething" time = %d ms',                   }
{                              [cpucalcms(tick2,tick1)])                       }
{                                                                              }
{------------------------------------------------------------------------------}
{ TRanslated from delphi Code to C++ (Borland C++Builder): Ikossan@gmail.com   }
{-----------------------------------------------------------------------------*/
//---------------------------------------------------------------------------
#include <windows.h>
#include <winreg.h>
#include <math.h>
#include <iostream.h>
#include <conio.h>

//-------------------------------------------------------
  const unsigned long SleepTime = 200;  //  Used when determine the CPU frequency
  unsigned long CPUFrequency = 0;       //  CPU frequency MHz
  long double _1CPUFrequencyMS;         //  _1CPUFrequencyMS := 0.001/CPUFrequency
  long double _1CPUFrequencyMkS;        //  _1CPUFrequencyMkS := 1/CPUFrequency
  long double _1CPUFrequencyNS;         //  _1CPUFrequencyNS := 1000/CPUFrequency
  bool inited = false;

//-------------------------------------------------------
// Measure CPU frequency Functions
//-------------------------------------------------------
  unsigned short __pascal CpuGetSpeed();
  double __pascal CPUgetSpeedAccurate(const int SleepTime);
  __int64 __pascal CpuGetTick();
  unsigned long __pascal CpuGetMs(__int64 Tick);
  unsigned long __pascal CpuGetMks(__int64 Tick );
  unsigned long __pascal cpuGetNs(__int64 Tick);
//-------------------------------------------------------
  __int64 __pascal CpuMsToTick(unsigned long Value );
  __int64 __pascal CpuMksToTick(unsigned long Value);
  __int64 __pascal CpuNsToTick(unsigned long Value);
//-------------------------------------------------------
  __int64 __pascal CpuCalcMs(const __int64 Tick1,const __int64 Tick2);
  __int64 __pascal CpuCalcMks(const __int64 Tick1,const __int64 Tick2);
  __int64 __pascal CpuCalcNs(const __int64 Tick1,const __int64 Tick2);





//-------------------------------------------------------
/*
Description cpugettick(): The time-stamp counter is contained in a 64-bit MSR.
The high-order 32 bits of the MSR are loaded into the EDX register,
and the low-order 32 bits are loaded into the EAX register.
The processor increments the time-stamp counter MSR every clock cycle
and resets it to 0 whenever the processor is reset.
*/
//-------------------------------------------------------
  __int64 __pascal CpuGetTick()
  {
  asm dw 310Fh    // Read time-stamp counter into EDX:EAX
  }


//-------------------------------------------------------
unsigned long  __pascal CpuGetMs(__int64 Tick)
{
  asm
  {
    dw 310Fh // the rdtsc assembler instruction(not defined in Borland assmblers
    sub eax, DWORD [Tick]
    sbb edx, DWORD [Tick+4]
    mov DWORD[Tick], eax
    mov DWORD[Tick+4], edx
  }
  if (!inited) CpuGetSpeed();
  return (Tick * _1CPUFrequencyMS);
}


//-------------------------------------------------------
unsigned long  __pascal CpuGetMks(__int64 Tick)
{
  asm
  {
    dw 310Fh // the rdtsc assembler instruction(not defined in Borland assmblers
    sub eax, DWORD [Tick]
    sbb edx, DWORD [Tick+4]
    mov DWORD[Tick], eax
    mov DWORD[Tick+4], edx
  }
  if (!inited) CpuGetSpeed();
  return (Tick * _1CPUFrequencyMkS);
}


//-------------------------------------------------------
unsigned long  __pascal CpuGetNs(__int64 Tick)
{
  asm
  {
    dw 310Fh // the rdtsc assembler instruction(not defined in Borland assmblers
    sub eax, DWORD [Tick]
    sbb edx, DWORD [Tick+4]
    mov DWORD[Tick], eax
    mov DWORD[Tick+4], edx
  }
  if (!inited) CpuGetSpeed();
  return (Tick * _1CPUFrequencyNS);
}

//-------------------------------------------------------
__int64 __pascal cpucalcms(const __int64 Tick1,const __int64 Tick2 )
{
  if (!inited) CpuGetSpeed();
  return ((Tick2 - Tick1) * _1CPUFrequencyMS);
}


//-------------------------------------------------------
__int64 __pascal  cpumstotick(unsigned long Value)
{
  if (!inited) CpuGetSpeed();
  return (Value / _1CPUFrequencyMS);
}

//-------------------------------------------------------
unsigned short __pascal CpuGetSpeed()
{
  unsigned long _cpufreq;
  unsigned long len;
  HKEY rh;
  unsigned long pr;
  unsigned short result = 0;
{
  if (CPUFrequency != 0)
   {
    return CPUFrequency;
   }
   if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                   "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
                   0, KEY_READ, &rh) == ERROR_SUCCESS)
    {
    if (RegQueryValueExA(
                rh,
                "~MHz",
                NULL,
                &pr,
                (unsigned char*)&_cpufreq,
                 &len) == ERROR_SUCCESS)
     {
     result = _cpufreq;
     CPUFrequency = _cpufreq;
    }
    else
    {
      result = CPUgetSpeedAccurate(SleepTime) + 0.5;
      CPUFrequency = result;
    }
    RegCloseKey( rh );
   }
  _1CPUFrequencyMS =    0.001/CPUFrequency;
  _1CPUFrequencyMkS =   1.0/CPUFrequency;
  _1CPUFrequencyNS =    1000.0/CPUFrequency;
   inited = true;
}
return result;
}

//-------------------------------------------------------
double __pascal CPUgetSpeedAccurate(const int SleepTime)
{
    __int64 i1,i2,t;
    unsigned long pr;

  pr = GetThreadPriority(GetCurrentThread );
  SetThreadPriority(GetCurrentThread,THREAD_PRIORITY_TIME_CRITICAL);
  QueryPerformanceCounter((LARGE_INTEGER*) &i1);
  t = CpuGetTick();
  Sleep(SleepTime);
  asm
  {
    dw 310Fh    //the rdtsc assembler instruction(not defined in Borland assmblers
    sub eax, DWORD[t]
    sbb edx, DWORD[t+4]
    mov DWORD[t], eax
    mov DWORD[t+4], edx
  }
  QueryPerformanceCounter((LARGE_INTEGER*) &i2 );
  i2 = i2-i1;
  QueryPerformanceFrequency((LARGE_INTEGER*) &i1 );
  i2 = i2*1000000 /i1;
  SetThreadPriority(GetCurrentThread,pr);
  return t/(i2);
}
//---------------------------------------------------------------------------

B)

تم تعديل هذه المشاركة بواسطة ikossan في 25 أبريل 2005 في 06:00

سأتغيب عن المنتدى وستكون لي زيارات خفيفة بين الحين و الحين

يا صاحب الهم إن الهم منفرج.....................أبشر بخير فإن الفارج الله.

اليأس يقطع أحيانا بصاحبه..........................لا تيأس فإن الكافي الله.

الله يحدث بعد العسر ميسرة.........................لا تفزعن فإن الصانع الله.

إذا بليت فثق بالله وارض به...................فإن الذي يكشف البلوى هو الله.

و الله ما لك غير الله من أحد.......................فحسبك الله في كل لك الله

أخوكم مراد

27_06_06_05_21_41_1151410901logo_adl_wal_ihsan.jpg

#5

موضوع قديم, لكني تذكرته الان, و وجدت خللا يستدعي التصحيح,

بما ان rdtsc تضع القيمة في edx:eax, فإن اخذ الـ eax فقط قد يضر بعض الشيء, لانه بعد فترة معينة من الزمن, سوف يعود eax الى الصفر و يبدأ من جديد, فيجب استغلال كل من eax و edx, و ذلك بجعل الـ return type من تلك الدالة unsigned long long بدل unsigned int و ذلك يكلي يكون الرقم 64 بت و من حسن الحظ انه عندما نغير النوع الى long long فإن القيمة المرجعة ستكون في edx:eax يعني لا داعي للقلق فالعملية اوتوماتيكية

unsigned long long tick()
{
    _asm rdtsc;
}

هذا قد يضيف بعض الـ overhead الزايد ... يعني يمكنت امرين في الأسمبلي :lol: يعني الدقة شووويية راح تكون أقل!

هذا الموضوع مغلق.

مواضيع مشابهة