diff --git a/debug.rad b/debug.rad index 7e76a88..d555d4d 100644 --- a/debug.rad +++ b/debug.rad @@ -1,9 +1,10 @@ // raddbg 0.9.21 project file recent_file: path: "inc/genetic.h" +recent_file: path: "d:/os/obj/amd64fre/minkernel/crts/ucrt/src/appcrt/misc/mt/objfre/amd64/minkernel/crts/ucrt/src/appcrt/misc/invalid_parameter.cpp" +recent_file: path: "src/main.cpp" recent_file: path: "../../../../../Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/include/algorithm" recent_file: path: "../../../../../Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/include/xutility" -recent_file: path: "src/main.cpp" target: { executable: "bin/main.exe" diff --git a/inc/genetic.h b/inc/genetic.h index 90aa2cd..6512155 100644 --- a/inc/genetic.h +++ b/inc/genetic.h @@ -16,15 +16,17 @@ template struct Stats; template struct Strategy; struct CellTracker; -template Stats run(Strategy); +template T run(Strategy); template struct Strategy { // Number of worker threads that will be evaluating cell fitness int num_threads; + float stats_print_period; + int batch_size; // Number of cells a worker thread tries to work on in a row // before accessing/locking the work queue again. - int num_cells; // Size of the population pool + int num_cells_per_thread; // Size of the population pool per sim thread int num_generations; // Number of times (epochs) to run the algorithm bool test_all; // Sets whether or not every cell's fitness is evaluated every // generation @@ -60,8 +62,23 @@ template struct Strategy { template struct Stats { std::vector best_cell; std::vector best_cell_fitness; - TimeSpan setup_time; - TimeSpan run_time; + int gen; + bool done; + TimeSpan start, end; + + TimeSpan total_crossover_time; + int total_crossovers; + + TimeSpan total_mutate_time; + int total_mutates; + + TimeSpan total_fitness_time; + int total_evaluations; + + TimeSpan total_sorting_time; + int total_sorts; + + Mutex m; }; struct CellTracker { @@ -83,58 +100,75 @@ template Array make_array(int len) { }; } -template Stats run(Strategy strat) { - Stats stats; +template +struct WorkerThreadArgs { + Strategy strat; + Array cells; + Array trackers; + Stats &stats; +}; - // ************* SETUP ************** - TimeSpan start_setup = now(); +template T* _cellp(Array cells, CellTracker tracker) { return &cells[tracker.cellid]; } - // Create cells - Array cells = make_array(strat.num_cells); - for (int i = 0; i < cells.len; i++) cells[i] = strat.make_default_cell(); +template DWORD worker(LPVOID args) { + // Unpack everything... + WorkerThreadArgs* worker_args = static_cast*>(args); + Strategy strat = worker_args->strat; + Array cells = worker_args->cells; + Array trackers = worker_args->trackers; + Stats &stats = worker_args->stats; - // Create cell trackers - Array trackers = make_array(strat.num_cells); - for (int i = 0; i < trackers.len; i++) trackers[i] = { .score=0, .cellid=i }; + // Prepare crossover operations as these will be the same every time except + // for the exact cell pointers + int npar = strat.crossover_parent_num; + int nchild = strat.crossover_children_num; + Array parents = make_array(npar); + Array children = make_array(nchild); - stats.setup_time = now() - start_setup; - - // *********** ALGORITHM ************ TimeSpan start_algo = now(); - for (int gen = 0; gen < strat.num_generations; gen++) { - // 1. mutate - for (int i = 0; i < trackers.len; i++) { - if (abs(norm_rand(strat.rand_seed)) < strat.mutation_chance) { - strat.mutate(cells[trackers[i].cellid]); - } - } - // 2. crossover - if (strat.enable_crossover) { - int npar = strat.crossover_parent_num; - int nchild = strat.crossover_children_num; + TimeSpan start; + while(stats.gen < strat.num_generations) { + // 1. crossover + start = now(); + if (strat.enable_crossover) { int parent_end = npar; int child_begin = trackers.len-nchild; - Array parents = make_array(npar); - Array children = make_array(nchild); while (parent_end <= child_begin) { // Get pointers to all the parent cells for (int i = parent_end-npar; i < parent_end; i++) { - parents[i - (parent_end-npar)] = &cells[trackers[i].cellid]; + parents[i - (parent_end-npar)] = _cellp(cells, trackers[i]); } // Get pointers to all the child cells (these will be overwritten) for (int i = child_begin; i < child_begin+nchild; i++) { - children[i-child_begin] = &cells[trackers[i].cellid]; + children[i-child_begin] = _cellp(cells, trackers[i]); } strat.crossover(parents, children); parent_end += strat.crossover_parent_stride; child_begin -= nchild; } - free(parents.data); - free(children.data); } + lock(stats.m); + stats.total_crossover_time = stats.total_crossover_time + (now() - start); + stats.total_crossovers++; + unlock(stats.m); + + + // 2. mutate + start = now(); + for (int i = 0; i < trackers.len; i++) { + if (abs(norm_rand(strat.rand_seed)) < strat.mutation_chance) { + strat.mutate(cells[trackers[i].cellid]); + } + } + lock(stats.m); + stats.total_mutate_time = stats.total_mutate_time + (now() - start); + stats.total_mutates++; + unlock(stats.m); + // 3. evaluate + start = now(); if (strat.test_all) { for (int i = 0; i < trackers.len; i++) { trackers[i].score = strat.fitness(cells[trackers[i].cellid]); @@ -146,15 +180,118 @@ template Stats run(Strategy strat) { } } } - // 4. sort - std::sort(&trackers[0], &trackers[trackers.len-1], [strat](CellTracker &a, CellTracker &b){ return strat.higher_fitness_is_better ? a.score > b.score : a.score < b.score; }); + lock(stats.m); + stats.total_fitness_time = stats.total_fitness_time + (now() - start); + stats.total_evaluations++; + unlock(stats.m); + + // 4. sort + start = now(); + std::sort(&trackers[0], &trackers[trackers.len-1], [strat](CellTracker &a, CellTracker &b){ return strat.higher_fitness_is_better ? a.score > b.score : a.score < b.score; }); + lock(stats.m); + stats.total_sorting_time = stats.total_sorting_time + (now() - start); + stats.total_sorts++; - printf("Gen: %d, Best Score: %f\n", gen, trackers[0].score); stats.best_cell.push_back(cells[trackers[0].cellid]); stats.best_cell_fitness.push_back(trackers[0].score); + stats.gen++; + unlock(stats.m); } - stats.run_time = now() - start_algo; - return stats; + stats.done = true; + stats.end = now(); + return 0; +} + +template T run(Strategy strat) { + Array> stats = make_array>(strat.num_threads); + Array threads = make_array(strat.num_threads); + Array cells = make_array(strat.num_threads*strat.num_cells_per_thread); + Array trackers = make_array(cells.len); + + Array> args = make_array>(strat.num_threads); + + for (int i = 0; i < cells.len; i++) { + cells[i] = strat.make_default_cell(); + trackers[i] = {0, i}; + } + + for (int i = 0; i < strat.num_threads; i++) { + stats[i] = { + .gen=0, + .m=make_mutex() + }; + Array tcells = { &cells[i*strat.num_cells_per_thread], strat.num_cells_per_thread }; + Array ttrackers = { &trackers[i*strat.num_cells_per_thread], strat.num_cells_per_thread }; + + args[i].strat=strat; + args[i].cells=tcells; + args[i].trackers=ttrackers; + args[i].stats=stats[i]; + + threads[i] = make_thread(worker, &args[i]); + } + + // We are the stats thread + bool complete = false; + while (!complete) { + sleep(from_s(strat.stats_print_period)); + + printf("**********************\n"); + float g_avg_crossover_time = 0; + float g_avg_mutate_time = 0; + float g_avg_fitness_time = 0; + float g_avg_sorting_time = 0; + float g_progress_per = 0; + + complete = true; + + for (int i = 0; i < stats.len; i++) { + lock(stats[i].m); + complete &= stats[i].done; + + float avg_crossover_time = to_s(stats[i].total_crossover_time) / static_cast(stats[i].total_crossovers); + + float avg_mutate_time = to_s(stats[i].total_mutate_time) / static_cast(stats[i].total_mutates); + + float avg_fitness_time = to_s(stats[i].total_fitness_time) / static_cast(stats[i].total_evaluations); + + float avg_sorting_time = to_s(stats[i].total_sorting_time) / static_cast(stats[i].total_sorts); + + float progress_per = static_cast(stats[i].gen) / static_cast(strat.num_generations) * 100; + + g_avg_crossover_time += avg_crossover_time; + g_avg_mutate_time += avg_mutate_time; + g_avg_fitness_time += avg_fitness_time; + g_avg_sorting_time += avg_sorting_time; + g_progress_per += progress_per; + + printf("THREAD %d, Progress %.1f%, Average Crossover Time/Cell %.5f (s), Average Mutate Time/Cell: %.5f (s), Average Fitness Time/Cell: %.5f (s), Average Sorting Time: %.5f (s)\n", i, progress_per, avg_crossover_time, avg_mutate_time, avg_fitness_time, avg_sorting_time); + unlock(stats[i].m); + } + + g_avg_crossover_time /= stats.len; + g_avg_mutate_time /= stats.len; + g_avg_fitness_time /= stats.len; + g_avg_sorting_time /= stats.len; + g_progress_per /= stats.len; + + printf("OVERALL, Progress %.1f%, Average Crossover Time/Cell %.5f (s), Average Mutate Time/Cell: %.5f (s), Average Fitness Time/Cell: %.5f (s), Average Sorting Time: %.5f (s)\n", g_progress_per, g_avg_crossover_time, g_avg_mutate_time, g_avg_fitness_time, g_avg_sorting_time); + + if (complete) break; + } + + T best_cell; + // TODO: bad + float best_score = strat.higher_fitness_is_better ? 999999999999999999.9 : 0.0; + for (int i = 0; i < stats.len; i++) { + float score = stats[i].best_cell_fitness.back(); + if (strat.higher_fitness_is_better ? score > best_score : score < best_score) { + best_cell = stats[i].best_cell.back(); + best_score = score; + } + } + + return best_cell; } } // namespace genetic diff --git a/inc/sync.h b/inc/sync.h index b744a67..4f28a24 100644 --- a/inc/sync.h +++ b/inc/sync.h @@ -1,7 +1,7 @@ #pragma once #ifdef _WIN32 -#include "windows.h" +#include #endif namespace sync { @@ -28,6 +28,7 @@ static LARGE_INTEGER freq = _init_freq(); Thread make_thread(ThreadFunc t, ThreadArg a); void join(Thread t); +void sleep(TimeSpan ts); Mutex make_mutex(); void lock(Mutex &m); @@ -52,11 +53,14 @@ TimeSpan from_min(double minutes); TimeSpan from_hours(double hours); TimeSpan now(); TimeSpan operator-(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator+(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator*(const TimeSpan &a, const TimeSpan &b); +TimeSpan operator/(const TimeSpan &a, const TimeSpan &b); -double to_ms(TimeSpan &sp); -double to_s(TimeSpan &sp); -double to_min(TimeSpan &sp); -double to_hours(TimeSpan &sp); +double to_ms(TimeSpan &ts); +double to_s(TimeSpan &ts); +double to_min(TimeSpan &ts); +double to_hours(TimeSpan &ts); #ifdef _WIN32 @@ -69,6 +73,10 @@ void join(Thread t) { WaitForSingleObject(t, INFINITE); } +void sleep(TimeSpan ts) { + Sleep(static_cast(to_ms(ts))); +} + Mutex make_mutex() { Mutex m; InitializeCriticalSection(&m); @@ -135,25 +143,25 @@ void dispose(Semaphore &s) { TimeSpan from_ms(double milliseconds) { TimeSpan ts; - ts.QuadPart = static_cast(milliseconds/1000.0)*freq.QuadPart; + ts.QuadPart = static_cast(milliseconds/1000.0)*freq.QuadPart; return ts; } TimeSpan from_s(double seconds) { TimeSpan ts; - ts.QuadPart = static_cast(seconds)*freq.QuadPart; + ts.QuadPart = static_cast(seconds)*freq.QuadPart; return ts; } TimeSpan from_min(double minutes) { TimeSpan ts; - ts.QuadPart = static_cast(minutes*60.0)*freq.QuadPart; + ts.QuadPart = static_cast(minutes*60.0)*freq.QuadPart; return ts; } TimeSpan from_hours(double hours) { TimeSpan ts; - ts.QuadPart = static_cast(hours*60.0*60.0)*freq.QuadPart; + ts.QuadPart = static_cast(hours*60.0*60.0)*freq.QuadPart; return ts; } @@ -163,26 +171,44 @@ TimeSpan now() { return ts; } -TimeSpan operator-(const TimeSpan &a, TimeSpan &b) { +TimeSpan operator-(const TimeSpan &a, const TimeSpan &b) { TimeSpan ts; ts.QuadPart = a.QuadPart - b.QuadPart; return ts; } -double to_ms(TimeSpan &sp) { - return static_cast(sp.QuadPart*1000)/static_cast(freq.QuadPart); +TimeSpan operator+(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart + b.QuadPart; + return ts; } -double to_s(TimeSpan &sp) { - return static_cast(sp.QuadPart)/static_cast(freq.QuadPart); +TimeSpan operator*(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart * b.QuadPart; + return ts; } -double to_min(TimeSpan &sp) { - return static_cast(sp.QuadPart)/static_cast(freq.QuadPart*60); +TimeSpan operator/(const TimeSpan &a, const TimeSpan &b) { + TimeSpan ts; + ts.QuadPart = a.QuadPart / b.QuadPart; + return ts; } -double to_hours(TimeSpan &sp) { - return static_cast(sp.QuadPart)/static_cast(freq.QuadPart*60*60); +double to_ms(TimeSpan &ts) { + return static_cast(ts.QuadPart*1000)/static_cast(freq.QuadPart); +} + +double to_s(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart); +} + +double to_min(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart*60); +} + +double to_hours(TimeSpan &ts) { + return static_cast(ts.QuadPart)/static_cast(freq.QuadPart*60*60); } #endif diff --git a/src/main.cpp b/src/main.cpp index 04f5b0e..16e0317 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,7 +58,7 @@ int main(int argc, char **argv) { Strategy> strat { .num_threads = 1, .batch_size = 1, - .num_cells = 100000, + .num_cells_per_thread = 100000, .num_generations = num_gens, .test_all = true, .test_chance = 0.0, // doesn't matter @@ -76,13 +76,13 @@ int main(int argc, char **argv) { .fitness=fitness }; - auto res = run(strat); + auto best_cell = run(strat); float sum = 0; float product = 1; printf("Winning cell: "); - for (int i = 0; i < res.best_cell.back().len; i++) { - float val = res.best_cell.back()[i]; + for (int i = 0; i < best_cell.len; i++) { + float val = best_cell[i]; sum += val; product *= val; printf("%f ", val); @@ -90,7 +90,4 @@ int main(int argc, char **argv) { printf("\n"); printf("Final Sum: %f\n", sum); printf("Final Product: %f\n", product); - printf("Setup Time (s): %f\n", sync::to_s(res.setup_time)); - printf("Run Time (s): %f\n", sync::to_s(res.run_time)); - printf("Average Gen Time (s): %f\n", sync::to_s(res.run_time)/num_gens); }