xref: /aosp_15_r20/external/zstd/contrib/largeNbDicts/largeNbDicts.c (revision 01826a4963a0d8a59bc3812d29bdf0fb76416722)
1*01826a49SYabin Cui /*
2*01826a49SYabin Cui  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*01826a49SYabin Cui  * All rights reserved.
4*01826a49SYabin Cui  *
5*01826a49SYabin Cui  * This source code is licensed under both the BSD-style license (found in the
6*01826a49SYabin Cui  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*01826a49SYabin Cui  * in the COPYING file in the root directory of this source tree).
8*01826a49SYabin Cui  * You may select, at your option, one of the above-listed licenses.
9*01826a49SYabin Cui  */
10*01826a49SYabin Cui 
11*01826a49SYabin Cui /* largeNbDicts
12*01826a49SYabin Cui  * This is a benchmark test tool
13*01826a49SYabin Cui  * dedicated to the specific case of dictionary decompression
14*01826a49SYabin Cui  * using a very large nb of dictionaries
15*01826a49SYabin Cui  * thus suffering latency from lots of cache misses.
16*01826a49SYabin Cui  * It's created in a bid to investigate performance and find optimizations. */
17*01826a49SYabin Cui 
18*01826a49SYabin Cui 
19*01826a49SYabin Cui /*---  Dependencies  ---*/
20*01826a49SYabin Cui 
21*01826a49SYabin Cui #include <stddef.h>   /* size_t */
22*01826a49SYabin Cui #include <stdlib.h>   /* malloc, free, abort, qsort*/
23*01826a49SYabin Cui #include <stdio.h>    /* fprintf */
24*01826a49SYabin Cui #include <limits.h>   /* UINT_MAX */
25*01826a49SYabin Cui #include <assert.h>   /* assert */
26*01826a49SYabin Cui 
27*01826a49SYabin Cui #include "util.h"
28*01826a49SYabin Cui #include "benchfn.h"
29*01826a49SYabin Cui #define ZSTD_STATIC_LINKING_ONLY
30*01826a49SYabin Cui #include "zstd.h"
31*01826a49SYabin Cui #include "zdict.h"
32*01826a49SYabin Cui 
33*01826a49SYabin Cui 
34*01826a49SYabin Cui /*---  Constants  --- */
35*01826a49SYabin Cui 
36*01826a49SYabin Cui #define KB  *(1<<10)
37*01826a49SYabin Cui #define MB  *(1<<20)
38*01826a49SYabin Cui 
39*01826a49SYabin Cui #define BLOCKSIZE_DEFAULT 0  /* no slicing into blocks */
40*01826a49SYabin Cui #define DICTSIZE  (4 KB)
41*01826a49SYabin Cui #define CLEVEL_DEFAULT 3
42*01826a49SYabin Cui #define DICT_LOAD_METHOD ZSTD_dlm_byCopy
43*01826a49SYabin Cui 
44*01826a49SYabin Cui #define BENCH_TIME_DEFAULT_S   6
45*01826a49SYabin Cui #define RUN_TIME_DEFAULT_MS    1000
46*01826a49SYabin Cui #define BENCH_TIME_DEFAULT_MS (BENCH_TIME_DEFAULT_S * RUN_TIME_DEFAULT_MS)
47*01826a49SYabin Cui 
48*01826a49SYabin Cui #define DISPLAY_LEVEL_DEFAULT 3
49*01826a49SYabin Cui 
50*01826a49SYabin Cui #define BENCH_SIZE_MAX (1200 MB)
51*01826a49SYabin Cui 
52*01826a49SYabin Cui 
53*01826a49SYabin Cui /*---  Macros  ---*/
54*01826a49SYabin Cui 
55*01826a49SYabin Cui #define CONTROL(c)   { if (!(c)) abort(); }
56*01826a49SYabin Cui #undef MIN
57*01826a49SYabin Cui #define MIN(a,b)     ((a) < (b) ? (a) : (b))
58*01826a49SYabin Cui 
59*01826a49SYabin Cui 
60*01826a49SYabin Cui /*---  Display Macros  ---*/
61*01826a49SYabin Cui 
62*01826a49SYabin Cui #define DISPLAY(...)         fprintf(stdout, __VA_ARGS__)
63*01826a49SYabin Cui #define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
64*01826a49SYabin Cui static int g_displayLevel = DISPLAY_LEVEL_DEFAULT;   /* 0 : no display,  1: errors,  2 : + result + interaction + warnings,  3 : + progression,  4 : + information */
65*01826a49SYabin Cui 
66*01826a49SYabin Cui 
67*01826a49SYabin Cui /*---  buffer_t  ---*/
68*01826a49SYabin Cui 
69*01826a49SYabin Cui typedef struct {
70*01826a49SYabin Cui     void* ptr;
71*01826a49SYabin Cui     size_t size;
72*01826a49SYabin Cui     size_t capacity;
73*01826a49SYabin Cui } buffer_t;
74*01826a49SYabin Cui 
75*01826a49SYabin Cui static const buffer_t kBuffNull = { NULL, 0, 0 };
76*01826a49SYabin Cui 
77*01826a49SYabin Cui /* @return : kBuffNull if any error */
createBuffer(size_t capacity)78*01826a49SYabin Cui static buffer_t createBuffer(size_t capacity)
79*01826a49SYabin Cui {
80*01826a49SYabin Cui     assert(capacity > 0);
81*01826a49SYabin Cui     void* const ptr = malloc(capacity);
82*01826a49SYabin Cui     if (ptr==NULL) return kBuffNull;
83*01826a49SYabin Cui 
84*01826a49SYabin Cui     buffer_t buffer;
85*01826a49SYabin Cui     buffer.ptr = ptr;
86*01826a49SYabin Cui     buffer.capacity = capacity;
87*01826a49SYabin Cui     buffer.size = 0;
88*01826a49SYabin Cui     return buffer;
89*01826a49SYabin Cui }
90*01826a49SYabin Cui 
freeBuffer(buffer_t buff)91*01826a49SYabin Cui static void freeBuffer(buffer_t buff)
92*01826a49SYabin Cui {
93*01826a49SYabin Cui     free(buff.ptr);
94*01826a49SYabin Cui }
95*01826a49SYabin Cui 
96*01826a49SYabin Cui 
fillBuffer_fromHandle(buffer_t * buff,FILE * f)97*01826a49SYabin Cui static void fillBuffer_fromHandle(buffer_t* buff, FILE* f)
98*01826a49SYabin Cui {
99*01826a49SYabin Cui     size_t const readSize = fread(buff->ptr, 1, buff->capacity, f);
100*01826a49SYabin Cui     buff->size = readSize;
101*01826a49SYabin Cui }
102*01826a49SYabin Cui 
103*01826a49SYabin Cui 
104*01826a49SYabin Cui /* @return : kBuffNull if any error */
createBuffer_fromFile(const char * fileName)105*01826a49SYabin Cui static buffer_t createBuffer_fromFile(const char* fileName)
106*01826a49SYabin Cui {
107*01826a49SYabin Cui     U64 const fileSize = UTIL_getFileSize(fileName);
108*01826a49SYabin Cui     size_t const bufferSize = (size_t) fileSize;
109*01826a49SYabin Cui 
110*01826a49SYabin Cui     if (fileSize == UTIL_FILESIZE_UNKNOWN) return kBuffNull;
111*01826a49SYabin Cui     assert((U64)bufferSize == fileSize);   /* check overflow */
112*01826a49SYabin Cui 
113*01826a49SYabin Cui     {   FILE* const f = fopen(fileName, "rb");
114*01826a49SYabin Cui         if (f == NULL) return kBuffNull;
115*01826a49SYabin Cui 
116*01826a49SYabin Cui         buffer_t buff = createBuffer(bufferSize);
117*01826a49SYabin Cui         CONTROL(buff.ptr != NULL);
118*01826a49SYabin Cui 
119*01826a49SYabin Cui         fillBuffer_fromHandle(&buff, f);
120*01826a49SYabin Cui         CONTROL(buff.size == buff.capacity);
121*01826a49SYabin Cui 
122*01826a49SYabin Cui         fclose(f);   /* do nothing specific if fclose() fails */
123*01826a49SYabin Cui         return buff;
124*01826a49SYabin Cui     }
125*01826a49SYabin Cui }
126*01826a49SYabin Cui 
127*01826a49SYabin Cui 
128*01826a49SYabin Cui /* @return : kBuffNull if any error */
129*01826a49SYabin Cui static buffer_t
createDictionaryBuffer(const char * dictionaryName,const void * srcBuffer,const size_t * srcBlockSizes,size_t nbBlocks,size_t requestedDictSize)130*01826a49SYabin Cui createDictionaryBuffer(const char* dictionaryName,
131*01826a49SYabin Cui                        const void* srcBuffer,
132*01826a49SYabin Cui                        const size_t* srcBlockSizes, size_t nbBlocks,
133*01826a49SYabin Cui                        size_t requestedDictSize)
134*01826a49SYabin Cui {
135*01826a49SYabin Cui     if (dictionaryName) {
136*01826a49SYabin Cui         DISPLAYLEVEL(3, "loading dictionary %s \n", dictionaryName);
137*01826a49SYabin Cui         return createBuffer_fromFile(dictionaryName);  /* note : result might be kBuffNull */
138*01826a49SYabin Cui 
139*01826a49SYabin Cui     } else {
140*01826a49SYabin Cui 
141*01826a49SYabin Cui         DISPLAYLEVEL(3, "creating dictionary, of target size %u bytes \n",
142*01826a49SYabin Cui                         (unsigned)requestedDictSize);
143*01826a49SYabin Cui         void* const dictBuffer = malloc(requestedDictSize);
144*01826a49SYabin Cui         CONTROL(dictBuffer != NULL);
145*01826a49SYabin Cui 
146*01826a49SYabin Cui         assert(nbBlocks <= UINT_MAX);
147*01826a49SYabin Cui         size_t const dictSize = ZDICT_trainFromBuffer(dictBuffer, requestedDictSize,
148*01826a49SYabin Cui                                                       srcBuffer,
149*01826a49SYabin Cui                                                       srcBlockSizes, (unsigned)nbBlocks);
150*01826a49SYabin Cui         CONTROL(!ZSTD_isError(dictSize));
151*01826a49SYabin Cui 
152*01826a49SYabin Cui         buffer_t result;
153*01826a49SYabin Cui         result.ptr = dictBuffer;
154*01826a49SYabin Cui         result.capacity = requestedDictSize;
155*01826a49SYabin Cui         result.size = dictSize;
156*01826a49SYabin Cui         return result;
157*01826a49SYabin Cui     }
158*01826a49SYabin Cui }
159*01826a49SYabin Cui 
160*01826a49SYabin Cui /*! BMK_loadFiles() :
161*01826a49SYabin Cui  *  Loads `buffer`, with content from files listed within `fileNamesTable`.
162*01826a49SYabin Cui  *  Fills `buffer` entirely.
163*01826a49SYabin Cui  * @return : 0 on success, !=0 on error */
loadFiles(void * buffer,size_t bufferSize,size_t * fileSizes,const char * const * fileNamesTable,unsigned nbFiles)164*01826a49SYabin Cui static int loadFiles(void* buffer, size_t bufferSize,
165*01826a49SYabin Cui                      size_t* fileSizes,
166*01826a49SYabin Cui                      const char* const * fileNamesTable, unsigned nbFiles)
167*01826a49SYabin Cui {
168*01826a49SYabin Cui     size_t pos = 0, totalSize = 0;
169*01826a49SYabin Cui 
170*01826a49SYabin Cui     for (unsigned n=0; n<nbFiles; n++) {
171*01826a49SYabin Cui         U64 fileSize = UTIL_getFileSize(fileNamesTable[n]);
172*01826a49SYabin Cui         if (UTIL_isDirectory(fileNamesTable[n])) {
173*01826a49SYabin Cui             fileSizes[n] = 0;
174*01826a49SYabin Cui             continue;
175*01826a49SYabin Cui         }
176*01826a49SYabin Cui         if (fileSize == UTIL_FILESIZE_UNKNOWN) {
177*01826a49SYabin Cui             fileSizes[n] = 0;
178*01826a49SYabin Cui             continue;
179*01826a49SYabin Cui         }
180*01826a49SYabin Cui 
181*01826a49SYabin Cui         FILE* const f = fopen(fileNamesTable[n], "rb");
182*01826a49SYabin Cui         assert(f!=NULL);
183*01826a49SYabin Cui 
184*01826a49SYabin Cui         assert(pos <= bufferSize);
185*01826a49SYabin Cui         assert(fileSize <= bufferSize - pos);
186*01826a49SYabin Cui 
187*01826a49SYabin Cui         {   size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f);
188*01826a49SYabin Cui             assert(readSize == fileSize);
189*01826a49SYabin Cui             pos += readSize;
190*01826a49SYabin Cui         }
191*01826a49SYabin Cui         fileSizes[n] = (size_t)fileSize;
192*01826a49SYabin Cui         totalSize += (size_t)fileSize;
193*01826a49SYabin Cui         fclose(f);
194*01826a49SYabin Cui     }
195*01826a49SYabin Cui 
196*01826a49SYabin Cui     assert(totalSize == bufferSize);
197*01826a49SYabin Cui     return 0;
198*01826a49SYabin Cui }
199*01826a49SYabin Cui 
200*01826a49SYabin Cui 
201*01826a49SYabin Cui 
202*01826a49SYabin Cui /*---  slice_collection_t  ---*/
203*01826a49SYabin Cui 
204*01826a49SYabin Cui typedef struct {
205*01826a49SYabin Cui     void** slicePtrs;
206*01826a49SYabin Cui     size_t* capacities;
207*01826a49SYabin Cui     size_t nbSlices;
208*01826a49SYabin Cui } slice_collection_t;
209*01826a49SYabin Cui 
210*01826a49SYabin Cui static const slice_collection_t kNullCollection = { NULL, NULL, 0 };
211*01826a49SYabin Cui 
freeSliceCollection(slice_collection_t collection)212*01826a49SYabin Cui static void freeSliceCollection(slice_collection_t collection)
213*01826a49SYabin Cui {
214*01826a49SYabin Cui     free(collection.slicePtrs);
215*01826a49SYabin Cui     free(collection.capacities);
216*01826a49SYabin Cui }
217*01826a49SYabin Cui 
218*01826a49SYabin Cui /* shrinkSizes() :
219*01826a49SYabin Cui  * downsizes sizes of slices within collection, according to `newSizes`.
220*01826a49SYabin Cui  * every `newSizes` entry must be <= than its corresponding collection size */
shrinkSizes(slice_collection_t collection,const size_t * newSizes)221*01826a49SYabin Cui void shrinkSizes(slice_collection_t collection,
222*01826a49SYabin Cui                  const size_t* newSizes)  /* presumed same size as collection */
223*01826a49SYabin Cui {
224*01826a49SYabin Cui     size_t const nbSlices = collection.nbSlices;
225*01826a49SYabin Cui     for (size_t blockNb = 0; blockNb < nbSlices; blockNb++) {
226*01826a49SYabin Cui         assert(newSizes[blockNb] <= collection.capacities[blockNb]);
227*01826a49SYabin Cui         collection.capacities[blockNb] = newSizes[blockNb];
228*01826a49SYabin Cui     }
229*01826a49SYabin Cui }
230*01826a49SYabin Cui 
231*01826a49SYabin Cui 
232*01826a49SYabin Cui /* splitSlices() :
233*01826a49SYabin Cui  * nbSlices : if == 0, nbSlices is automatically determined from srcSlices and blockSize.
234*01826a49SYabin Cui  *            otherwise, creates exactly nbSlices slices,
235*01826a49SYabin Cui  *            by either truncating input (when smaller)
236*01826a49SYabin Cui  *            or repeating input from beginning */
237*01826a49SYabin Cui static slice_collection_t
splitSlices(slice_collection_t srcSlices,size_t blockSize,size_t nbSlices)238*01826a49SYabin Cui splitSlices(slice_collection_t srcSlices, size_t blockSize, size_t nbSlices)
239*01826a49SYabin Cui {
240*01826a49SYabin Cui     if (blockSize==0) blockSize = (size_t)(-1);   /* means "do not cut" */
241*01826a49SYabin Cui     size_t nbSrcBlocks = 0;
242*01826a49SYabin Cui     for (size_t ssnb=0; ssnb < srcSlices.nbSlices; ssnb++) {
243*01826a49SYabin Cui         size_t pos = 0;
244*01826a49SYabin Cui         while (pos <= srcSlices.capacities[ssnb]) {
245*01826a49SYabin Cui             nbSrcBlocks++;
246*01826a49SYabin Cui             pos += blockSize;
247*01826a49SYabin Cui         }
248*01826a49SYabin Cui     }
249*01826a49SYabin Cui 
250*01826a49SYabin Cui     if (nbSlices == 0) nbSlices = nbSrcBlocks;
251*01826a49SYabin Cui 
252*01826a49SYabin Cui     void** const sliceTable = (void**)malloc(nbSlices * sizeof(*sliceTable));
253*01826a49SYabin Cui     size_t* const capacities = (size_t*)malloc(nbSlices * sizeof(*capacities));
254*01826a49SYabin Cui     if (sliceTable == NULL || capacities == NULL) {
255*01826a49SYabin Cui         free(sliceTable);
256*01826a49SYabin Cui         free(capacities);
257*01826a49SYabin Cui         return kNullCollection;
258*01826a49SYabin Cui     }
259*01826a49SYabin Cui 
260*01826a49SYabin Cui     size_t ssnb = 0;
261*01826a49SYabin Cui     for (size_t sliceNb=0; sliceNb < nbSlices; ) {
262*01826a49SYabin Cui         ssnb = (ssnb + 1) % srcSlices.nbSlices;
263*01826a49SYabin Cui         size_t pos = 0;
264*01826a49SYabin Cui         char* const ptr = (char*)srcSlices.slicePtrs[ssnb];
265*01826a49SYabin Cui         while (pos < srcSlices.capacities[ssnb] && sliceNb < nbSlices) {
266*01826a49SYabin Cui             size_t const size = MIN(blockSize, srcSlices.capacities[ssnb] - pos);
267*01826a49SYabin Cui             sliceTable[sliceNb] = ptr + pos;
268*01826a49SYabin Cui             capacities[sliceNb] = size;
269*01826a49SYabin Cui             sliceNb++;
270*01826a49SYabin Cui             pos += blockSize;
271*01826a49SYabin Cui         }
272*01826a49SYabin Cui     }
273*01826a49SYabin Cui 
274*01826a49SYabin Cui     slice_collection_t result;
275*01826a49SYabin Cui     result.nbSlices = nbSlices;
276*01826a49SYabin Cui     result.slicePtrs = sliceTable;
277*01826a49SYabin Cui     result.capacities = capacities;
278*01826a49SYabin Cui     return result;
279*01826a49SYabin Cui }
280*01826a49SYabin Cui 
281*01826a49SYabin Cui 
sliceCollection_totalCapacity(slice_collection_t sc)282*01826a49SYabin Cui static size_t sliceCollection_totalCapacity(slice_collection_t sc)
283*01826a49SYabin Cui {
284*01826a49SYabin Cui     size_t totalSize = 0;
285*01826a49SYabin Cui     for (size_t n=0; n<sc.nbSlices; n++)
286*01826a49SYabin Cui         totalSize += sc.capacities[n];
287*01826a49SYabin Cui     return totalSize;
288*01826a49SYabin Cui }
289*01826a49SYabin Cui 
290*01826a49SYabin Cui 
291*01826a49SYabin Cui /* ---  buffer collection  --- */
292*01826a49SYabin Cui 
293*01826a49SYabin Cui typedef struct {
294*01826a49SYabin Cui     buffer_t buffer;
295*01826a49SYabin Cui     slice_collection_t slices;
296*01826a49SYabin Cui } buffer_collection_t;
297*01826a49SYabin Cui 
298*01826a49SYabin Cui 
freeBufferCollection(buffer_collection_t bc)299*01826a49SYabin Cui static void freeBufferCollection(buffer_collection_t bc)
300*01826a49SYabin Cui {
301*01826a49SYabin Cui     freeBuffer(bc.buffer);
302*01826a49SYabin Cui     freeSliceCollection(bc.slices);
303*01826a49SYabin Cui }
304*01826a49SYabin Cui 
305*01826a49SYabin Cui 
306*01826a49SYabin Cui static buffer_collection_t
createBufferCollection_fromSliceCollectionSizes(slice_collection_t sc)307*01826a49SYabin Cui createBufferCollection_fromSliceCollectionSizes(slice_collection_t sc)
308*01826a49SYabin Cui {
309*01826a49SYabin Cui     size_t const bufferSize = sliceCollection_totalCapacity(sc);
310*01826a49SYabin Cui 
311*01826a49SYabin Cui     buffer_t buffer = createBuffer(bufferSize);
312*01826a49SYabin Cui     CONTROL(buffer.ptr != NULL);
313*01826a49SYabin Cui 
314*01826a49SYabin Cui     size_t const nbSlices = sc.nbSlices;
315*01826a49SYabin Cui     void** const slices = (void**)malloc(nbSlices * sizeof(*slices));
316*01826a49SYabin Cui     CONTROL(slices != NULL);
317*01826a49SYabin Cui 
318*01826a49SYabin Cui     size_t* const capacities = (size_t*)malloc(nbSlices * sizeof(*capacities));
319*01826a49SYabin Cui     CONTROL(capacities != NULL);
320*01826a49SYabin Cui 
321*01826a49SYabin Cui     char* const ptr = (char*)buffer.ptr;
322*01826a49SYabin Cui     size_t pos = 0;
323*01826a49SYabin Cui     for (size_t n=0; n < nbSlices; n++) {
324*01826a49SYabin Cui         capacities[n] = sc.capacities[n];
325*01826a49SYabin Cui         slices[n] = ptr + pos;
326*01826a49SYabin Cui         pos += capacities[n];
327*01826a49SYabin Cui     }
328*01826a49SYabin Cui 
329*01826a49SYabin Cui     buffer_collection_t result;
330*01826a49SYabin Cui     result.buffer = buffer;
331*01826a49SYabin Cui     result.slices.nbSlices = nbSlices;
332*01826a49SYabin Cui     result.slices.capacities = capacities;
333*01826a49SYabin Cui     result.slices.slicePtrs = slices;
334*01826a49SYabin Cui     return result;
335*01826a49SYabin Cui }
336*01826a49SYabin Cui 
337*01826a49SYabin Cui static buffer_collection_t
createBufferCollection_fromSliceCollection(slice_collection_t sc)338*01826a49SYabin Cui createBufferCollection_fromSliceCollection(slice_collection_t sc)
339*01826a49SYabin Cui {
340*01826a49SYabin Cui     size_t const bufferSize = sliceCollection_totalCapacity(sc);
341*01826a49SYabin Cui 
342*01826a49SYabin Cui     buffer_t buffer = createBuffer(bufferSize);
343*01826a49SYabin Cui     CONTROL(buffer.ptr != NULL);
344*01826a49SYabin Cui 
345*01826a49SYabin Cui     size_t const nbSlices = sc.nbSlices;
346*01826a49SYabin Cui     void** const slices = (void**)malloc(nbSlices * sizeof(*slices));
347*01826a49SYabin Cui     CONTROL(slices != NULL);
348*01826a49SYabin Cui 
349*01826a49SYabin Cui     size_t* const capacities = (size_t*)malloc(nbSlices * sizeof(*capacities));
350*01826a49SYabin Cui     CONTROL(capacities != NULL);
351*01826a49SYabin Cui 
352*01826a49SYabin Cui     char* const ptr = (char*)buffer.ptr;
353*01826a49SYabin Cui     size_t pos = 0;
354*01826a49SYabin Cui     for (size_t n=0; n < nbSlices; n++) {
355*01826a49SYabin Cui         capacities[n] = sc.capacities[n];
356*01826a49SYabin Cui         slices[n] = ptr + pos;
357*01826a49SYabin Cui         pos += capacities[n];
358*01826a49SYabin Cui     }
359*01826a49SYabin Cui 
360*01826a49SYabin Cui     for (size_t i = 0; i < nbSlices; i++) {
361*01826a49SYabin Cui         memcpy(slices[i], sc.slicePtrs[i], sc.capacities[i]);
362*01826a49SYabin Cui         capacities[i] = sc.capacities[i];
363*01826a49SYabin Cui     }
364*01826a49SYabin Cui 
365*01826a49SYabin Cui     buffer_collection_t result;
366*01826a49SYabin Cui     result.buffer = buffer;
367*01826a49SYabin Cui     result.slices.nbSlices = nbSlices;
368*01826a49SYabin Cui     result.slices.capacities = capacities;
369*01826a49SYabin Cui     result.slices.slicePtrs = slices;
370*01826a49SYabin Cui 
371*01826a49SYabin Cui     return result;
372*01826a49SYabin Cui }
373*01826a49SYabin Cui 
374*01826a49SYabin Cui /* @return : kBuffNull if any error */
375*01826a49SYabin Cui static buffer_collection_t
createBufferCollection_fromFiles(const char * const * fileNamesTable,unsigned nbFiles)376*01826a49SYabin Cui createBufferCollection_fromFiles(const char* const * fileNamesTable, unsigned nbFiles)
377*01826a49SYabin Cui {
378*01826a49SYabin Cui     U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles);
379*01826a49SYabin Cui     assert(totalSizeToLoad != UTIL_FILESIZE_UNKNOWN);
380*01826a49SYabin Cui     assert(totalSizeToLoad <= BENCH_SIZE_MAX);
381*01826a49SYabin Cui     size_t const loadedSize = (size_t)totalSizeToLoad;
382*01826a49SYabin Cui     assert(loadedSize > 0);
383*01826a49SYabin Cui     void* const srcBuffer = malloc(loadedSize);
384*01826a49SYabin Cui     assert(srcBuffer != NULL);
385*01826a49SYabin Cui 
386*01826a49SYabin Cui     assert(nbFiles > 0);
387*01826a49SYabin Cui     size_t* const fileSizes = (size_t*)calloc(nbFiles, sizeof(*fileSizes));
388*01826a49SYabin Cui     assert(fileSizes != NULL);
389*01826a49SYabin Cui 
390*01826a49SYabin Cui     /* Load input buffer */
391*01826a49SYabin Cui     int const errorCode = loadFiles(srcBuffer, loadedSize,
392*01826a49SYabin Cui                                     fileSizes,
393*01826a49SYabin Cui                                     fileNamesTable, nbFiles);
394*01826a49SYabin Cui     assert(errorCode == 0);
395*01826a49SYabin Cui 
396*01826a49SYabin Cui     void** sliceTable = (void**)malloc(nbFiles * sizeof(*sliceTable));
397*01826a49SYabin Cui     assert(sliceTable != NULL);
398*01826a49SYabin Cui 
399*01826a49SYabin Cui     char* const ptr = (char*)srcBuffer;
400*01826a49SYabin Cui     size_t pos = 0;
401*01826a49SYabin Cui     unsigned fileNb = 0;
402*01826a49SYabin Cui     for ( ; (pos < loadedSize) && (fileNb < nbFiles); fileNb++) {
403*01826a49SYabin Cui         sliceTable[fileNb] = ptr + pos;
404*01826a49SYabin Cui         pos += fileSizes[fileNb];
405*01826a49SYabin Cui     }
406*01826a49SYabin Cui     assert(pos == loadedSize);
407*01826a49SYabin Cui     assert(fileNb == nbFiles);
408*01826a49SYabin Cui 
409*01826a49SYabin Cui 
410*01826a49SYabin Cui     buffer_t buffer;
411*01826a49SYabin Cui     buffer.ptr = srcBuffer;
412*01826a49SYabin Cui     buffer.capacity = loadedSize;
413*01826a49SYabin Cui     buffer.size = loadedSize;
414*01826a49SYabin Cui 
415*01826a49SYabin Cui     slice_collection_t slices;
416*01826a49SYabin Cui     slices.slicePtrs = sliceTable;
417*01826a49SYabin Cui     slices.capacities = fileSizes;
418*01826a49SYabin Cui     slices.nbSlices = nbFiles;
419*01826a49SYabin Cui 
420*01826a49SYabin Cui     buffer_collection_t bc;
421*01826a49SYabin Cui     bc.buffer = buffer;
422*01826a49SYabin Cui     bc.slices = slices;
423*01826a49SYabin Cui     return bc;
424*01826a49SYabin Cui }
425*01826a49SYabin Cui 
426*01826a49SYabin Cui 
427*01826a49SYabin Cui 
428*01826a49SYabin Cui 
429*01826a49SYabin Cui /*---  ddict_collection_t  ---*/
430*01826a49SYabin Cui 
431*01826a49SYabin Cui typedef struct {
432*01826a49SYabin Cui     ZSTD_DDict** ddicts;
433*01826a49SYabin Cui     size_t nbDDict;
434*01826a49SYabin Cui } ddict_collection_t;
435*01826a49SYabin Cui 
436*01826a49SYabin Cui typedef struct {
437*01826a49SYabin Cui     ZSTD_CDict** cdicts;
438*01826a49SYabin Cui     size_t nbCDict;
439*01826a49SYabin Cui } cdict_collection_t;
440*01826a49SYabin Cui 
441*01826a49SYabin Cui static const cdict_collection_t kNullCDictCollection = { NULL, 0 };
442*01826a49SYabin Cui 
freeCDictCollection(cdict_collection_t cdictc)443*01826a49SYabin Cui static void freeCDictCollection(cdict_collection_t cdictc)
444*01826a49SYabin Cui {
445*01826a49SYabin Cui     for (size_t dictNb=0; dictNb < cdictc.nbCDict; dictNb++) {
446*01826a49SYabin Cui         ZSTD_freeCDict(cdictc.cdicts[dictNb]);
447*01826a49SYabin Cui     }
448*01826a49SYabin Cui     free(cdictc.cdicts);
449*01826a49SYabin Cui }
450*01826a49SYabin Cui 
451*01826a49SYabin Cui /* returns .buffers=NULL if operation fails */
createCDictCollection(const void * dictBuffer,size_t dictSize,size_t nbCDict,ZSTD_dictContentType_e dictContentType,ZSTD_CCtx_params * cctxParams)452*01826a49SYabin Cui static cdict_collection_t createCDictCollection(const void* dictBuffer, size_t dictSize, size_t nbCDict, ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams)
453*01826a49SYabin Cui {
454*01826a49SYabin Cui     ZSTD_CDict** const cdicts = malloc(nbCDict * sizeof(ZSTD_CDict*));
455*01826a49SYabin Cui     if (cdicts==NULL) return kNullCDictCollection;
456*01826a49SYabin Cui     for (size_t dictNb=0; dictNb < nbCDict; dictNb++) {
457*01826a49SYabin Cui         cdicts[dictNb] = ZSTD_createCDict_advanced2(dictBuffer, dictSize, DICT_LOAD_METHOD, dictContentType, cctxParams, ZSTD_defaultCMem);
458*01826a49SYabin Cui         CONTROL(cdicts[dictNb] != NULL);
459*01826a49SYabin Cui     }
460*01826a49SYabin Cui     cdict_collection_t cdictc;
461*01826a49SYabin Cui     cdictc.cdicts = cdicts;
462*01826a49SYabin Cui     cdictc.nbCDict = nbCDict;
463*01826a49SYabin Cui     return cdictc;
464*01826a49SYabin Cui }
465*01826a49SYabin Cui 
466*01826a49SYabin Cui static const ddict_collection_t kNullDDictCollection = { NULL, 0 };
467*01826a49SYabin Cui 
freeDDictCollection(ddict_collection_t ddictc)468*01826a49SYabin Cui static void freeDDictCollection(ddict_collection_t ddictc)
469*01826a49SYabin Cui {
470*01826a49SYabin Cui     for (size_t dictNb=0; dictNb < ddictc.nbDDict; dictNb++) {
471*01826a49SYabin Cui         ZSTD_freeDDict(ddictc.ddicts[dictNb]);
472*01826a49SYabin Cui     }
473*01826a49SYabin Cui     free(ddictc.ddicts);
474*01826a49SYabin Cui }
475*01826a49SYabin Cui 
476*01826a49SYabin Cui /* returns .buffers=NULL if operation fails */
createDDictCollection(const void * dictBuffer,size_t dictSize,size_t nbDDict)477*01826a49SYabin Cui static ddict_collection_t createDDictCollection(const void* dictBuffer, size_t dictSize, size_t nbDDict)
478*01826a49SYabin Cui {
479*01826a49SYabin Cui     ZSTD_DDict** const ddicts = malloc(nbDDict * sizeof(ZSTD_DDict*));
480*01826a49SYabin Cui     assert(ddicts != NULL);
481*01826a49SYabin Cui     if (ddicts==NULL) return kNullDDictCollection;
482*01826a49SYabin Cui     for (size_t dictNb=0; dictNb < nbDDict; dictNb++) {
483*01826a49SYabin Cui         ddicts[dictNb] = ZSTD_createDDict(dictBuffer, dictSize);
484*01826a49SYabin Cui         assert(ddicts[dictNb] != NULL);
485*01826a49SYabin Cui     }
486*01826a49SYabin Cui     ddict_collection_t ddictc;
487*01826a49SYabin Cui     ddictc.ddicts = ddicts;
488*01826a49SYabin Cui     ddictc.nbDDict = nbDDict;
489*01826a49SYabin Cui     return ddictc;
490*01826a49SYabin Cui }
491*01826a49SYabin Cui 
492*01826a49SYabin Cui 
493*01826a49SYabin Cui /* mess with addresses, so that linear scanning dictionaries != linear address scanning */
shuffleCDictionaries(cdict_collection_t dicts)494*01826a49SYabin Cui void shuffleCDictionaries(cdict_collection_t dicts)
495*01826a49SYabin Cui {
496*01826a49SYabin Cui     size_t const nbDicts = dicts.nbCDict;
497*01826a49SYabin Cui     for (size_t r=0; r<nbDicts; r++) {
498*01826a49SYabin Cui         size_t const d = (size_t)rand() % nbDicts;
499*01826a49SYabin Cui         ZSTD_CDict* tmpd = dicts.cdicts[d];
500*01826a49SYabin Cui         dicts.cdicts[d] = dicts.cdicts[r];
501*01826a49SYabin Cui         dicts.cdicts[r] = tmpd;
502*01826a49SYabin Cui     }
503*01826a49SYabin Cui     for (size_t r=0; r<nbDicts; r++) {
504*01826a49SYabin Cui         size_t const d1 = (size_t)rand() % nbDicts;
505*01826a49SYabin Cui         size_t const d2 = (size_t)rand() % nbDicts;
506*01826a49SYabin Cui         ZSTD_CDict* tmpd = dicts.cdicts[d1];
507*01826a49SYabin Cui         dicts.cdicts[d1] = dicts.cdicts[d2];
508*01826a49SYabin Cui         dicts.cdicts[d2] = tmpd;
509*01826a49SYabin Cui     }
510*01826a49SYabin Cui }
511*01826a49SYabin Cui 
512*01826a49SYabin Cui /* mess with addresses, so that linear scanning dictionaries != linear address scanning */
shuffleDDictionaries(ddict_collection_t dicts)513*01826a49SYabin Cui void shuffleDDictionaries(ddict_collection_t dicts)
514*01826a49SYabin Cui {
515*01826a49SYabin Cui     size_t const nbDicts = dicts.nbDDict;
516*01826a49SYabin Cui     for (size_t r=0; r<nbDicts; r++) {
517*01826a49SYabin Cui         size_t const d = (size_t)rand() % nbDicts;
518*01826a49SYabin Cui         ZSTD_DDict* tmpd = dicts.ddicts[d];
519*01826a49SYabin Cui         dicts.ddicts[d] = dicts.ddicts[r];
520*01826a49SYabin Cui         dicts.ddicts[r] = tmpd;
521*01826a49SYabin Cui     }
522*01826a49SYabin Cui     for (size_t r=0; r<nbDicts; r++) {
523*01826a49SYabin Cui         size_t const d1 = (size_t)rand() % nbDicts;
524*01826a49SYabin Cui         size_t const d2 = (size_t)rand() % nbDicts;
525*01826a49SYabin Cui         ZSTD_DDict* tmpd = dicts.ddicts[d1];
526*01826a49SYabin Cui         dicts.ddicts[d1] = dicts.ddicts[d2];
527*01826a49SYabin Cui         dicts.ddicts[d2] = tmpd;
528*01826a49SYabin Cui     }
529*01826a49SYabin Cui }
530*01826a49SYabin Cui 
531*01826a49SYabin Cui 
532*01826a49SYabin Cui /* ---   Compression  --- */
533*01826a49SYabin Cui 
534*01826a49SYabin Cui /* compressBlocks() :
535*01826a49SYabin Cui  * @return : total compressed size of all blocks,
536*01826a49SYabin Cui  *        or 0 if error.
537*01826a49SYabin Cui  */
compressBlocks(size_t * cSizes,slice_collection_t dstBlockBuffers,slice_collection_t srcBlockBuffers,ZSTD_CDict * cdict,int cLevel)538*01826a49SYabin Cui static size_t compressBlocks(size_t* cSizes,   /* optional (can be NULL). If present, must contain at least nbBlocks fields */
539*01826a49SYabin Cui                              slice_collection_t dstBlockBuffers,
540*01826a49SYabin Cui                              slice_collection_t srcBlockBuffers,
541*01826a49SYabin Cui                              ZSTD_CDict* cdict, int cLevel)
542*01826a49SYabin Cui {
543*01826a49SYabin Cui     size_t const nbBlocks = srcBlockBuffers.nbSlices;
544*01826a49SYabin Cui     assert(dstBlockBuffers.nbSlices == srcBlockBuffers.nbSlices);
545*01826a49SYabin Cui 
546*01826a49SYabin Cui     ZSTD_CCtx* const cctx = ZSTD_createCCtx();
547*01826a49SYabin Cui     assert(cctx != NULL);
548*01826a49SYabin Cui 
549*01826a49SYabin Cui     size_t totalCSize = 0;
550*01826a49SYabin Cui     for (size_t blockNb=0; blockNb < nbBlocks; blockNb++) {
551*01826a49SYabin Cui         size_t cBlockSize;
552*01826a49SYabin Cui         if (cdict == NULL) {
553*01826a49SYabin Cui             cBlockSize = ZSTD_compressCCtx(cctx,
554*01826a49SYabin Cui                             dstBlockBuffers.slicePtrs[blockNb], dstBlockBuffers.capacities[blockNb],
555*01826a49SYabin Cui                             srcBlockBuffers.slicePtrs[blockNb], srcBlockBuffers.capacities[blockNb],
556*01826a49SYabin Cui                             cLevel);
557*01826a49SYabin Cui         } else {
558*01826a49SYabin Cui             cBlockSize = ZSTD_compress_usingCDict(cctx,
559*01826a49SYabin Cui                             dstBlockBuffers.slicePtrs[blockNb], dstBlockBuffers.capacities[blockNb],
560*01826a49SYabin Cui                             srcBlockBuffers.slicePtrs[blockNb], srcBlockBuffers.capacities[blockNb],
561*01826a49SYabin Cui                             cdict);
562*01826a49SYabin Cui         }
563*01826a49SYabin Cui         CONTROL(!ZSTD_isError(cBlockSize));
564*01826a49SYabin Cui         if (cSizes) cSizes[blockNb] = cBlockSize;
565*01826a49SYabin Cui         totalCSize += cBlockSize;
566*01826a49SYabin Cui     }
567*01826a49SYabin Cui     return totalCSize;
568*01826a49SYabin Cui }
569*01826a49SYabin Cui 
570*01826a49SYabin Cui 
571*01826a49SYabin Cui /* ---  Benchmark  --- */
572*01826a49SYabin Cui 
573*01826a49SYabin Cui typedef struct {
574*01826a49SYabin Cui     ZSTD_CCtx* cctx;
575*01826a49SYabin Cui     size_t nbDicts;
576*01826a49SYabin Cui     size_t dictNb;
577*01826a49SYabin Cui     cdict_collection_t dictionaries;
578*01826a49SYabin Cui } compressInstructions;
579*01826a49SYabin Cui 
createCompressInstructions(cdict_collection_t dictionaries,ZSTD_CCtx_params * cctxParams)580*01826a49SYabin Cui compressInstructions createCompressInstructions(cdict_collection_t dictionaries, ZSTD_CCtx_params* cctxParams)
581*01826a49SYabin Cui {
582*01826a49SYabin Cui     compressInstructions ci;
583*01826a49SYabin Cui     ci.cctx = ZSTD_createCCtx();
584*01826a49SYabin Cui     CONTROL(ci.cctx != NULL);
585*01826a49SYabin Cui     if (cctxParams)
586*01826a49SYabin Cui       ZSTD_CCtx_setParametersUsingCCtxParams(ci.cctx, cctxParams);
587*01826a49SYabin Cui     ci.nbDicts = dictionaries.nbCDict;
588*01826a49SYabin Cui     ci.dictNb = 0;
589*01826a49SYabin Cui     ci.dictionaries = dictionaries;
590*01826a49SYabin Cui     return ci;
591*01826a49SYabin Cui }
592*01826a49SYabin Cui 
freeCompressInstructions(compressInstructions ci)593*01826a49SYabin Cui void freeCompressInstructions(compressInstructions ci)
594*01826a49SYabin Cui {
595*01826a49SYabin Cui     ZSTD_freeCCtx(ci.cctx);
596*01826a49SYabin Cui }
597*01826a49SYabin Cui 
598*01826a49SYabin Cui typedef struct {
599*01826a49SYabin Cui     ZSTD_DCtx* dctx;
600*01826a49SYabin Cui     size_t nbDicts;
601*01826a49SYabin Cui     size_t dictNb;
602*01826a49SYabin Cui     ddict_collection_t dictionaries;
603*01826a49SYabin Cui } decompressInstructions;
604*01826a49SYabin Cui 
createDecompressInstructions(ddict_collection_t dictionaries)605*01826a49SYabin Cui decompressInstructions createDecompressInstructions(ddict_collection_t dictionaries)
606*01826a49SYabin Cui {
607*01826a49SYabin Cui     decompressInstructions di;
608*01826a49SYabin Cui     di.dctx = ZSTD_createDCtx();
609*01826a49SYabin Cui     assert(di.dctx != NULL);
610*01826a49SYabin Cui     di.nbDicts = dictionaries.nbDDict;
611*01826a49SYabin Cui     di.dictNb = 0;
612*01826a49SYabin Cui     di.dictionaries = dictionaries;
613*01826a49SYabin Cui     return di;
614*01826a49SYabin Cui }
615*01826a49SYabin Cui 
freeDecompressInstructions(decompressInstructions di)616*01826a49SYabin Cui void freeDecompressInstructions(decompressInstructions di)
617*01826a49SYabin Cui {
618*01826a49SYabin Cui     ZSTD_freeDCtx(di.dctx);
619*01826a49SYabin Cui }
620*01826a49SYabin Cui 
621*01826a49SYabin Cui /* benched function */
compress(const void * src,size_t srcSize,void * dst,size_t dstCapacity,void * payload)622*01826a49SYabin Cui size_t compress(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* payload)
623*01826a49SYabin Cui {
624*01826a49SYabin Cui     compressInstructions* const ci = (compressInstructions*) payload;
625*01826a49SYabin Cui     (void)dstCapacity;
626*01826a49SYabin Cui 
627*01826a49SYabin Cui     ZSTD_CCtx_refCDict(ci->cctx, ci->dictionaries.cdicts[ci->dictNb]);
628*01826a49SYabin Cui     ZSTD_compress2(ci->cctx,
629*01826a49SYabin Cui             dst, srcSize,
630*01826a49SYabin Cui             src, srcSize);
631*01826a49SYabin Cui 
632*01826a49SYabin Cui     ci->dictNb = ci->dictNb + 1;
633*01826a49SYabin Cui     if (ci->dictNb >= ci->nbDicts) ci->dictNb = 0;
634*01826a49SYabin Cui 
635*01826a49SYabin Cui     return srcSize;
636*01826a49SYabin Cui }
637*01826a49SYabin Cui 
638*01826a49SYabin Cui /* benched function */
decompress(const void * src,size_t srcSize,void * dst,size_t dstCapacity,void * payload)639*01826a49SYabin Cui size_t decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* payload)
640*01826a49SYabin Cui {
641*01826a49SYabin Cui     decompressInstructions* const di = (decompressInstructions*) payload;
642*01826a49SYabin Cui 
643*01826a49SYabin Cui     size_t const result = ZSTD_decompress_usingDDict(di->dctx,
644*01826a49SYabin Cui                                         dst, dstCapacity,
645*01826a49SYabin Cui                                         src, srcSize,
646*01826a49SYabin Cui                                         di->dictionaries.ddicts[di->dictNb]);
647*01826a49SYabin Cui 
648*01826a49SYabin Cui     di->dictNb = di->dictNb + 1;
649*01826a49SYabin Cui     if (di->dictNb >= di->nbDicts) di->dictNb = 0;
650*01826a49SYabin Cui 
651*01826a49SYabin Cui     return result;
652*01826a49SYabin Cui }
653*01826a49SYabin Cui 
654*01826a49SYabin Cui typedef enum {
655*01826a49SYabin Cui   fastest = 0,
656*01826a49SYabin Cui   median = 1,
657*01826a49SYabin Cui } metricAggregatePref_e;
658*01826a49SYabin Cui 
659*01826a49SYabin Cui /* compareFunction() :
660*01826a49SYabin Cui  * Sort input in decreasing order when used with qsort() */
compareFunction(const void * a,const void * b)661*01826a49SYabin Cui int compareFunction(const void *a, const void *b)
662*01826a49SYabin Cui {
663*01826a49SYabin Cui   double x = *(const double *)a;
664*01826a49SYabin Cui   double y = *(const double *)b;
665*01826a49SYabin Cui   if (x < y)
666*01826a49SYabin Cui     return 1;
667*01826a49SYabin Cui   else if (x > y)
668*01826a49SYabin Cui     return -1;
669*01826a49SYabin Cui   return 0;
670*01826a49SYabin Cui }
671*01826a49SYabin Cui 
aggregateData(double * data,size_t size,metricAggregatePref_e metricAggregatePref)672*01826a49SYabin Cui double aggregateData(double *data, size_t size,
673*01826a49SYabin Cui                      metricAggregatePref_e metricAggregatePref)
674*01826a49SYabin Cui {
675*01826a49SYabin Cui   qsort(data, size, sizeof(*data), compareFunction);
676*01826a49SYabin Cui   if (metricAggregatePref == fastest)
677*01826a49SYabin Cui     return data[0];
678*01826a49SYabin Cui   else /* median */
679*01826a49SYabin Cui     return (data[(size - 1) / 2] + data[size / 2]) / 2;
680*01826a49SYabin Cui }
681*01826a49SYabin Cui 
benchMem(slice_collection_t dstBlocks,slice_collection_t srcBlocks,ddict_collection_t ddictionaries,cdict_collection_t cdictionaries,unsigned nbRounds,int benchCompression,const char * exeName,ZSTD_CCtx_params * cctxParams,metricAggregatePref_e metricAggregatePref)682*01826a49SYabin Cui static int benchMem(slice_collection_t dstBlocks, slice_collection_t srcBlocks,
683*01826a49SYabin Cui                     ddict_collection_t ddictionaries,
684*01826a49SYabin Cui                     cdict_collection_t cdictionaries, unsigned nbRounds,
685*01826a49SYabin Cui                     int benchCompression, const char *exeName,
686*01826a49SYabin Cui                     ZSTD_CCtx_params *cctxParams,
687*01826a49SYabin Cui                     metricAggregatePref_e metricAggregatePref)
688*01826a49SYabin Cui {
689*01826a49SYabin Cui     assert(dstBlocks.nbSlices == srcBlocks.nbSlices);
690*01826a49SYabin Cui     if (benchCompression) assert(cctxParams);
691*01826a49SYabin Cui 
692*01826a49SYabin Cui     unsigned const ms_per_round = RUN_TIME_DEFAULT_MS;
693*01826a49SYabin Cui     unsigned const total_time_ms = nbRounds * ms_per_round;
694*01826a49SYabin Cui 
695*01826a49SYabin Cui     double *const speedPerRound = (double *)malloc(nbRounds * sizeof(double));
696*01826a49SYabin Cui 
697*01826a49SYabin Cui     BMK_timedFnState_t* const benchState =
698*01826a49SYabin Cui             BMK_createTimedFnState(total_time_ms, ms_per_round);
699*01826a49SYabin Cui 
700*01826a49SYabin Cui     decompressInstructions di = createDecompressInstructions(ddictionaries);
701*01826a49SYabin Cui     compressInstructions ci =
702*01826a49SYabin Cui         createCompressInstructions(cdictionaries, cctxParams);
703*01826a49SYabin Cui     void* payload = benchCompression ? (void*)&ci : (void*)&di;
704*01826a49SYabin Cui     BMK_benchParams_t const bp = {
705*01826a49SYabin Cui         .benchFn = benchCompression ? compress : decompress,
706*01826a49SYabin Cui         .benchPayload = payload,
707*01826a49SYabin Cui         .initFn = NULL,
708*01826a49SYabin Cui         .initPayload = NULL,
709*01826a49SYabin Cui         .errorFn = ZSTD_isError,
710*01826a49SYabin Cui         .blockCount = dstBlocks.nbSlices,
711*01826a49SYabin Cui         .srcBuffers = (const void* const*) srcBlocks.slicePtrs,
712*01826a49SYabin Cui         .srcSizes = srcBlocks.capacities,
713*01826a49SYabin Cui         .dstBuffers = dstBlocks.slicePtrs,
714*01826a49SYabin Cui         .dstCapacities = dstBlocks.capacities,
715*01826a49SYabin Cui         .blockResults = NULL
716*01826a49SYabin Cui     };
717*01826a49SYabin Cui 
718*01826a49SYabin Cui     size_t roundNb = 0;
719*01826a49SYabin Cui     for (;;) {
720*01826a49SYabin Cui         BMK_runOutcome_t const outcome = BMK_benchTimedFn(benchState, bp);
721*01826a49SYabin Cui         CONTROL(BMK_isSuccessful_runOutcome(outcome));
722*01826a49SYabin Cui 
723*01826a49SYabin Cui         BMK_runTime_t const result = BMK_extract_runTime(outcome);
724*01826a49SYabin Cui         double const dTime_ns = result.nanoSecPerRun;
725*01826a49SYabin Cui         double const dTime_sec = (double)dTime_ns / 1000000000;
726*01826a49SYabin Cui         size_t const srcSize = result.sumOfReturn;
727*01826a49SYabin Cui         double const speed_MBps = (double)srcSize / dTime_sec / (1 MB);
728*01826a49SYabin Cui         speedPerRound[roundNb] = speed_MBps;
729*01826a49SYabin Cui         if (benchCompression)
730*01826a49SYabin Cui             DISPLAY("Compression Speed : %.1f MB/s \r", speed_MBps);
731*01826a49SYabin Cui         else
732*01826a49SYabin Cui             DISPLAY("Decompression Speed : %.1f MB/s \r", speed_MBps);
733*01826a49SYabin Cui 
734*01826a49SYabin Cui         fflush(stdout);
735*01826a49SYabin Cui         if (BMK_isCompleted_TimedFn(benchState)) break;
736*01826a49SYabin Cui         roundNb++;
737*01826a49SYabin Cui     }
738*01826a49SYabin Cui     DISPLAY("\n");
739*01826a49SYabin Cui     /* BMK_benchTimedFn may not run exactly nbRounds iterations */
740*01826a49SYabin Cui     double speedAggregated =
741*01826a49SYabin Cui         aggregateData(speedPerRound, roundNb + 1, metricAggregatePref);
742*01826a49SYabin Cui     if (metricAggregatePref == fastest)
743*01826a49SYabin Cui       DISPLAY("Fastest Speed : %.1f MB/s \n", speedAggregated);
744*01826a49SYabin Cui     else
745*01826a49SYabin Cui       DISPLAY("Median Speed : %.1f MB/s \n", speedAggregated);
746*01826a49SYabin Cui 
747*01826a49SYabin Cui     char* csvFileName = malloc(strlen(exeName) + 5);
748*01826a49SYabin Cui     strcpy(csvFileName, exeName);
749*01826a49SYabin Cui     strcat(csvFileName, ".csv");
750*01826a49SYabin Cui     FILE* csvFile = fopen(csvFileName, "r");
751*01826a49SYabin Cui     if (!csvFile) {
752*01826a49SYabin Cui         csvFile = fopen(csvFileName, "wt");
753*01826a49SYabin Cui         assert(csvFile);
754*01826a49SYabin Cui         fprintf(csvFile, "%s\n", exeName);
755*01826a49SYabin Cui         /* Print table headers */
756*01826a49SYabin Cui         fprintf(
757*01826a49SYabin Cui             csvFile,
758*01826a49SYabin Cui             "Compression/Decompression,Level,nbDicts,dictAttachPref,metricAggregatePref,Speed\n");
759*01826a49SYabin Cui     } else {
760*01826a49SYabin Cui         fclose(csvFile);
761*01826a49SYabin Cui         csvFile = fopen(csvFileName, "at");
762*01826a49SYabin Cui         assert(csvFile);
763*01826a49SYabin Cui     }
764*01826a49SYabin Cui 
765*01826a49SYabin Cui     int cLevel = -1;
766*01826a49SYabin Cui     int dictAttachPref = -1;
767*01826a49SYabin Cui     if (benchCompression) {
768*01826a49SYabin Cui       ZSTD_CCtxParams_getParameter(cctxParams, ZSTD_c_compressionLevel,
769*01826a49SYabin Cui                                    &cLevel);
770*01826a49SYabin Cui       ZSTD_CCtxParams_getParameter(cctxParams, ZSTD_c_forceAttachDict,
771*01826a49SYabin Cui                                    &dictAttachPref);
772*01826a49SYabin Cui     }
773*01826a49SYabin Cui     fprintf(csvFile, "%s,%d,%ld,%d,%d,%.1f\n",
774*01826a49SYabin Cui             benchCompression ? "Compression" : "Decompression", cLevel,
775*01826a49SYabin Cui             benchCompression ? ci.nbDicts : di.nbDicts, dictAttachPref,
776*01826a49SYabin Cui             metricAggregatePref, speedAggregated);
777*01826a49SYabin Cui     fclose(csvFile);
778*01826a49SYabin Cui     free(csvFileName);
779*01826a49SYabin Cui 
780*01826a49SYabin Cui     freeDecompressInstructions(di);
781*01826a49SYabin Cui     freeCompressInstructions(ci);
782*01826a49SYabin Cui     BMK_freeTimedFnState(benchState);
783*01826a49SYabin Cui 
784*01826a49SYabin Cui     return 0;   /* success */
785*01826a49SYabin Cui }
786*01826a49SYabin Cui 
787*01826a49SYabin Cui 
788*01826a49SYabin Cui /*! bench() :
789*01826a49SYabin Cui  *  fileName : file to load for benchmarking purpose
790*01826a49SYabin Cui  *  dictionary : optional (can be NULL), file to load as dictionary,
791*01826a49SYabin Cui  *              if none provided : will be calculated on the fly by the program.
792*01826a49SYabin Cui  * @return : 0 is success, 1+ otherwise */
bench(const char ** fileNameTable,unsigned nbFiles,const char * dictionary,size_t blockSize,int clevel,unsigned nbDictMax,unsigned nbBlocks,unsigned nbRounds,int benchCompression,ZSTD_dictContentType_e dictContentType,ZSTD_CCtx_params * cctxParams,const char * exeName,metricAggregatePref_e metricAggregatePref)793*01826a49SYabin Cui int bench(const char **fileNameTable, unsigned nbFiles, const char *dictionary,
794*01826a49SYabin Cui           size_t blockSize, int clevel, unsigned nbDictMax, unsigned nbBlocks,
795*01826a49SYabin Cui           unsigned nbRounds, int benchCompression,
796*01826a49SYabin Cui           ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params *cctxParams,
797*01826a49SYabin Cui           const char *exeName, metricAggregatePref_e metricAggregatePref)
798*01826a49SYabin Cui {
799*01826a49SYabin Cui     int result = 0;
800*01826a49SYabin Cui 
801*01826a49SYabin Cui     DISPLAYLEVEL(3, "loading %u files... \n", nbFiles);
802*01826a49SYabin Cui     buffer_collection_t const srcs = createBufferCollection_fromFiles(fileNameTable, nbFiles);
803*01826a49SYabin Cui     CONTROL(srcs.buffer.ptr != NULL);
804*01826a49SYabin Cui     buffer_t srcBuffer = srcs.buffer;
805*01826a49SYabin Cui     size_t const srcSize = srcBuffer.size;
806*01826a49SYabin Cui     DISPLAYLEVEL(3, "created src buffer of size %.1f MB \n",
807*01826a49SYabin Cui                     (double)srcSize / (1 MB));
808*01826a49SYabin Cui 
809*01826a49SYabin Cui     slice_collection_t const srcSlices = splitSlices(srcs.slices, blockSize, nbBlocks);
810*01826a49SYabin Cui     nbBlocks = (unsigned)(srcSlices.nbSlices);
811*01826a49SYabin Cui     DISPLAYLEVEL(3, "split input into %u blocks ", nbBlocks);
812*01826a49SYabin Cui     if (blockSize)
813*01826a49SYabin Cui         DISPLAYLEVEL(3, "of max size %u bytes ", (unsigned)blockSize);
814*01826a49SYabin Cui     DISPLAYLEVEL(3, "\n");
815*01826a49SYabin Cui     size_t const totalSrcSlicesSize = sliceCollection_totalCapacity(srcSlices);
816*01826a49SYabin Cui 
817*01826a49SYabin Cui 
818*01826a49SYabin Cui     size_t* const dstCapacities = malloc(nbBlocks * sizeof(*dstCapacities));
819*01826a49SYabin Cui     CONTROL(dstCapacities != NULL);
820*01826a49SYabin Cui     size_t dstBufferCapacity = 0;
821*01826a49SYabin Cui     for (size_t bnb=0; bnb<nbBlocks; bnb++) {
822*01826a49SYabin Cui         dstCapacities[bnb] = ZSTD_compressBound(srcSlices.capacities[bnb]);
823*01826a49SYabin Cui         dstBufferCapacity += dstCapacities[bnb];
824*01826a49SYabin Cui     }
825*01826a49SYabin Cui 
826*01826a49SYabin Cui     buffer_t dstBuffer = createBuffer(dstBufferCapacity);
827*01826a49SYabin Cui     CONTROL(dstBuffer.ptr != NULL);
828*01826a49SYabin Cui 
829*01826a49SYabin Cui     void** const sliceTable = malloc(nbBlocks * sizeof(*sliceTable));
830*01826a49SYabin Cui     CONTROL(sliceTable != NULL);
831*01826a49SYabin Cui 
832*01826a49SYabin Cui     {   char* const ptr = dstBuffer.ptr;
833*01826a49SYabin Cui         size_t pos = 0;
834*01826a49SYabin Cui         for (size_t snb=0; snb < nbBlocks; snb++) {
835*01826a49SYabin Cui             sliceTable[snb] = ptr + pos;
836*01826a49SYabin Cui             pos += dstCapacities[snb];
837*01826a49SYabin Cui     }   }
838*01826a49SYabin Cui 
839*01826a49SYabin Cui     slice_collection_t dstSlices;
840*01826a49SYabin Cui     dstSlices.capacities = dstCapacities;
841*01826a49SYabin Cui     dstSlices.slicePtrs = sliceTable;
842*01826a49SYabin Cui     dstSlices.nbSlices = nbBlocks;
843*01826a49SYabin Cui 
844*01826a49SYabin Cui 
845*01826a49SYabin Cui     /* dictionary determination */
846*01826a49SYabin Cui     buffer_t const dictBuffer = createDictionaryBuffer(dictionary,
847*01826a49SYabin Cui                                 srcs.buffer.ptr,
848*01826a49SYabin Cui                                 srcSlices.capacities, srcSlices.nbSlices,
849*01826a49SYabin Cui                                 DICTSIZE);
850*01826a49SYabin Cui     CONTROL(dictBuffer.ptr != NULL);
851*01826a49SYabin Cui 
852*01826a49SYabin Cui     ZSTD_CDict* const cdict = ZSTD_createCDict_advanced2(dictBuffer.ptr, dictBuffer.size, DICT_LOAD_METHOD, dictContentType, cctxParams, ZSTD_defaultCMem);
853*01826a49SYabin Cui     CONTROL(cdict != NULL);
854*01826a49SYabin Cui 
855*01826a49SYabin Cui     size_t const cTotalSizeNoDict = compressBlocks(NULL, dstSlices, srcSlices, NULL, clevel);
856*01826a49SYabin Cui     CONTROL(cTotalSizeNoDict != 0);
857*01826a49SYabin Cui     DISPLAYLEVEL(3, "compressing at level %u without dictionary : Ratio=%.2f  (%u bytes) \n",
858*01826a49SYabin Cui                     clevel,
859*01826a49SYabin Cui                     (double)totalSrcSlicesSize / (double)cTotalSizeNoDict, (unsigned)cTotalSizeNoDict);
860*01826a49SYabin Cui 
861*01826a49SYabin Cui     size_t* const cSizes = malloc(nbBlocks * sizeof(size_t));
862*01826a49SYabin Cui     CONTROL(cSizes != NULL);
863*01826a49SYabin Cui 
864*01826a49SYabin Cui     size_t const cTotalSize = compressBlocks(cSizes, dstSlices, srcSlices, cdict, clevel);
865*01826a49SYabin Cui     CONTROL(cTotalSize != 0);
866*01826a49SYabin Cui     DISPLAYLEVEL(3, "compressed using a %u bytes dictionary : Ratio=%.2f  (%u bytes) \n",
867*01826a49SYabin Cui                     (unsigned)dictBuffer.size,
868*01826a49SYabin Cui                     (double)totalSrcSlicesSize / (double)cTotalSize, (unsigned)cTotalSize);
869*01826a49SYabin Cui 
870*01826a49SYabin Cui     /* now dstSlices contain the real compressed size of each block, instead of the maximum capacity */
871*01826a49SYabin Cui     shrinkSizes(dstSlices, cSizes);
872*01826a49SYabin Cui 
873*01826a49SYabin Cui     unsigned const nbDicts = nbDictMax ? nbDictMax : nbBlocks;
874*01826a49SYabin Cui 
875*01826a49SYabin Cui     cdict_collection_t const cdictionaries = createCDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts, dictContentType, cctxParams);
876*01826a49SYabin Cui     CONTROL(cdictionaries.cdicts != NULL);
877*01826a49SYabin Cui 
878*01826a49SYabin Cui     ddict_collection_t const ddictionaries = createDDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts);
879*01826a49SYabin Cui     CONTROL(ddictionaries.ddicts != NULL);
880*01826a49SYabin Cui 
881*01826a49SYabin Cui     if (benchCompression) {
882*01826a49SYabin Cui         size_t const dictMem = ZSTD_sizeof_CDict(cdictionaries.cdicts[0]);
883*01826a49SYabin Cui         size_t const allDictMem = dictMem * nbDicts;
884*01826a49SYabin Cui         DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
885*01826a49SYabin Cui                         nbDicts, (double)allDictMem / (1 MB));
886*01826a49SYabin Cui 
887*01826a49SYabin Cui         shuffleCDictionaries(cdictionaries);
888*01826a49SYabin Cui 
889*01826a49SYabin Cui         buffer_collection_t resultCollection = createBufferCollection_fromSliceCollection(srcSlices);
890*01826a49SYabin Cui         CONTROL(resultCollection.buffer.ptr != NULL);
891*01826a49SYabin Cui 
892*01826a49SYabin Cui         result = benchMem(dstSlices, resultCollection.slices, ddictionaries,
893*01826a49SYabin Cui                           cdictionaries, nbRounds, benchCompression, exeName,
894*01826a49SYabin Cui                           cctxParams, metricAggregatePref);
895*01826a49SYabin Cui 
896*01826a49SYabin Cui         freeBufferCollection(resultCollection);
897*01826a49SYabin Cui     } else {
898*01826a49SYabin Cui         size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, DICT_LOAD_METHOD);
899*01826a49SYabin Cui         size_t const allDictMem = dictMem * nbDicts;
900*01826a49SYabin Cui         DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
901*01826a49SYabin Cui                         nbDicts, (double)allDictMem / (1 MB));
902*01826a49SYabin Cui 
903*01826a49SYabin Cui         shuffleDDictionaries(ddictionaries);
904*01826a49SYabin Cui 
905*01826a49SYabin Cui         buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
906*01826a49SYabin Cui         CONTROL(resultCollection.buffer.ptr != NULL);
907*01826a49SYabin Cui 
908*01826a49SYabin Cui         result = benchMem(resultCollection.slices, dstSlices, ddictionaries,
909*01826a49SYabin Cui                           cdictionaries, nbRounds, benchCompression, exeName,
910*01826a49SYabin Cui                           NULL, metricAggregatePref);
911*01826a49SYabin Cui 
912*01826a49SYabin Cui         freeBufferCollection(resultCollection);
913*01826a49SYabin Cui     }
914*01826a49SYabin Cui 
915*01826a49SYabin Cui     /* free all heap objects in reverse order */
916*01826a49SYabin Cui     freeCDictCollection(cdictionaries);
917*01826a49SYabin Cui     freeDDictCollection(ddictionaries);
918*01826a49SYabin Cui     free(cSizes);
919*01826a49SYabin Cui     ZSTD_freeCDict(cdict);
920*01826a49SYabin Cui     freeBuffer(dictBuffer);
921*01826a49SYabin Cui     freeSliceCollection(dstSlices);
922*01826a49SYabin Cui     freeBuffer(dstBuffer);
923*01826a49SYabin Cui     freeSliceCollection(srcSlices);
924*01826a49SYabin Cui     freeBufferCollection(srcs);
925*01826a49SYabin Cui 
926*01826a49SYabin Cui     return result;
927*01826a49SYabin Cui }
928*01826a49SYabin Cui 
929*01826a49SYabin Cui 
930*01826a49SYabin Cui 
931*01826a49SYabin Cui /* ---  Command Line  --- */
932*01826a49SYabin Cui 
933*01826a49SYabin Cui /*! readU32FromChar() :
934*01826a49SYabin Cui  * @return : unsigned integer value read from input in `char` format.
935*01826a49SYabin Cui  *  allows and interprets K, KB, KiB, M, MB and MiB suffix.
936*01826a49SYabin Cui  *  Will also modify `*stringPtr`, advancing it to position where it stopped reading.
937*01826a49SYabin Cui  *  Note : function will exit() program if digit sequence overflows */
readU32FromChar(const char ** stringPtr)938*01826a49SYabin Cui static unsigned readU32FromChar(const char** stringPtr)
939*01826a49SYabin Cui {
940*01826a49SYabin Cui     unsigned result = 0;
941*01826a49SYabin Cui     while ((**stringPtr >='0') && (**stringPtr <='9')) {
942*01826a49SYabin Cui         unsigned const max = (((unsigned)(-1)) / 10) - 1;
943*01826a49SYabin Cui         assert(result <= max);   /* check overflow */
944*01826a49SYabin Cui         result *= 10, result += (unsigned)**stringPtr - '0', (*stringPtr)++ ;
945*01826a49SYabin Cui     }
946*01826a49SYabin Cui     if ((**stringPtr=='K') || (**stringPtr=='M')) {
947*01826a49SYabin Cui         unsigned const maxK = ((unsigned)(-1)) >> 10;
948*01826a49SYabin Cui         assert(result <= maxK);   /* check overflow */
949*01826a49SYabin Cui         result <<= 10;
950*01826a49SYabin Cui         if (**stringPtr=='M') {
951*01826a49SYabin Cui             assert(result <= maxK);   /* check overflow */
952*01826a49SYabin Cui             result <<= 10;
953*01826a49SYabin Cui         }
954*01826a49SYabin Cui         (*stringPtr)++;  /* skip `K` or `M` */
955*01826a49SYabin Cui         if (**stringPtr=='i') (*stringPtr)++;
956*01826a49SYabin Cui         if (**stringPtr=='B') (*stringPtr)++;
957*01826a49SYabin Cui     }
958*01826a49SYabin Cui     return result;
959*01826a49SYabin Cui }
960*01826a49SYabin Cui 
961*01826a49SYabin Cui /** longCommandWArg() :
962*01826a49SYabin Cui  *  check if *stringPtr is the same as longCommand.
963*01826a49SYabin Cui  *  If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
964*01826a49SYabin Cui  * @return 0 and doesn't modify *stringPtr otherwise.
965*01826a49SYabin Cui  */
longCommandWArg(const char ** stringPtr,const char * longCommand)966*01826a49SYabin Cui static int longCommandWArg(const char** stringPtr, const char* longCommand)
967*01826a49SYabin Cui {
968*01826a49SYabin Cui     size_t const comSize = strlen(longCommand);
969*01826a49SYabin Cui     int const result = !strncmp(*stringPtr, longCommand, comSize);
970*01826a49SYabin Cui     if (result) *stringPtr += comSize;
971*01826a49SYabin Cui     return result;
972*01826a49SYabin Cui }
973*01826a49SYabin Cui 
974*01826a49SYabin Cui 
usage(const char * exeName)975*01826a49SYabin Cui int usage(const char* exeName)
976*01826a49SYabin Cui {
977*01826a49SYabin Cui     DISPLAY (" \n");
978*01826a49SYabin Cui     DISPLAY (" %s [Options] filename(s) \n", exeName);
979*01826a49SYabin Cui     DISPLAY (" \n");
980*01826a49SYabin Cui     DISPLAY ("Options : \n");
981*01826a49SYabin Cui     DISPLAY ("-z          : benchmark compression (default) \n");
982*01826a49SYabin Cui     DISPLAY ("-d          : benchmark decompression \n");
983*01826a49SYabin Cui     DISPLAY ("-r          : recursively load all files in subdirectories (default: off) \n");
984*01826a49SYabin Cui     DISPLAY ("-B#         : split input into blocks of size # (default: no split) \n");
985*01826a49SYabin Cui     DISPLAY ("-#          : use compression level # (default: %u) \n", CLEVEL_DEFAULT);
986*01826a49SYabin Cui     DISPLAY ("-D #        : use # as a dictionary (default: create one) \n");
987*01826a49SYabin Cui     DISPLAY ("-i#         : nb benchmark rounds (default: %u) \n", BENCH_TIME_DEFAULT_S);
988*01826a49SYabin Cui     DISPLAY ("-p#         : print speed for all rounds 0=fastest 1=median (default: 0) \n");
989*01826a49SYabin Cui     DISPLAY ("--nbBlocks=#: use # blocks for bench (default: one per file) \n");
990*01826a49SYabin Cui     DISPLAY ("--nbDicts=# : create # dictionaries for bench (default: one per block) \n");
991*01826a49SYabin Cui     DISPLAY ("-h          : help (this text) \n");
992*01826a49SYabin Cui     DISPLAY (" \n");
993*01826a49SYabin Cui     DISPLAY ("Advanced Options (see zstd.h for documentation) : \n");
994*01826a49SYabin Cui     DISPLAY ("--dedicated-dict-search\n");
995*01826a49SYabin Cui     DISPLAY ("--dict-content-type=#\n");
996*01826a49SYabin Cui     DISPLAY ("--dict-attach-pref=#\n");
997*01826a49SYabin Cui     return 0;
998*01826a49SYabin Cui }
999*01826a49SYabin Cui 
bad_usage(const char * exeName)1000*01826a49SYabin Cui int bad_usage(const char* exeName)
1001*01826a49SYabin Cui {
1002*01826a49SYabin Cui     DISPLAY (" bad usage : \n");
1003*01826a49SYabin Cui     usage(exeName);
1004*01826a49SYabin Cui     return 1;
1005*01826a49SYabin Cui }
1006*01826a49SYabin Cui 
main(int argc,const char ** argv)1007*01826a49SYabin Cui int main (int argc, const char** argv)
1008*01826a49SYabin Cui {
1009*01826a49SYabin Cui     int recursiveMode = 0;
1010*01826a49SYabin Cui     int benchCompression = 1;
1011*01826a49SYabin Cui     int dedicatedDictSearch = 0;
1012*01826a49SYabin Cui     unsigned nbRounds = BENCH_TIME_DEFAULT_S;
1013*01826a49SYabin Cui     const char* const exeName = argv[0];
1014*01826a49SYabin Cui 
1015*01826a49SYabin Cui     if (argc < 2) return bad_usage(exeName);
1016*01826a49SYabin Cui 
1017*01826a49SYabin Cui     const char** nameTable = (const char**)malloc((size_t)argc * sizeof(const char*));
1018*01826a49SYabin Cui     assert(nameTable != NULL);
1019*01826a49SYabin Cui     unsigned nameIdx = 0;
1020*01826a49SYabin Cui 
1021*01826a49SYabin Cui     const char* dictionary = NULL;
1022*01826a49SYabin Cui     int cLevel = CLEVEL_DEFAULT;
1023*01826a49SYabin Cui     size_t blockSize = BLOCKSIZE_DEFAULT;
1024*01826a49SYabin Cui     unsigned nbDicts = 0;  /* determine nbDicts automatically: 1 dictionary per block */
1025*01826a49SYabin Cui     unsigned nbBlocks = 0; /* determine nbBlocks automatically, from source and blockSize */
1026*01826a49SYabin Cui     ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
1027*01826a49SYabin Cui     ZSTD_dictAttachPref_e dictAttachPref = ZSTD_dictDefaultAttach;
1028*01826a49SYabin Cui     ZSTD_paramSwitch_e prefetchCDictTables = ZSTD_ps_auto;
1029*01826a49SYabin Cui     metricAggregatePref_e metricAggregatePref = fastest;
1030*01826a49SYabin Cui 
1031*01826a49SYabin Cui     for (int argNb = 1; argNb < argc ; argNb++) {
1032*01826a49SYabin Cui         const char* argument = argv[argNb];
1033*01826a49SYabin Cui         if (!strcmp(argument, "-h")) { free(nameTable); return usage(exeName); }
1034*01826a49SYabin Cui         if (!strcmp(argument, "-d")) { benchCompression = 0; continue; }
1035*01826a49SYabin Cui         if (!strcmp(argument, "-z")) { benchCompression = 1; continue; }
1036*01826a49SYabin Cui         if (!strcmp(argument, "-r")) { recursiveMode = 1; continue; }
1037*01826a49SYabin Cui         if (!strcmp(argument, "-D")) { argNb++; assert(argNb < argc); dictionary = argv[argNb]; continue; }
1038*01826a49SYabin Cui         if (longCommandWArg(&argument, "-i")) { nbRounds = readU32FromChar(&argument); continue; }
1039*01826a49SYabin Cui         if (longCommandWArg(&argument, "-p")) { metricAggregatePref = (int)readU32FromChar(&argument); continue;}
1040*01826a49SYabin Cui         if (longCommandWArg(&argument, "--dictionary=")) { dictionary = argument; continue; }
1041*01826a49SYabin Cui         if (longCommandWArg(&argument, "-B")) { blockSize = readU32FromChar(&argument); continue; }
1042*01826a49SYabin Cui         if (longCommandWArg(&argument, "--blockSize=")) { blockSize = readU32FromChar(&argument); continue; }
1043*01826a49SYabin Cui         if (longCommandWArg(&argument, "--nbDicts=")) { nbDicts = readU32FromChar(&argument); continue; }
1044*01826a49SYabin Cui         if (longCommandWArg(&argument, "--nbBlocks=")) { nbBlocks = readU32FromChar(&argument); continue; }
1045*01826a49SYabin Cui         if (longCommandWArg(&argument, "--clevel=")) { cLevel = (int)readU32FromChar(&argument); continue; }
1046*01826a49SYabin Cui         if (longCommandWArg(&argument, "--dedicated-dict-search")) { dedicatedDictSearch = 1; continue; }
1047*01826a49SYabin Cui         if (longCommandWArg(&argument, "--dict-content-type=")) { dictContentType = (int)readU32FromChar(&argument); continue; }
1048*01826a49SYabin Cui         if (longCommandWArg(&argument, "--dict-attach-pref=")) { dictAttachPref = (int)readU32FromChar(&argument); continue; }
1049*01826a49SYabin Cui         if (longCommandWArg(&argument, "--prefetch-cdict-tables=")) { prefetchCDictTables = (int)readU32FromChar(&argument); continue; }
1050*01826a49SYabin Cui         if (longCommandWArg(&argument, "-")) { cLevel = (int)readU32FromChar(&argument); continue; }
1051*01826a49SYabin Cui         /* anything that's not a command is a filename */
1052*01826a49SYabin Cui         nameTable[nameIdx++] = argument;
1053*01826a49SYabin Cui     }
1054*01826a49SYabin Cui 
1055*01826a49SYabin Cui     FileNamesTable* filenameTable;
1056*01826a49SYabin Cui 
1057*01826a49SYabin Cui     if (recursiveMode) {
1058*01826a49SYabin Cui #ifndef UTIL_HAS_CREATEFILELIST
1059*01826a49SYabin Cui         assert(0);   /* missing capability, do not run */
1060*01826a49SYabin Cui #endif
1061*01826a49SYabin Cui         filenameTable = UTIL_createExpandedFNT(nameTable, nameIdx, 1 /* follow_links */);
1062*01826a49SYabin Cui     } else {
1063*01826a49SYabin Cui         filenameTable = UTIL_assembleFileNamesTable(nameTable, nameIdx, NULL);
1064*01826a49SYabin Cui         nameTable = NULL;  /* UTIL_createFileNamesTable() takes ownership of nameTable */
1065*01826a49SYabin Cui     }
1066*01826a49SYabin Cui 
1067*01826a49SYabin Cui     ZSTD_CCtx_params* cctxParams = ZSTD_createCCtxParams();
1068*01826a49SYabin Cui     ZSTD_CCtxParams_init(cctxParams, cLevel);
1069*01826a49SYabin Cui     ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_enableDedicatedDictSearch, dedicatedDictSearch);
1070*01826a49SYabin Cui     ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_nbWorkers, 0);
1071*01826a49SYabin Cui     ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_forceAttachDict, dictAttachPref);
1072*01826a49SYabin Cui     ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_prefetchCDictTables, prefetchCDictTables);
1073*01826a49SYabin Cui 
1074*01826a49SYabin Cui     int result =
1075*01826a49SYabin Cui         bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize,
1076*01826a49SYabin Cui               dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds,
1077*01826a49SYabin Cui               benchCompression, dictContentType, cctxParams, exeName,
1078*01826a49SYabin Cui               metricAggregatePref);
1079*01826a49SYabin Cui 
1080*01826a49SYabin Cui     UTIL_freeFileNamesTable(filenameTable);
1081*01826a49SYabin Cui     free(nameTable);
1082*01826a49SYabin Cui     ZSTD_freeCCtxParams(cctxParams);
1083*01826a49SYabin Cui 
1084*01826a49SYabin Cui     return result;
1085*01826a49SYabin Cui }
1086