Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,23 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
endif()
unset(${PROJECT_NAME}_soversion)

if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS)
if(WIN32)
add_library(clock_interface INTERFACE) # It is empty on Windows.
else()
find_package(ClockGettime REQUIRED)
add_library(clock_interface ALIAS POSIX::clock_gettime)
endif()
endif()


if(SECP256K1_BUILD_BENCHMARK)
add_executable(bench bench.c)
target_link_libraries(bench secp256k1)
target_link_libraries(bench secp256k1 clock_interface)
add_executable(bench_internal bench_internal.c)
target_link_libraries(bench_internal secp256k1_precomputed secp256k1_asm)
target_link_libraries(bench_internal secp256k1_precomputed secp256k1_asm clock_interface)
add_executable(bench_ecmult bench_ecmult.c)
target_link_libraries(bench_ecmult secp256k1_precomputed secp256k1_asm)
target_link_libraries(bench_ecmult secp256k1_precomputed secp256k1_asm clock_interface)
endif()

if(SECP256K1_BUILD_TESTS)
Expand All @@ -145,13 +155,13 @@ if(SECP256K1_BUILD_TESTS)
endif()

add_executable(noverify_tests tests.c)
target_link_libraries(noverify_tests secp256k1_precomputed secp256k1_asm)
target_link_libraries(noverify_tests secp256k1_precomputed secp256k1_asm clock_interface)
target_compile_definitions(noverify_tests PRIVATE ${TEST_DEFINITIONS})
add_test(NAME secp256k1_noverify_tests COMMAND noverify_tests)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage")
add_executable(tests tests.c)
target_compile_definitions(tests PRIVATE VERIFY ${TEST_DEFINITIONS})
target_link_libraries(tests secp256k1_precomputed secp256k1_asm)
target_link_libraries(tests secp256k1_precomputed secp256k1_asm clock_interface)
add_test(NAME secp256k1_tests COMMAND tests)
endif()
unset(TEST_DEFINITIONS)
Expand Down
4 changes: 2 additions & 2 deletions src/bench.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setu
if (setup != NULL) {
setup(data);
}
begin = gettime_i64();
begin = gettime_us();
benchmark(data, iter);
total = gettime_i64() - begin;
total = gettime_us() - begin;
if (teardown != NULL) {
teardown(data, iter);
}
Expand Down
47 changes: 30 additions & 17 deletions src/tests_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,38 @@

#include <stdint.h>

#if (defined(_MSC_VER) && _MSC_VER >= 1900)
# include <time.h>
#else
# include <sys/time.h>
#if defined(_WIN32)
# include <windows.h>
#else /* POSIX */
# include <time.h>
#endif

static int64_t gettime_i64(void) {
#if (defined(_MSC_VER) && _MSC_VER >= 1900)
/* C11 way to get wallclock time */
struct timespec tv;
if (!timespec_get(&tv, TIME_UTC)) {
fputs("timespec_get failed!", stderr);
exit(EXIT_FAILURE);
}
return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL;
static int64_t gettime_us(void) {
#if defined(_WIN32)

LARGE_INTEGER freq, counter;
QueryPerformanceFrequency(&freq);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency, freq should be static and initialized only once. The function's description says:

The frequency of the performance counter is fixed at system boot and is consistent across all processors. Therefore, the frequency need only be queried upon application initialization, and the result can be cached.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, it's not mandatory but recommended. It is redundant to initialize it every time but I found no other solution since we are not in an OOP environment. and using an if statement to initialize it only the first time doesn't seem optimal.

Copy link
Member

@furszy furszy Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Using an init_time(void) function could work in that case, but it would require every binary to call this method early in every program’s execution. All good anyway.

Another nit; QueryPerformanceFrequency returns 0 if the call fails, so it would be good to handle that as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another nit; QueryPerformanceFrequency returns 0 if the call fails, so it would be good to handle that as well.

my reasoning Is the same here. It would be one extra branch. and a failure in the clock is not a huge deal.

QueryPerformanceCounter(&counter);
return (int64_t)(counter.QuadPart * 1000000 / freq.QuadPart);

#else /* POSIX */

# if defined(CLOCK_PROCESS_CPUTIME_ID)
/* In theory, CLOCK_PROCESS_CPUTIME_ID is only useful if the process is locked to a core,
* see `man clock_gettime` on Linux. In practice, modern CPUs have synchronized TSCs which
* address this issue, see https://docs.amd.com/r/en-US/ug1586-onload-user/Timer-TSC-Stability . */
const clockid_t clock_type = CLOCK_PROCESS_CPUTIME_ID;
# elif defined(CLOCK_MONOTONIC)
/* fallback to global timer */
const clockid_t clock_type = CLOCK_MONOTONIC;
# else
/* fallback to wall-clock timer */
const clockid_t clock_type = CLOCK_REALTIME;
# endif

struct timespec ts;
clock_gettime(clock_type, &ts);
return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
#endif
}

Expand Down
8 changes: 4 additions & 4 deletions src/unit_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,10 @@ static int read_args(int argc, char** argv, int start, struct tf_framework* tf)
}

static void run_test_log(const struct tf_test_entry* t) {
int64_t start_time = gettime_i64();
int64_t start_time = gettime_us();
printf("Running %s..\n", t->name);
t->func();
printf("Test %s PASSED (%.3f sec)\n", t->name, (double)(gettime_i64() - start_time) / 1000000);
printf("Test %s PASSED (%.3f sec)\n", t->name, (double)(gettime_us() - start_time) / 1000000);
}

static void run_test(const struct tf_test_entry* t) { t->func(); }
Expand Down Expand Up @@ -416,7 +416,7 @@ static int tf_run(struct tf_framework* tf) {
/* Loop iterator */
int it;
/* Initial test time */
int64_t start_time = gettime_i64();
int64_t start_time = gettime_us();
/* Verify 'tf_init' has been called */
if (!tf->fn_run_test) {
fprintf(stderr, "Error: No test runner set. You must call 'tf_init' first to initialize the framework "
Expand Down Expand Up @@ -472,7 +472,7 @@ static int tf_run(struct tf_framework* tf) {
}

/* Print accumulated time */
printf("Total execution time: %.3f seconds\n", (double)(gettime_i64() - start_time) / 1000000);
printf("Total execution time: %.3f seconds\n", (double)(gettime_us() - start_time) / 1000000);
if (tf->fn_teardown && tf->fn_teardown() != 0) return EXIT_FAILURE;

return status;
Expand Down