paramgrill.c 102 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967
  1. /*
  2. * Copyright (c) 2015-2021, Yann Collet, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under both the BSD-style license (found in the
  6. * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  7. * in the COPYING file in the root directory of this source tree).
  8. * You may select, at your option, one of the above-listed licenses.
  9. */
  10. /*-************************************
  11. * Dependencies
  12. **************************************/
  13. #include "util.h" /* Ensure platform.h is compiled first; also : compiler options, UTIL_GetFileSize */
  14. #include <stdlib.h> /* malloc */
  15. #include <stdio.h> /* fprintf, fopen, ftello64 */
  16. #include <string.h> /* strcmp */
  17. #include <math.h> /* log */
  18. #include <assert.h>
  19. #include "timefn.h" /* SEC_TO_MICRO, UTIL_time_t, UTIL_clockSpanMicro, UTIL_clockSpanNano, UTIL_getTime */
  20. #include "mem.h"
  21. #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */
  22. #include "zstd.h"
  23. #include "datagen.h"
  24. #include "xxhash.h"
  25. #include "benchfn.h"
  26. #include "benchzstd.h"
  27. #include "zstd_errors.h"
  28. #include "zstd_internal.h" /* should not be needed */
  29. /*-************************************
  30. * Constants
  31. **************************************/
  32. #define PROGRAM_DESCRIPTION "ZSTD parameters tester"
  33. #define AUTHOR "Yann Collet"
  34. #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR
  35. #define TIMELOOP_NANOSEC (1*1000000000ULL) /* 1 second */
  36. #define NB_LEVELS_TRACKED 22 /* ensured being >= ZSTD_maxCLevel() in BMK_init_level_constraints() */
  37. static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31));
  38. #define COMPRESSIBILITY_DEFAULT 0.50
  39. static const U64 g_maxVariationTime = 60 * SEC_TO_MICRO;
  40. static const int g_maxNbVariations = 64;
  41. /*-************************************
  42. * Macros
  43. **************************************/
  44. #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
  45. #define DISPLAYLEVEL(n, ...) if(g_displayLevel >= n) { fprintf(stderr, __VA_ARGS__); }
  46. #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); }
  47. #define TIMED 0
  48. #ifndef DEBUG
  49. # define DEBUG 0
  50. #endif
  51. #undef MIN
  52. #undef MAX
  53. #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
  54. #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
  55. #define CUSTOM_LEVEL 99
  56. #define BASE_CLEVEL 1
  57. #define FADT_MIN 0
  58. #define FADT_MAX ((U32)-1)
  59. #define WLOG_RANGE (ZSTD_WINDOWLOG_MAX - ZSTD_WINDOWLOG_MIN + 1)
  60. #define CLOG_RANGE (ZSTD_CHAINLOG_MAX - ZSTD_CHAINLOG_MIN + 1)
  61. #define HLOG_RANGE (ZSTD_HASHLOG_MAX - ZSTD_HASHLOG_MIN + 1)
  62. #define SLOG_RANGE (ZSTD_SEARCHLOG_MAX - ZSTD_SEARCHLOG_MIN + 1)
  63. #define MML_RANGE (ZSTD_MINMATCH_MAX - ZSTD_MINMATCH_MIN + 1)
  64. #define TLEN_RANGE 17
  65. #define STRT_RANGE (ZSTD_STRATEGY_MAX - ZSTD_STRATEGY_MIN + 1)
  66. #define FADT_RANGE 3
  67. #define CHECKTIME(r) { if(BMK_timeSpan_s(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); return r; } }
  68. #define CHECKTIMEGT(ret, val, _gototag) { if(BMK_timeSpan_s(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); ret = val; goto _gototag; } }
  69. #define PARAM_UNSET ((U32)-2) /* can't be -1 b/c fadt uses -1 */
  70. static const char* g_stratName[ZSTD_STRATEGY_MAX+1] = {
  71. "(none) ", "ZSTD_fast ", "ZSTD_dfast ",
  72. "ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ",
  73. "ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra ",
  74. "ZSTD_btultra2"};
  75. static const U32 tlen_table[TLEN_RANGE] = { 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 256, 512, 999 };
  76. /*-************************************
  77. * Setup for Adding new params
  78. **************************************/
  79. /* indices for each of the variables */
  80. typedef enum {
  81. wlog_ind = 0,
  82. clog_ind = 1,
  83. hlog_ind = 2,
  84. slog_ind = 3,
  85. mml_ind = 4,
  86. tlen_ind = 5,
  87. strt_ind = 6,
  88. fadt_ind = 7, /* forceAttachDict */
  89. NUM_PARAMS = 8
  90. } varInds_t;
  91. typedef struct {
  92. U32 vals[NUM_PARAMS];
  93. } paramValues_t;
  94. /* minimum value of parameters */
  95. static const U32 mintable[NUM_PARAMS] =
  96. { ZSTD_WINDOWLOG_MIN, ZSTD_CHAINLOG_MIN, ZSTD_HASHLOG_MIN, ZSTD_SEARCHLOG_MIN, ZSTD_MINMATCH_MIN, ZSTD_TARGETLENGTH_MIN, ZSTD_STRATEGY_MIN, FADT_MIN };
  97. /* maximum value of parameters */
  98. static const U32 maxtable[NUM_PARAMS] =
  99. { ZSTD_WINDOWLOG_MAX, ZSTD_CHAINLOG_MAX, ZSTD_HASHLOG_MAX, ZSTD_SEARCHLOG_MAX, ZSTD_MINMATCH_MAX, ZSTD_TARGETLENGTH_MAX, ZSTD_STRATEGY_MAX, FADT_MAX };
  100. /* # of values parameters can take on */
  101. static const U32 rangetable[NUM_PARAMS] =
  102. { WLOG_RANGE, CLOG_RANGE, HLOG_RANGE, SLOG_RANGE, MML_RANGE, TLEN_RANGE, STRT_RANGE, FADT_RANGE };
  103. /* ZSTD_cctxSetParameter() index to set */
  104. static const ZSTD_cParameter cctxSetParamTable[NUM_PARAMS] =
  105. { ZSTD_c_windowLog, ZSTD_c_chainLog, ZSTD_c_hashLog, ZSTD_c_searchLog, ZSTD_c_minMatch, ZSTD_c_targetLength, ZSTD_c_strategy, ZSTD_c_forceAttachDict };
  106. /* names of parameters */
  107. static const char* g_paramNames[NUM_PARAMS] =
  108. { "windowLog", "chainLog", "hashLog","searchLog", "minMatch", "targetLength", "strategy", "forceAttachDict" };
  109. /* shortened names of parameters */
  110. static const char* g_shortParamNames[NUM_PARAMS] =
  111. { "wlog", "clog", "hlog", "slog", "mml", "tlen", "strat", "fadt" };
  112. /* maps value from { 0 to rangetable[param] - 1 } to valid paramvalues */
  113. static U32 rangeMap(varInds_t param, int ind)
  114. {
  115. U32 const uind = (U32)MAX(MIN(ind, (int)rangetable[param] - 1), 0);
  116. switch(param) {
  117. case wlog_ind: /* using default: triggers -Wswitch-enum */
  118. case clog_ind:
  119. case hlog_ind:
  120. case slog_ind:
  121. case mml_ind:
  122. case strt_ind:
  123. return mintable[param] + uind;
  124. case tlen_ind:
  125. return tlen_table[uind];
  126. case fadt_ind: /* 0, 1, 2 -> -1, 0, 1 */
  127. return uind - 1;
  128. case NUM_PARAMS:
  129. default:;
  130. }
  131. DISPLAY("Error, not a valid param\n ");
  132. assert(0);
  133. return (U32)-1;
  134. }
  135. /* inverse of rangeMap */
  136. static int invRangeMap(varInds_t param, U32 value)
  137. {
  138. value = MIN(MAX(mintable[param], value), maxtable[param]);
  139. switch(param) {
  140. case wlog_ind:
  141. case clog_ind:
  142. case hlog_ind:
  143. case slog_ind:
  144. case mml_ind:
  145. case strt_ind:
  146. return (int)(value - mintable[param]);
  147. case tlen_ind: /* bin search */
  148. {
  149. int lo = 0;
  150. int hi = TLEN_RANGE;
  151. while(lo < hi) {
  152. int mid = (lo + hi) / 2;
  153. if(tlen_table[mid] < value) {
  154. lo = mid + 1;
  155. } if(tlen_table[mid] == value) {
  156. return mid;
  157. } else {
  158. hi = mid;
  159. }
  160. }
  161. return lo;
  162. }
  163. case fadt_ind:
  164. return (int)value + 1;
  165. case NUM_PARAMS:
  166. default:;
  167. }
  168. DISPLAY("Error, not a valid param\n ");
  169. assert(0);
  170. return -2;
  171. }
  172. /* display of params */
  173. static void displayParamVal(FILE* f, varInds_t param, unsigned value, int width)
  174. {
  175. switch(param) {
  176. case wlog_ind:
  177. case clog_ind:
  178. case hlog_ind:
  179. case slog_ind:
  180. case mml_ind:
  181. case tlen_ind:
  182. if(width) {
  183. fprintf(f, "%*u", width, value);
  184. } else {
  185. fprintf(f, "%u", value);
  186. }
  187. break;
  188. case strt_ind:
  189. if(width) {
  190. fprintf(f, "%*s", width, g_stratName[value]);
  191. } else {
  192. fprintf(f, "%s", g_stratName[value]);
  193. }
  194. break;
  195. case fadt_ind: /* force attach dict */
  196. if(width) {
  197. fprintf(f, "%*d", width, (int)value);
  198. } else {
  199. fprintf(f, "%d", (int)value);
  200. }
  201. break;
  202. case NUM_PARAMS:
  203. default:
  204. DISPLAY("Error, not a valid param\n ");
  205. assert(0);
  206. break;
  207. }
  208. }
  209. /*-************************************
  210. * Benchmark Parameters/Global Variables
  211. **************************************/
  212. /* General Utility */
  213. static U32 g_timeLimit_s = 99999; /* about 27 hours */
  214. static UTIL_time_t g_time; /* to be used to compare solution finding speeds to compare to original */
  215. static U32 g_blockSize = 0;
  216. static U32 g_rand = 1;
  217. /* Display */
  218. static int g_displayLevel = 3;
  219. static BYTE g_silenceParams[NUM_PARAMS]; /* can selectively silence some params when displaying them */
  220. /* Mode Selection */
  221. static U32 g_singleRun = 0;
  222. static U32 g_optimizer = 0;
  223. static int g_optmode = 0;
  224. /* For cLevel Table generation */
  225. static U32 g_target = 0;
  226. static U32 g_noSeed = 0;
  227. /* For optimizer */
  228. static paramValues_t g_params; /* Initialized at the beginning of main w/ emptyParams() function */
  229. static double g_ratioMultiplier = 5.;
  230. static U32 g_strictness = PARAM_UNSET; /* range 1 - 100, measure of how strict */
  231. static BMK_benchResult_t g_lvltarget;
  232. typedef enum {
  233. directMap,
  234. xxhashMap,
  235. noMemo
  236. } memoTableType_t;
  237. typedef struct {
  238. memoTableType_t tableType;
  239. BYTE* table;
  240. size_t tableLen;
  241. varInds_t varArray[NUM_PARAMS];
  242. size_t varLen;
  243. } memoTable_t;
  244. typedef struct {
  245. BMK_benchResult_t result;
  246. paramValues_t params;
  247. } winnerInfo_t;
  248. typedef struct {
  249. U32 cSpeed; /* bytes / sec */
  250. U32 dSpeed;
  251. U32 cMem; /* bytes */
  252. } constraint_t;
  253. typedef struct winner_ll_node winner_ll_node;
  254. struct winner_ll_node {
  255. winnerInfo_t res;
  256. winner_ll_node* next;
  257. };
  258. static winner_ll_node* g_winners; /* linked list sorted ascending by cSize & cSpeed */
  259. /*
  260. * Additional Global Variables (Defined Above Use)
  261. * g_level_constraint
  262. * g_alreadyTested
  263. * g_maxTries
  264. * g_clockGranularity
  265. */
  266. /*-*******************************************************
  267. * General Util Functions
  268. *********************************************************/
  269. /* nullified useless params, to ensure count stats */
  270. /* cleans up params for memoizing / display */
  271. static paramValues_t sanitizeParams(paramValues_t params)
  272. {
  273. if (params.vals[strt_ind] == ZSTD_fast)
  274. params.vals[clog_ind] = 0, params.vals[slog_ind] = 0;
  275. if (params.vals[strt_ind] == ZSTD_dfast)
  276. params.vals[slog_ind] = 0;
  277. if ( (params.vals[strt_ind] < ZSTD_btopt) && (params.vals[strt_ind] != ZSTD_fast) )
  278. params.vals[tlen_ind] = 0;
  279. return params;
  280. }
  281. static ZSTD_compressionParameters pvalsToCParams(paramValues_t p)
  282. {
  283. ZSTD_compressionParameters c;
  284. memset(&c, 0, sizeof(ZSTD_compressionParameters));
  285. c.windowLog = p.vals[wlog_ind];
  286. c.chainLog = p.vals[clog_ind];
  287. c.hashLog = p.vals[hlog_ind];
  288. c.searchLog = p.vals[slog_ind];
  289. c.minMatch = p.vals[mml_ind];
  290. c.targetLength = p.vals[tlen_ind];
  291. c.strategy = p.vals[strt_ind];
  292. /* no forceAttachDict */
  293. return c;
  294. }
  295. static paramValues_t cParamsToPVals(ZSTD_compressionParameters c)
  296. {
  297. paramValues_t p;
  298. varInds_t i;
  299. p.vals[wlog_ind] = c.windowLog;
  300. p.vals[clog_ind] = c.chainLog;
  301. p.vals[hlog_ind] = c.hashLog;
  302. p.vals[slog_ind] = c.searchLog;
  303. p.vals[mml_ind] = c.minMatch;
  304. p.vals[tlen_ind] = c.targetLength;
  305. p.vals[strt_ind] = c.strategy;
  306. /* set all other params to their minimum value */
  307. for (i = strt_ind + 1; i < NUM_PARAMS; i++) {
  308. p.vals[i] = mintable[i];
  309. }
  310. return p;
  311. }
  312. /* equivalent of ZSTD_adjustCParams for paramValues_t */
  313. static paramValues_t
  314. adjustParams(paramValues_t p, const size_t maxBlockSize, const size_t dictSize)
  315. {
  316. paramValues_t ot = p;
  317. varInds_t i;
  318. p = cParamsToPVals(ZSTD_adjustCParams(pvalsToCParams(p), maxBlockSize, dictSize));
  319. if (!dictSize) { p.vals[fadt_ind] = 0; }
  320. /* retain value of all other parameters */
  321. for(i = strt_ind + 1; i < NUM_PARAMS; i++) {
  322. p.vals[i] = ot.vals[i];
  323. }
  324. return p;
  325. }
  326. static size_t BMK_findMaxMem(U64 requiredMem)
  327. {
  328. size_t const step = 64 MB;
  329. void* testmem = NULL;
  330. requiredMem = (((requiredMem >> 26) + 1) << 26);
  331. if (requiredMem > maxMemory) requiredMem = maxMemory;
  332. requiredMem += 2 * step;
  333. while (!testmem && requiredMem > 0) {
  334. testmem = malloc ((size_t)requiredMem);
  335. requiredMem -= step;
  336. }
  337. free (testmem);
  338. return (size_t) requiredMem;
  339. }
  340. /* accuracy in seconds only, span can be multiple years */
  341. static U32 BMK_timeSpan_s(const UTIL_time_t tStart)
  342. {
  343. return (U32)(UTIL_clockSpanMicro(tStart) / 1000000ULL);
  344. }
  345. static U32 FUZ_rotl32(U32 x, U32 r)
  346. {
  347. return ((x << r) | (x >> (32 - r)));
  348. }
  349. static U32 FUZ_rand(U32* src)
  350. {
  351. const U32 prime1 = 2654435761U;
  352. const U32 prime2 = 2246822519U;
  353. U32 rand32 = *src;
  354. rand32 *= prime1;
  355. rand32 += prime2;
  356. rand32 = FUZ_rotl32(rand32, 13);
  357. *src = rand32;
  358. return rand32 >> 5;
  359. }
  360. #define BOUNDCHECK(val,min,max) { \
  361. if (((val)<(min)) | ((val)>(max))) { \
  362. DISPLAY("INVALID PARAMETER CONSTRAINTS\n"); \
  363. return 0; \
  364. } }
  365. static int paramValid(const paramValues_t paramTarget)
  366. {
  367. U32 i;
  368. for(i = 0; i < NUM_PARAMS; i++) {
  369. BOUNDCHECK(paramTarget.vals[i], mintable[i], maxtable[i]);
  370. }
  371. return 1;
  372. }
  373. /* cParamUnsetMin() :
  374. * if any parameter in paramTarget is not yet set,
  375. * it will receive its corresponding minimal value.
  376. * This function never fails */
  377. static paramValues_t cParamUnsetMin(paramValues_t paramTarget)
  378. {
  379. varInds_t vi;
  380. for (vi = 0; vi < NUM_PARAMS; vi++) {
  381. if (paramTarget.vals[vi] == PARAM_UNSET) {
  382. paramTarget.vals[vi] = mintable[vi];
  383. }
  384. }
  385. return paramTarget;
  386. }
  387. static paramValues_t emptyParams(void)
  388. {
  389. U32 i;
  390. paramValues_t p;
  391. for(i = 0; i < NUM_PARAMS; i++) {
  392. p.vals[i] = PARAM_UNSET;
  393. }
  394. return p;
  395. }
  396. static winnerInfo_t initWinnerInfo(const paramValues_t p)
  397. {
  398. winnerInfo_t w1;
  399. w1.result.cSpeed = 0;
  400. w1.result.dSpeed = 0;
  401. w1.result.cMem = (size_t)-1;
  402. w1.result.cSize = (size_t)-1;
  403. w1.params = p;
  404. return w1;
  405. }
  406. static paramValues_t
  407. overwriteParams(paramValues_t base, const paramValues_t mask)
  408. {
  409. U32 i;
  410. for(i = 0; i < NUM_PARAMS; i++) {
  411. if(mask.vals[i] != PARAM_UNSET) {
  412. base.vals[i] = mask.vals[i];
  413. }
  414. }
  415. return base;
  416. }
  417. static void
  418. paramVaryOnce(const varInds_t paramIndex, const int amt, paramValues_t* ptr)
  419. {
  420. ptr->vals[paramIndex] = rangeMap(paramIndex,
  421. invRangeMap(paramIndex, ptr->vals[paramIndex]) + amt);
  422. }
  423. /* varies ptr by nbChanges respecting varyParams*/
  424. static void
  425. paramVariation(paramValues_t* ptr, memoTable_t* mtAll, const U32 nbChanges)
  426. {
  427. paramValues_t p;
  428. int validated = 0;
  429. while (!validated) {
  430. U32 i;
  431. p = *ptr;
  432. for (i = 0 ; i < nbChanges ; i++) {
  433. const U32 changeID = (U32)FUZ_rand(&g_rand) % (mtAll[p.vals[strt_ind]].varLen << 1);
  434. paramVaryOnce(mtAll[p.vals[strt_ind]].varArray[changeID >> 1],
  435. (int)((changeID & 1) << 1) - 1,
  436. &p);
  437. }
  438. validated = paramValid(p);
  439. }
  440. *ptr = p;
  441. }
  442. /* Completely random parameter selection */
  443. static paramValues_t randomParams(void)
  444. {
  445. varInds_t v; paramValues_t p;
  446. for(v = 0; v < NUM_PARAMS; v++) {
  447. p.vals[v] = rangeMap(v, (int)(FUZ_rand(&g_rand) % rangetable[v]));
  448. }
  449. return p;
  450. }
  451. static U64 g_clockGranularity = 100000000ULL;
  452. static void init_clockGranularity(void)
  453. {
  454. UTIL_time_t const clockStart = UTIL_getTime();
  455. U64 el1 = 0, el2 = 0;
  456. int i = 0;
  457. do {
  458. el1 = el2;
  459. el2 = UTIL_clockSpanNano(clockStart);
  460. if(el1 < el2) {
  461. U64 iv = el2 - el1;
  462. if(g_clockGranularity > iv) {
  463. g_clockGranularity = iv;
  464. i = 0;
  465. } else {
  466. i++;
  467. }
  468. }
  469. } while(i < 10);
  470. DEBUGOUTPUT("Granularity: %llu\n", (unsigned long long)g_clockGranularity);
  471. }
  472. /*-************************************
  473. * Optimizer Util Functions
  474. **************************************/
  475. /* checks results are feasible */
  476. static int feasible(const BMK_benchResult_t results, const constraint_t target) {
  477. return (results.cSpeed >= target.cSpeed)
  478. && (results.dSpeed >= target.dSpeed)
  479. && (results.cMem <= target.cMem)
  480. && (!g_optmode || results.cSize <= g_lvltarget.cSize);
  481. }
  482. /* hill climbing value for part 1 */
  483. /* Scoring here is a linear reward for all set constraints normalized between 0 to 1
  484. * (with 0 at 0 and 1 being fully fulfilling the constraint), summed with a logarithmic
  485. * bonus to exceeding the constraint value. We also give linear ratio for compression ratio.
  486. * The constant factors are experimental.
  487. */
  488. static double
  489. resultScore(const BMK_benchResult_t res, const size_t srcSize, const constraint_t target)
  490. {
  491. double cs = 0., ds = 0., rt, cm = 0.;
  492. const double r1 = 1, r2 = 0.1, rtr = 0.5;
  493. double ret;
  494. if(target.cSpeed) { cs = res.cSpeed / (double)target.cSpeed; }
  495. if(target.dSpeed) { ds = res.dSpeed / (double)target.dSpeed; }
  496. if(target.cMem != (U32)-1) { cm = (double)target.cMem / res.cMem; }
  497. rt = ((double)srcSize / res.cSize);
  498. ret = (MIN(1, cs) + MIN(1, ds) + MIN(1, cm))*r1 + rt * rtr +
  499. (MAX(0, log(cs))+ MAX(0, log(ds))+ MAX(0, log(cm))) * r2;
  500. return ret;
  501. }
  502. /* calculates normalized squared euclidean distance of result1 if it is in the first quadrant relative to lvlRes */
  503. static double
  504. resultDistLvl(const BMK_benchResult_t result1, const BMK_benchResult_t lvlRes)
  505. {
  506. double normalizedCSpeedGain1 = ((double)result1.cSpeed / lvlRes.cSpeed) - 1;
  507. double normalizedRatioGain1 = ((double)lvlRes.cSize / result1.cSize) - 1;
  508. if(normalizedRatioGain1 < 0 || normalizedCSpeedGain1 < 0) {
  509. return 0.0;
  510. }
  511. return normalizedRatioGain1 * g_ratioMultiplier + normalizedCSpeedGain1;
  512. }
  513. /* return true if r2 strictly better than r1 */
  514. static int
  515. compareResultLT(const BMK_benchResult_t result1, const BMK_benchResult_t result2, const constraint_t target, size_t srcSize)
  516. {
  517. if(feasible(result1, target) && feasible(result2, target)) {
  518. if(g_optmode) {
  519. return resultDistLvl(result1, g_lvltarget) < resultDistLvl(result2, g_lvltarget);
  520. } else {
  521. return (result1.cSize > result2.cSize)
  522. || (result1.cSize == result2.cSize && result2.cSpeed > result1.cSpeed)
  523. || (result1.cSize == result2.cSize && result2.cSpeed == result1.cSpeed && result2.dSpeed > result1.dSpeed);
  524. }
  525. }
  526. return feasible(result2, target)
  527. || (!feasible(result1, target)
  528. && (resultScore(result1, srcSize, target) < resultScore(result2, srcSize, target)));
  529. }
  530. static constraint_t relaxTarget(constraint_t target) {
  531. target.cMem = (U32)-1;
  532. target.cSpeed = (target.cSpeed * g_strictness) / 100;
  533. target.dSpeed = (target.dSpeed * g_strictness) / 100;
  534. return target;
  535. }
  536. static void optimizerAdjustInput(paramValues_t* pc, const size_t maxBlockSize)
  537. {
  538. varInds_t v;
  539. for(v = 0; v < NUM_PARAMS; v++) {
  540. if(pc->vals[v] != PARAM_UNSET) {
  541. U32 newval = MIN(MAX(pc->vals[v], mintable[v]), maxtable[v]);
  542. if(newval != pc->vals[v]) {
  543. pc->vals[v] = newval;
  544. DISPLAY("Warning: parameter %s not in valid range, adjusting to ",
  545. g_paramNames[v]);
  546. displayParamVal(stderr, v, newval, 0); DISPLAY("\n");
  547. }
  548. }
  549. }
  550. if(pc->vals[wlog_ind] != PARAM_UNSET) {
  551. U32 sshb = maxBlockSize > 1 ? ZSTD_highbit32((U32)(maxBlockSize-1)) + 1 : 1;
  552. /* edge case of highBit not working for 0 */
  553. if(maxBlockSize < (1ULL << 31) && sshb + 1 < pc->vals[wlog_ind]) {
  554. U32 adjust = MAX(mintable[wlog_ind], sshb);
  555. if(adjust != pc->vals[wlog_ind]) {
  556. pc->vals[wlog_ind] = adjust;
  557. DISPLAY("Warning: windowLog larger than src/block size, adjusted to %u\n",
  558. (unsigned)pc->vals[wlog_ind]);
  559. }
  560. }
  561. }
  562. if(pc->vals[wlog_ind] != PARAM_UNSET && pc->vals[clog_ind] != PARAM_UNSET) {
  563. U32 maxclog;
  564. if(pc->vals[strt_ind] == PARAM_UNSET || pc->vals[strt_ind] >= (U32)ZSTD_btlazy2) {
  565. maxclog = pc->vals[wlog_ind] + 1;
  566. } else {
  567. maxclog = pc->vals[wlog_ind];
  568. }
  569. if(pc->vals[clog_ind] > maxclog) {
  570. pc->vals[clog_ind] = maxclog;
  571. DISPLAY("Warning: chainlog too much larger than windowLog size, adjusted to %u\n",
  572. (unsigned)pc->vals[clog_ind]);
  573. }
  574. }
  575. if(pc->vals[wlog_ind] != PARAM_UNSET && pc->vals[hlog_ind] != PARAM_UNSET) {
  576. if(pc->vals[wlog_ind] + 1 < pc->vals[hlog_ind]) {
  577. pc->vals[hlog_ind] = pc->vals[wlog_ind] + 1;
  578. DISPLAY("Warning: hashlog too much larger than windowLog size, adjusted to %u\n",
  579. (unsigned)pc->vals[hlog_ind]);
  580. }
  581. }
  582. if(pc->vals[slog_ind] != PARAM_UNSET && pc->vals[clog_ind] != PARAM_UNSET) {
  583. if(pc->vals[slog_ind] > pc->vals[clog_ind]) {
  584. pc->vals[clog_ind] = pc->vals[slog_ind];
  585. DISPLAY("Warning: searchLog larger than chainLog, adjusted to %u\n",
  586. (unsigned)pc->vals[slog_ind]);
  587. }
  588. }
  589. }
  590. static int
  591. redundantParams(const paramValues_t paramValues, const constraint_t target, const size_t maxBlockSize)
  592. {
  593. return
  594. (ZSTD_estimateCStreamSize_usingCParams(pvalsToCParams(paramValues)) > (size_t)target.cMem) /* Uses too much memory */
  595. || ((1ULL << (paramValues.vals[wlog_ind] - 1)) >= maxBlockSize && paramValues.vals[wlog_ind] != mintable[wlog_ind]) /* wlog too much bigger than src size */
  596. || (paramValues.vals[clog_ind] > (paramValues.vals[wlog_ind] + (paramValues.vals[strt_ind] > ZSTD_btlazy2))) /* chainLog larger than windowLog*/
  597. || (paramValues.vals[slog_ind] > paramValues.vals[clog_ind]) /* searchLog larger than chainLog */
  598. || (paramValues.vals[hlog_ind] > paramValues.vals[wlog_ind] + 1); /* hashLog larger than windowLog + 1 */
  599. }
  600. /*-************************************
  601. * Display Functions
  602. **************************************/
  603. /* BMK_paramValues_into_commandLine() :
  604. * transform a set of parameters paramValues_t
  605. * into a command line compatible with `zstd` syntax
  606. * and writes it into FILE* f.
  607. * f must be already opened and writable */
  608. static void
  609. BMK_paramValues_into_commandLine(FILE* f, const paramValues_t params)
  610. {
  611. varInds_t v;
  612. int first = 1;
  613. fprintf(f,"--zstd=");
  614. for (v = 0; v < NUM_PARAMS; v++) {
  615. if (g_silenceParams[v]) { continue; }
  616. if (!first) { fprintf(f, ","); }
  617. fprintf(f,"%s=", g_paramNames[v]);
  618. if (v == strt_ind) { fprintf(f,"%u", (unsigned)params.vals[v]); }
  619. else { displayParamVal(f, v, params.vals[v], 0); }
  620. first = 0;
  621. }
  622. fprintf(f, "\n");
  623. }
  624. /* comparison function: */
  625. /* strictly better, strictly worse, equal, speed-side adv, size-side adv */
  626. #define WORSE_RESULT 0
  627. #define BETTER_RESULT 1
  628. #define ERROR_RESULT 2
  629. #define SPEED_RESULT 4
  630. #define SIZE_RESULT 5
  631. /* maybe have epsilon-eq to limit table size? */
  632. static int
  633. speedSizeCompare(const BMK_benchResult_t r1, const BMK_benchResult_t r2)
  634. {
  635. if(r1.cSpeed < r2.cSpeed) {
  636. if(r1.cSize >= r2.cSize) {
  637. return BETTER_RESULT;
  638. }
  639. return SPEED_RESULT; /* r2 is smaller but not faster. */
  640. } else {
  641. if(r1.cSize <= r2.cSize) {
  642. return WORSE_RESULT;
  643. }
  644. return SIZE_RESULT; /* r2 is faster but not smaller */
  645. }
  646. }
  647. /* 0 for insertion, 1 for no insert */
  648. /* maintain invariant speedSizeCompare(n, n->next) = SPEED_RESULT */
  649. static int
  650. insertWinner(const winnerInfo_t w, const constraint_t targetConstraints)
  651. {
  652. BMK_benchResult_t r = w.result;
  653. winner_ll_node* cur_node = g_winners;
  654. /* first node to insert */
  655. if(!feasible(r, targetConstraints)) {
  656. return 1;
  657. }
  658. if(g_winners == NULL) {
  659. winner_ll_node* first_node = malloc(sizeof(winner_ll_node));
  660. if(first_node == NULL) {
  661. return 1;
  662. }
  663. first_node->next = NULL;
  664. first_node->res = w;
  665. g_winners = first_node;
  666. return 0;
  667. }
  668. while(cur_node->next != NULL) {
  669. switch(speedSizeCompare(cur_node->res.result, r)) {
  670. case WORSE_RESULT:
  671. {
  672. return 1; /* never insert if better */
  673. }
  674. case BETTER_RESULT:
  675. {
  676. winner_ll_node* tmp;
  677. cur_node->res = cur_node->next->res;
  678. tmp = cur_node->next;
  679. cur_node->next = cur_node->next->next;
  680. free(tmp);
  681. break;
  682. }
  683. case SIZE_RESULT:
  684. {
  685. cur_node = cur_node->next;
  686. break;
  687. }
  688. case SPEED_RESULT: /* insert after first size result, then return */
  689. {
  690. winner_ll_node* newnode = malloc(sizeof(winner_ll_node));
  691. if(newnode == NULL) {
  692. return 1;
  693. }
  694. newnode->res = cur_node->res;
  695. cur_node->res = w;
  696. newnode->next = cur_node->next;
  697. cur_node->next = newnode;
  698. return 0;
  699. }
  700. }
  701. }
  702. assert(cur_node->next == NULL);
  703. switch(speedSizeCompare(cur_node->res.result, r)) {
  704. case WORSE_RESULT:
  705. {
  706. return 1; /* never insert if better */
  707. }
  708. case BETTER_RESULT:
  709. {
  710. cur_node->res = w;
  711. return 0;
  712. }
  713. case SIZE_RESULT:
  714. {
  715. winner_ll_node* newnode = malloc(sizeof(winner_ll_node));
  716. if(newnode == NULL) {
  717. return 1;
  718. }
  719. newnode->res = w;
  720. newnode->next = NULL;
  721. cur_node->next = newnode;
  722. return 0;
  723. }
  724. case SPEED_RESULT: /* insert before first size result, then return */
  725. {
  726. winner_ll_node* newnode = malloc(sizeof(winner_ll_node));
  727. if(newnode == NULL) {
  728. return 1;
  729. }
  730. newnode->res = cur_node->res;
  731. cur_node->res = w;
  732. newnode->next = cur_node->next;
  733. cur_node->next = newnode;
  734. return 0;
  735. }
  736. default:
  737. return 1;
  738. }
  739. }
  740. static void
  741. BMK_displayOneResult(FILE* f, winnerInfo_t res, const size_t srcSize)
  742. {
  743. varInds_t v;
  744. int first = 1;
  745. res.params = cParamUnsetMin(res.params);
  746. fprintf(f, " {");
  747. for (v = 0; v < NUM_PARAMS; v++) {
  748. if (g_silenceParams[v]) { continue; }
  749. if (!first) { fprintf(f, ","); }
  750. displayParamVal(f, v, res.params.vals[v], 3);
  751. first = 0;
  752. }
  753. { double const ratio = res.result.cSize ?
  754. (double)srcSize / res.result.cSize : 0;
  755. double const cSpeedMBps = (double)res.result.cSpeed / MB_UNIT;
  756. double const dSpeedMBps = (double)res.result.dSpeed / MB_UNIT;
  757. fprintf(f, " }, /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */\n",
  758. ratio, cSpeedMBps, dSpeedMBps);
  759. }
  760. }
  761. /* Writes to f the results of a parameter benchmark */
  762. /* when used with --optimize, will only print results better than previously discovered */
  763. static void
  764. BMK_printWinner(FILE* f, const int cLevel, const BMK_benchResult_t result, const paramValues_t params, const size_t srcSize)
  765. {
  766. char lvlstr[15] = "Custom Level";
  767. winnerInfo_t w;
  768. w.params = params;
  769. w.result = result;
  770. fprintf(f, "\r%79s\r", "");
  771. if(cLevel != CUSTOM_LEVEL) {
  772. snprintf(lvlstr, 15, " Level %2d ", cLevel);
  773. }
  774. if(TIMED) {
  775. const U64 mn_in_ns = 60ULL * TIMELOOP_NANOSEC;
  776. const U64 time_ns = UTIL_clockSpanNano(g_time);
  777. const U64 minutes = time_ns / mn_in_ns;
  778. fprintf(f, "%1lu:%2lu:%05.2f - ",
  779. (unsigned long) minutes / 60,
  780. (unsigned long) minutes % 60,
  781. (double)(time_ns - (minutes * mn_in_ns)) / TIMELOOP_NANOSEC );
  782. }
  783. fprintf(f, "/* %s */ ", lvlstr);
  784. BMK_displayOneResult(f, w, srcSize);
  785. }
  786. static void
  787. BMK_printWinnerOpt(FILE* f, const U32 cLevel, const BMK_benchResult_t result, const paramValues_t params, const constraint_t targetConstraints, const size_t srcSize)
  788. {
  789. /* global winner used for constraints */
  790. /* cSize, cSpeed, dSpeed, cMem */
  791. static winnerInfo_t g_winner = { { (size_t)-1LL, 0, 0, (size_t)-1LL },
  792. { { PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET } }
  793. };
  794. if ( DEBUG
  795. || compareResultLT(g_winner.result, result, targetConstraints, srcSize)
  796. || g_displayLevel >= 4) {
  797. if ( DEBUG
  798. && compareResultLT(g_winner.result, result, targetConstraints, srcSize)) {
  799. DISPLAY("New Winner: \n");
  800. }
  801. if(g_displayLevel >= 2) {
  802. BMK_printWinner(f, cLevel, result, params, srcSize);
  803. }
  804. if(compareResultLT(g_winner.result, result, targetConstraints, srcSize)) {
  805. if(g_displayLevel >= 1) { BMK_paramValues_into_commandLine(f, params); }
  806. g_winner.result = result;
  807. g_winner.params = params;
  808. }
  809. }
  810. if(g_optmode && g_optimizer && (DEBUG || g_displayLevel == 3)) {
  811. winnerInfo_t w;
  812. winner_ll_node* n;
  813. w.result = result;
  814. w.params = params;
  815. insertWinner(w, targetConstraints);
  816. if(!DEBUG) { fprintf(f, "\033c"); }
  817. fprintf(f, "\n");
  818. /* the table */
  819. fprintf(f, "================================\n");
  820. for(n = g_winners; n != NULL; n = n->next) {
  821. BMK_displayOneResult(f, n->res, srcSize);
  822. }
  823. fprintf(f, "================================\n");
  824. fprintf(f, "Level Bounds: R: > %.3f AND C: < %.1f MB/s \n\n",
  825. (double)srcSize / g_lvltarget.cSize, (double)g_lvltarget.cSpeed / MB_UNIT);
  826. fprintf(f, "Overall Winner: \n");
  827. BMK_displayOneResult(f, g_winner, srcSize);
  828. BMK_paramValues_into_commandLine(f, g_winner.params);
  829. fprintf(f, "Latest BMK: \n");\
  830. BMK_displayOneResult(f, w, srcSize);
  831. }
  832. }
  833. /* BMK_print_cLevelEntry() :
  834. * Writes one cLevelTable entry, for one level.
  835. * f must exist, be already opened, and be seekable.
  836. * this function cannot error.
  837. */
  838. static void
  839. BMK_print_cLevelEntry(FILE* f, const int cLevel,
  840. paramValues_t params,
  841. const BMK_benchResult_t result, const size_t srcSize)
  842. {
  843. varInds_t v;
  844. int first = 1;
  845. assert(cLevel >= 0);
  846. assert(cLevel <= NB_LEVELS_TRACKED);
  847. params = cParamUnsetMin(params);
  848. fprintf(f, " {");
  849. /* print cParams.
  850. * assumption : all cParams are present and in order in the following range */
  851. for (v = 0; v <= strt_ind; v++) {
  852. if (!first) { fprintf(f, ","); }
  853. displayParamVal(f, v, params.vals[v], 3);
  854. first = 0;
  855. }
  856. /* print comment */
  857. { double const ratio = result.cSize ?
  858. (double)srcSize / result.cSize : 0;
  859. double const cSpeedMBps = (double)result.cSpeed / MB_UNIT;
  860. double const dSpeedMBps = (double)result.dSpeed / MB_UNIT;
  861. fprintf(f, " }, /* level %2i: R=%5.3f at %5.1f MB/s - %5.1f MB/s */\n",
  862. cLevel, ratio, cSpeedMBps, dSpeedMBps);
  863. }
  864. }
  865. /* BMK_print_cLevelTable() :
  866. * print candidate compression table into proposed FILE* f.
  867. * f must exist, be already opened, and be seekable.
  868. * winners must be a table of NB_LEVELS_TRACKED+1 elements winnerInfo_t, all entries presumed initialized
  869. * this function cannot error.
  870. */
  871. static void
  872. BMK_print_cLevelTable(FILE* f, const winnerInfo_t* winners, const size_t srcSize)
  873. {
  874. int cLevel;
  875. fprintf(f, "\n /* Proposed configurations : */ \n");
  876. fprintf(f, " /* W, C, H, S, L, T, strat */ \n");
  877. for (cLevel=0; cLevel <= NB_LEVELS_TRACKED; cLevel++)
  878. BMK_print_cLevelEntry(f,
  879. cLevel, winners[cLevel].params,
  880. winners[cLevel].result, srcSize);
  881. }
  882. /* BMK_saveAndPrint_cLevelTable() :
  883. * save candidate compression table into FILE* f,
  884. * and then to stdout.
  885. * f must exist, be already opened, and be seekable.
  886. * winners must be a table of NB_LEVELS_TRACKED+1 elements winnerInfo_t, all entries presumed initialized
  887. * this function cannot error.
  888. */
  889. static void
  890. BMK_saveAndPrint_cLevelTable(FILE* const f,
  891. const winnerInfo_t* winners,
  892. const size_t srcSize)
  893. {
  894. fseek(f, 0, SEEK_SET);
  895. BMK_print_cLevelTable(f, winners, srcSize);
  896. fflush(f);
  897. BMK_print_cLevelTable(stdout, winners, srcSize);
  898. }
  899. /*-*******************************************************
  900. * Functions to Benchmark
  901. *********************************************************/
  902. typedef struct {
  903. ZSTD_CCtx* cctx;
  904. const void* dictBuffer;
  905. size_t dictBufferSize;
  906. int cLevel;
  907. const paramValues_t* comprParams;
  908. } BMK_initCCtxArgs;
  909. static size_t local_initCCtx(void* payload) {
  910. const BMK_initCCtxArgs* ag = (const BMK_initCCtxArgs*)payload;
  911. varInds_t i;
  912. ZSTD_CCtx_reset(ag->cctx, ZSTD_reset_session_and_parameters);
  913. ZSTD_CCtx_setParameter(ag->cctx, ZSTD_c_compressionLevel, ag->cLevel);
  914. for(i = 0; i < NUM_PARAMS; i++) {
  915. if(ag->comprParams->vals[i] != PARAM_UNSET)
  916. ZSTD_CCtx_setParameter(ag->cctx, cctxSetParamTable[i], ag->comprParams->vals[i]);
  917. }
  918. ZSTD_CCtx_loadDictionary(ag->cctx, ag->dictBuffer, ag->dictBufferSize);
  919. return 0;
  920. }
  921. typedef struct {
  922. ZSTD_DCtx* dctx;
  923. const void* dictBuffer;
  924. size_t dictBufferSize;
  925. } BMK_initDCtxArgs;
  926. static size_t local_initDCtx(void* payload) {
  927. const BMK_initDCtxArgs* ag = (const BMK_initDCtxArgs*)payload;
  928. ZSTD_DCtx_reset(ag->dctx, ZSTD_reset_session_and_parameters);
  929. ZSTD_DCtx_loadDictionary(ag->dctx, ag->dictBuffer, ag->dictBufferSize);
  930. return 0;
  931. }
  932. /* additional argument is just the context */
  933. static size_t local_defaultCompress(
  934. const void* srcBuffer, size_t srcSize,
  935. void* dstBuffer, size_t dstSize,
  936. void* addArgs)
  937. {
  938. ZSTD_CCtx* cctx = (ZSTD_CCtx*)addArgs;
  939. assert(dstSize == ZSTD_compressBound(srcSize)); /* specific to this version, which is only used in paramgrill */
  940. return ZSTD_compress2(cctx, dstBuffer, dstSize, srcBuffer, srcSize);
  941. }
  942. /* additional argument is just the context */
  943. static size_t local_defaultDecompress(
  944. const void* srcBuffer, size_t srcSize,
  945. void* dstBuffer, size_t dstSize,
  946. void* addArgs) {
  947. size_t moreToFlush = 1;
  948. ZSTD_DCtx* dctx = (ZSTD_DCtx*)addArgs;
  949. ZSTD_inBuffer in;
  950. ZSTD_outBuffer out;
  951. in.src = srcBuffer;
  952. in.size = srcSize;
  953. in.pos = 0;
  954. out.dst = dstBuffer;
  955. out.size = dstSize;
  956. out.pos = 0;
  957. while (moreToFlush) {
  958. if(out.pos == out.size) {
  959. return (size_t)-ZSTD_error_dstSize_tooSmall;
  960. }
  961. moreToFlush = ZSTD_decompressStream(dctx,
  962. &out, &in);
  963. if (ZSTD_isError(moreToFlush)) {
  964. return moreToFlush;
  965. }
  966. }
  967. return out.pos;
  968. }
  969. /*-************************************
  970. * Data Initialization Functions
  971. **************************************/
  972. typedef struct {
  973. void* srcBuffer;
  974. size_t srcSize;
  975. const void** srcPtrs;
  976. size_t* srcSizes;
  977. void** dstPtrs;
  978. size_t* dstCapacities;
  979. size_t* dstSizes;
  980. void** resPtrs;
  981. size_t* resSizes;
  982. size_t nbBlocks;
  983. size_t maxBlockSize;
  984. } buffers_t;
  985. typedef struct {
  986. size_t dictSize;
  987. void* dictBuffer;
  988. ZSTD_CCtx* cctx;
  989. ZSTD_DCtx* dctx;
  990. } contexts_t;
  991. static void freeNonSrcBuffers(const buffers_t b) {
  992. free((void*)b.srcPtrs);
  993. free(b.srcSizes);
  994. if(b.dstPtrs != NULL) {
  995. free(b.dstPtrs[0]);
  996. }
  997. free(b.dstPtrs);
  998. free(b.dstCapacities);
  999. free(b.dstSizes);
  1000. if(b.resPtrs != NULL) {
  1001. free(b.resPtrs[0]);
  1002. }
  1003. free(b.resPtrs);
  1004. free(b.resSizes);
  1005. }
  1006. static void freeBuffers(const buffers_t b) {
  1007. if(b.srcPtrs != NULL) {
  1008. free(b.srcBuffer);
  1009. }
  1010. freeNonSrcBuffers(b);
  1011. }
  1012. /* srcBuffer will be freed by freeBuffers now */
  1013. static int createBuffersFromMemory(buffers_t* buff, void * srcBuffer, const size_t nbFiles,
  1014. const size_t* fileSizes)
  1015. {
  1016. size_t pos = 0, n, blockSize;
  1017. U32 maxNbBlocks, blockNb = 0;
  1018. buff->srcSize = 0;
  1019. for(n = 0; n < nbFiles; n++) {
  1020. buff->srcSize += fileSizes[n];
  1021. }
  1022. if(buff->srcSize == 0) {
  1023. DISPLAY("No data to bench\n");
  1024. return 1;
  1025. }
  1026. blockSize = g_blockSize ? g_blockSize : buff->srcSize;
  1027. maxNbBlocks = (U32) ((buff->srcSize + (blockSize-1)) / blockSize) + (U32)nbFiles;
  1028. buff->srcPtrs = (const void**)calloc(maxNbBlocks, sizeof(void*));
  1029. buff->srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t));
  1030. buff->dstPtrs = (void**)calloc(maxNbBlocks, sizeof(void*));
  1031. buff->dstCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t));
  1032. buff->dstSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t));
  1033. buff->resPtrs = (void**)calloc(maxNbBlocks, sizeof(void*));
  1034. buff->resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t));
  1035. if(!buff->srcPtrs || !buff->srcSizes || !buff->dstPtrs || !buff->dstCapacities || !buff->dstSizes || !buff->resPtrs || !buff->resSizes) {
  1036. DISPLAY("alloc error\n");
  1037. freeNonSrcBuffers(*buff);
  1038. return 1;
  1039. }
  1040. buff->srcBuffer = srcBuffer;
  1041. buff->srcPtrs[0] = (const void*)buff->srcBuffer;
  1042. buff->dstPtrs[0] = malloc(ZSTD_compressBound(buff->srcSize) + (maxNbBlocks * 1024));
  1043. buff->resPtrs[0] = malloc(buff->srcSize);
  1044. if(!buff->dstPtrs[0] || !buff->resPtrs[0]) {
  1045. DISPLAY("alloc error\n");
  1046. freeNonSrcBuffers(*buff);
  1047. return 1;
  1048. }
  1049. for(n = 0; n < nbFiles; n++) {
  1050. size_t pos_end = pos + fileSizes[n];
  1051. for(; pos < pos_end; blockNb++) {
  1052. buff->srcPtrs[blockNb] = (const void*)((char*)srcBuffer + pos);
  1053. buff->srcSizes[blockNb] = blockSize;
  1054. pos += blockSize;
  1055. }
  1056. if(fileSizes[n] > 0) { buff->srcSizes[blockNb - 1] = ((fileSizes[n] - 1) % blockSize) + 1; }
  1057. pos = pos_end;
  1058. }
  1059. buff->dstCapacities[0] = ZSTD_compressBound(buff->srcSizes[0]);
  1060. buff->dstSizes[0] = buff->dstCapacities[0];
  1061. buff->resSizes[0] = buff->srcSizes[0];
  1062. buff->maxBlockSize = buff->srcSizes[0];
  1063. for(n = 1; n < blockNb; n++) {
  1064. buff->dstPtrs[n] = ((char*)buff->dstPtrs[n-1]) + buff->dstCapacities[n-1];
  1065. buff->resPtrs[n] = ((char*)buff->resPtrs[n-1]) + buff->resSizes[n-1];
  1066. buff->dstCapacities[n] = ZSTD_compressBound(buff->srcSizes[n]);
  1067. buff->dstSizes[n] = buff->dstCapacities[n];
  1068. buff->resSizes[n] = buff->srcSizes[n];
  1069. buff->maxBlockSize = MAX(buff->maxBlockSize, buff->srcSizes[n]);
  1070. }
  1071. buff->nbBlocks = blockNb;
  1072. return 0;
  1073. }
  1074. /* allocates buffer's arguments. returns success / failure */
  1075. static int createBuffers(buffers_t* buff, const char* const * const fileNamesTable,
  1076. size_t nbFiles) {
  1077. size_t pos = 0;
  1078. size_t n;
  1079. size_t totalSizeToLoad = (size_t)UTIL_getTotalFileSize(fileNamesTable, (U32)nbFiles);
  1080. size_t benchedSize = MIN(BMK_findMaxMem(totalSizeToLoad * 3) / 3, totalSizeToLoad);
  1081. size_t* fileSizes = calloc(sizeof(size_t), nbFiles);
  1082. void* srcBuffer = NULL;
  1083. int ret = 0;
  1084. if(!totalSizeToLoad || !benchedSize) {
  1085. ret = 1;
  1086. DISPLAY("Nothing to Bench\n");
  1087. goto _cleanUp;
  1088. }
  1089. srcBuffer = malloc(benchedSize);
  1090. if(!fileSizes || !srcBuffer) {
  1091. ret = 1;
  1092. goto _cleanUp;
  1093. }
  1094. for(n = 0; n < nbFiles; n++) {
  1095. FILE* f;
  1096. U64 fileSize = UTIL_getFileSize(fileNamesTable[n]);
  1097. if (UTIL_isDirectory(fileNamesTable[n])) {
  1098. DISPLAY("Ignoring %s directory... \n", fileNamesTable[n]);
  1099. continue;
  1100. }
  1101. if (fileSize == UTIL_FILESIZE_UNKNOWN) {
  1102. DISPLAY("Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]);
  1103. continue;
  1104. }
  1105. f = fopen(fileNamesTable[n], "rb");
  1106. if (f==NULL) {
  1107. DISPLAY("impossible to open file %s\n", fileNamesTable[n]);
  1108. fclose(f);
  1109. ret = 10;
  1110. goto _cleanUp;
  1111. }
  1112. DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[n]);
  1113. if (fileSize + pos > benchedSize) fileSize = benchedSize - pos, nbFiles=n; /* buffer too small - stop after this file */
  1114. {
  1115. char* buffer = (char*)(srcBuffer);
  1116. size_t const readSize = fread((buffer)+pos, 1, (size_t)fileSize, f);
  1117. fclose(f);
  1118. if (readSize != (size_t)fileSize) {
  1119. DISPLAY("could not read %s", fileNamesTable[n]);
  1120. ret = 1;
  1121. goto _cleanUp;
  1122. }
  1123. fileSizes[n] = readSize;
  1124. pos += readSize;
  1125. }
  1126. }
  1127. ret = createBuffersFromMemory(buff, srcBuffer, nbFiles, fileSizes);
  1128. _cleanUp:
  1129. if(ret) { free(srcBuffer); }
  1130. free(fileSizes);
  1131. return ret;
  1132. }
  1133. static void freeContexts(const contexts_t ctx) {
  1134. free(ctx.dictBuffer);
  1135. ZSTD_freeCCtx(ctx.cctx);
  1136. ZSTD_freeDCtx(ctx.dctx);
  1137. }
  1138. static int createContexts(contexts_t* ctx, const char* dictFileName) {
  1139. FILE* f;
  1140. size_t readSize;
  1141. ctx->cctx = ZSTD_createCCtx();
  1142. ctx->dctx = ZSTD_createDCtx();
  1143. assert(ctx->cctx != NULL);
  1144. assert(ctx->dctx != NULL);
  1145. if(dictFileName == NULL) {
  1146. ctx->dictSize = 0;
  1147. ctx->dictBuffer = NULL;
  1148. return 0;
  1149. }
  1150. { U64 const dictFileSize = UTIL_getFileSize(dictFileName);
  1151. assert(dictFileSize != UTIL_FILESIZE_UNKNOWN);
  1152. ctx->dictSize = (size_t)dictFileSize;
  1153. assert((U64)ctx->dictSize == dictFileSize); /* check overflow */
  1154. }
  1155. ctx->dictBuffer = malloc(ctx->dictSize);
  1156. f = fopen(dictFileName, "rb");
  1157. if (f==NULL) {
  1158. DISPLAY("unable to open file\n");
  1159. freeContexts(*ctx);
  1160. return 1;
  1161. }
  1162. if (ctx->dictSize > 64 MB || !(ctx->dictBuffer)) {
  1163. DISPLAY("dictionary too large\n");
  1164. fclose(f);
  1165. freeContexts(*ctx);
  1166. return 1;
  1167. }
  1168. readSize = fread(ctx->dictBuffer, 1, ctx->dictSize, f);
  1169. fclose(f);
  1170. if (readSize != ctx->dictSize) {
  1171. DISPLAY("unable to read file\n");
  1172. freeContexts(*ctx);
  1173. return 1;
  1174. }
  1175. return 0;
  1176. }
  1177. /*-************************************
  1178. * Optimizer Memoization Functions
  1179. **************************************/
  1180. /* return: new length */
  1181. /* keep old array, will need if iter over strategy. */
  1182. /* prunes useless params */
  1183. static size_t sanitizeVarArray(varInds_t* varNew, const size_t varLength, const varInds_t* varArray, const ZSTD_strategy strat) {
  1184. size_t i, j = 0;
  1185. for(i = 0; i < varLength; i++) {
  1186. if( !((varArray[i] == clog_ind && strat == ZSTD_fast)
  1187. || (varArray[i] == slog_ind && strat == ZSTD_fast)
  1188. || (varArray[i] == slog_ind && strat == ZSTD_dfast)
  1189. || (varArray[i] == tlen_ind && strat < ZSTD_btopt && strat != ZSTD_fast))) {
  1190. varNew[j] = varArray[i];
  1191. j++;
  1192. }
  1193. }
  1194. return j;
  1195. }
  1196. /* res should be NUM_PARAMS size */
  1197. /* constructs varArray from paramValues_t style parameter */
  1198. /* pass in using dict. */
  1199. static size_t variableParams(const paramValues_t paramConstraints, varInds_t* res, const int usingDictionary) {
  1200. varInds_t i;
  1201. size_t j = 0;
  1202. for(i = 0; i < NUM_PARAMS; i++) {
  1203. if(paramConstraints.vals[i] == PARAM_UNSET) {
  1204. if(i == fadt_ind && !usingDictionary) continue; /* don't use fadt if no dictionary */
  1205. res[j] = i; j++;
  1206. }
  1207. }
  1208. return j;
  1209. }
  1210. /* length of memo table given free variables */
  1211. static size_t memoTableLen(const varInds_t* varyParams, const size_t varyLen) {
  1212. size_t arrayLen = 1;
  1213. size_t i;
  1214. for(i = 0; i < varyLen; i++) {
  1215. if(varyParams[i] == strt_ind) continue; /* strategy separated by table */
  1216. arrayLen *= rangetable[varyParams[i]];
  1217. }
  1218. return arrayLen;
  1219. }
  1220. /* returns unique index in memotable of compression parameters */
  1221. static unsigned memoTableIndDirect(const paramValues_t* ptr, const varInds_t* varyParams, const size_t varyLen) {
  1222. size_t i;
  1223. unsigned ind = 0;
  1224. for(i = 0; i < varyLen; i++) {
  1225. varInds_t v = varyParams[i];
  1226. if(v == strt_ind) continue; /* exclude strategy from memotable */
  1227. ind *= rangetable[v]; ind += (unsigned)invRangeMap(v, ptr->vals[v]);
  1228. }
  1229. return ind;
  1230. }
  1231. static size_t memoTableGet(const memoTable_t* memoTableArray, const paramValues_t p) {
  1232. const memoTable_t mt = memoTableArray[p.vals[strt_ind]];
  1233. switch(mt.tableType) {
  1234. case directMap:
  1235. return mt.table[memoTableIndDirect(&p, mt.varArray, mt.varLen)];
  1236. case xxhashMap:
  1237. return mt.table[(XXH64(&p.vals, sizeof(U32) * NUM_PARAMS, 0) >> 3) % mt.tableLen];
  1238. case noMemo:
  1239. return 0;
  1240. }
  1241. return 0; /* should never happen, stop compiler warnings */
  1242. }
  1243. static void memoTableSet(const memoTable_t* memoTableArray, const paramValues_t p, const BYTE value) {
  1244. const memoTable_t mt = memoTableArray[p.vals[strt_ind]];
  1245. switch(mt.tableType) {
  1246. case directMap:
  1247. mt.table[memoTableIndDirect(&p, mt.varArray, mt.varLen)] = value; break;
  1248. case xxhashMap:
  1249. mt.table[(XXH64(&p.vals, sizeof(U32) * NUM_PARAMS, 0) >> 3) % mt.tableLen] = value; break;
  1250. case noMemo:
  1251. break;
  1252. }
  1253. }
  1254. /* frees all allocated memotables */
  1255. /* secret contract :
  1256. * mtAll is a table of (ZSTD_STRATEGY_MAX+1) memoTable_t */
  1257. static void freeMemoTableArray(memoTable_t* const mtAll) {
  1258. int i;
  1259. if(mtAll == NULL) { return; }
  1260. for(i = 1; i <= (int)ZSTD_STRATEGY_MAX; i++) {
  1261. free(mtAll[i].table);
  1262. }
  1263. free(mtAll);
  1264. }
  1265. /* inits memotables for all (including mallocs), all strategies */
  1266. /* takes unsanitized varyParams */
  1267. static memoTable_t*
  1268. createMemoTableArray(const paramValues_t p,
  1269. const varInds_t* const varyParams,
  1270. const size_t varyLen,
  1271. const U32 memoTableLog)
  1272. {
  1273. memoTable_t* const mtAll = (memoTable_t*)calloc(sizeof(memoTable_t),(ZSTD_STRATEGY_MAX + 1));
  1274. ZSTD_strategy i, stratMin = ZSTD_STRATEGY_MIN, stratMax = ZSTD_STRATEGY_MAX;
  1275. if(mtAll == NULL) {
  1276. return NULL;
  1277. }
  1278. for(i = 1; i <= (int)ZSTD_STRATEGY_MAX; i++) {
  1279. mtAll[i].varLen = sanitizeVarArray(mtAll[i].varArray, varyLen, varyParams, i);
  1280. }
  1281. /* no memoization */
  1282. if(memoTableLog == 0) {
  1283. for(i = 1; i <= (int)ZSTD_STRATEGY_MAX; i++) {
  1284. mtAll[i].tableType = noMemo;
  1285. mtAll[i].table = NULL;
  1286. mtAll[i].tableLen = 0;
  1287. }
  1288. return mtAll;
  1289. }
  1290. if(p.vals[strt_ind] != PARAM_UNSET) {
  1291. stratMin = p.vals[strt_ind];
  1292. stratMax = p.vals[strt_ind];
  1293. }
  1294. for(i = stratMin; i <= stratMax; i++) {
  1295. size_t mtl = memoTableLen(mtAll[i].varArray, mtAll[i].varLen);
  1296. mtAll[i].tableType = directMap;
  1297. if(memoTableLog != PARAM_UNSET && mtl > (1ULL << memoTableLog)) { /* use hash table */ /* provide some option to only use hash tables? */
  1298. mtAll[i].tableType = xxhashMap;
  1299. mtl = ((size_t)1 << memoTableLog);
  1300. }
  1301. mtAll[i].table = (BYTE*)calloc(sizeof(BYTE), mtl);
  1302. mtAll[i].tableLen = mtl;
  1303. if(mtAll[i].table == NULL) {
  1304. freeMemoTableArray(mtAll);
  1305. return NULL;
  1306. }
  1307. }
  1308. return mtAll;
  1309. }
  1310. /* Sets pc to random unmeasured set of parameters */
  1311. /* specify strategy */
  1312. static void randomConstrainedParams(paramValues_t* pc, const memoTable_t* memoTableArray, const ZSTD_strategy st)
  1313. {
  1314. size_t j;
  1315. const memoTable_t mt = memoTableArray[st];
  1316. pc->vals[strt_ind] = st;
  1317. for(j = 0; j < mt.tableLen; j++) {
  1318. int i;
  1319. for(i = 0; i < NUM_PARAMS; i++) {
  1320. varInds_t v = mt.varArray[i];
  1321. if(v == strt_ind) continue;
  1322. pc->vals[v] = rangeMap(v, FUZ_rand(&g_rand) % rangetable[v]);
  1323. }
  1324. if(!(memoTableGet(memoTableArray, *pc))) break; /* only pick unpicked params. */
  1325. }
  1326. }
  1327. /*-************************************
  1328. * Benchmarking Functions
  1329. **************************************/
  1330. static void display_params_tested(paramValues_t cParams)
  1331. {
  1332. varInds_t vi;
  1333. DISPLAYLEVEL(3, "\r testing :");
  1334. for (vi=0; vi < NUM_PARAMS; vi++) {
  1335. DISPLAYLEVEL(3, "%3u,", (unsigned)cParams.vals[vi]);
  1336. }
  1337. DISPLAYLEVEL(3, "\b \r");
  1338. }
  1339. /* Replicate functionality of benchMemAdvanced, but with pre-split src / dst buffers */
  1340. /* The purpose is so that sufficient information is returned so that a decompression call to benchMemInvertible is possible */
  1341. /* BMK_benchMemAdvanced(srcBuffer,srcSize, dstBuffer, dstSize, fileSizes, nbFiles, 0, &cParams, dictBuffer, dictSize, ctx, dctx, 0, "File", &adv); */
  1342. /* nbSeconds used in same way as in BMK_advancedParams_t */
  1343. /* if in decodeOnly, then srcPtr's will be compressed blocks, and uncompressedBlocks will be written to dstPtrs */
  1344. /* dictionary nullable, nothing else though. */
  1345. /* note : it would be a lot better if this function was present in benchzstd.c,
  1346. * sharing code with benchMemAdvanced(), since it's technically a part of it */
  1347. static BMK_benchOutcome_t
  1348. BMK_benchMemInvertible( buffers_t buf, contexts_t ctx,
  1349. int cLevel, const paramValues_t* comprParams,
  1350. BMK_mode_t mode, unsigned nbSeconds)
  1351. {
  1352. U32 i;
  1353. BMK_benchResult_t bResult;
  1354. const void *const *const srcPtrs = (const void *const *const)buf.srcPtrs;
  1355. size_t const *const srcSizes = buf.srcSizes;
  1356. void** const dstPtrs = buf.dstPtrs;
  1357. size_t const *const dstCapacities = buf.dstCapacities;
  1358. size_t* const dstSizes = buf.dstSizes;
  1359. void** const resPtrs = buf.resPtrs;
  1360. size_t const *const resSizes = buf.resSizes;
  1361. const void* dictBuffer = ctx.dictBuffer;
  1362. const size_t dictBufferSize = ctx.dictSize;
  1363. const size_t nbBlocks = buf.nbBlocks;
  1364. const size_t srcSize = buf.srcSize;
  1365. ZSTD_CCtx* cctx = ctx.cctx;
  1366. ZSTD_DCtx* dctx = ctx.dctx;
  1367. /* init */
  1368. display_params_tested(*comprParams);
  1369. memset(&bResult, 0, sizeof(bResult));
  1370. /* warming up memory */
  1371. for (i = 0; i < buf.nbBlocks; i++) {
  1372. if (mode != BMK_decodeOnly) {
  1373. RDG_genBuffer(dstPtrs[i], dstCapacities[i], 0.10, 0.50, 1);
  1374. } else {
  1375. RDG_genBuffer(resPtrs[i], resSizes[i], 0.10, 0.50, 1);
  1376. }
  1377. }
  1378. /* Bench */
  1379. {
  1380. /* init args */
  1381. int compressionCompleted = (mode == BMK_decodeOnly);
  1382. int decompressionCompleted = (mode == BMK_compressOnly);
  1383. BMK_timedFnState_t* timeStateCompress = BMK_createTimedFnState(nbSeconds * 1000, 1000);
  1384. BMK_timedFnState_t* timeStateDecompress = BMK_createTimedFnState(nbSeconds * 1000, 1000);
  1385. BMK_benchParams_t cbp, dbp;
  1386. BMK_initCCtxArgs cctxprep;
  1387. BMK_initDCtxArgs dctxprep;
  1388. cbp.benchFn = local_defaultCompress;
  1389. cbp.benchPayload = cctx;
  1390. cbp.initFn = local_initCCtx;
  1391. cbp.initPayload = &cctxprep;
  1392. cbp.errorFn = ZSTD_isError;
  1393. cbp.blockCount = nbBlocks;
  1394. cbp.srcBuffers = srcPtrs;
  1395. cbp.srcSizes = srcSizes;
  1396. cbp.dstBuffers = dstPtrs;
  1397. cbp.dstCapacities = dstCapacities;
  1398. cbp.blockResults = dstSizes;
  1399. cctxprep.cctx = cctx;
  1400. cctxprep.dictBuffer = dictBuffer;
  1401. cctxprep.dictBufferSize = dictBufferSize;
  1402. cctxprep.cLevel = cLevel;
  1403. cctxprep.comprParams = comprParams;
  1404. dbp.benchFn = local_defaultDecompress;
  1405. dbp.benchPayload = dctx;
  1406. dbp.initFn = local_initDCtx;
  1407. dbp.initPayload = &dctxprep;
  1408. dbp.errorFn = ZSTD_isError;
  1409. dbp.blockCount = nbBlocks;
  1410. dbp.srcBuffers = (const void* const *) dstPtrs;
  1411. dbp.srcSizes = dstCapacities;
  1412. dbp.dstBuffers = resPtrs;
  1413. dbp.dstCapacities = resSizes;
  1414. dbp.blockResults = NULL;
  1415. dctxprep.dctx = dctx;
  1416. dctxprep.dictBuffer = dictBuffer;
  1417. dctxprep.dictBufferSize = dictBufferSize;
  1418. assert(timeStateCompress != NULL);
  1419. assert(timeStateDecompress != NULL);
  1420. while(!compressionCompleted) {
  1421. BMK_runOutcome_t const cOutcome = BMK_benchTimedFn(timeStateCompress, cbp);
  1422. if (!BMK_isSuccessful_runOutcome(cOutcome)) {
  1423. BMK_benchOutcome_t bOut;
  1424. memset(&bOut, 0, sizeof(bOut));
  1425. bOut.tag = 1; /* should rather be a function or a constant */
  1426. BMK_freeTimedFnState(timeStateCompress);
  1427. BMK_freeTimedFnState(timeStateDecompress);
  1428. return bOut;
  1429. }
  1430. { BMK_runTime_t const rResult = BMK_extract_runTime(cOutcome);
  1431. bResult.cSpeed = (unsigned long long)((double)srcSize * TIMELOOP_NANOSEC / rResult.nanoSecPerRun);
  1432. bResult.cSize = rResult.sumOfReturn;
  1433. }
  1434. compressionCompleted = BMK_isCompleted_TimedFn(timeStateCompress);
  1435. }
  1436. while (!decompressionCompleted) {
  1437. BMK_runOutcome_t const dOutcome = BMK_benchTimedFn(timeStateDecompress, dbp);
  1438. if (!BMK_isSuccessful_runOutcome(dOutcome)) {
  1439. BMK_benchOutcome_t bOut;
  1440. memset(&bOut, 0, sizeof(bOut));
  1441. bOut.tag = 1; /* should rather be a function or a constant */
  1442. BMK_freeTimedFnState(timeStateCompress);
  1443. BMK_freeTimedFnState(timeStateDecompress);
  1444. return bOut;
  1445. }
  1446. { BMK_runTime_t const rResult = BMK_extract_runTime(dOutcome);
  1447. bResult.dSpeed = (unsigned long long)((double)srcSize * TIMELOOP_NANOSEC / rResult.nanoSecPerRun);
  1448. }
  1449. decompressionCompleted = BMK_isCompleted_TimedFn(timeStateDecompress);
  1450. }
  1451. BMK_freeTimedFnState(timeStateCompress);
  1452. BMK_freeTimedFnState(timeStateDecompress);
  1453. }
  1454. /* Bench */
  1455. bResult.cMem = ((size_t)1 << (comprParams->vals[wlog_ind])) + ZSTD_sizeof_CCtx(cctx);
  1456. { BMK_benchOutcome_t bOut;
  1457. bOut.tag = 0;
  1458. bOut.internal_never_use_directly = bResult; /* should be a function */
  1459. return bOut;
  1460. }
  1461. }
  1462. /* BMK_benchParam() :
  1463. * benchmark a set of `cParams` over sample `buf`,
  1464. * store the result in `resultPtr`.
  1465. * @return : 0 if success, 1 if error */
  1466. static int BMK_benchParam ( BMK_benchResult_t* resultPtr,
  1467. buffers_t buf, contexts_t ctx,
  1468. paramValues_t cParams)
  1469. {
  1470. BMK_benchOutcome_t const outcome = BMK_benchMemInvertible(buf, ctx,
  1471. BASE_CLEVEL, &cParams,
  1472. BMK_both, 3);
  1473. if (!BMK_isSuccessful_benchOutcome(outcome)) return 1;
  1474. *resultPtr = BMK_extract_benchResult(outcome);
  1475. return 0;
  1476. }
  1477. /* Benchmarking which stops when we are sufficiently sure the solution is infeasible / worse than the winner */
  1478. #define VARIANCE 1.2
  1479. static int allBench(BMK_benchResult_t* resultPtr,
  1480. const buffers_t buf, const contexts_t ctx,
  1481. const paramValues_t cParams,
  1482. const constraint_t target,
  1483. BMK_benchResult_t* winnerResult, int feas)
  1484. {
  1485. BMK_benchResult_t benchres;
  1486. double uncertaintyConstantC = 3., uncertaintyConstantD = 3.;
  1487. double winnerRS;
  1488. BMK_benchOutcome_t const outcome = BMK_benchMemInvertible(buf, ctx, BASE_CLEVEL, &cParams, BMK_both, 2);
  1489. if (!BMK_isSuccessful_benchOutcome(outcome)) {
  1490. DEBUGOUTPUT("Benchmarking failed \n");
  1491. return ERROR_RESULT;
  1492. }
  1493. benchres = BMK_extract_benchResult(outcome);
  1494. winnerRS = resultScore(*winnerResult, buf.srcSize, target);
  1495. DEBUGOUTPUT("WinnerScore: %f \n ", winnerRS);
  1496. *resultPtr = benchres;
  1497. /* anything with worse ratio in feas is definitely worse, discard */
  1498. if(feas && benchres.cSize < winnerResult->cSize && !g_optmode) {
  1499. return WORSE_RESULT;
  1500. }
  1501. /* calculate uncertainty in compression / decompression runs */
  1502. if (benchres.cSpeed) {
  1503. U64 const loopDurationC = (((U64)buf.srcSize * TIMELOOP_NANOSEC) / benchres.cSpeed);
  1504. uncertaintyConstantC = ((loopDurationC + (double)(2 * g_clockGranularity))/loopDurationC);
  1505. }
  1506. if (benchres.dSpeed) {
  1507. U64 const loopDurationD = (((U64)buf.srcSize * TIMELOOP_NANOSEC) / benchres.dSpeed);
  1508. uncertaintyConstantD = ((loopDurationD + (double)(2 * g_clockGranularity))/loopDurationD);
  1509. }
  1510. /* optimistic assumption of benchres */
  1511. { BMK_benchResult_t resultMax = benchres;
  1512. resultMax.cSpeed = (unsigned long long)(resultMax.cSpeed * uncertaintyConstantC * VARIANCE);
  1513. resultMax.dSpeed = (unsigned long long)(resultMax.dSpeed * uncertaintyConstantD * VARIANCE);
  1514. /* disregard infeasible results in feas mode */
  1515. /* disregard if resultMax < winner in infeas mode */
  1516. if((feas && !feasible(resultMax, target)) ||
  1517. (!feas && (winnerRS > resultScore(resultMax, buf.srcSize, target)))) {
  1518. return WORSE_RESULT;
  1519. }
  1520. }
  1521. /* compare by resultScore when in infeas */
  1522. /* compare by compareResultLT when in feas */
  1523. if((!feas && (resultScore(benchres, buf.srcSize, target) > resultScore(*winnerResult, buf.srcSize, target))) ||
  1524. (feas && (compareResultLT(*winnerResult, benchres, target, buf.srcSize))) ) {
  1525. return BETTER_RESULT;
  1526. } else {
  1527. return WORSE_RESULT;
  1528. }
  1529. }
  1530. #define INFEASIBLE_THRESHOLD 200
  1531. /* Memoized benchmarking, won't benchmark anything which has already been benchmarked before. */
  1532. static int benchMemo(BMK_benchResult_t* resultPtr,
  1533. const buffers_t buf, const contexts_t ctx,
  1534. const paramValues_t cParams,
  1535. const constraint_t target,
  1536. BMK_benchResult_t* winnerResult, memoTable_t* const memoTableArray,
  1537. const int feas) {
  1538. static int bmcount = 0;
  1539. int res;
  1540. if ( memoTableGet(memoTableArray, cParams) >= INFEASIBLE_THRESHOLD
  1541. || redundantParams(cParams, target, buf.maxBlockSize) ) {
  1542. return WORSE_RESULT;
  1543. }
  1544. res = allBench(resultPtr, buf, ctx, cParams, target, winnerResult, feas);
  1545. if(DEBUG && !(bmcount % 250)) {
  1546. DISPLAY("Count: %d\n", bmcount);
  1547. bmcount++;
  1548. }
  1549. BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, *resultPtr, cParams, target, buf.srcSize);
  1550. if(res == BETTER_RESULT || feas) {
  1551. memoTableSet(memoTableArray, cParams, 255); /* what happens if collisions are frequent */
  1552. }
  1553. return res;
  1554. }
  1555. typedef struct {
  1556. U64 cSpeed_min;
  1557. U64 dSpeed_min;
  1558. U32 windowLog_max;
  1559. ZSTD_strategy strategy_max;
  1560. } level_constraints_t;
  1561. static level_constraints_t g_level_constraint[NB_LEVELS_TRACKED+1];
  1562. static void BMK_init_level_constraints(int bytePerSec_level1)
  1563. {
  1564. assert(NB_LEVELS_TRACKED >= ZSTD_maxCLevel());
  1565. memset(g_level_constraint, 0, sizeof(g_level_constraint));
  1566. g_level_constraint[1].cSpeed_min = bytePerSec_level1;
  1567. g_level_constraint[1].dSpeed_min = 0;
  1568. g_level_constraint[1].windowLog_max = 19;
  1569. g_level_constraint[1].strategy_max = ZSTD_fast;
  1570. /* establish speed objectives (relative to level 1) */
  1571. { int l;
  1572. for (l=2; l<=NB_LEVELS_TRACKED; l++) {
  1573. g_level_constraint[l].cSpeed_min = (g_level_constraint[l-1].cSpeed_min * 49) / 64;
  1574. g_level_constraint[l].dSpeed_min = 0;
  1575. g_level_constraint[l].windowLog_max = (l<20) ? 23 : l+5; /* only --ultra levels >= 20 can use windowlog > 23 */
  1576. g_level_constraint[l].strategy_max = ZSTD_STRATEGY_MAX;
  1577. } }
  1578. }
  1579. static int BMK_seed(winnerInfo_t* winners,
  1580. const paramValues_t params,
  1581. const buffers_t buf,
  1582. const contexts_t ctx)
  1583. {
  1584. BMK_benchResult_t testResult;
  1585. int better = 0;
  1586. int cLevel;
  1587. BMK_benchParam(&testResult, buf, ctx, params);
  1588. for (cLevel = 1; cLevel <= NB_LEVELS_TRACKED; cLevel++) {
  1589. if (testResult.cSpeed < g_level_constraint[cLevel].cSpeed_min)
  1590. continue; /* not fast enough for this level */
  1591. if (testResult.dSpeed < g_level_constraint[cLevel].dSpeed_min)
  1592. continue; /* not fast enough for this level */
  1593. if (params.vals[wlog_ind] > g_level_constraint[cLevel].windowLog_max)
  1594. continue; /* too much memory for this level */
  1595. if (params.vals[strt_ind] > (U32)g_level_constraint[cLevel].strategy_max)
  1596. continue; /* forbidden strategy for this level */
  1597. if (winners[cLevel].result.cSize==0) {
  1598. /* first solution for this cLevel */
  1599. winners[cLevel].result = testResult;
  1600. winners[cLevel].params = params;
  1601. BMK_print_cLevelEntry(stdout, cLevel, params, testResult, buf.srcSize);
  1602. better = 1;
  1603. continue;
  1604. }
  1605. if ((double)testResult.cSize <= ((double)winners[cLevel].result.cSize * (1. + (0.02 / cLevel))) ) {
  1606. /* Validate solution is "good enough" */
  1607. double W_ratio = (double)buf.srcSize / testResult.cSize;
  1608. double O_ratio = (double)buf.srcSize / winners[cLevel].result.cSize;
  1609. double W_ratioNote = log (W_ratio);
  1610. double O_ratioNote = log (O_ratio);
  1611. size_t W_DMemUsed = (1 << params.vals[wlog_ind]) + (16 KB);
  1612. size_t O_DMemUsed = (1 << winners[cLevel].params.vals[wlog_ind]) + (16 KB);
  1613. double W_DMemUsed_note = W_ratioNote * ( 40 + 9*cLevel) - log((double)W_DMemUsed);
  1614. double O_DMemUsed_note = O_ratioNote * ( 40 + 9*cLevel) - log((double)O_DMemUsed);
  1615. size_t W_CMemUsed = ((size_t)1 << params.vals[wlog_ind]) + ZSTD_estimateCCtxSize_usingCParams(pvalsToCParams(params));
  1616. size_t O_CMemUsed = ((size_t)1 << winners[cLevel].params.vals[wlog_ind]) + ZSTD_estimateCCtxSize_usingCParams(pvalsToCParams(winners[cLevel].params));
  1617. double W_CMemUsed_note = W_ratioNote * ( 50 + 13*cLevel) - log((double)W_CMemUsed);
  1618. double O_CMemUsed_note = O_ratioNote * ( 50 + 13*cLevel) - log((double)O_CMemUsed);
  1619. double W_CSpeed_note = W_ratioNote * (double)( 30 + 10*cLevel) + log(testResult.cSpeed);
  1620. double O_CSpeed_note = O_ratioNote * (double)( 30 + 10*cLevel) + log(winners[cLevel].result.cSpeed);
  1621. double W_DSpeed_note = W_ratioNote * (double)( 20 + 2*cLevel) + log(testResult.dSpeed);
  1622. double O_DSpeed_note = O_ratioNote * (double)( 20 + 2*cLevel) + log(winners[cLevel].result.dSpeed);
  1623. if (W_DMemUsed_note < O_DMemUsed_note) {
  1624. /* uses too much Decompression memory for too little benefit */
  1625. if (W_ratio > O_ratio)
  1626. DISPLAYLEVEL(3, "Decompression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
  1627. W_ratio, (double)(W_DMemUsed) / 1024 / 1024,
  1628. O_ratio, (double)(O_DMemUsed) / 1024 / 1024, cLevel);
  1629. continue;
  1630. }
  1631. if (W_CMemUsed_note < O_CMemUsed_note) {
  1632. /* uses too much memory for compression for too little benefit */
  1633. if (W_ratio > O_ratio)
  1634. DISPLAYLEVEL(3, "Compression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
  1635. W_ratio, (double)(W_CMemUsed) / 1024 / 1024,
  1636. O_ratio, (double)(O_CMemUsed) / 1024 / 1024,
  1637. cLevel);
  1638. continue;
  1639. }
  1640. if (W_CSpeed_note < O_CSpeed_note ) {
  1641. /* too large compression speed difference for the compression benefit */
  1642. if (W_ratio > O_ratio)
  1643. DISPLAYLEVEL(3, "Compression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
  1644. W_ratio, (double)testResult.cSpeed / MB_UNIT,
  1645. O_ratio, (double)winners[cLevel].result.cSpeed / MB_UNIT,
  1646. cLevel);
  1647. continue;
  1648. }
  1649. if (W_DSpeed_note < O_DSpeed_note ) {
  1650. /* too large decompression speed difference for the compression benefit */
  1651. if (W_ratio > O_ratio)
  1652. DISPLAYLEVEL(3, "Decompression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
  1653. W_ratio, (double)testResult.dSpeed / MB_UNIT,
  1654. O_ratio, (double)winners[cLevel].result.dSpeed / MB_UNIT,
  1655. cLevel);
  1656. continue;
  1657. }
  1658. if (W_ratio < O_ratio)
  1659. DISPLAYLEVEL(3, "Solution %4.3f selected over %4.3f at level %i, due to better secondary statistics \n",
  1660. W_ratio, O_ratio, cLevel);
  1661. winners[cLevel].result = testResult;
  1662. winners[cLevel].params = params;
  1663. BMK_print_cLevelEntry(stdout, cLevel, params, testResult, buf.srcSize);
  1664. better = 1;
  1665. } }
  1666. return better;
  1667. }
  1668. /*-************************************
  1669. * Compression Level Table Generation Functions
  1670. **************************************/
  1671. #define PARAMTABLELOG 25
  1672. #define PARAMTABLESIZE (1<<PARAMTABLELOG)
  1673. #define PARAMTABLEMASK (PARAMTABLESIZE-1)
  1674. static BYTE g_alreadyTested[PARAMTABLESIZE] = {0}; /* init to zero */
  1675. static BYTE* NB_TESTS_PLAYED(paramValues_t p)
  1676. {
  1677. ZSTD_compressionParameters const cParams = pvalsToCParams(sanitizeParams(p));
  1678. unsigned long long const h64 = XXH64(&cParams, sizeof(cParams), 0);
  1679. return &g_alreadyTested[(h64 >> 3) & PARAMTABLEMASK];
  1680. }
  1681. static void playAround(FILE* f,
  1682. winnerInfo_t* winners,
  1683. paramValues_t p,
  1684. const buffers_t buf, const contexts_t ctx)
  1685. {
  1686. int nbVariations = 0;
  1687. UTIL_time_t const clockStart = UTIL_getTime();
  1688. while (UTIL_clockSpanMicro(clockStart) < g_maxVariationTime) {
  1689. if (nbVariations++ > g_maxNbVariations) break;
  1690. do {
  1691. int i;
  1692. for(i = 0; i < 4; i++) {
  1693. paramVaryOnce(FUZ_rand(&g_rand) % (strt_ind + 1),
  1694. ((FUZ_rand(&g_rand) & 1) << 1) - 1,
  1695. &p);
  1696. }
  1697. } while (!paramValid(p));
  1698. /* exclude faster if already played params */
  1699. if (FUZ_rand(&g_rand) & ((1 << *NB_TESTS_PLAYED(p))-1))
  1700. continue;
  1701. /* test */
  1702. { BYTE* const b = NB_TESTS_PLAYED(p);
  1703. (*b)++;
  1704. }
  1705. if (!BMK_seed(winners, p, buf, ctx)) continue;
  1706. /* improvement found => search more */
  1707. BMK_saveAndPrint_cLevelTable(f, winners, buf.srcSize);
  1708. playAround(f, winners, p, buf, ctx);
  1709. }
  1710. }
  1711. static void
  1712. BMK_selectRandomStart( FILE* f,
  1713. winnerInfo_t* winners,
  1714. const buffers_t buf, const contexts_t ctx)
  1715. {
  1716. U32 const id = FUZ_rand(&g_rand) % (NB_LEVELS_TRACKED+1);
  1717. if ((id==0) || (winners[id].params.vals[wlog_ind]==0)) {
  1718. /* use some random entry */
  1719. paramValues_t const p = adjustParams(cParamsToPVals(pvalsToCParams(randomParams())), /* defaults nonCompression parameters */
  1720. buf.srcSize, 0);
  1721. playAround(f, winners, p, buf, ctx);
  1722. } else {
  1723. playAround(f, winners, winners[id].params, buf, ctx);
  1724. }
  1725. }
  1726. /* BMK_generate_cLevelTable() :
  1727. * test a large number of configurations
  1728. * and distribute them across compression levels according to speed conditions.
  1729. * display and save all intermediate results into rfName = "grillResults.txt".
  1730. * the function automatically stops after g_timeLimit_s.
  1731. * this function cannot error, it directly exit() in case of problem.
  1732. */
  1733. static void BMK_generate_cLevelTable(const buffers_t buf, const contexts_t ctx)
  1734. {
  1735. paramValues_t params;
  1736. winnerInfo_t winners[NB_LEVELS_TRACKED+1];
  1737. const char* const rfName = "grillResults.txt";
  1738. FILE* const f = fopen(rfName, "w");
  1739. /* init */
  1740. assert(g_singleRun==0);
  1741. memset(winners, 0, sizeof(winners));
  1742. if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); }
  1743. if (g_target) {
  1744. BMK_init_level_constraints(g_target * MB_UNIT);
  1745. } else {
  1746. /* baseline config for level 1 */
  1747. paramValues_t const l1params = cParamsToPVals(ZSTD_getCParams(1, buf.maxBlockSize, ctx.dictSize));
  1748. BMK_benchResult_t testResult;
  1749. BMK_benchParam(&testResult, buf, ctx, l1params);
  1750. BMK_init_level_constraints((int)((testResult.cSpeed * 31) / 32));
  1751. }
  1752. /* populate initial solution */
  1753. { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel();
  1754. int i;
  1755. for (i=0; i<=maxSeeds; i++) {
  1756. params = cParamsToPVals(ZSTD_getCParams(i, buf.maxBlockSize, 0));
  1757. BMK_seed(winners, params, buf, ctx);
  1758. } }
  1759. BMK_saveAndPrint_cLevelTable(f, winners, buf.srcSize);
  1760. /* start tests */
  1761. { const UTIL_time_t grillStart = UTIL_getTime();
  1762. do {
  1763. BMK_selectRandomStart(f, winners, buf, ctx);
  1764. } while (BMK_timeSpan_s(grillStart) < g_timeLimit_s);
  1765. }
  1766. /* end summary */
  1767. BMK_saveAndPrint_cLevelTable(f, winners, buf.srcSize);
  1768. DISPLAY("grillParams operations completed \n");
  1769. /* clean up*/
  1770. fclose(f);
  1771. }
  1772. /*-************************************
  1773. * Single Benchmark Functions
  1774. **************************************/
  1775. static int
  1776. benchOnce(const buffers_t buf, const contexts_t ctx, const int cLevel)
  1777. {
  1778. BMK_benchResult_t testResult;
  1779. g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevel, buf.maxBlockSize, ctx.dictSize)), g_params), buf.maxBlockSize, ctx.dictSize);
  1780. if (BMK_benchParam(&testResult, buf, ctx, g_params)) {
  1781. DISPLAY("Error during benchmarking\n");
  1782. return 1;
  1783. }
  1784. BMK_printWinner(stdout, CUSTOM_LEVEL, testResult, g_params, buf.srcSize);
  1785. return 0;
  1786. }
  1787. static int benchSample(double compressibility, int cLevel)
  1788. {
  1789. const char* const name = "Sample 10MB";
  1790. size_t const benchedSize = 10 MB;
  1791. void* const srcBuffer = malloc(benchedSize);
  1792. int ret = 0;
  1793. buffers_t buf;
  1794. contexts_t ctx;
  1795. if(srcBuffer == NULL) {
  1796. DISPLAY("Out of Memory\n");
  1797. return 2;
  1798. }
  1799. RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0);
  1800. if(createBuffersFromMemory(&buf, srcBuffer, 1, &benchedSize)) {
  1801. DISPLAY("Buffer Creation Error\n");
  1802. free(srcBuffer);
  1803. return 3;
  1804. }
  1805. if(createContexts(&ctx, NULL)) {
  1806. DISPLAY("Context Creation Error\n");
  1807. freeBuffers(buf);
  1808. return 1;
  1809. }
  1810. /* bench */
  1811. DISPLAY("\r%79s\r", "");
  1812. DISPLAY("using %s %i%%: \n", name, (int)(compressibility*100));
  1813. if(g_singleRun) {
  1814. ret = benchOnce(buf, ctx, cLevel);
  1815. } else {
  1816. BMK_generate_cLevelTable(buf, ctx);
  1817. }
  1818. freeBuffers(buf);
  1819. freeContexts(ctx);
  1820. return ret;
  1821. }
  1822. /* benchFiles() :
  1823. * note: while this function takes a table of filenames,
  1824. * in practice, only the first filename will be used */
  1825. static int benchFiles(const char** fileNamesTable, int nbFiles,
  1826. const char* dictFileName, int cLevel)
  1827. {
  1828. buffers_t buf;
  1829. contexts_t ctx;
  1830. int ret = 0;
  1831. if (createBuffers(&buf, fileNamesTable, nbFiles)) {
  1832. DISPLAY("unable to load files\n");
  1833. return 1;
  1834. }
  1835. if (createContexts(&ctx, dictFileName)) {
  1836. DISPLAY("unable to load dictionary\n");
  1837. freeBuffers(buf);
  1838. return 2;
  1839. }
  1840. DISPLAY("\r%79s\r", "");
  1841. if (nbFiles == 1) {
  1842. DISPLAY("using %s : \n", fileNamesTable[0]);
  1843. } else {
  1844. DISPLAY("using %d Files : \n", nbFiles);
  1845. }
  1846. if (g_singleRun) {
  1847. ret = benchOnce(buf, ctx, cLevel);
  1848. } else {
  1849. BMK_generate_cLevelTable(buf, ctx);
  1850. }
  1851. freeBuffers(buf);
  1852. freeContexts(ctx);
  1853. return ret;
  1854. }
  1855. /*-************************************
  1856. * Local Optimization Functions
  1857. **************************************/
  1858. /* One iteration of hill climbing. Specifically, it first tries all
  1859. * valid parameter configurations w/ manhattan distance 1 and picks the best one
  1860. * failing that, it progressively tries candidates further and further away (up to #dim + 2)
  1861. * if it finds a candidate exceeding winnerInfo, it will repeat. Otherwise, it will stop the
  1862. * current stage of hill climbing.
  1863. * Each iteration of hill climbing proceeds in 2 'phases'. Phase 1 climbs according to
  1864. * the resultScore function, which is effectively a linear increase in reward until it reaches
  1865. * the constraint-satisfying value, it which point any excess results in only logarithmic reward.
  1866. * This aims to find some constraint-satisfying point.
  1867. * Phase 2 optimizes in accordance with what the original function sets out to maximize, with
  1868. * all feasible solutions valued over all infeasible solutions.
  1869. */
  1870. /* sanitize all params here.
  1871. * all generation after random should be sanitized. (maybe sanitize random)
  1872. */
  1873. static winnerInfo_t climbOnce(const constraint_t target,
  1874. memoTable_t* mtAll,
  1875. const buffers_t buf, const contexts_t ctx,
  1876. const paramValues_t init)
  1877. {
  1878. /*
  1879. * cparam - currently considered 'center'
  1880. * candidate - params to benchmark/results
  1881. * winner - best option found so far.
  1882. */
  1883. paramValues_t cparam = init;
  1884. winnerInfo_t candidateInfo, winnerInfo;
  1885. int better = 1;
  1886. int feas = 0;
  1887. winnerInfo = initWinnerInfo(init);
  1888. candidateInfo = winnerInfo;
  1889. { winnerInfo_t bestFeasible1 = initWinnerInfo(cparam);
  1890. DEBUGOUTPUT("Climb Part 1\n");
  1891. while(better) {
  1892. int offset;
  1893. size_t i, dist;
  1894. const size_t varLen = mtAll[cparam.vals[strt_ind]].varLen;
  1895. better = 0;
  1896. DEBUGOUTPUT("Start\n");
  1897. cparam = winnerInfo.params;
  1898. candidateInfo.params = cparam;
  1899. /* all dist-1 candidates */
  1900. for (i = 0; i < varLen; i++) {
  1901. for (offset = -1; offset <= 1; offset += 2) {
  1902. CHECKTIME(winnerInfo);
  1903. candidateInfo.params = cparam;
  1904. paramVaryOnce(mtAll[cparam.vals[strt_ind]].varArray[i],
  1905. offset,
  1906. &candidateInfo.params);
  1907. if(paramValid(candidateInfo.params)) {
  1908. int res;
  1909. res = benchMemo(&candidateInfo.result, buf, ctx,
  1910. sanitizeParams(candidateInfo.params), target, &winnerInfo.result, mtAll, feas);
  1911. DEBUGOUTPUT("Res: %d\n", res);
  1912. if(res == BETTER_RESULT) { /* synonymous with better when called w/ infeasibleBM */
  1913. winnerInfo = candidateInfo;
  1914. better = 1;
  1915. if(compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) {
  1916. bestFeasible1 = winnerInfo;
  1917. }
  1918. }
  1919. }
  1920. } /* for (offset = -1; offset <= 1; offset += 2) */
  1921. } /* for (i = 0; i < varLen; i++) */
  1922. if(better) {
  1923. continue;
  1924. }
  1925. for (dist = 2; dist < varLen + 2; dist++) { /* varLen is # dimensions */
  1926. for (i = 0; i < (1ULL << varLen) / varLen + 2; i++) {
  1927. int res;
  1928. CHECKTIME(winnerInfo);
  1929. candidateInfo.params = cparam;
  1930. /* param error checking already done here */
  1931. paramVariation(&candidateInfo.params, mtAll, (U32)dist);
  1932. res = benchMemo(&candidateInfo.result,
  1933. buf, ctx,
  1934. sanitizeParams(candidateInfo.params), target,
  1935. &winnerInfo.result, mtAll, feas);
  1936. DEBUGOUTPUT("Res: %d\n", res);
  1937. if (res == BETTER_RESULT) { /* synonymous with better in this case*/
  1938. winnerInfo = candidateInfo;
  1939. better = 1;
  1940. if (compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) {
  1941. bestFeasible1 = winnerInfo;
  1942. }
  1943. break;
  1944. }
  1945. }
  1946. if (better) {
  1947. break;
  1948. }
  1949. } /* for(dist = 2; dist < varLen + 2; dist++) */
  1950. if (!better) { /* infeas -> feas -> stop */
  1951. if (feas) return winnerInfo;
  1952. feas = 1;
  1953. better = 1;
  1954. winnerInfo = bestFeasible1; /* note with change, bestFeasible may not necessarily be feasible, but if one has been benchmarked, it will be. */
  1955. DEBUGOUTPUT("Climb Part 2\n");
  1956. }
  1957. }
  1958. winnerInfo = bestFeasible1;
  1959. }
  1960. return winnerInfo;
  1961. }
  1962. /* Optimizes for a fixed strategy */
  1963. /* flexible parameters: iterations of failed climbing (or if we do non-random, maybe this is when everything is close to visited)
  1964. weight more on visit for bad results, less on good results/more on later results / ones with more failures.
  1965. allocate memoTable here.
  1966. */
  1967. static winnerInfo_t
  1968. optimizeFixedStrategy(const buffers_t buf, const contexts_t ctx,
  1969. const constraint_t target, paramValues_t paramTarget,
  1970. const ZSTD_strategy strat,
  1971. memoTable_t* memoTableArray, const int tries)
  1972. {
  1973. int i = 0;
  1974. paramValues_t init;
  1975. winnerInfo_t winnerInfo, candidateInfo;
  1976. winnerInfo = initWinnerInfo(emptyParams());
  1977. /* so climb is given the right fixed strategy */
  1978. paramTarget.vals[strt_ind] = strat;
  1979. /* to pass ZSTD_checkCParams */
  1980. paramTarget = cParamUnsetMin(paramTarget);
  1981. init = paramTarget;
  1982. for(i = 0; i < tries; i++) {
  1983. DEBUGOUTPUT("Restart\n");
  1984. do {
  1985. randomConstrainedParams(&init, memoTableArray, strat);
  1986. } while(redundantParams(init, target, buf.maxBlockSize));
  1987. candidateInfo = climbOnce(target, memoTableArray, buf, ctx, init);
  1988. if (compareResultLT(winnerInfo.result, candidateInfo.result, target, buf.srcSize)) {
  1989. winnerInfo = candidateInfo;
  1990. BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, winnerInfo.params, target, buf.srcSize);
  1991. i = 0;
  1992. continue;
  1993. }
  1994. CHECKTIME(winnerInfo);
  1995. i++;
  1996. }
  1997. return winnerInfo;
  1998. }
  1999. /* goes best, best-1, best+1, best-2, ... */
  2000. /* return 0 if nothing remaining */
  2001. static int nextStrategy(const int currentStrategy, const int bestStrategy)
  2002. {
  2003. if(bestStrategy <= currentStrategy) {
  2004. int candidate = 2 * bestStrategy - currentStrategy - 1;
  2005. if(candidate < 1) {
  2006. candidate = currentStrategy + 1;
  2007. if(candidate > (int)ZSTD_STRATEGY_MAX) {
  2008. return 0;
  2009. } else {
  2010. return candidate;
  2011. }
  2012. } else {
  2013. return candidate;
  2014. }
  2015. } else { /* bestStrategy >= currentStrategy */
  2016. int candidate = 2 * bestStrategy - currentStrategy;
  2017. if(candidate > (int)ZSTD_STRATEGY_MAX) {
  2018. candidate = currentStrategy - 1;
  2019. if(candidate < 1) {
  2020. return 0;
  2021. } else {
  2022. return candidate;
  2023. }
  2024. } else {
  2025. return candidate;
  2026. }
  2027. }
  2028. }
  2029. /* experiment with playing with this and decay value */
  2030. /* main fn called when using --optimize */
  2031. /* Does strategy selection by benchmarking default compression levels
  2032. * then optimizes by strategy, starting with the best one and moving
  2033. * progressively moving further away by number
  2034. * args:
  2035. * fileNamesTable - list of files to benchmark
  2036. * nbFiles - length of fileNamesTable
  2037. * dictFileName - name of dictionary file if one, else NULL
  2038. * target - performance constraints (cSpeed, dSpeed, cMem)
  2039. * paramTarget - parameter constraints (i.e. restriction search space to where strategy = ZSTD_fast)
  2040. * cLevel - compression level to exceed (all solutions must be > lvl in cSpeed + ratio)
  2041. */
  2042. static unsigned g_maxTries = 5;
  2043. #define TRY_DECAY 1
  2044. static int
  2045. optimizeForSize(const char* const * const fileNamesTable, const size_t nbFiles,
  2046. const char* dictFileName,
  2047. constraint_t target, paramValues_t paramTarget,
  2048. const int cLevelOpt, const int cLevelRun,
  2049. const U32 memoTableLog)
  2050. {
  2051. varInds_t varArray [NUM_PARAMS];
  2052. int ret = 0;
  2053. const size_t varLen = variableParams(paramTarget, varArray, dictFileName != NULL);
  2054. winnerInfo_t winner = initWinnerInfo(emptyParams());
  2055. memoTable_t* allMT = NULL;
  2056. paramValues_t paramBase;
  2057. contexts_t ctx;
  2058. buffers_t buf;
  2059. g_time = UTIL_getTime();
  2060. if (createBuffers(&buf, fileNamesTable, nbFiles)) {
  2061. DISPLAY("unable to load files\n");
  2062. return 1;
  2063. }
  2064. if (createContexts(&ctx, dictFileName)) {
  2065. DISPLAY("unable to load dictionary\n");
  2066. freeBuffers(buf);
  2067. return 2;
  2068. }
  2069. if (nbFiles == 1) {
  2070. DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[0]);
  2071. } else {
  2072. DISPLAYLEVEL(2, "Loading %lu Files... \r", (unsigned long)nbFiles);
  2073. }
  2074. /* sanitize paramTarget */
  2075. optimizerAdjustInput(&paramTarget, buf.maxBlockSize);
  2076. paramBase = cParamUnsetMin(paramTarget);
  2077. allMT = createMemoTableArray(paramTarget, varArray, varLen, memoTableLog);
  2078. if (!allMT) {
  2079. DISPLAY("MemoTable Init Error\n");
  2080. ret = 2;
  2081. goto _cleanUp;
  2082. }
  2083. /* default strictnesses */
  2084. if (g_strictness == PARAM_UNSET) {
  2085. if(g_optmode) {
  2086. g_strictness = 100;
  2087. } else {
  2088. g_strictness = 90;
  2089. }
  2090. } else {
  2091. if(0 >= g_strictness || g_strictness > 100) {
  2092. DISPLAY("Strictness Outside of Bounds\n");
  2093. ret = 4;
  2094. goto _cleanUp;
  2095. }
  2096. }
  2097. /* use level'ing mode instead of normal target mode */
  2098. if (g_optmode) {
  2099. winner.params = cParamsToPVals(ZSTD_getCParams(cLevelOpt, buf.maxBlockSize, ctx.dictSize));
  2100. if(BMK_benchParam(&winner.result, buf, ctx, winner.params)) {
  2101. ret = 3;
  2102. goto _cleanUp;
  2103. }
  2104. g_lvltarget = winner.result;
  2105. g_lvltarget.cSpeed = (g_lvltarget.cSpeed * g_strictness) / 100;
  2106. g_lvltarget.dSpeed = (g_lvltarget.dSpeed * g_strictness) / 100;
  2107. g_lvltarget.cSize = (g_lvltarget.cSize * 100) / g_strictness;
  2108. target.cSpeed = (U32)g_lvltarget.cSpeed;
  2109. target.dSpeed = (U32)g_lvltarget.dSpeed;
  2110. BMK_printWinnerOpt(stdout, cLevelOpt, winner.result, winner.params, target, buf.srcSize);
  2111. }
  2112. /* Don't want it to return anything worse than the best known result */
  2113. if (g_singleRun) {
  2114. BMK_benchResult_t res;
  2115. g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevelRun, buf.maxBlockSize, ctx.dictSize)), g_params), buf.maxBlockSize, ctx.dictSize);
  2116. if (BMK_benchParam(&res, buf, ctx, g_params)) {
  2117. ret = 45;
  2118. goto _cleanUp;
  2119. }
  2120. if(compareResultLT(winner.result, res, relaxTarget(target), buf.srcSize)) {
  2121. winner.result = res;
  2122. winner.params = g_params;
  2123. }
  2124. }
  2125. /* bench */
  2126. DISPLAYLEVEL(2, "\r%79s\r", "");
  2127. if(nbFiles == 1) {
  2128. DISPLAYLEVEL(2, "optimizing for %s", fileNamesTable[0]);
  2129. } else {
  2130. DISPLAYLEVEL(2, "optimizing for %lu Files", (unsigned long)nbFiles);
  2131. }
  2132. if(target.cSpeed != 0) { DISPLAYLEVEL(2," - limit compression speed %u MB/s", (unsigned)(target.cSpeed >> 20)); }
  2133. if(target.dSpeed != 0) { DISPLAYLEVEL(2, " - limit decompression speed %u MB/s", (unsigned)(target.dSpeed >> 20)); }
  2134. if(target.cMem != (U32)-1) { DISPLAYLEVEL(2, " - limit memory %u MB", (unsigned)(target.cMem >> 20)); }
  2135. DISPLAYLEVEL(2, "\n");
  2136. init_clockGranularity();
  2137. { paramValues_t CParams;
  2138. /* find best solution from default params */
  2139. { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel();
  2140. DEBUGOUTPUT("Strategy Selection\n");
  2141. if (paramTarget.vals[strt_ind] == PARAM_UNSET) {
  2142. BMK_benchResult_t candidate;
  2143. int i;
  2144. for (i=1; i<=maxSeeds; i++) {
  2145. int ec;
  2146. CParams = overwriteParams(cParamsToPVals(ZSTD_getCParams(i, buf.maxBlockSize, ctx.dictSize)), paramTarget);
  2147. ec = BMK_benchParam(&candidate, buf, ctx, CParams);
  2148. BMK_printWinnerOpt(stdout, i, candidate, CParams, target, buf.srcSize);
  2149. if(!ec && compareResultLT(winner.result, candidate, relaxTarget(target), buf.srcSize)) {
  2150. winner.result = candidate;
  2151. winner.params = CParams;
  2152. }
  2153. CHECKTIMEGT(ret, 0, _displayCleanUp); /* if pass time limit, stop */
  2154. /* if the current params are too slow, just stop. */
  2155. if(target.cSpeed > candidate.cSpeed * 3 / 2) { break; }
  2156. }
  2157. BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winner.result, winner.params, target, buf.srcSize);
  2158. }
  2159. }
  2160. DEBUGOUTPUT("Real Opt\n");
  2161. /* start 'real' optimization */
  2162. { int bestStrategy = (int)winner.params.vals[strt_ind];
  2163. if (paramTarget.vals[strt_ind] == PARAM_UNSET) {
  2164. int st = bestStrategy;
  2165. int tries = g_maxTries;
  2166. /* one iterations of hill climbing with the level-defined parameters. */
  2167. { winnerInfo_t const w1 = climbOnce(target, allMT, buf, ctx, winner.params);
  2168. if (compareResultLT(winner.result, w1.result, target, buf.srcSize)) {
  2169. winner = w1;
  2170. }
  2171. CHECKTIMEGT(ret, 0, _displayCleanUp);
  2172. }
  2173. while(st && tries > 0) {
  2174. winnerInfo_t wc;
  2175. DEBUGOUTPUT("StrategySwitch: %s\n", g_stratName[st]);
  2176. wc = optimizeFixedStrategy(buf, ctx, target, paramBase, st, allMT, tries);
  2177. if(compareResultLT(winner.result, wc.result, target, buf.srcSize)) {
  2178. winner = wc;
  2179. tries = g_maxTries;
  2180. bestStrategy = st;
  2181. } else {
  2182. st = nextStrategy(st, bestStrategy);
  2183. tries -= TRY_DECAY;
  2184. }
  2185. CHECKTIMEGT(ret, 0, _displayCleanUp);
  2186. }
  2187. } else {
  2188. winner = optimizeFixedStrategy(buf, ctx, target, paramBase, paramTarget.vals[strt_ind], allMT, g_maxTries);
  2189. }
  2190. }
  2191. /* no solution found */
  2192. if(winner.result.cSize == (size_t)-1) {
  2193. ret = 1;
  2194. DISPLAY("No feasible solution found\n");
  2195. goto _cleanUp;
  2196. }
  2197. /* end summary */
  2198. _displayCleanUp:
  2199. if (g_displayLevel >= 0) {
  2200. BMK_displayOneResult(stdout, winner, buf.srcSize);
  2201. }
  2202. BMK_paramValues_into_commandLine(stdout, winner.params);
  2203. DISPLAYLEVEL(1, "grillParams size - optimizer completed \n");
  2204. }
  2205. _cleanUp:
  2206. freeContexts(ctx);
  2207. freeBuffers(buf);
  2208. freeMemoTableArray(allMT);
  2209. return ret;
  2210. }
  2211. /*-************************************
  2212. * CLI parsing functions
  2213. **************************************/
  2214. /** longCommandWArg() :
  2215. * check if *stringPtr is the same as longCommand.
  2216. * If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
  2217. * @return 0 and doesn't modify *stringPtr otherwise.
  2218. * from zstdcli.c
  2219. */
  2220. static int longCommandWArg(const char** stringPtr, const char* longCommand)
  2221. {
  2222. size_t const comSize = strlen(longCommand);
  2223. int const result = !strncmp(*stringPtr, longCommand, comSize);
  2224. if (result) *stringPtr += comSize;
  2225. return result;
  2226. }
  2227. static void errorOut(const char* msg)
  2228. {
  2229. DISPLAY("%s \n", msg); exit(1);
  2230. }
  2231. /*! readU32FromChar() :
  2232. * @return : unsigned integer value read from input in `char` format.
  2233. * allows and interprets K, KB, KiB, M, MB and MiB suffix.
  2234. * Will also modify `*stringPtr`, advancing it to position where it stopped reading.
  2235. * Note : function will exit() program if digit sequence overflows */
  2236. static unsigned readU32FromChar(const char** stringPtr)
  2237. {
  2238. const char errorMsg[] = "error: numeric value too large";
  2239. unsigned sign = 1;
  2240. unsigned result = 0;
  2241. if(**stringPtr == '-') { sign = (unsigned)-1; (*stringPtr)++; }
  2242. while ((**stringPtr >='0') && (**stringPtr <='9')) {
  2243. unsigned const max = (((unsigned)(-1)) / 10) - 1;
  2244. if (result > max) errorOut(errorMsg);
  2245. result *= 10;
  2246. assert(**stringPtr >= '0');
  2247. result += (unsigned)(**stringPtr - '0');
  2248. (*stringPtr)++ ;
  2249. }
  2250. if ((**stringPtr=='K') || (**stringPtr=='M')) {
  2251. unsigned const maxK = ((unsigned)(-1)) >> 10;
  2252. if (result > maxK) errorOut(errorMsg);
  2253. result <<= 10;
  2254. if (**stringPtr=='M') {
  2255. if (result > maxK) errorOut(errorMsg);
  2256. result <<= 10;
  2257. }
  2258. (*stringPtr)++; /* skip `K` or `M` */
  2259. if (**stringPtr=='i') (*stringPtr)++;
  2260. if (**stringPtr=='B') (*stringPtr)++;
  2261. }
  2262. return result * sign;
  2263. }
  2264. static double readDoubleFromChar(const char** stringPtr)
  2265. {
  2266. double result = 0, divide = 10;
  2267. while ((**stringPtr >='0') && (**stringPtr <='9')) {
  2268. result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
  2269. }
  2270. if(**stringPtr!='.') {
  2271. return result;
  2272. }
  2273. (*stringPtr)++;
  2274. while ((**stringPtr >='0') && (**stringPtr <='9')) {
  2275. result += (double)(**stringPtr - '0') / divide, divide *= 10, (*stringPtr)++ ;
  2276. }
  2277. return result;
  2278. }
  2279. static int usage(const char* exename)
  2280. {
  2281. DISPLAY( "Usage :\n");
  2282. DISPLAY( " %s [arg] file\n", exename);
  2283. DISPLAY( "Arguments :\n");
  2284. DISPLAY( " file : path to the file used as reference (if none, generates a compressible sample)\n");
  2285. DISPLAY( " -H/-h : Help (this text + advanced options)\n");
  2286. return 0;
  2287. }
  2288. static int usage_advanced(void)
  2289. {
  2290. DISPLAY( "\nAdvanced options :\n");
  2291. DISPLAY( " -T# : set level 1 speed objective \n");
  2292. DISPLAY( " -B# : cut input into blocks of size # (default : single block) \n");
  2293. DISPLAY( " --optimize= : same as -O with more verbose syntax (see README.md)\n");
  2294. DISPLAY( " -S : Single run \n");
  2295. DISPLAY( " --zstd : Single run, parameter selection same as zstdcli \n");
  2296. DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100);
  2297. DISPLAY( " -t# : Caps runtime of operation in seconds (default : %u seconds (%.1f hours)) \n",
  2298. (unsigned)g_timeLimit_s, (double)g_timeLimit_s / 3600);
  2299. DISPLAY( " -v : Prints Benchmarking output\n");
  2300. DISPLAY( " -D : Next argument dictionary file\n");
  2301. DISPLAY( " -s : Seperate Files\n");
  2302. return 0;
  2303. }
  2304. static int badusage(const char* exename)
  2305. {
  2306. DISPLAY("Wrong parameters\n");
  2307. usage(exename);
  2308. return 1;
  2309. }
  2310. #define PARSE_SUB_ARGS(stringLong, stringShort, variable) { \
  2311. if ( longCommandWArg(&argument, stringLong) \
  2312. || longCommandWArg(&argument, stringShort) ) { \
  2313. variable = readU32FromChar(&argument); \
  2314. if (argument[0]==',') { \
  2315. argument++; continue; \
  2316. } else break; \
  2317. } }
  2318. /* 1 if successful parse, 0 otherwise */
  2319. static int parse_params(const char** argptr, paramValues_t* pv) {
  2320. int matched = 0;
  2321. const char* argOrig = *argptr;
  2322. varInds_t v;
  2323. for(v = 0; v < NUM_PARAMS; v++) {
  2324. if ( longCommandWArg(argptr,g_shortParamNames[v])
  2325. || longCommandWArg(argptr, g_paramNames[v]) ) {
  2326. if(**argptr == '=') {
  2327. (*argptr)++;
  2328. pv->vals[v] = readU32FromChar(argptr);
  2329. matched = 1;
  2330. break;
  2331. }
  2332. }
  2333. /* reset and try again */
  2334. *argptr = argOrig;
  2335. }
  2336. return matched;
  2337. }
  2338. /*-************************************
  2339. * Main
  2340. **************************************/
  2341. int main(int argc, const char** argv)
  2342. {
  2343. int i,
  2344. filenamesStart=0,
  2345. result;
  2346. const char* exename=argv[0];
  2347. const char* input_filename = NULL;
  2348. const char* dictFileName = NULL;
  2349. U32 main_pause = 0;
  2350. int cLevelOpt = 0, cLevelRun = 0;
  2351. int seperateFiles = 0;
  2352. double compressibility = COMPRESSIBILITY_DEFAULT;
  2353. U32 memoTableLog = PARAM_UNSET;
  2354. constraint_t target = { 0, 0, (U32)-1 };
  2355. paramValues_t paramTarget = emptyParams();
  2356. g_params = emptyParams();
  2357. assert(argc>=1); /* for exename */
  2358. for(i=1; i<argc; i++) {
  2359. const char* argument = argv[i];
  2360. DEBUGOUTPUT("%d: %s\n", i, argument);
  2361. assert(argument != NULL);
  2362. if(!strcmp(argument,"--no-seed")) { g_noSeed = 1; continue; }
  2363. if (longCommandWArg(&argument, "--optimize=")) {
  2364. g_optimizer = 1;
  2365. for ( ; ;) {
  2366. if(parse_params(&argument, &paramTarget)) { if(argument[0] == ',') { argument++; continue; } else break; }
  2367. PARSE_SUB_ARGS("compressionSpeed=" , "cSpeed=", target.cSpeed);
  2368. PARSE_SUB_ARGS("decompressionSpeed=", "dSpeed=", target.dSpeed);
  2369. PARSE_SUB_ARGS("compressionMemory=" , "cMem=", target.cMem);
  2370. PARSE_SUB_ARGS("strict=", "stc=", g_strictness);
  2371. PARSE_SUB_ARGS("maxTries=", "tries=", g_maxTries);
  2372. PARSE_SUB_ARGS("memoLimitLog=", "memLog=", memoTableLog);
  2373. if (longCommandWArg(&argument, "level=") || longCommandWArg(&argument, "lvl=")) { cLevelOpt = (int)readU32FromChar(&argument); g_optmode = 1; if (argument[0]==',') { argument++; continue; } else break; }
  2374. if (longCommandWArg(&argument, "speedForRatio=") || longCommandWArg(&argument, "speedRatio=")) { g_ratioMultiplier = readDoubleFromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
  2375. DISPLAY("invalid optimization parameter \n");
  2376. return 1;
  2377. }
  2378. if (argument[0] != 0) {
  2379. DISPLAY("invalid --optimize= format\n");
  2380. return 1; /* check the end of string */
  2381. }
  2382. continue;
  2383. } else if (longCommandWArg(&argument, "--zstd=")) {
  2384. /* Decode command (note : aggregated commands are allowed) */
  2385. g_singleRun = 1;
  2386. for ( ; ;) {
  2387. if(parse_params(&argument, &g_params)) { if(argument[0] == ',') { argument++; continue; } else break; }
  2388. if (longCommandWArg(&argument, "level=") || longCommandWArg(&argument, "lvl=")) { cLevelRun = (int)readU32FromChar(&argument); g_params = emptyParams(); if (argument[0]==',') { argument++; continue; } else break; }
  2389. DISPLAY("invalid compression parameter \n");
  2390. return 1;
  2391. }
  2392. if (argument[0] != 0) {
  2393. DISPLAY("invalid --zstd= format\n");
  2394. return 1; /* check the end of string */
  2395. }
  2396. continue;
  2397. /* if not return, success */
  2398. } else if (longCommandWArg(&argument, "--display=")) {
  2399. /* Decode command (note : aggregated commands are allowed) */
  2400. memset(g_silenceParams, 1, sizeof(g_silenceParams));
  2401. for ( ; ;) {
  2402. int found = 0;
  2403. varInds_t v;
  2404. for(v = 0; v < NUM_PARAMS; v++) {
  2405. if(longCommandWArg(&argument, g_shortParamNames[v]) || longCommandWArg(&argument, g_paramNames[v])) {
  2406. g_silenceParams[v] = 0;
  2407. found = 1;
  2408. }
  2409. }
  2410. if(longCommandWArg(&argument, "compressionParameters") || longCommandWArg(&argument, "cParams")) {
  2411. for(v = 0; v <= strt_ind; v++) {
  2412. g_silenceParams[v] = 0;
  2413. }
  2414. found = 1;
  2415. }
  2416. if(found) {
  2417. if(argument[0]==',') {
  2418. continue;
  2419. } else {
  2420. break;
  2421. }
  2422. }
  2423. DISPLAY("invalid parameter name parameter \n");
  2424. return 1;
  2425. }
  2426. if (argument[0] != 0) {
  2427. DISPLAY("invalid --display format\n");
  2428. return 1; /* check the end of string */
  2429. }
  2430. continue;
  2431. } else if (argument[0]=='-') {
  2432. argument++;
  2433. while (argument[0]!=0) {
  2434. switch(argument[0])
  2435. {
  2436. /* Display help on usage */
  2437. case 'h' :
  2438. case 'H': usage(exename); usage_advanced(); return 0;
  2439. /* Pause at the end (hidden option) */
  2440. case 'p': main_pause = 1; argument++; break;
  2441. /* Sample compressibility (when no file provided) */
  2442. case 'P':
  2443. argument++;
  2444. { U32 const proba32 = readU32FromChar(&argument);
  2445. compressibility = (double)proba32 / 100.;
  2446. }
  2447. break;
  2448. /* Run Single conf */
  2449. case 'S':
  2450. g_singleRun = 1;
  2451. argument++;
  2452. for ( ; ; ) {
  2453. switch(*argument)
  2454. {
  2455. case 'w':
  2456. argument++;
  2457. g_params.vals[wlog_ind] = readU32FromChar(&argument);
  2458. continue;
  2459. case 'c':
  2460. argument++;
  2461. g_params.vals[clog_ind] = readU32FromChar(&argument);
  2462. continue;
  2463. case 'h':
  2464. argument++;
  2465. g_params.vals[hlog_ind] = readU32FromChar(&argument);
  2466. continue;
  2467. case 's':
  2468. argument++;
  2469. g_params.vals[slog_ind] = readU32FromChar(&argument);
  2470. continue;
  2471. case 'l': /* search length */
  2472. argument++;
  2473. g_params.vals[mml_ind] = readU32FromChar(&argument);
  2474. continue;
  2475. case 't': /* target length */
  2476. argument++;
  2477. g_params.vals[tlen_ind] = readU32FromChar(&argument);
  2478. continue;
  2479. case 'S': /* strategy */
  2480. argument++;
  2481. g_params.vals[strt_ind] = readU32FromChar(&argument);
  2482. continue;
  2483. case 'f': /* forceAttachDict */
  2484. argument++;
  2485. g_params.vals[fadt_ind] = readU32FromChar(&argument);
  2486. continue;
  2487. case 'L':
  2488. { argument++;
  2489. cLevelRun = (int)readU32FromChar(&argument);
  2490. g_params = emptyParams();
  2491. continue;
  2492. }
  2493. default : ;
  2494. }
  2495. break;
  2496. }
  2497. break;
  2498. /* target level1 speed objective, in MB/s */
  2499. case 'T':
  2500. argument++;
  2501. g_target = readU32FromChar(&argument);
  2502. break;
  2503. /* cut input into blocks */
  2504. case 'B':
  2505. argument++;
  2506. g_blockSize = readU32FromChar(&argument);
  2507. DISPLAY("using %u KB block size \n", (unsigned)(g_blockSize>>10));
  2508. break;
  2509. /* caps runtime (in seconds) */
  2510. case 't':
  2511. argument++;
  2512. g_timeLimit_s = readU32FromChar(&argument);
  2513. break;
  2514. case 's':
  2515. argument++;
  2516. seperateFiles = 1;
  2517. break;
  2518. case 'q':
  2519. while (argument[0] == 'q') { argument++; g_displayLevel--; }
  2520. break;
  2521. case 'v':
  2522. while (argument[0] == 'v') { argument++; g_displayLevel++; }
  2523. break;
  2524. /* load dictionary file (only applicable for optimizer rn) */
  2525. case 'D':
  2526. if(i == argc - 1) { /* last argument, return error. */
  2527. DISPLAY("Dictionary file expected but not given : %d\n", i);
  2528. return 1;
  2529. } else {
  2530. i++;
  2531. dictFileName = argv[i];
  2532. argument += strlen(argument);
  2533. }
  2534. break;
  2535. /* Unknown command */
  2536. default : return badusage(exename);
  2537. }
  2538. }
  2539. continue;
  2540. } /* if (argument[0]=='-') */
  2541. /* first provided filename is input */
  2542. if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
  2543. }
  2544. /* Welcome message */
  2545. DISPLAYLEVEL(2, WELCOME_MESSAGE);
  2546. if (filenamesStart==0) {
  2547. if (g_optimizer) {
  2548. DISPLAY("Optimizer Expects File\n");
  2549. return 1;
  2550. } else {
  2551. result = benchSample(compressibility, cLevelRun);
  2552. }
  2553. } else {
  2554. if(seperateFiles) {
  2555. for(i = 0; i < argc - filenamesStart; i++) {
  2556. if (g_optimizer) {
  2557. result = optimizeForSize(argv+filenamesStart + i, 1, dictFileName, target, paramTarget, cLevelOpt, cLevelRun, memoTableLog);
  2558. if(result) { DISPLAY("Error on File %d", i); return result; }
  2559. } else {
  2560. result = benchFiles(argv+filenamesStart + i, 1, dictFileName, cLevelRun);
  2561. if(result) { DISPLAY("Error on File %d", i); return result; }
  2562. }
  2563. }
  2564. } else {
  2565. if (g_optimizer) {
  2566. assert(filenamesStart < argc);
  2567. result = optimizeForSize(argv+filenamesStart, (size_t)(argc-filenamesStart), dictFileName, target, paramTarget, cLevelOpt, cLevelRun, memoTableLog);
  2568. } else {
  2569. result = benchFiles(argv+filenamesStart, argc-filenamesStart, dictFileName, cLevelRun);
  2570. }
  2571. }
  2572. }
  2573. if (main_pause) { int unused; printf("press enter...\n"); unused = getchar(); (void)unused; }
  2574. return result;
  2575. }