Add timers for measuring elapsed time
This commit is contained in:
@@ -102,14 +102,17 @@ file(GLOB BH_HEADER
|
||||
|
||||
set(BH_SOURCE_DUMMY
|
||||
src/Platform/Dummy/File.c
|
||||
src/Platform/Dummy/Timer.c
|
||||
)
|
||||
|
||||
set(BH_SOURCE_WIN32
|
||||
src/Platform/Win32/File.c
|
||||
src/Platform/Win32/Timer.c
|
||||
)
|
||||
|
||||
set(BH_SOURCE_POSIX
|
||||
src/Platform/Posix/File.c
|
||||
src/Platform/Posix/Timer.c
|
||||
)
|
||||
|
||||
set(BH_SOURCE_WIN32_MT
|
||||
|
||||
9
configure
vendored
9
configure
vendored
@@ -164,9 +164,12 @@ done
|
||||
add_source "${source_path}src/Platform/Spinlock.c"
|
||||
|
||||
case "$platform" in
|
||||
Posix) add_source "${source_path}src/Platform/Posix/File.c" ;;
|
||||
Win32) add_source "${source_path}src/Platform/Win32/File.c" ;;
|
||||
*) add_source "${source_path}src/Platform/Dummy/File.c" ;;
|
||||
Posix) add_source "${source_path}src/Platform/Posix/File.c" ;
|
||||
add_source "${source_path}src/Platform/Posix/Timer.c" ;;
|
||||
Win32) add_source "${source_path}src/Platform/Win32/File.c" ;
|
||||
add_source "${source_path}src/Platform/Win32/Timer.c" ;;
|
||||
*) add_source "${source_path}src/Platform/Dummy/File.c" ;
|
||||
add_source "${source_path}src/Platform/Dummy/Timer.c" ;;
|
||||
esac
|
||||
|
||||
if [ "$enable_mt" = "yes" ]; then
|
||||
|
||||
82
doc/Manual/en/BH_Timer.pod
Normal file
82
doc/Manual/en/BH_Timer.pod
Normal file
@@ -0,0 +1,82 @@
|
||||
=encoding UTF-8
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
BH_Timer - timer utilities
|
||||
|
||||
|
||||
=head1 SYNTAX
|
||||
|
||||
#include <BH/Timer.h>
|
||||
|
||||
cc prog.c -o prog -lbh
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides a simple high-resolution timer interface for measuring
|
||||
elapsed time. The timer uses a monotonic clock source when available, making it
|
||||
suitable for accurate time measurements, such as profiling or timeouts. It
|
||||
supports millisecond and nanosecond precision and can be started, restarted,
|
||||
and queried for elapsed time.
|
||||
|
||||
|
||||
|
||||
=head1 API CALLS
|
||||
|
||||
|
||||
=head2 BH_TimerNew
|
||||
|
||||
BH_Timer *BH_TimerNew(void);
|
||||
|
||||
Creates and initializes a new timer object.
|
||||
|
||||
Returns a pointer to a newly allocated BH_Timer on success, or NULL on failure.
|
||||
|
||||
|
||||
=head2 BH_TimerFree
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer);
|
||||
|
||||
Destroys the timer.
|
||||
|
||||
=head2 BH_TimerIsMonotonic
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer);
|
||||
|
||||
Checks whether the timer uses a monotonic clock source.
|
||||
|
||||
|
||||
=head2 BH_TimerStart
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer);
|
||||
|
||||
Starts or resets the timer to begin counting from the current time.
|
||||
|
||||
|
||||
=head2 BH_TimerRestart
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer);
|
||||
|
||||
Restarts the timer and returns the number of milliseconds that had elapsed since
|
||||
the start of the timer.
|
||||
|
||||
|
||||
=head2 BH_TimerMilliseconds
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer);
|
||||
|
||||
Returns the number of milliseconds that had elapsed since the start of the
|
||||
timer.
|
||||
|
||||
|
||||
=head2 BH_TimerNanoseconds
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer);
|
||||
|
||||
Returns the number of nanoseconds that had elapsed since the start of the timer.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<BH>
|
||||
@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
|
||||
BH_Ray3f.html \
|
||||
BH_String.html \
|
||||
BH_Thread.html \
|
||||
BH_Timer.html \
|
||||
BH_Unicode.html \
|
||||
BH_Util.html \
|
||||
BH_Vec2f.html \
|
||||
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
|
||||
BH_Ray3f.3 \
|
||||
BH_String.3 \
|
||||
BH_Thread.3 \
|
||||
BH_Timer.3 \
|
||||
BH_Unicode.3 \
|
||||
BH_Util.3 \
|
||||
BH_Vec2f.3 \
|
||||
|
||||
83
doc/Manual/ru/BH_Timer.pod
Normal file
83
doc/Manual/ru/BH_Timer.pod
Normal file
@@ -0,0 +1,83 @@
|
||||
=encoding UTF-8
|
||||
|
||||
|
||||
=head1 НАЗВАНИЕ
|
||||
|
||||
BH_Timer - утилиты таймера
|
||||
|
||||
|
||||
=head1 СИНТАКСИС
|
||||
|
||||
#include <BH/Timer.h>
|
||||
|
||||
cc prog.c -o prog -lbh
|
||||
|
||||
|
||||
=head1 ОПИСАНИЕ
|
||||
|
||||
Этот модуль предоставляет простой интерфейс высокоточного таймера для измерения
|
||||
прошедшего времени. По возможности таймер использует монотонный источник часов,
|
||||
что делает его подходящим для точных измерений времени, например, профилирования
|
||||
или тайм-аутов. Поддерживается точность в миллисекундах и наносекундах; таймер
|
||||
можно запускать, перезапускать и запрашивать прошедшее время.
|
||||
|
||||
|
||||
=head1 API ВЫЗОВЫ
|
||||
|
||||
|
||||
=head2 BH_TimerNew
|
||||
|
||||
BH_Timer *BH_TimerNew(void);
|
||||
|
||||
Создаёт и инициализирует новый объект таймера.
|
||||
|
||||
В случае успеха функция возвращает указатель на новый объект BH_Timer,
|
||||
или NULL в случае ошибки.
|
||||
|
||||
|
||||
=head2 BH_TimerFree
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer);
|
||||
|
||||
Уничтожает таймер.
|
||||
|
||||
|
||||
=head2 BH_TimerIsMonotonic
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer);
|
||||
|
||||
Проверяет, использует ли таймер монотонный источник времени.
|
||||
|
||||
|
||||
=head2 BH_TimerStart
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer);
|
||||
|
||||
Запускает или сбрасывает таймер, начиная отсчёт с текущего момента времени.
|
||||
|
||||
|
||||
=head2 BH_TimerRestart
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer);
|
||||
|
||||
Перезапускает таймер и возвращает количество миллисекунд, прошедших с момента
|
||||
его запуска.
|
||||
|
||||
|
||||
=head2 BH_TimerMilliseconds
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer);
|
||||
|
||||
Возвращает количество миллисекунд, прошедших с момента запуска таймера.
|
||||
|
||||
|
||||
=head2 BH_TimerNanoseconds
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer);
|
||||
|
||||
Возвращает количество наносекунд, прошедших с момента запуска таймера.
|
||||
|
||||
|
||||
=head1 СМ. ТАКЖЕ
|
||||
|
||||
L<BH>
|
||||
@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
|
||||
BH_Ray3f.html \
|
||||
BH_String.html \
|
||||
BH_Thread.html \
|
||||
BH_Timer.html \
|
||||
BH_Unicode.html \
|
||||
BH_Util.html \
|
||||
BH_Vec2f.html \
|
||||
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
|
||||
BH_Ray3f.3 \
|
||||
BH_String.3 \
|
||||
BH_Thread.3 \
|
||||
BH_Timer.3 \
|
||||
BH_Unicode.3 \
|
||||
BH_Util.3 \
|
||||
BH_Vec2f.3 \
|
||||
|
||||
32
include/BH/Timer.h
Normal file
32
include/BH/Timer.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef BH_TIMER_H
|
||||
#define BH_TIMER_H
|
||||
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
|
||||
typedef struct BH_Timer BH_Timer;
|
||||
|
||||
|
||||
BH_Timer *BH_TimerNew(void);
|
||||
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer);
|
||||
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer);
|
||||
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer);
|
||||
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer);
|
||||
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer);
|
||||
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer);
|
||||
|
||||
|
||||
#endif /* BH_TIMER_H */
|
||||
50
src/Platform/Dummy/Timer.c
Normal file
50
src/Platform/Dummy/Timer.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <BH/Timer.h>
|
||||
|
||||
|
||||
BH_Timer *BH_TimerNew(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
}
|
||||
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer)
|
||||
{
|
||||
BH_UNUSED(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
139
src/Platform/Posix/Timer.c
Normal file
139
src/Platform/Posix/Timer.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <BH/Timer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define TYPE_OLD_REALTIME 0x0000
|
||||
#define TYPE_NEW_REALTIME 0x0001
|
||||
#define TYPE_MONOTONIC 0x0002
|
||||
|
||||
|
||||
struct BH_Timer
|
||||
{
|
||||
int type;
|
||||
int64_t sec;
|
||||
int64_t nsec;
|
||||
};
|
||||
|
||||
|
||||
static int checkAvailableTimer(void)
|
||||
{
|
||||
#if (_POSIX_TIMERS > 0) || defined(BH_USE_CLOCK_GETTIME)
|
||||
struct timespec ts;
|
||||
|
||||
if (!clock_gettime(CLOCK_MONOTONIC, &ts))
|
||||
return TYPE_MONOTONIC;
|
||||
|
||||
if (!clock_gettime(CLOCK_REALTIME, &ts))
|
||||
return TYPE_NEW_REALTIME;
|
||||
#endif
|
||||
|
||||
return TYPE_OLD_REALTIME;
|
||||
}
|
||||
|
||||
|
||||
static void getTimer(int type,
|
||||
int64_t *sec,
|
||||
int64_t *nsec)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
#if (_POSIX_TIMERS > 0) || defined(BH_USE_CLOCK_GETTIME)
|
||||
case TYPE_MONOTONIC:
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
*sec = ts.tv_sec;
|
||||
*nsec = ts.tv_nsec;
|
||||
break;
|
||||
|
||||
case TYPE_NEW_REALTIME:
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
*sec = ts.tv_sec;
|
||||
*nsec = ts.tv_nsec;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
case TYPE_OLD_REALTIME:
|
||||
gettimeofday(&tv, NULL);
|
||||
*sec = tv.tv_sec;
|
||||
*nsec = tv.tv_usec * (uint64_t)1000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BH_Timer *BH_TimerNew(void)
|
||||
{
|
||||
BH_Timer *result;
|
||||
|
||||
result = malloc(sizeof(*result));
|
||||
if (result)
|
||||
{
|
||||
result->type = checkAvailableTimer();
|
||||
BH_TimerStart(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer)
|
||||
{
|
||||
free(timer);
|
||||
}
|
||||
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer)
|
||||
{
|
||||
return timer->type == TYPE_MONOTONIC;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer)
|
||||
{
|
||||
getTimer(timer->type, &timer->sec, &timer->nsec);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer)
|
||||
{
|
||||
int64_t oldSec, oldNsec;
|
||||
|
||||
oldSec = timer->sec; oldNsec = timer->nsec;
|
||||
|
||||
getTimer(timer->type, &timer->sec, &timer->nsec);
|
||||
oldSec = timer->sec - oldSec;
|
||||
oldNsec = timer->nsec - oldNsec;
|
||||
|
||||
return (oldSec * 1000) + (oldNsec / 1000000);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer)
|
||||
{
|
||||
int64_t newSec, newNsec;
|
||||
|
||||
getTimer(timer->type, &newSec, &newNsec);
|
||||
newSec -= timer->sec;
|
||||
newNsec -= timer->nsec;
|
||||
|
||||
return (newSec * 1000) + (newNsec / 1000000);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer)
|
||||
{
|
||||
int64_t newSec, newNsec;
|
||||
|
||||
getTimer(timer->type, &newSec, &newNsec);
|
||||
newSec -= timer->sec;
|
||||
newNsec -= timer->nsec;
|
||||
|
||||
return (newSec * 1000000000) + newNsec;
|
||||
}
|
||||
130
src/Platform/Win32/Timer.c
Normal file
130
src/Platform/Win32/Timer.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <BH/Timer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
#define TYPE_TICKS 0x0000
|
||||
#define TYPE_COUNTER 0x0001
|
||||
|
||||
|
||||
struct BH_Timer
|
||||
{
|
||||
int type;
|
||||
LARGE_INTEGER frequency;
|
||||
int64_t sec;
|
||||
int64_t nsec;
|
||||
};
|
||||
|
||||
|
||||
static int checkAvailableTimer(LARGE_INTEGER *frequency)
|
||||
{
|
||||
LARGE_INTEGER dummy;
|
||||
|
||||
if (QueryPerformanceFrequency(frequency))
|
||||
{
|
||||
if (QueryPerformanceCounter(&dummy))
|
||||
{
|
||||
return TYPE_COUNTER;
|
||||
}
|
||||
}
|
||||
|
||||
return TYPE_TICKS;
|
||||
}
|
||||
|
||||
|
||||
static void getTimer(int type,
|
||||
LARGE_INTEGER *frequency,
|
||||
int64_t *sec,
|
||||
int64_t *nsec)
|
||||
{
|
||||
LARGE_INTEGER newCount;
|
||||
int64_t newTicks;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_COUNTER:
|
||||
QueryPerformanceCounter(&newCount);
|
||||
*sec = newCount.QuadPart / frequency->QuadPart;
|
||||
*nsec = (newCount.QuadPart - *sec * frequency->QuadPart) * 1000000000 / frequency->QuadPart;
|
||||
break;
|
||||
|
||||
default:
|
||||
case TYPE_TICKS:
|
||||
newTicks = GetTickCount64();
|
||||
*sec = newTicks / 1000;
|
||||
*nsec = (newTicks - *sec * 1000) * 1000000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BH_Timer *BH_TimerNew(void)
|
||||
{
|
||||
BH_Timer *result;
|
||||
|
||||
result = malloc(sizeof(*result));
|
||||
if (result)
|
||||
{
|
||||
result->type = checkAvailableTimer(&result->frequency);
|
||||
BH_TimerStart(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerFree(BH_Timer *timer)
|
||||
{
|
||||
free(timer);
|
||||
}
|
||||
|
||||
|
||||
int BH_TimerIsMonotonic(BH_Timer *timer)
|
||||
{
|
||||
return timer->type == TYPE_COUNTER;
|
||||
}
|
||||
|
||||
|
||||
void BH_TimerStart(BH_Timer *timer)
|
||||
{
|
||||
getTimer(timer->type, &timer->frequency, &timer->sec, &timer->nsec);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerRestart(BH_Timer *timer)
|
||||
{
|
||||
int64_t oldSec, oldNsec;
|
||||
|
||||
oldSec = timer->sec; oldNsec = timer->nsec;
|
||||
|
||||
getTimer(timer->type, &timer->frequency, &timer->sec, &timer->nsec);
|
||||
oldSec = timer->sec - oldSec;
|
||||
oldNsec = timer->nsec - oldNsec;
|
||||
|
||||
return (oldSec * 1000) + (oldNsec / 1000000);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerMilliseconds(BH_Timer *timer)
|
||||
{
|
||||
int64_t newSec, newNsec;
|
||||
|
||||
getTimer(timer->type, &timer->frequency, &newSec, &newNsec);
|
||||
newSec -= timer->sec;
|
||||
newNsec -= timer->nsec;
|
||||
|
||||
return (newSec * 1000) + (newNsec / 1000000);
|
||||
}
|
||||
|
||||
|
||||
int64_t BH_TimerNanoseconds(BH_Timer *timer)
|
||||
{
|
||||
int64_t newSec, newNsec;
|
||||
|
||||
getTimer(timer->type, &timer->frequency, &newSec, &newNsec);
|
||||
newSec -= timer->sec;
|
||||
newNsec -= timer->nsec;
|
||||
|
||||
return (newSec * 1000000000) + newNsec;
|
||||
}
|
||||
36
test/src/TestTimer.c
Normal file
36
test/src/TestTimer.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <BH/Unit.h>
|
||||
#include <BH/Timer.h>
|
||||
#include <BH/Thread.h>
|
||||
|
||||
|
||||
BH_UNIT_TEST(General)
|
||||
{
|
||||
BH_Timer *timer;
|
||||
|
||||
BH_VERIFY((timer = BH_TimerNew()) != NULL);
|
||||
BH_ThreadSleep(5000);
|
||||
|
||||
BH_VERIFY(BH_TimerMilliseconds(timer) >= 5000);
|
||||
BH_VERIFY(BH_TimerNanoseconds(timer) >= 5000000000);
|
||||
BH_VERIFY(BH_TimerRestart(timer) >= 5000);
|
||||
BH_VERIFY(BH_TimerRestart(timer) < 5000);
|
||||
|
||||
BH_ThreadSleep(5000);
|
||||
BH_VERIFY(BH_TimerMilliseconds(timer) >= 5000);
|
||||
BH_TimerStart(timer);
|
||||
BH_VERIFY(BH_TimerMilliseconds(timer) < 5000);
|
||||
|
||||
BH_TimerFree(timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
BH_UNIT_ADD(General);
|
||||
|
||||
return BH_UnitRun();
|
||||
}
|
||||
Reference in New Issue
Block a user