char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
f280b2698
char/aliceai-t5-moe
1#include "bench.h" 2 3#include <algorithm> 4#include <chrono> 5#include <cmath> 6#include <cstdio> 7#include <thread> 8#include <utility> 9 10perf_cell build_perf_cell (ggml_backend_t backend , 11const build_graph_fn & build , 12const init_tensors_fn & init , 13const op_flops_fn & flops ) { 14perf_cell cell ; 15 16const size_t graph_nodes = 1024 ; 17 18ggml_init_params params = { 19/* .mem_size = */ ggml_tensor_overhead ()* 128 + ggml_graph_overhead_custom (graph_nodes , false), 20/* .mem_base = */ NULL , 21/* .no_alloc = */ true, 22 }; 23 24cell .ctx .reset (ggml_init (params )); 25GGML_ASSERT (cell .ctx ); 26 27ggml_tensor * out = build (cell .ctx .get ()); 28if (!out || !ggml_backend_supports_op (backend ,out )) { 29return cell ; 30 } 31 32cell .buf .reset (ggml_backend_alloc_ctx_tensors (cell .ctx .get (),backend )); 33if (!cell .buf ) { 34return cell ; 35 } 36 37init (cell .ctx .get ()); 38 39cell .gf = ggml_new_graph_custom (cell .ctx .get (),graph_nodes , false); 40ggml_build_forward_expand (cell .gf ,out ); 41 42// replicate the op to amortize overhead (target ~50 GFLOP/compute, capped to bound graph size) 43cell .n_runs = 1 ; 44const uint64_t n_flops = flops (out ); 45if (n_flops > 0 ) { 46const uint64_t target_flops = 50ULL * 1000 * 1000 * 1000 ; 47const int cap = 512 ; 48const int by_flops = (int ) std::min < int64_t > (cap , (int64_t ) (target_flops /n_flops )); 49cell .n_runs = 50 std::max (1 , std::min < int > (by_flops , (int ) (ggml_graph_size (cell .gf )- ggml_graph_n_nodes (cell .gf )))); 51 } 52for (int i = 1 ;i < cell .n_runs ;++ i ) { 53ggml_graph_add_node (cell .gf ,out ); 54 } 55 56return cell ; 57} 58 59double time_cell_median (ggml_backend_t backend ,const perf_cell & cell ,int reps ) { 60if (cell .gf == nullptr ) { 61return -1.0 ; 62 } 63 64ggml_backend_graph_compute (backend ,cell .gf );// warmup (compiles the pipeline for this config) 65ggml_backend_synchronize (backend ); 66 67 std::vector < double > samples ; 68samples .reserve (reps ); 69for (int r = 0 ;r < reps ;++ r ) { 70const int64_t t0 = ggml_time_us (); 71ggml_backend_graph_compute (backend ,cell .gf ); 72ggml_backend_synchronize (backend ); 73samples .push_back ((double ) (ggml_time_us ()- t0 )); 74 } 75 std::nth_element (samples .begin (),samples .begin ()+ samples .size () /2 ,samples .end ()); 76 77return samples [samples .size () /2 ] /cell .n_runs ; 78} 79 80static double measure_one (ggml_backend_t backend , 81const perf_cell & cell , 82int reps , 83const set_candidate_fn & set_cand , 84const clear_candidate_fn & clear_cand , 85int cand ) { 86set_cand (cand ); 87const double t = time_cell_median (backend ,cell ,reps ); 88clear_cand (); 89 90return t ; 91} 92 93// waits for the anchor to come back within eps of anchor_ref, with exponential backoff. 94// returns the converged anchor, or -1 if it never converged within max_wait. 95static double cool_until_steady (ggml_backend_t backend , 96const perf_cell & cell , 97int reps , 98const set_candidate_fn & set_cand , 99const clear_candidate_fn & clear_cand , 100int baseline_cand , 101double & anchor_ref , 102const cooldown_opts & cool , 103const char * cell_label ) { 104int total_wait = 0 ; 105 106for (int sleep_s = 2 ;total_wait < cool .max_wait ;sleep_s = std::min (sleep_s * 2 ,32 )) { 107const int this_wait = std::min (sleep_s ,cool .max_wait - total_wait ); 108 109fprintf (stderr ,"# COOL sleeping %ds (%ds/%ds) %s\n" ,this_wait ,total_wait + this_wait ,cool .max_wait , 110cell_label ); 111 std::this_thread::sleep_for (std::chrono::seconds (this_wait )); 112total_wait += this_wait ; 113 114const double a = measure_one (backend ,cell ,reps ,set_cand ,clear_cand ,baseline_cand ); 115if (a <=0.0 ) { 116continue ; 117 } 118 119// a faster anchor means the machine got cooler than anything seen so far: adopt it 120if (a < anchor_ref ) { 121anchor_ref = a ; 122 } 123 124if (a <=anchor_ref * (1.0 + cool .eps )) { 125fprintf (stderr ,"# COOL steady after %ds %s\n" ,total_wait ,cell_label ); 126return a ; 127 } 128 } 129 130fprintf (stderr ,"# COOL gave up after %ds %s\n" ,total_wait ,cell_label ); 131 132return -1.0 ; 133} 134 135cell_result measure_cell (ggml_backend_t backend , 136const perf_cell & cell , 137int reps , 138const std::vector < int > & order , 139const set_candidate_fn & set_cand , 140const clear_candidate_fn & clear_cand , 141int baseline_cand , 142const cooldown_opts & cool , 143const char * cell_label ) { 144cell_result res ; 145res .t .assign (order .size (),0.0 ); 146 147double anchor_ref = 0.0 ; 148 149// anchors accepted as clean, as (value, position in order[]). the dirty window starts 150// at the position of the last anchor still within eps of anchor_ref, so a downward 151// drift (anchor_ref dropping) naturally widens the window to the whole cell. 152 std::vector < std::pair < double ,size_t >> anchors ; 153 154auto window_start = [& ]()-> size_t { 155for (size_t i = anchors .size ();i -- > 0 ;) { 156if (anchors [i ].first <=anchor_ref * (1.0 + cool .eps )) { 157return anchors [i ].second ; 158 } 159 } 160return 0 ;// no clean anchor left -> the whole cell is suspect 161 }; 162 163int retries_left = cool .max_retry ; 164 165for (size_t i = 0 ;i < order .size ();++ i ) { 166res .t [order [i ]]= measure_one (backend ,cell ,reps ,set_cand ,clear_cand ,order [i ]); 167 168if (i %4 != 0 ) { 169continue ; 170 } 171 172const double a = measure_one (backend ,cell ,reps ,set_cand ,clear_cand ,baseline_cand ); 173if (a <=0.0 ) { 174continue ; 175 } 176 177res .anchor_min = res .anchor_min > 0.0 ? std::min (res .anchor_min ,a ) :a ; 178res .anchor_max = std::max (res .anchor_max ,a ); 179 180if (anchor_ref == 0.0 ) { 181anchor_ref = a ; 182anchors .push_back ({a ,i }); 183continue ; 184 } 185 186const double drift = std::fabs (a - anchor_ref ) /anchor_ref ; 187 188// a cooler anchor than any so far becomes the reference: whatever was measured 189// before it was measured on a hotter machine 190if (a < anchor_ref ) { 191anchor_ref = a ; 192 } 193 194if (drift <=cool .drift ) { 195anchors .push_back ({a ,i }); 196continue ; 197 } 198 199fprintf (stderr ,"# WARN throttling? anchor drift %.1f%% %s\n" ,100.0 * drift ,cell_label ); 200 201if (!cool .enabled ) { 202anchors .push_back ({a ,i }); 203continue ; 204 } 205 206if (retries_left <=0 ) { 207fprintf (stderr ,"# DIRTY retries exhausted %s\n" ,cell_label ); 208res .trusted = false; 209return res ; 210 } 211 212const size_t dirty_from = window_start (); 213 214const double a_cool = 215cool_until_steady (backend ,cell ,reps ,set_cand ,clear_cand ,baseline_cand ,anchor_ref ,cool ,cell_label ); 216if (a_cool <=0.0 ) { 217res .trusted = false; 218return res ; 219 } 220 221// the converged anchor is the only clean one now; re-measure the dirty window from it 222anchors .clear (); 223anchors .push_back ({a_cool ,dirty_from }); 224 225retries_left -- ; 226 227fprintf (stderr ,"# REDO candidates %zu..%zu %s\n" ,dirty_from ,i ,cell_label ); 228for (size_t j = dirty_from ;j <=i ;++ j ) { 229res .t [order [j ]]= measure_one (backend ,cell ,reps ,set_cand ,clear_cand ,order [j ]); 230 } 231 } 232 233return res ; 234}