1 /*
2     bench.c - Demo program to benchmark open-source compression algorithms
3     Copyright (C) Yann Collet 2012-2015
4 
5     GPL v2 License
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write to the Free Software Foundation, Inc.,
19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21     You can contact the author at :
22     - LZ4 source repository : https://github.com/Cyan4973/lz4
23     - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
24 */
25 
26 /**************************************
27 *  Compiler Options
28 ***************************************/
29 #if defined(_MSC_VER) || defined(_WIN32)
30 #  define _CRT_SECURE_NO_WARNINGS
31 #  define _CRT_SECURE_NO_DEPRECATE     /* VS2005 */
32 #  define BMK_LEGACY_TIMER 1           /* S_ISREG & gettimeofday() are not supported by MSVC */
33 #endif
34 
35 /* Unix Large Files support (>4GB) */
36 #define _FILE_OFFSET_BITS 64
37 #if (defined(__sun__) && (!defined(__LP64__)))   /* Sun Solaris 32-bits requires specific definitions */
38 #  define _LARGEFILE_SOURCE
39 #elif ! defined(__LP64__)                        /* No point defining Large file for 64 bit */
40 #  define _LARGEFILE64_SOURCE
41 #endif
42 
43 
44 /**************************************
45 *  Includes
46 ***************************************/
47 #include <stdlib.h>      /* malloc */
48 #include <stdio.h>       /* fprintf, fopen, ftello64 */
49 #include <sys/types.h>   /* stat64 */
50 #include <sys/stat.h>    /* stat64 */
51 
52 /* Use ftime() if gettimeofday() is not available on your target */
53 #if defined(BMK_LEGACY_TIMER)
54 #  include <sys/timeb.h>   /* timeb, ftime */
55 #else
56 #  include <sys/time.h>    /* gettimeofday */
57 #endif
58 
59 #include "lz4.h"
60 #define COMPRESSOR0 LZ4_compress_local
LZ4_compress_local(const char * src,char * dst,int srcSize,int dstSize,int clevel)61 static int LZ4_compress_local(const char* src, char* dst, int srcSize, int dstSize, int clevel) { (void)clevel; return LZ4_compress_default(src, dst, srcSize, dstSize); }
62 #include "lz4hc.h"
63 #define COMPRESSOR1 LZ4_compress_HC
64 #define DEFAULTCOMPRESSOR COMPRESSOR0
65 
66 #include "xxhash.h"
67 
68 
69 /**************************************
70 *  Compiler specifics
71 ***************************************/
72 #if !defined(S_ISREG)
73 #  define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
74 #endif
75 
76 
77 /**************************************
78 *  Basic Types
79 ***************************************/
80 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
81 # include <stdint.h>
82   typedef uint8_t  BYTE;
83   typedef uint16_t U16;
84   typedef uint32_t U32;
85   typedef  int32_t S32;
86   typedef uint64_t U64;
87 #else
88   typedef unsigned char       BYTE;
89   typedef unsigned short      U16;
90   typedef unsigned int        U32;
91   typedef   signed int        S32;
92   typedef unsigned long long  U64;
93 #endif
94 
95 
96 /**************************************
97 *  Constants
98 ***************************************/
99 #define NBLOOPS    3
100 #define TIMELOOP   2000
101 
102 #define KB *(1 <<10)
103 #define MB *(1 <<20)
104 #define GB *(1U<<30)
105 
106 #define MAX_MEM             (2 GB - 64 MB)
107 #define DEFAULT_CHUNKSIZE   (4 MB)
108 
109 
110 /**************************************
111 *  Local structures
112 ***************************************/
113 struct chunkParameters
114 {
115     U32   id;
116     char* origBuffer;
117     char* compressedBuffer;
118     int   origSize;
119     int   compressedSize;
120 };
121 
122 struct compressionParameters
123 {
124     int (*compressionFunction)(const char* src, char* dst, int srcSize, int dstSize, int cLevel);
125     int (*decompressionFunction)(const char* src, char* dst, int dstSize);
126 };
127 
128 
129 /**************************************
130 *  MACRO
131 ***************************************/
132 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
133 
134 
135 /**************************************
136 *  Benchmark Parameters
137 ***************************************/
138 static int chunkSize = DEFAULT_CHUNKSIZE;
139 static int nbIterations = NBLOOPS;
140 static int BMK_pause = 0;
141 
BMK_setBlocksize(int bsize)142 void BMK_setBlocksize(int bsize) { chunkSize = bsize; }
143 
BMK_setNbIterations(int nbLoops)144 void BMK_setNbIterations(int nbLoops)
145 {
146     nbIterations = nbLoops;
147     DISPLAY("- %i iterations -\n", nbIterations);
148 }
149 
BMK_setPause(void)150 void BMK_setPause(void) { BMK_pause = 1; }
151 
152 
153 /*********************************************************
154 *  Private functions
155 **********************************************************/
156 
157 #if defined(BMK_LEGACY_TIMER)
158 
BMK_GetMilliStart(void)159 static int BMK_GetMilliStart(void)
160 {
161   /* Based on Legacy ftime()
162      Rolls over every ~ 12.1 days (0x100000/24/60/60)
163      Use GetMilliSpan to correct for rollover */
164   struct timeb tb;
165   int nCount;
166   ftime( &tb );
167   nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000);
168   return nCount;
169 }
170 
171 #else
172 
BMK_GetMilliStart(void)173 static int BMK_GetMilliStart(void)
174 {
175   /* Based on newer gettimeofday()
176      Use GetMilliSpan to correct for rollover */
177   struct timeval tv;
178   int nCount;
179   gettimeofday(&tv, NULL);
180   nCount = (int) (tv.tv_usec/1000 + (tv.tv_sec & 0xfffff) * 1000);
181   return nCount;
182 }
183 
184 #endif
185 
186 
BMK_GetMilliSpan(int nTimeStart)187 static int BMK_GetMilliSpan( int nTimeStart )
188 {
189   int nSpan = BMK_GetMilliStart() - nTimeStart;
190   if ( nSpan < 0 )
191     nSpan += 0x100000 * 1000;
192   return nSpan;
193 }
194 
195 
BMK_findMaxMem(U64 requiredMem)196 static size_t BMK_findMaxMem(U64 requiredMem)
197 {
198     size_t step = 64 MB;
199     BYTE* testmem=NULL;
200 
201     requiredMem = (((requiredMem >> 26) + 1) << 26);
202     requiredMem += 2*step;
203     if (requiredMem > MAX_MEM) requiredMem = MAX_MEM;
204 
205     while (!testmem)
206     {
207         if (requiredMem > step) requiredMem -= step;
208         else requiredMem >>= 1;
209         testmem = (BYTE*) malloc ((size_t)requiredMem);
210     }
211     free (testmem);
212 
213     /* keep some space available */
214     if (requiredMem > step) requiredMem -= step;
215     else requiredMem >>= 1;
216 
217     return (size_t)requiredMem;
218 }
219 
220 
BMK_GetFileSize(const char * infilename)221 static U64 BMK_GetFileSize(const char* infilename)
222 {
223     int r;
224 #if defined(_MSC_VER)
225     struct _stat64 statbuf;
226     r = _stat64(infilename, &statbuf);
227 #else
228     struct stat statbuf;
229     r = stat(infilename, &statbuf);
230 #endif
231     if (r || !S_ISREG(statbuf.st_mode)) return 0;   /* No good... */
232     return (U64)statbuf.st_size;
233 }
234 
235 
236 /*********************************************************
237 *  Public function
238 **********************************************************/
239 
BMK_benchFiles(const char ** fileNamesTable,int nbFiles,int cLevel)240 int BMK_benchFiles(const char** fileNamesTable, int nbFiles, int cLevel)
241 {
242   int fileIdx=0;
243   char* orig_buff;
244   struct compressionParameters compP;
245   int cfunctionId;
246 
247   U64 totals = 0;
248   U64 totalz = 0;
249   double totalc = 0.;
250   double totald = 0.;
251 
252 
253   /* Init */
254   if (cLevel <= 3) cfunctionId = 0; else cfunctionId = 1;
255   switch (cfunctionId)
256   {
257 #ifdef COMPRESSOR0
258   case 0 : compP.compressionFunction = COMPRESSOR0; break;
259 #endif
260 #ifdef COMPRESSOR1
261   case 1 : compP.compressionFunction = COMPRESSOR1; break;
262 #endif
263   default : compP.compressionFunction = DEFAULTCOMPRESSOR;
264   }
265   compP.decompressionFunction = LZ4_decompress_fast;
266 
267   /* Loop for each file */
268   while (fileIdx<nbFiles)
269   {
270       FILE*  inFile;
271       const char*  inFileName;
272       U64    inFileSize;
273       size_t benchedSize;
274       int nbChunks;
275       int maxCompressedChunkSize;
276       size_t readSize;
277       char* compressedBuffer; int compressedBuffSize;
278       struct chunkParameters* chunkP;
279       U32 crcOrig;
280 
281       /* Check file existence */
282       inFileName = fileNamesTable[fileIdx++];
283       inFile = fopen( inFileName, "rb" );
284       if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; }
285 
286       /* Memory allocation & restrictions */
287       inFileSize = BMK_GetFileSize(inFileName);
288       if (inFileSize==0) { DISPLAY( "file is empty\n"); fclose(inFile); return 11; }
289       benchedSize = (size_t) BMK_findMaxMem(inFileSize * 2) / 2;
290       if (benchedSize==0) { DISPLAY( "not enough memory\n"); fclose(inFile); return 11; }
291       if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize;
292       if (benchedSize < inFileSize)
293       {
294           DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", inFileName, (int)(benchedSize>>20));
295       }
296 
297       /* Alloc */
298       chunkP = (struct chunkParameters*) malloc(((benchedSize / (size_t)chunkSize)+1) * sizeof(struct chunkParameters));
299       orig_buff = (char*)malloc((size_t)benchedSize);
300       nbChunks = (int) ((int)benchedSize / chunkSize) + 1;
301       maxCompressedChunkSize = LZ4_compressBound(chunkSize);
302       compressedBuffSize = nbChunks * maxCompressedChunkSize;
303       compressedBuffer = (char*)malloc((size_t)compressedBuffSize);
304 
305       if (!orig_buff || !compressedBuffer)
306       {
307         DISPLAY("\nError: not enough memory!\n");
308         free(orig_buff);
309         free(compressedBuffer);
310         free(chunkP);
311         fclose(inFile);
312         return 12;
313       }
314 
315       /* Init chunks data */
316       {
317           int i;
318           size_t remaining = benchedSize;
319           char* in = orig_buff;
320           char* out = compressedBuffer;
321           for (i=0; i<nbChunks; i++)
322           {
323               chunkP[i].id = i;
324               chunkP[i].origBuffer = in; in += chunkSize;
325               if ((int)remaining > chunkSize) { chunkP[i].origSize = chunkSize; remaining -= chunkSize; } else { chunkP[i].origSize = (int)remaining; remaining = 0; }
326               chunkP[i].compressedBuffer = out; out += maxCompressedChunkSize;
327               chunkP[i].compressedSize = 0;
328           }
329       }
330 
331       /* Fill input buffer */
332       DISPLAY("Loading %s...       \r", inFileName);
333       readSize = fread(orig_buff, 1, benchedSize, inFile);
334       fclose(inFile);
335 
336       if (readSize != benchedSize)
337       {
338         DISPLAY("\nError: problem reading file '%s' !!    \n", inFileName);
339         free(orig_buff);
340         free(compressedBuffer);
341         free(chunkP);
342         return 13;
343       }
344 
345       /* Calculating input Checksum */
346       crcOrig = XXH32(orig_buff, (unsigned int)benchedSize,0);
347 
348 
349       /* Bench */
350       {
351         int loopNb, chunkNb;
352         size_t cSize=0;
353         double fastestC = 100000000., fastestD = 100000000.;
354         double ratio=0.;
355         U32 crcCheck=0;
356 
357         DISPLAY("\r%79s\r", "");
358         for (loopNb = 1; loopNb <= nbIterations; loopNb++)
359         {
360           int nbLoops;
361           int milliTime;
362 
363           /* Compression */
364           DISPLAY("%1i-%-14.14s : %9i ->\r", loopNb, inFileName, (int)benchedSize);
365           { size_t i; for (i=0; i<benchedSize; i++) compressedBuffer[i]=(char)i; }     /* warmimg up memory */
366 
367           nbLoops = 0;
368           milliTime = BMK_GetMilliStart();
369           while(BMK_GetMilliStart() == milliTime);
370           milliTime = BMK_GetMilliStart();
371           while(BMK_GetMilliSpan(milliTime) < TIMELOOP)
372           {
373             for (chunkNb=0; chunkNb<nbChunks; chunkNb++)
374                 chunkP[chunkNb].compressedSize = compP.compressionFunction(chunkP[chunkNb].origBuffer, chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origSize, maxCompressedChunkSize, cLevel);
375             nbLoops++;
376           }
377           milliTime = BMK_GetMilliSpan(milliTime);
378 
379           nbLoops += !nbLoops;   /* avoid division by zero */
380           if ((double)milliTime < fastestC*nbLoops) fastestC = (double)milliTime/nbLoops;
381           cSize=0; for (chunkNb=0; chunkNb<nbChunks; chunkNb++) cSize += chunkP[chunkNb].compressedSize;
382           ratio = (double)cSize/(double)benchedSize*100.;
383 
384           DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s\r", loopNb, inFileName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000.);
385 
386           /* Decompression */
387           { size_t i; for (i=0; i<benchedSize; i++) orig_buff[i]=0; }     /* zeroing area, for CRC checking */
388 
389           nbLoops = 0;
390           milliTime = BMK_GetMilliStart();
391           while(BMK_GetMilliStart() == milliTime);
392           milliTime = BMK_GetMilliStart();
393           while(BMK_GetMilliSpan(milliTime) < TIMELOOP)
394           {
395             for (chunkNb=0; chunkNb<nbChunks; chunkNb++)
396                 chunkP[chunkNb].compressedSize = LZ4_decompress_fast(chunkP[chunkNb].compressedBuffer, chunkP[chunkNb].origBuffer, chunkP[chunkNb].origSize);
397             nbLoops++;
398           }
399           milliTime = BMK_GetMilliSpan(milliTime);
400 
401           nbLoops += !nbLoops;   /* avoid division by zero */
402           if ((double)milliTime < fastestD*nbLoops) fastestD = (double)milliTime/nbLoops;
403           DISPLAY("%1i-%-14.14s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s \r", loopNb, inFileName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);
404 
405           /* CRC Checking */
406           crcCheck = XXH32(orig_buff, (unsigned int)benchedSize,0);
407           if (crcOrig!=crcCheck) { DISPLAY("\n!!! WARNING !!! %14s : Invalid Checksum : %x != %x\n", inFileName, (unsigned)crcOrig, (unsigned)crcCheck); break; }
408         }
409 
410         if (crcOrig==crcCheck)
411         {
412             if (ratio<100.)
413                 DISPLAY("%-16.16s : %9i -> %9i (%5.2f%%),%7.1f MB/s ,%7.1f MB/s \n", inFileName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);
414             else
415                 DISPLAY("%-16.16s : %9i -> %9i (%5.1f%%),%7.1f MB/s ,%7.1f MB/s  \n", inFileName, (int)benchedSize, (int)cSize, ratio, (double)benchedSize / fastestC / 1000., (double)benchedSize / fastestD / 1000.);
416         }
417         totals += benchedSize;
418         totalz += cSize;
419         totalc += fastestC;
420         totald += fastestD;
421       }
422 
423       free(orig_buff);
424       free(compressedBuffer);
425       free(chunkP);
426   }
427 
428   if (nbFiles > 1)
429         DISPLAY("%-16.16s :%10llu ->%10llu (%5.2f%%), %6.1f MB/s , %6.1f MB/s\n", "  TOTAL", (long long unsigned int)totals, (long long unsigned int)totalz, (double)totalz/(double)totals*100., (double)totals/totalc/1000., (double)totals/totald/1000.);
430 
431   if (BMK_pause) { DISPLAY("\npress enter...\n"); (void)getchar(); }
432 
433   return 0;
434 }
435 
436 
437 
438