memory & network usage statistics

This commit is contained in:
jacob 2025-02-08 09:28:38 -06:00
parent a78c5e1a47
commit ca664ac291
10 changed files with 91 additions and 14 deletions

View File

@ -31,6 +31,8 @@ struct exit_callback {
struct sys_thread thread; struct sys_thread thread;
}; };
struct app_statistics _g_app_statistics = ZI;
GLOBAL struct { GLOBAL struct {
struct arena arena; struct arena arena;
struct string write_path; struct string write_path;

View File

@ -4,6 +4,19 @@
#define APP_EXIT_CALLBACK_FUNC_DEF(name) void name(void) #define APP_EXIT_CALLBACK_FUNC_DEF(name) void name(void)
typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func); typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func);
struct app_statistics {
struct atomic_u64 host_bytes_sent;
struct atomic_u64 host_bytes_received;
struct atomic_u64 memory_committed;
struct atomic_u64 memory_reserved;
};
INLINE struct app_statistics *app_statistics(void)
{
extern struct app_statistics _g_app_statistics;
return &_g_app_statistics;
}
struct string app_write_path_cat(struct arena *arena, struct string filename); struct string app_write_path_cat(struct arena *arena, struct string filename);
/* Register a function that will be called when the application exits */ /* Register a function that will be called when the application exits */

View File

@ -2,6 +2,8 @@
#include "sys.h" #include "sys.h"
#include "memory.h" #include "memory.h"
#include "string.h" #include "string.h"
#include "atomic.h"
#include "app.h"
/* Arbitrary block size */ /* Arbitrary block size */
#define ARENA_BLOCK_SIZE 4096 #define ARENA_BLOCK_SIZE 4096
@ -25,16 +27,18 @@ struct arena arena_alloc(u64 reserve)
sys_panic(LIT("Failed to reserve memory")); sys_panic(LIT("Failed to reserve memory"));
} }
arena.reserved = reserve; arena.reserved = reserve;
atomic_u64_eval_add_u64(&app_statistics()->memory_reserved, arena.reserved);
/* Commit one block to start with */ /* Commit one block to start with */
arena.base = sys_memory_commit(arena.base, ARENA_BLOCK_SIZE); arena.base = sys_memory_commit(arena.base, ARENA_BLOCK_SIZE);
__profalloc(arena.base, ARENA_BLOCK_SIZE);
ASAN_POISON(arena.base, ARENA_BLOCK_SIZE);
if (!arena.base) { if (!arena.base) {
/* Hard fail on commit failure */ /* Hard fail on commit failure */
sys_panic(LIT("Failed to commit initial memory block: System may be out of memory")); sys_panic(LIT("Failed to commit initial memory block: System may be out of memory"));
} }
arena.committed = ARENA_BLOCK_SIZE; arena.committed = ARENA_BLOCK_SIZE;
atomic_u64_eval_add_u64(&app_statistics()->memory_committed, arena.committed);
__profalloc(arena.base, ARENA_BLOCK_SIZE);
ASAN_POISON(arena.base, ARENA_BLOCK_SIZE);
/* Arena should be 64k aligned */ /* Arena should be 64k aligned */
ASSERT(((u64)arena.base & 0xFFFF) == 0); ASSERT(((u64)arena.base & 0xFFFF) == 0);
@ -44,9 +48,11 @@ struct arena arena_alloc(u64 reserve)
void arena_release(struct arena *arena) void arena_release(struct arena *arena)
{ {
ASAN_UNPOISON(arena->base, arena->committed);
__prof; __prof;
__proffree(arena->base); __proffree(arena->base);
ASAN_UNPOISON(arena->base, arena->committed); atomic_u64_eval_add_i64(&app_statistics()->memory_committed, -((i64)arena->committed));
atomic_u64_eval_add_i64(&app_statistics()->memory_reserved, -((i64)arena->reserved));
sys_memory_release(arena->base); sys_memory_release(arena->base);
} }
@ -79,10 +85,11 @@ void *arena_push_bytes(struct arena *arena, u64 size, u64 align)
/* Hard fail on memory allocation failure for now */ /* Hard fail on memory allocation failure for now */
sys_panic(LIT("Failed to commit new memory block: System may be out of memory")); sys_panic(LIT("Failed to commit new memory block: System may be out of memory"));
} }
arena->committed += commit_bytes;
atomic_u64_eval_add_u64(&app_statistics()->memory_committed, commit_bytes);
__proffree(arena->base); __proffree(arena->base);
__profalloc(arena->base, arena->committed + commit_bytes); __profalloc(arena->base, arena->committed + commit_bytes);
ASAN_POISON(commit_address, commit_bytes); ASAN_POISON(commit_address, commit_bytes);
arena->committed += commit_bytes;
} }
start = arena->base + aligned_start_pos; start = arena->base + aligned_start_pos;
arena->pos = new_pos; arena->pos = new_pos;
@ -114,6 +121,7 @@ void arena_decommit_unused_blocks(struct arena *arena)
u64 decommit_size = (arena->base + arena->committed) - decommit_start; u64 decommit_size = (arena->base + arena->committed) - decommit_start;
sys_memory_decommit(decommit_start, decommit_size); sys_memory_decommit(decommit_start, decommit_size);
arena->committed = next_block_pos; arena->committed = next_block_pos;
atomic_u64_eval_add_i64(&app_statistics()->memory_committed, -((i64)decommit_size));
} }
} }

