Skip to content

Instantly share code, notes, and snippets.

@matias-eduardo
Created July 15, 2023 19:31
Show Gist options
  • Select an option

  • Save matias-eduardo/788fa6374c60e453b7534cd796f98867 to your computer and use it in GitHub Desktop.

Select an option

Save matias-eduardo/788fa6374c60e453b7534cd796f98867 to your computer and use it in GitHub Desktop.

Revisions

  1. matias-eduardo created this gist Jul 15, 2023.
    166 changes: 166 additions & 0 deletions particle-with-soa.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,166 @@
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>

    #if defined(__linux)
    #define HAVE_POSIX_TIMER
    #include <time.h>
    #ifdef CLOCK_MONOTONIC
    #define CLOCKID CLOCK_MONOTONIC
    #else
    #define CLOCKID CLOCK_REALTIME
    #endif
    #elif defined(__APPLE__)
    #define HAVE_MACH_TIMER
    #include <mach/mach_time.h>
    #elif defined(_WIN32)
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #endif

    static uint64_t ns() {
    static uint64_t is_init = 0;
    #if defined(__APPLE__)
    static mach_timebase_info_data_t info;
    if (0 == is_init) {
    mach_timebase_info(&info);
    is_init = 1;
    }
    uint64_t now;
    now = mach_absolute_time();
    now *= info.numer;
    now /= info.denom;
    return now;
    #elif defined(__linux)
    static struct timespec linux_rate;
    if (0 == is_init) {
    clock_getres(CLOCKID, &linux_rate);
    is_init = 1;
    }
    uint64_t now;
    struct timespec spec;
    clock_gettime(CLOCKID, &spec);
    now = spec.tv_sec * 1.0e9 + spec.tv_nsec;
    return now;
    #elif defined(_WIN32)
    static LARGE_INTEGER win_frequency;
    if (0 == is_init) {
    QueryPerformanceFrequency(&win_frequency);
    is_init = 1;
    }
    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart);
    #endif
    }

    ///////////////////////////////

    typedef struct vec3 {
    float x, y, z;
    } vec3;

    vec3 vec3_add(vec3 a, vec3 b) {
    return (vec3){
    a.x + b.x,
    a.y + b.y,
    a.z + b.z,
    };
    }

    vec3 vec3_sub(vec3 a, vec3 b) {
    return (vec3){
    a.x - b.x,
    a.y - b.y,
    a.z - b.z,
    };
    }

    vec3 vec3_mulf(vec3 a, float f) {
    return (vec3){
    a.x * f,
    a.y * f,
    a.z * f,
    };
    }

    float rand_float(float min, float max) {
    return ((float)rand() / (float)RAND_MAX) * (max - min) + min;
    }

    ///////////////////////////////

    #define PARTICLES_MAX 32768

    typedef struct Globals {
    int particle_count;
    vec3 particle_pos[PARTICLES_MAX];
    vec3 particle_vel[PARTICLES_MAX];
    float particle_time[PARTICLES_MAX];
    } Globals;

    static Globals o = {0};

    static void particles_create() {
    #define SPAWN_CHANCE 50
    int new_chance = rand() % SPAWN_CHANCE;
    if (new_chance == 0) {
    int new_count = rand() % 1000;
    if ((o.particle_count + new_count) > PARTICLES_MAX)
    new_count = PARTICLES_MAX - o.particle_count;
    for (int i = 0; i < new_count; i++)
    o.particle_pos[o.particle_count+i] = (vec3){0, 0, 0};
    for (int i = 0; i < new_count; i++)
    o.particle_vel[o.particle_count+i] = (vec3){rand_float(-1000, 1000), rand_float(0, 1000), rand_float(-1000, 1000)};
    for (int i = 0; i < new_count; i++)
    o.particle_time[o.particle_count+i] = rand_float(0.5, 5.0);
    o.particle_count += new_count;
    }
    }

    static void particles_update() {
    float tick = 1.0 / 60.0;
    float half_tick = tick * 0.5;
    for (int i = 0; i < o.particle_count; i++)
    o.particle_vel[i].y += 8.0;
    for (int i = 0; i < o.particle_count; i++)
    o.particle_vel[i] = vec3_sub(o.particle_vel[i], vec3_mulf(o.particle_vel[i], half_tick));
    for (int i = 0; i < o.particle_count; i++)
    o.particle_pos[i] = vec3_add(o.particle_pos[i], vec3_mulf(o.particle_vel[i], tick));
    for (int i = 0; i < o.particle_count; i++) {
    o.particle_time[i] -= tick;
    if (o.particle_time[i] < 0) {
    o.particle_count--;
    o.particle_pos[i] = o.particle_pos[o.particle_count];
    o.particle_vel[i] = o.particle_vel[o.particle_count];
    o.particle_time[i] = o.particle_time[o.particle_count];
    i--;
    }
    }
    }

    int main(int argc, char **argv) {
    // bench: init
    srand(0xC0FFE);

    // bench: warmup
    #define WARMUP_RUNS 1000
    for (int benchmark_i = 0; benchmark_i < WARMUP_RUNS; benchmark_i++) {
    particles_create();
    particles_update();
    }

    // bench: run
    #define RUNS 100000
    uint64_t time_start = ns();
    for (int benchmark_i = 0; benchmark_i < RUNS; benchmark_i++) {
    particles_create();
    particles_update();
    }

    // bench: time
    uint64_t total_time = ns() - time_start;
    printf("In-place move, spawn chance 1/50, SOA: %8.2f ms\n", (double)total_time/1000000.0);
    return 0;
    }