Compare commits

...

2 Commits

14 changed files with 758 additions and 67 deletions

@ -19,21 +19,17 @@ add_compile_options(-Wall -Wextra -pedantic -Werror -fno-omit-frame-pointer)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
OPTION (USE_OpenMP "Use OpenMP" ON)
OPTION (USE_Threads "Use Threads" ON)
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_options(--coverage)
add_link_options(--coverage)
message( "Forcing OpenMP off for Debug build" )
set( USE_OpenMP OFF )
endif()
IF(USE_OpenMP)
FIND_PACKAGE(OpenMP REQUIRED)
IF(OPENMP_C_FOUND)
add_compile_options(${OpenMP_C_FLAGS})
add_link_options(${OpenMP_C_FLAGS})
ENDIF()
IF(USE_Threads)
find_package(Threads REQUIRED)
link_libraries(Threads::Threads)
add_definitions(-DUSE_THREADS)
ENDIF()
#jemalloc seems to be quite a bit faster

@ -107,3 +107,7 @@ target_link_libraries(texture_map PRIVATE
module_raytracer
module_patterns
module_shapes)
add_executable(metrics_histogram metrics_histogram.c)
target_link_libraries(metrics_histogram PRIVATE
module_utilities)

37
demo/metrics_histogram.c Normal file