View File

@ -22,7 +22,8 @@ FORCE_INLINE volatile i64 *atomic_i64_raw(struct atomic_i64 *x) { return &x->_v;
FORCE_INLINE u32 atomic_u32_eval(struct atomic_u32 *x) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, 0); } FORCE_INLINE u32 atomic_u32_eval(struct atomic_u32 *x) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, 0); }
FORCE_INLINE u32 atomic_u32_inc_eval(struct atomic_u32 *x) { return (u32)_InterlockedIncrement((volatile long *)&x->_v); } FORCE_INLINE u32 atomic_u32_inc_eval(struct atomic_u32 *x) { return (u32)_InterlockedIncrement((volatile long *)&x->_v); }
FORCE_INLINE u32 atomic_u32_dec_eval(struct atomic_u32 *x) { return (u32)_InterlockedDecrement((volatile long *)&x->_v); } FORCE_INLINE u32 atomic_u32_dec_eval(struct atomic_u32 *x) { return (u32)_InterlockedDecrement((volatile long *)&x->_v); }
FORCE_INLINE u32 atomic_u32_eval_add(struct atomic_u32 *x, u32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); } FORCE_INLINE u32 atomic_u32_eval_add_u32(struct atomic_u32 *x, u32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); }
FORCE_INLINE u32 atomic_u32_eval_add_i32(struct atomic_u32 *x, i32 a) { return (u32)_InterlockedExchangeAdd((volatile long *)&x->_v, (long)a); }
FORCE_INLINE u32 atomic_u32_eval_exchange(struct atomic_u32 *x, u32 e) { return (u32)_InterlockedExchange((volatile long *)&x->_v, (long)e); } FORCE_INLINE u32 atomic_u32_eval_exchange(struct atomic_u32 *x, u32 e) { return (u32)_InterlockedExchange((volatile long *)&x->_v, (long)e); }
FORCE_INLINE u32 atomic_u32_eval_compare_exchange(struct atomic_u32 *x, u32 c, u32 e) { return (u32)_InterlockedCompareExchange((volatile long *)&x->_v, (long)e, (long)c); } FORCE_INLINE u32 atomic_u32_eval_compare_exchange(struct atomic_u32 *x, u32 c, u32 e) { return (u32)_InterlockedCompareExchange((volatile long *)&x->_v, (long)e, (long)c); }
FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v; } FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v; }
@ -30,7 +31,8 @@ FORCE_INLINE volatile u32 *atomic_u32_raw(struct atomic_u32 *x) { return &x->_v;
FORCE_INLINE u64 atomic_u64_eval(struct atomic_u64 *x) { return (u64)_InterlockedOr64((volatile i64 *)&x->_v, 0); } FORCE_INLINE u64 atomic_u64_eval(struct atomic_u64 *x) { return (u64)_InterlockedOr64((volatile i64 *)&x->_v, 0); }
FORCE_INLINE u64 atomic_u64_inc_eval(struct atomic_u64 *x) { return (u64)_InterlockedIncrement64((volatile i64 *)&x->_v); } FORCE_INLINE u64 atomic_u64_inc_eval(struct atomic_u64 *x) { return (u64)_InterlockedIncrement64((volatile i64 *)&x->_v); }
FORCE_INLINE u64 atomic_u64_dec_eval(struct atomic_u64 *x) { return (u64)_InterlockedDecrement64((volatile i64 *)&x->_v); } FORCE_INLINE u64 atomic_u64_dec_eval(struct atomic_u64 *x) { return (u64)_InterlockedDecrement64((volatile i64 *)&x->_v); }
FORCE_INLINE u64 atomic_u64_eval_add(struct atomic_u64 *x, u64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); } FORCE_INLINE u64 atomic_u64_eval_add_u64(struct atomic_u64 *x, u64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); }
FORCE_INLINE u64 atomic_u64_eval_add_i64(struct atomic_u64 *x, i64 a) { return (u64)_InterlockedExchangeAdd64((volatile i64 *)&x->_v, (i64)a); }
FORCE_INLINE u64 atomic_u64_eval_exchange(struct atomic_u64 *x, u64 e) { return (u64)_InterlockedExchange64((volatile i64 *)&x->_v, (i64)e); } FORCE_INLINE u64 atomic_u64_eval_exchange(struct atomic_u64 *x, u64 e) { return (u64)_InterlockedExchange64((volatile i64 *)&x->_v, (i64)e); }
FORCE_INLINE u64 atomic_u64_eval_compare_exchange(struct atomic_u64 *x, u64 c, u64 e) { return (u64)_InterlockedCompareExchange64((volatile i64 *)&x->_v, (i64)e, (i64)c); } FORCE_INLINE u64 atomic_u64_eval_compare_exchange(struct atomic_u64 *x, u64 c, u64 e) { return (u64)_InterlockedCompareExchange64((volatile i64 *)&x->_v, (i64)e, (i64)c); }
FORCE_INLINE volatile u64 *atomic_u64_raw(struct atomic_u64 *x) { return &x->_v; } FORCE_INLINE volatile u64 *atomic_u64_raw(struct atomic_u64 *x) { return &x->_v; }

