Add timers for measuring elapsed time

This commit is contained in:
2025-09-21 22:07:54 +03:00
parent 9bd2007023
commit b1870bd709
11 changed files with 565 additions and 3 deletions

View File

@@ -102,14 +102,17 @@ file(GLOB BH_HEADER
set(BH_SOURCE_DUMMY set(BH_SOURCE_DUMMY
src/Platform/Dummy/File.c src/Platform/Dummy/File.c
src/Platform/Dummy/Timer.c
) )
set(BH_SOURCE_WIN32 set(BH_SOURCE_WIN32
src/Platform/Win32/File.c src/Platform/Win32/File.c
src/Platform/Win32/Timer.c
) )
set(BH_SOURCE_POSIX set(BH_SOURCE_POSIX
src/Platform/Posix/File.c src/Platform/Posix/File.c
src/Platform/Posix/Timer.c
) )
set(BH_SOURCE_WIN32_MT set(BH_SOURCE_WIN32_MT

9
configure vendored
View File

@@ -164,9 +164,12 @@ done
add_source "${source_path}src/Platform/Spinlock.c" add_source "${source_path}src/Platform/Spinlock.c"
case "$platform" in case "$platform" in
Posix) add_source "${source_path}src/Platform/Posix/File.c" ;; 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/Posix/Timer.c" ;;
*) add_source "${source_path}src/Platform/Dummy/File.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 esac
if [ "$enable_mt" = "yes" ]; then if [ "$enable_mt" = "yes" ]; then

View 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>

View File

@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
BH_Ray3f.html \ BH_Ray3f.html \
BH_String.html \ BH_String.html \
BH_Thread.html \ BH_Thread.html \
BH_Timer.html \
BH_Unicode.html \ BH_Unicode.html \
BH_Util.html \ BH_Util.html \
BH_Vec2f.html \ BH_Vec2f.html \
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
BH_Ray3f.3 \ BH_Ray3f.3 \
BH_String.3 \ BH_String.3 \
BH_Thread.3 \ BH_Thread.3 \
BH_Timer.3 \
BH_Unicode.3 \ BH_Unicode.3 \
BH_Util.3 \ BH_Util.3 \
BH_Vec2f.3 \ BH_Vec2f.3 \

View 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>

View File

@@ -20,6 +20,7 @@ HTMLS = BH_Algo.html \
BH_Ray3f.html \ BH_Ray3f.html \
BH_String.html \ BH_String.html \
BH_Thread.html \ BH_Thread.html \
BH_Timer.html \
BH_Unicode.html \ BH_Unicode.html \
BH_Util.html \ BH_Util.html \
BH_Vec2f.html \ BH_Vec2f.html \
@@ -48,6 +49,7 @@ MANS = BH_Algo.3 \
BH_Ray3f.3 \ BH_Ray3f.3 \
BH_String.3 \ BH_String.3 \
BH_Thread.3 \ BH_Thread.3 \
BH_Timer.3 \
BH_Unicode.3 \ BH_Unicode.3 \
BH_Util.3 \ BH_Util.3 \
BH_Vec2f.3 \ BH_Vec2f.3 \

32
include/BH/Timer.h Normal file
View 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 */

View 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
View 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
View 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
View 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();
}