@ -0,0 +1,37 @@
#include "metrics.h"
#include "exceptions.h"
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
CEXCEPTION_T e;
static uint64_t hash_string(const char *name) {
uint64_t hash = 5381;
int c;
while ((c = *name++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
int main(void) {
Try {
METRICS_INIT_DEFAULT_REGISTRY();
for (int i = 0; i < 100; i++) {
char name[20];
sprintf(name, "test_metric_%d", i);
uint64_t hash = hash_string(name) % 256;
METRICS_HISTOGRAM_UPDATE("hash_string", hash);
printf("Hashed String: %s -> %" PRIu64 "\n", name, hash);
}
METRICS_Histogram h = METRICS_HISTOGRAM_VALUE("hash_string");
printf("Histogram: min=%lu, max=%lu, count=%lu, mean=%f\n", h.min, h.max, h.count, h.mean);
} Catch(e) {
printf("exception %i occurred\n", e);
}
METRICS_DESTROY_DEFAULT_REGISTRY();
}

@ -11,12 +11,6 @@
#include <math.h>
#include <wavefrontobj.h>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_max_threads() 1
#endif
CEXCEPTION_T global_exception;
static void build_world(WORLD_World* world) {
TUPLES_Color red, green, blue;
@ -61,11 +55,21 @@ int main(int argc, char *argv[]) {
Try {
LOGGER_log(LOGGER_INFO, "Building world...\n");
TUPLES_Point* light_position = TUPLES_new_point(-10, 10, -10);
TUPLES_Color* light_color = TUPLES_new_color(1, 1, 1);
LIGHTS_Light* light = LIGHTS_new_pointlight(light_position, light_color);
TUPLES_delete_all(light_position, light_color);
TUPLES_Vector light_position, light_color;
TUPLES_init_point(&light_position, -10, 10, -10);
TUPLES_init_color(&light_color, 1, 1, 1);
#if 0
LIGHTS_Light* light = LIGHTS_new_pointlight(&light_position, &light_color);
#else
TUPLES_Vector v1, v2;
TUPLES_init_vector(&v1, 4, 0, 0);
TUPLES_init_vector(&v2, 0, 3, 0);
LIGHTS_Light* light = LIGHTS_new_arealight(&light_position, &v1, 8, &v2, 8, &light_color);
SEQUENCES_Sequence* seq = SEQUENCES_new_random(4096);
LIGHTS_set_jitter_on_area_light(light, seq);
SEQUENCES_delete(seq);
#endif
CAMERA_Camera* camera = CAMERA_new(1920,1080, M_PI / 3.0);
TUPLES_Point* from = TUPLES_new_point(0, 1.5, -5);
TUPLES_Point* to = TUPLES_new_point(0, 1, 0);
@ -103,7 +107,6 @@ int main(int argc, char *argv[]) {
UTILITIES_Timer_Results divide_results = UTILITIES_Timer_stop(divide_timer);
log_perf(divide_results, "Divide time: ");
WORLD_add_object(world, obj_group);
LOGGER_log(LOGGER_INFO, "Rendering with %i thread(s)...\n", omp_get_max_threads());
UTILITIES_Timer* render_timer = UTILITIES_Timer_start();
CANVAS_Canvas* canvas = CAMERA_render(camera, world);
UTILITIES_Timer_Results render_results = UTILITIES_Timer_stop(render_timer);

@ -7,6 +7,8 @@
#include <logger.h>
#include <math.h>
#include <memory.h>
#include <pthread.h>
#include <stdatomic.h>
static void calculate_pixel_size(CAMERA_Camera *camera) {
assert(camera);
@ -106,35 +108,70 @@ void CAMERA_ray_for_pixel(RAY_Ray *dest, const CAMERA_Camera *camera, unsigned i
RAY_init_from_tuples(dest, &origin, &direction);
}
CANVAS_Canvas *CAMERA_render(const CAMERA_Camera *camera, const WORLD_World *world) {
assert(camera);
assert(world);
CANVAS_Canvas *canvas = CANVAS_new(camera->hsize, camera->vsize);
const uint total_pixels = camera->vsize * camera->hsize;
const uint one_percent_pixels = total_pixels / 100;
uint pixel_count = 0;
#ifdef _OPENMP
#pragma omp parallel for collapse(2) schedule(dynamic)
#endif
for (uint y = 0; y < camera->vsize - 1; y++) {
for (uint x = 0; x < camera->hsize - 1; x++) {
uint this_pixel_count;
#ifdef _OPENMP
#pragma omp atomic capture
#endif
this_pixel_count = pixel_count++;
if (this_pixel_count % one_percent_pixels == 0) {
const uint percent = this_pixel_count * 100 / total_pixels;
LOGGER_log(LOGGER_INFO, "Rendering %u%% complete\n", percent);
typedef struct {
const CAMERA_Camera *camera;
const WORLD_World *world;
CANVAS_Canvas *canvas;
uint thread_num;
uint total_threads;
atomic_uint pixels_processed;
} render_thread_args;
static void *render_thread(void *args) {
assert(args);
render_thread_args *rta = (render_thread_args *)args;
assert(rta->camera);
assert(rta->world);
for (uint y = 0; y < rta->camera->vsize; y++) {
for (uint x = 0; x < rta->camera->hsize; x++) {
const uint this_pixel = y * rta->camera->hsize + x;
if (this_pixel % rta->total_threads != rta->thread_num) {
continue;
}
RAY_Ray ray;
TUPLES_Color color;
CAMERA_ray_for_pixel(&ray, camera, x, y);
WORLD_color_at(&color, world, &ray, WORLD_default_ttl);
CANVAS_write_pixel(canvas, x, y, &color);
CAMERA_ray_for_pixel(&ray, rta->camera, x, y);
WORLD_color_at(&color, rta->world, &ray, WORLD_default_ttl);
CANVAS_write_pixel(rta->canvas, x, y, &color);
rta->pixels_processed++;
}
}
return NULL;
}
CANVAS_Canvas *CAMERA_render(const CAMERA_Camera *camera, const WORLD_World *world) {
assert(camera);
assert(world);
CANVAS_Canvas *canvas = CANVAS_new(camera->hsize, camera->vsize);
long thread_count = UTILITIES_get_thread_count();
LOGGER_log(LOGGER_INFO, "Rendering with %i thread(s)...\n", thread_count);
pthread_t threads[thread_count];
render_thread_args args[thread_count];
for (uint i = 0; i < thread_count; i++) {
args[i].camera = camera;
args[i].world = world;
args[i].canvas = canvas;
args[i].thread_num = i;
args[i].total_threads = thread_count;
args[i].pixels_processed = 0;
pthread_create(&threads[i], NULL, &render_thread, &args[i]);
}
const uint total_pixels = camera->vsize * camera->hsize;
uint pixel_count = 0;
while (pixel_count != total_pixels) {
pixel_count = 0;
for (uint i = 0; i < thread_count; i++) {
pixel_count += args[i].pixels_processed;
}
const uint percent = pixel_count * 100 / total_pixels;
LOGGER_log(LOGGER_INFO, "Rendering %u%% complete (%u/%u)\n", percent, pixel_count, total_pixels);
UTILITIES_msleep(500);
}
for (uint i = 0; i < thread_count; i++) {
pthread_join(threads[i], NULL);
}
return canvas;
}

@ -5,18 +5,11 @@
#include <stdlib.h>
#include <string.h>
#include <utilities.h>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_num_threads() 1
#define omp_get_thread_num() 0
#define omp_get_max_threads() 1
#endif
#include <pthread.h>
typedef struct SEQUENCES_Sequence {
unsigned int count;
unsigned int *next_ndx;
pthread_key_t tl_next_ndx;
double seq[];
} SEQUENCES_Sequence;
@ -28,28 +21,35 @@ SEQUENCES_Sequence *SEQUENCES_new(unsigned int count, double numbers[]) {
}
seq->count = count;
memcpy(seq->seq, numbers, count * sizeof(numbers[0]));
seq->next_ndx = calloc(omp_get_max_threads(), sizeof(unsigned int));
pthread_key_create(&seq->tl_next_ndx, free);
return seq;
}
void SEQUENCES_delete(SEQUENCES_Sequence *sequence) {
assert(sequence);
free(sequence->next_ndx);
/* TODO: Is this needed? */
size_t* ndx = pthread_getspecific(sequence->tl_next_ndx);
if (ndx) {
free(ndx);
}
pthread_key_delete(sequence->tl_next_ndx);
free(sequence);
}
double SEQUENCES_next(SEQUENCES_Sequence *sequence) {
assert(sequence);
int thread_id = omp_get_thread_num();
unsigned int ndx = sequence->next_ndx[thread_id];
sequence->next_ndx[thread_id] = (ndx + 1) % sequence->count;
return sequence->seq[ndx];
size_t* ndx = pthread_getspecific(sequence->tl_next_ndx);
if (!ndx) {
ndx = calloc(1, sizeof(size_t));
pthread_setspecific(sequence->tl_next_ndx, ndx);
}
double val = sequence->seq[*ndx];
*ndx = (*ndx + 1) % sequence->count;
return val;
}
SEQUENCES_Sequence *SEQUENCES_copy(SEQUENCES_Sequence *sequence) {
SEQUENCES_Sequence *seq = SEQUENCES_new(sequence->count, sequence->seq);
memcpy(seq->next_ndx, sequence->next_ndx, omp_get_max_threads());
return seq;
}

@ -1,6 +1,8 @@
add_library(module_utilities STATIC
exceptions.h exceptions.c
logger.c logger.h utilities.h utilities.c)
exceptions.c exceptions.h
logger.c logger.h
utilities.c utilities.h
metrics.c metrics.h)
target_include_directories(module_utilities PUBLIC
${CMAKE_CURRENT_LIST_DIR}
@ -9,4 +11,5 @@ target_include_directories(module_utilities PUBLIC
target_link_libraries(module_utilities
CException
m
)

@ -8,7 +8,6 @@ typedef enum LOGGER_LEVEL { LOGGER_ERROR, LOGGER_WARN, LOGGER_INFO, LOGGER_DEBUG
* @param level A member of the LogLevel enum used to colorize terminal output
* @param msg The null terminated string
*/
//void LOGGER_log(LOGGER_LEVEL level, const char *fmt, ...);
void LOGGER_log_with_file_line(LOGGER_LEVEL level, char *file, int line, const char *fmt, ...);
#ifdef __clang__

349
module_utilities/metrics.c Normal file

@ -0,0 +1,349 @@
#include "metrics.h"
#include "exceptions.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_REGISTRY_CAPACITY 16
#define MAX_LOAD_FACTOR 0.75
METRICS_Registry *METRICS_DEFAULT_REGISTRY = NULL;
typedef enum MetricType { COUNTER, GAUGE, TIMER, HISTOGRAM } MetricType;
typedef struct METRICS_Counter {
long value;
} METRICS_Counter;
typedef struct METRICS_Gauge {
METRICS_Gauge_measure measure;
void *state;
} METRICS_Gauge;
typedef struct Registry_Entry {
char *name;
uint64_t hash;
MetricType type;
union {
METRICS_Counter counter;
METRICS_Gauge gauge;
// METRICS_Timer timer;
METRICS_Histogram histogram;
};
} Registry_Entry;
struct METRICS_Registry {
Registry_Entry *metric;
size_t capacity;
size_t count;
};
/* Forward declarations */
static Registry_Entry *find(METRICS_Registry *registry, const char *name, uint64_t hash);
static void init_counter_entry(METRICS_Registry *registry, Registry_Entry **entry, const char *name);
static void initialize_name(Registry_Entry *entry, const char *name);
static uint64_t hash_string(const char *name);
static bool check_load_factor(const METRICS_Registry *registry);
static void for_each(METRICS_Registry *registry, void (*func)(Registry_Entry *));
static void print_entry(Registry_Entry *entry);
void METRICS_dump_registry(METRICS_Registry *registry) {
assert(registry);
printf("Size: %zu\n", registry->count);
printf("Capacity: %zu\n", registry->capacity);
printf("Metrics:\n");
for_each(registry, print_entry);
}
static bool is_empty_entry(const Registry_Entry *entry) {
assert(entry);
return !entry->name;
}
long METRICS_Counter_inc(METRICS_Registry *registry, const char *name) {
assert(registry);
assert(name);
uint64_t hash = hash_string(name);
long value;
{
Registry_Entry *entry = find(registry, name, hash);
if (is_empty_entry(entry)) {
init_counter_entry(registry, &entry, name);
}
assert(entry->type == COUNTER);
value = entry->counter.value++;
}
return value;
}
long METRICS_Counter_dec(METRICS_Registry *registry, const char *name) {
assert(registry);
assert(name);
uint64_t hash = hash_string(name);
long value;
{
Registry_Entry *entry = find(registry, name, hash);
if (is_empty_entry(entry)) {
init_counter_entry(registry, &entry, name);
}
assert(entry->type == COUNTER);
value = entry->counter.value--;
}
return value;
}
long METRICS_Counter_value(METRICS_Registry *registry, const char *name) {
assert(registry);
assert(name);
uint64_t hash = hash_string(name);
long value;
{
Registry_Entry *entry = find(registry, name, hash);
if (is_empty_entry(entry)) {
init_counter_entry(registry, &entry, name);
}
assert(entry->type == COUNTER);
value = entry->counter.value;
}
return value;
}
long METRICS_Gauge_value(METRICS_Registry *registry, const char *name) {
assert(registry);
assert(name);
uint64_t hash = hash_string(name);
long value = 0;
bool found = false;
{
Registry_Entry *entry = find(registry, name, hash);
found = !is_empty_entry(entry) && entry->type == GAUGE;
if (found) {
value = entry->gauge.measure(entry->gauge.state);
}
}
if (!found) {
Throw(E_INVALID_ARGUMENT);
}
return value;
}
METRICS_Registry *METRICS_Registry_new(void) {
METRICS_Registry *registry = calloc(1, sizeof(METRICS_Registry));
if (!registry) {
Throw(E_MALLOC_FAILED);
return NULL;
}
registry->metric = calloc(INITIAL_REGISTRY_CAPACITY, sizeof(Registry_Entry));
if (!registry->metric) {
free(registry);
Throw(E_MALLOC_FAILED);
return NULL;
}
registry->capacity = INITIAL_REGISTRY_CAPACITY;
return registry;
}
void METRICS_Registry_delete(METRICS_Registry *registry) {
assert(registry);
assert(registry->metric);
free(registry->metric);
free(registry);
}
static uint64_t hash_string(const char *name) {
assert(name);
uint64_t hash = 5381;
int c;
while ((c = *name++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
static size_t calculate_index(Registry_Entry *entry, size_t capacity, const char *name, uint64_t hash) {
assert(entry);
assert(name);
assert(capacity > 0);
size_t index = hash % capacity;
while (!is_empty_entry(&entry[index])) {
if (entry[index].hash == hash && strcmp(entry[index].name, name) == 0) {
/* found the entry */
return index;
}
index = (index + 1) % capacity;
}
/* found an empty entry */
return index;
}
static void initialize_name(Registry_Entry *entry, const char *name) {
assert(entry);
assert(name);
entry->name = strdup(name);
if (!entry->name) {
Throw(E_MALLOC_FAILED);
}
}
static Registry_Entry *find(METRICS_Registry *registry, const char *name, uint64_t hash) {
assert(registry);
assert(name);
size_t index = calculate_index(registry->metric, registry->capacity, name, hash);
return &registry->metric[index];
}
static void init_entry(METRICS_Registry *registry, Registry_Entry *entry, MetricType type, const char *name, uint64_t hash) {
assert(registry);
assert(entry);
assert(name);
entry->type = type;
initialize_name(entry, name);
registry->count++;
entry->hash = hash;
}
static bool check_load_factor(const METRICS_Registry *registry) {
assert(registry);
return registry->count + 1 > registry->capacity * MAX_LOAD_FACTOR;
}
static void expand_map(METRICS_Registry *registry) {
assert(registry);
if (check_load_factor(registry)) {
size_t new_capacity = registry->capacity * 2;
Registry_Entry *new_metric = calloc(new_capacity, sizeof(Registry_Entry));
if (!new_metric) {
Throw(E_MALLOC_FAILED);
}
for (size_t i = 0; i < registry->capacity; i++) {
if (!is_empty_entry(&registry->metric[i])) {
uint64_t hash = hash_string(registry->metric[i].name);
size_t index = calculate_index(new_metric, new_capacity, registry->metric[i].name, hash);
new_metric[index] = registry->metric[i];
}
}
free(registry->metric);
registry->metric = new_metric;
registry->capacity = new_capacity;
}
}
static void init_counter_entry(METRICS_Registry *registry, Registry_Entry **entry, const char *name) {
assert(registry);
assert(entry);
assert(name);
uint64_t hash = hash_string(name);
expand_map(registry);
*entry = find(registry, name, hash);
init_entry(registry, *entry, COUNTER, name, hash);
(*entry)->counter.value = 0;
}
static void init_histogram_entry(METRICS_Registry *registry, Registry_Entry **entry, const char *name) {
assert(registry);
assert(entry);
assert(name);
uint64_t hash = hash_string(name);
expand_map(registry);
*entry = find(registry, name, hash);
init_entry(registry, *entry, HISTOGRAM, name, hash);
(*entry)->histogram.min = 0;
(*entry)->histogram.max = 0;
(*entry)->histogram.count = 0;
(*entry)->histogram.mean = 0;
}
void METRICS_Histogram_update(METRICS_Registry *registry, const char *name, long value) {
assert(registry);
assert(name);
uint64_t hash = hash_string(name);
{
Registry_Entry *entry = find(registry, name, hash);
if (is_empty_entry(entry)) {
init_histogram_entry(registry, &entry, name);
}
assert(entry->type == HISTOGRAM);
if (entry->histogram.count == 0) {
entry->histogram.min = value;
entry->histogram.max = value;
entry->histogram.mean = value;
} else {
if (value < entry->histogram.min) {
entry->histogram.min = value;
}
if (value > entry->histogram.max) {
entry->histogram.max = value;
}
entry->histogram.mean = (entry->histogram.mean * entry->histogram.count + value) / (entry->histogram.count + 1);
}
entry->histogram.count++;
}
}
METRICS_Histogram METRICS_Histogram_value(METRICS_Registry *registry, const char *name) {
assert(registry);
assert(name);
METRICS_Histogram histogram;
uint64_t hash = hash_string(name);
{
Registry_Entry *entry = find(registry, name, hash);
if (is_empty_entry(entry)) {
init_histogram_entry(registry, &entry, name);
}
assert(entry->type == HISTOGRAM);
histogram = entry->histogram;
}
return histogram;
}
void METRICS_Gauge_register(METRICS_Registry *registry, const char *name, METRICS_Gauge_measure measure, void *state) {
assert(registry);
assert(name);
assert(measure);
uint64_t hash = hash_string(name);
bool exists = false;
{
expand_map(registry);
size_t index = calculate_index(registry->metric, registry->capacity, name, hash);
if (is_empty_entry(&registry->metric[index])) {
init_entry(registry, &registry->metric[index], GAUGE, name, hash);
registry->metric[index].gauge.measure = measure;
registry->metric[index].gauge.state = state;
} else {
exists = true;
}
}
if (exists) {
Throw(E_INVALID_ARGUMENT);
}
}
static void for_each(METRICS_Registry *registry, void (*func)(Registry_Entry *)) {
assert(registry);
assert(func);
for (size_t i = 0; i < registry->capacity; i++) {
if (!is_empty_entry(&registry->metric[i])) {
func(&registry->metric[i]);
}
}
}
static void print_entry(Registry_Entry *entry) {
assert(entry);
switch (entry->type) {
case COUNTER:
printf("%s: %ld\n", entry->name, entry->counter.value);
break;
case GAUGE:
printf("%s: %ld\n", entry->name, entry->gauge.measure(entry->gauge.state));
break;
default:
break;
}
}

@ -0,0 +1,82 @@
#ifndef SIMPLE_RAYTRACER_METRICS_H
#define SIMPLE_RAYTRACER_METRICS_H
/*
* TODO:
* - [X] Add registry for metrics
* - [ ] Add metric types:
* - [X] Counter - incrementing and decrementing
* - [X] Gauge - measure an instantaneous value
* - [ ] Meter - measure a rate of events
* - [ ] Timer - measure the duration of an event
* - [X] Histogram - measure the distribution of values
* - [ ] Add reporter implementation
* - [X] Add default registry
* - [ ] Improve histogram - double instead of long? Add percentiles? Options for sliding window vs. cumulative?
*/
typedef struct METRICS_Registry METRICS_Registry;
METRICS_Registry *METRICS_Registry_new(void);
void METRICS_Registry_delete(METRICS_Registry *registry);
/**
* Increments the counter by 1
* @param registry
* @param name
* @return pre-increment value
*/
long METRICS_Counter_inc(METRICS_Registry *registry, const char *name);
/**
* Decrements the counter by 1
* @param registry
* @param name
* @return pre-decrement value
*/
long METRICS_Counter_dec(METRICS_Registry *registry, const char *name);
/**
* Returns the current value of the counter
* @param registry
* @param name
* @return
*/
long METRICS_Counter_value(METRICS_Registry *registry, const char *name);
long METRICS_Gauge_value(METRICS_Registry *registry, const char *name);
typedef long (*METRICS_Gauge_measure)(void *state);
void METRICS_Gauge_register(METRICS_Registry *registry, const char *name, METRICS_Gauge_measure measure, void *state);
typedef struct METRICS_Histogram {
long min, max, count;
double mean;
} METRICS_Histogram;
void METRICS_Histogram_update(METRICS_Registry *registry, const char *name, long value);
METRICS_Histogram METRICS_Histogram_value(METRICS_Registry *registry, const char *name);
void METRICS_Timer_start(METRICS_Registry *registry, const char *name);
void METRICS_Timer_stop(METRICS_Registry *registry, const char *name);
/**
* Dumps metrics to stdout, generally for debugging purposes
* @param registry
*/
void METRICS_dump_registry(METRICS_Registry *registry);
/* Helpers for default registry */
extern METRICS_Registry *METRICS_DEFAULT_REGISTRY;
#define METRICS_INIT_DEFAULT_REGISTRY() METRICS_DEFAULT_REGISTRY = METRICS_Registry_new()
#define METRICS_DESTROY_DEFAULT_REGISTRY() METRICS_Registry_delete(METRICS_DEFAULT_REGISTRY)
#define METRICS_COUNTER_INC(name) METRICS_Counter_inc(METRICS_DEFAULT_REGISTRY, (name))
#define METRICS_COUNTER_DEC(name) METRICS_Counter_dec(METRICS_DEFAULT_REGISTRY, (name))
#define METRICS_COUNTER_VALUE(name) METRICS_Counter_value(METRICS_DEFAULT_REGISTRY, (name))
#define METRICS_GAUGE_REGISTER(name, measure, state) METRICS_Gauge_register(METRICS_DEFAULT_REGISTRY, (name), (measure), (state))
#define METRICS_GAUGE_VALUE(name) METRICS_Gauge_value(METRICS_DEFAULT_REGISTRY, (name))
#define METRICS_HISTOGRAM_UPDATE(name, value) METRICS_Histogram_update(METRICS_DEFAULT_REGISTRY, (name), (value))
#define METRICS_HISTOGRAM_VALUE(name) METRICS_Histogram_value(METRICS_DEFAULT_REGISTRY, (name))
#endif // SIMPLE_RAYTRACER_METRICS_H

@ -1,11 +1,13 @@
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/times.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/times.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include "exceptions.h"
#include "utilities.h"
@ -142,3 +144,25 @@ char *UTILITIES_slurp(char *input_filename) {
return buffer;
}
long UTILITIES_get_thread_count(void) {
return sysconf(_SC_NPROCESSORS_ONLN);
}
/* https://stackoverflow.com/questions/1157209/is-there-an-alternative-sleep-function-in-c-to-milliseconds */
void UTILITIES_msleep(long msec)
{
if (msec < 0) {
Throw(E_INVALID_ARGUMENT);
return;
}
struct timespec ts;
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
int res;
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
}

@ -76,4 +76,7 @@ UTILITIES_Timer_Results UTILITIES_Timer_stop(UTILITIES_Timer *timer);
char *UTILITIES_slurp(char *input_filename);
long UTILITIES_get_thread_count(void);
void UTILITIES_msleep(long milliseconds);
#endif // UTILITIES_UTILITIES_H

@ -11,3 +11,10 @@ target_link_libraries(module_utilities_utilities
module_utilities
Unity)
add_test(utilities_utilities module_utilities_utilities)
add_executable(module_utilities_metrics
test_metrics.c)
target_link_libraries(module_utilities_metrics
module_utilities
Unity)
add_test(utilities_metrics module_utilities_metrics)

@ -0,0 +1,147 @@
#include <unity.h>
#include "exceptions.h"
#include "metrics.h"
#include "utilities.h"
CEXCEPTION_T e;
METRICS_Registry *registry;
void setUp(void) { registry = METRICS_Registry_new(); }
void tearDown(void) { METRICS_Registry_delete(registry); }
void test_create_registry(void) { TEST_ASSERT_NOT_NULL(registry); }
void test_default_counter_value(void) { TEST_ASSERT_EQUAL(0, METRICS_Counter_value(registry, "test_counter")); }
void test_increment_counter(void) {
METRICS_Counter_inc(registry, "test_counter");
TEST_ASSERT_EQUAL(1, METRICS_Counter_value(registry, "test_counter"));
}
void test_decrement_counter(void) {
METRICS_Counter_dec(registry, "test_counter");
TEST_ASSERT_EQUAL(-1, METRICS_Counter_value(registry, "test_counter"));
}
void test_inc_dec_counter(void) {
for (int i = 0; i < 5; i++) {
METRICS_Counter_inc(registry, "test_counter");
}
TEST_ASSERT_EQUAL(5, METRICS_Counter_value(registry, "test_counter"));
for (int i = 0; i < 10; i++) {
METRICS_Counter_dec(registry, "test_counter");
}
TEST_ASSERT_EQUAL(-5, METRICS_Counter_value(registry, "test_counter"));
}
long get_value_2(void *state) {
UNUSED(state);
return 2;
}
void test_get_gauge_value(void) {
METRICS_Gauge_register(registry, "test_gauge", get_value_2, NULL);
TEST_ASSERT_EQUAL(2, METRICS_Gauge_value(registry, "test_gauge"));
}
void test_gauge_register_should_throw_exception_when_use_name_for_two_different_metric_types(void) {
METRICS_Counter_inc(registry, "test_metric");
bool exception_thrown = false;
Try { METRICS_Gauge_register(registry, "test_metric", get_value_2, NULL); }
Catch(e) { exception_thrown = true; }
TEST_ASSERT_TRUE(exception_thrown);
}
void test_getting_unregistered_gauge_value_should_throw_exception(void) {
bool exception_thrown = false;
Try { METRICS_Gauge_value(registry, "test_gauge"); }
Catch(e) { exception_thrown = true; }
TEST_ASSERT_TRUE(exception_thrown);
}
void test_adding_more_than_initial_registry_size_metrics(void) {
for (int i = 0; i < 100; i++) {
char name[20];
sprintf(name, "test_metric_%d", i);
METRICS_Counter_inc(registry, name);
}
METRICS_dump_registry(registry);
for (int i = 0; i < 100; i++) {
char name[20];
sprintf(name, "test_metric_%d", i);
TEST_ASSERT_EQUAL(1, METRICS_Counter_value(registry, name));
}
}
void test_initialize_default_registry(void) {
METRICS_INIT_DEFAULT_REGISTRY();
TEST_ASSERT_NOT_NULL(METRICS_DEFAULT_REGISTRY);
METRICS_DESTROY_DEFAULT_REGISTRY();
}
void test_inc_and_dec_counter_in_default_registry(void) {
METRICS_INIT_DEFAULT_REGISTRY();
METRICS_COUNTER_INC("test_counter");
TEST_ASSERT_EQUAL(1, METRICS_COUNTER_VALUE("test_counter"));
METRICS_COUNTER_DEC("test_counter");
TEST_ASSERT_EQUAL(0, METRICS_COUNTER_VALUE("test_counter"));
METRICS_DESTROY_DEFAULT_REGISTRY();
}
void test_gauge_in_default_registry(void) {
METRICS_INIT_DEFAULT_REGISTRY();
METRICS_GAUGE_REGISTER("test_gauge", get_value_2, NULL);
TEST_ASSERT_EQUAL(2, METRICS_GAUGE_VALUE("test_gauge"));
METRICS_DESTROY_DEFAULT_REGISTRY();
}
void test_update_histogram(void) {
METRICS_Histogram_update(registry, "test_histogram", 1);
METRICS_Histogram_update(registry, "test_histogram", 2);
}
void test_get_histogram_value(void) {
METRICS_Histogram_update(registry, "test_histogram", 1);
METRICS_Histogram_update(registry, "test_histogram", 3);
METRICS_Histogram_update(registry, "test_histogram", 5);
METRICS_Histogram_update(registry, "test_histogram", 7);
METRICS_Histogram histogram = METRICS_Histogram_value(registry, "test_histogram");
TEST_ASSERT_EQUAL(1, histogram.min);
TEST_ASSERT_EQUAL(7, histogram.max);
TEST_ASSERT_EQUAL(4, histogram.count);
TEST_ASSERT_EQUAL(4, histogram.mean);
}
void test_histogram_with_default_registry(void) {
METRICS_INIT_DEFAULT_REGISTRY();
METRICS_HISTOGRAM_UPDATE("test_histogram", 1);
METRICS_HISTOGRAM_UPDATE("test_histogram", 3);
METRICS_HISTOGRAM_UPDATE("test_histogram", 5);
METRICS_HISTOGRAM_UPDATE("test_histogram", 7);
METRICS_Histogram histogram = METRICS_HISTOGRAM_VALUE("test_histogram");
TEST_ASSERT_EQUAL(1, histogram.min);
TEST_ASSERT_EQUAL(7, histogram.max);
TEST_ASSERT_EQUAL(4, histogram.count);
TEST_ASSERT_EQUAL(4, histogram.mean);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_create_registry);
RUN_TEST(test_default_counter_value);
RUN_TEST(test_increment_counter);
RUN_TEST(test_decrement_counter);
RUN_TEST(test_inc_dec_counter);
RUN_TEST(test_get_gauge_value);
RUN_TEST(test_gauge_register_should_throw_exception_when_use_name_for_two_different_metric_types);
RUN_TEST(test_getting_unregistered_gauge_value_should_throw_exception);
RUN_TEST(test_adding_more_than_initial_registry_size_metrics);
RUN_TEST(test_initialize_default_registry);
RUN_TEST(test_inc_and_dec_counter_in_default_registry);
RUN_TEST(test_gauge_in_default_registry);
RUN_TEST(test_update_histogram);
RUN_TEST(test_get_histogram_value);
RUN_TEST(test_histogram_with_default_registry);
return UNITY_END();
}