View File

@ -100,6 +100,7 @@ INTERNAL void reset_world(void)
/* Release world */ /* Release world */
world_release(&G.tick); world_release(&G.tick);
/* Release bookkeeping */ /* Release bookkeeping */
space_release(G.space);
#if COLLIDER_DEBUG #if COLLIDER_DEBUG
entity_lookup_release(&G.collision_debug_lookup); entity_lookup_release(&G.collision_debug_lookup);
#endif #endif

View File

@ -6,6 +6,8 @@
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "buddy.h" #include "buddy.h"
#include "app.h"
#include "atomic.h"
//#define HOST_NETWORK_ADDRESS_STRING(str) //#define HOST_NETWORK_ADDRESS_STRING(str)
//#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port) //#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port)
@ -768,6 +770,8 @@ void host_update(struct host *host)
default: break; default: break;
} }
} }
host->bytes_received += packet->data.len;
atomic_u64_eval_add_u64(&app_statistics()->host_bytes_received, packet->data.len);
} }
} }
/* Reset read buffer */ /* Reset read buffer */
@ -931,15 +935,18 @@ void host_update(struct host *host)
for (u64 i = 0; i < host->num_channels_reserved; ++i) { for (u64 i = 0; i < host->num_channels_reserved; ++i) {
struct sock *sock = host->sock; struct sock *sock = host->sock;
struct host_channel *channel = &host->channels[i]; struct host_channel *channel = &host->channels[i];
u64 total_sent = 0;
if (channel->valid) { if (channel->valid) {
struct sock_address address = channel->address; struct sock_address address = channel->address;
/* Send reliable packets to channel */ /* Send reliable packets to channel */
for (struct host_snd_packet *host_packet = channel->first_reliable_packet; host_packet; host_packet = host_packet->next) { for (struct host_snd_packet *packet = channel->first_reliable_packet; packet; packet = packet->next) {
sock_write(sock, address, STRING(host_packet->data_len, host_packet->data)); sock_write(sock, address, STRING(packet->data_len, packet->data));
total_sent += packet->data_len;
} }
/* Send unreliable packets to channel */ /* Send unreliable packets to channel */
for (struct host_snd_packet *host_packet = channel->first_unreliable_packet; host_packet; host_packet = host_packet->next) { for (struct host_snd_packet *packet = channel->first_unreliable_packet; packet; packet = packet->next) {
sock_write(sock, address, STRING(host_packet->data_len, host_packet->data)); sock_write(sock, address, STRING(packet->data_len, packet->data));
total_sent += packet->data_len;
} }
/* Release unreliable packets */ /* Release unreliable packets */
if (channel->first_unreliable_packet) { if (channel->first_unreliable_packet) {
@ -949,6 +956,8 @@ void host_update(struct host *host)
channel->last_unreliable_packet = NULL; channel->last_unreliable_packet = NULL;
channel->num_unreliable_packets = 0; channel->num_unreliable_packets = 0;
} }
host->bytes_sent += total_sent;
atomic_u64_eval_add_u64(&app_statistics()->host_bytes_sent, total_sent);
} }
} }
} }

View File

@ -77,7 +77,7 @@ struct host {
struct host_channel *first_free_channel; struct host_channel *first_free_channel;
u64 num_channels_reserved; u64 num_channels_reserved;
struct host_snd_packet *first_free_packet; /* Allocated in `arena` */ struct host_snd_packet *first_free_packet; /* Allocated in `arena` */
struct host_msg_assembler *first_free_msg_assembler; /* Allocated in `arena` */ struct host_msg_assembler *first_free_msg_assembler; /* Allocated in `arena` */
struct host_channel_lookup_bucket *channel_lookup_buckets; /* Allocated in `arena` */ struct host_channel_lookup_bucket *channel_lookup_buckets; /* Allocated in `arena` */
@ -91,6 +91,9 @@ struct host {
struct host_rcv_buffer *rcv_buffer_read; struct host_rcv_buffer *rcv_buffer_read;
struct host_rcv_buffer *rcv_buffer_write; struct host_rcv_buffer *rcv_buffer_write;
u64 bytes_received;
u64 bytes_sent;
struct sys_thread receiver_thread; struct sys_thread receiver_thread;
}; };

View File

@ -381,7 +381,7 @@ INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag ta
} }
arena_set_readonly(&n->arena); arena_set_readonly(&n->arena);
n->memory_usage = n->arena.committed + memory_size; n->memory_usage = n->arena.committed + memory_size;
atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); atomic_u64_eval_add_u64(&G.cache.memory_usage, n->memory_usage);
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns); f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading sprite texture \"%F\" in %F seconds (cache size: %F bytes).", logf_info("Finished loading sprite texture \"%F\" in %F seconds (cache size: %F bytes).",
@ -681,7 +681,7 @@ INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag)
#endif #endif
arena_set_readonly(&n->arena); arena_set_readonly(&n->arena);
n->memory_usage = n->arena.committed; n->memory_usage = n->arena.committed;
atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); atomic_u64_eval_add_u64(&G.cache.memory_usage, n->memory_usage);
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns); f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading sprite sheet \"%F\" in %F seconds (cache size: %F bytes).", logf_info("Finished loading sprite sheet \"%F\" in %F seconds (cache size: %F bytes).",
@ -1178,7 +1178,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg)
if (n->next_hash) { if (n->next_hash) {
n->next_hash->prev_hash = n->prev_hash; n->next_hash->prev_hash = n->prev_hash;
} }
atomic_u64_eval_add(&G.cache.memory_usage, -((i64)n->memory_usage)); atomic_u64_eval_add_i64(&G.cache.memory_usage, -((i64)n->memory_usage));
/* Add to evicted list */ /* Add to evicted list */
en->next_evicted = head_evicted; en->next_evicted = head_evicted;
head_evicted = en; head_evicted = en;

View File

@ -35,6 +35,12 @@ struct blend_tick {
struct world world; struct world world;
}; };
struct second_stat {
u64 last_second_start;
u64 last_second_end;
u64 last_second;
};
GLOBAL struct { GLOBAL struct {
struct atomic_i32 user_thread_shutdown; struct atomic_i32 user_thread_shutdown;
struct sys_thread user_thread; struct sys_thread user_thread;
@ -45,6 +51,11 @@ GLOBAL struct {
struct host *host; struct host *host;
/* Usage stats */
i64 last_second_reset_ns;
struct second_stat client_bytes_read;
struct second_stat client_bytes_sent;
/* Render targets */ /* Render targets */
struct renderer_texture final_texture; struct renderer_texture final_texture;
struct renderer_texture world_texture; struct renderer_texture world_texture;
@ -1608,6 +1619,20 @@ INTERNAL void user_update(void)
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("entities: %F/%F"), FMT_UINT(G.world.entity_store->num_allocated), FMT_UINT(G.world.entity_store->num_reserved))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("entities: %F/%F"), FMT_UINT(G.world.entity_store->num_allocated), FMT_UINT(G.world.entity_store->num_reserved)));
pos.y += spacing; pos.y += spacing;
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data read: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1024 / 1024, 2)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Client data sent: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1024 / 1024, 2)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory committed: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 2)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory reserved: %F TiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_reserved) / 1024 / 1024 / 1024 / 1024, 2)));
pos.y += spacing;
#if 0
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y)));
pos.y += spacing; pos.y += spacing;
@ -1652,6 +1677,7 @@ INTERNAL void user_update(void)
struct v2 player_pos = entity_get_xform(entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)).og; struct v2 player_pos = entity_get_xform(entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)).og;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player pos: (%F, %F)"), FMT_FLOAT_P((f64)player_pos.x, 12), FMT_FLOAT_P((f64)player_pos.y, 12))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("player pos: (%F, %F)"), FMT_FLOAT_P((f64)player_pos.x, 12), FMT_FLOAT_P((f64)player_pos.y, 12)));
pos.y += spacing; pos.y += spacing;
#endif
#if COLLIDER_DEBUG #if COLLIDER_DEBUG
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("collider gjk steps: %F"), FMT_UINT(collider_debug_steps))); draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("collider gjk steps: %F"), FMT_UINT(collider_debug_steps)));
@ -1675,6 +1701,17 @@ INTERNAL void user_update(void)
host_update(G.host); host_update(G.host);
/* Update network usage stats */
G.client_bytes_read.last_second_end = G.host->bytes_received;
G.client_bytes_sent.last_second_end = G.host->bytes_sent;
if (now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
G.last_second_reset_ns = now_ns;
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
G.client_bytes_sent.last_second_start = G.client_bytes_sent.last_second_end;
}
/* ========================== * /* ========================== *
* Render * Render
* ========================== */ * ========================== */

View File

@ -15,6 +15,8 @@
* Hash utils * Hash utils
* ========================== */ * ========================== */
/* TODO: Replace with better hash functions */
/* FNV-1a parameters for different hash sizes: /* FNV-1a parameters for different hash sizes:
* https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
*/ */