/* paramgrill.c - parameter tester for zstd Copyright (C) Yann Collet 2015-2016 GPL v2 License This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. You can contact the author at : - zstd homepage : http://www.zstd.net/ */ /*-************************************ * Compiler Options **************************************/ /* gettimeofday() are not supported by MSVC */ #if defined(_MSC_VER) || defined(_WIN32) # define BMK_LEGACY_TIMER 1 #endif /*-************************************ * Dependencies **************************************/ #include "util.h" /* Compiler options, UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ /* Use ftime() if gettimeofday() is not available on your target */ #if defined(BMK_LEGACY_TIMER) # include /* timeb, ftime */ #else # include /* gettimeofday */ #endif #include "mem.h" #include "zstd_static.h" #include "datagen.h" #include "xxhash.h" /*-************************************ * Constants **************************************/ #define PROGRAM_DESCRIPTION "ZSTD parameters tester" #ifndef ZSTD_VERSION # define ZSTD_VERSION "" #endif #define AUTHOR "Yann Collet" #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s (%s) ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION, (int)(sizeof(void*)*8), AUTHOR, __DATE__ #define KB *(1<<10) #define MB *(1<<20) #define GB *(1ULL<<30) #define NBLOOPS 2 #define TIMELOOP 2000 #define NB_LEVELS_TRACKED 30 static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); #define COMPRESSIBILITY_DEFAULT 0.50 static const size_t sampleSize = 10000000; static const int g_grillDuration = 50000000; /* about 13 hours */ static const int g_maxParamTime = 15000; /* 15 sec */ static const int g_maxVariationTime = 60000; /* 60 sec */ static const int g_maxNbVariations = 64; /*-************************************ * Macros **************************************/ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) /*-************************************ * Benchmark Parameters **************************************/ static U32 g_nbIterations = NBLOOPS; static double g_compressibility = COMPRESSIBILITY_DEFAULT; static U32 g_blockSize = 0; static U32 g_rand = 1; static U32 g_singleRun = 0; static U32 g_target = 0; static U32 g_noSeed = 0; static ZSTD_compressionParameters g_params = { 0, 0, 0, 0, 0, 0, ZSTD_greedy }; void BMK_SetNbIterations(int nbLoops) { g_nbIterations = nbLoops; DISPLAY("- %u iterations -\n", g_nbIterations); } /*-******************************************************* * Private functions *********************************************************/ #if defined(BMK_LEGACY_TIMER) static int BMK_GetMilliStart(void) { /* Based on Legacy ftime() * Rolls over every ~ 12.1 days (0x100000/24/60/60) * Use GetMilliSpan to correct for rollover */ struct timeb tb; int nCount; ftime( &tb ); nCount = (int) (tb.millitm + (tb.time & 0xfffff) * 1000); return nCount; } #else static int BMK_GetMilliStart(void) { /* Based on newer gettimeofday() * Use GetMilliSpan to correct for rollover */ struct timeval tv; int nCount; gettimeofday(&tv, NULL); nCount = (int) (tv.tv_usec/1000 + (tv.tv_sec & 0xfffff) * 1000); return nCount; } #endif static int BMK_GetMilliSpan( int nTimeStart ) { int nSpan = BMK_GetMilliStart() - nTimeStart; if ( nSpan < 0 ) nSpan += 0x100000 * 1000; return nSpan; } static size_t BMK_findMaxMem(U64 requiredMem) { size_t step = 64 MB; BYTE* testmem=NULL; requiredMem = (((requiredMem >> 26) + 1) << 26); if (requiredMem > maxMemory) requiredMem = maxMemory; requiredMem += 2*step; while (!testmem) { requiredMem -= step; testmem = (BYTE*) malloc ((size_t)requiredMem); } free (testmem); return (size_t) (requiredMem - step); } # define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r))) U32 FUZ_rand(U32* src) { const U32 prime1 = 2654435761U; const U32 prime2 = 2246822519U; U32 rand32 = *src; rand32 *= prime1; rand32 += prime2; rand32 = FUZ_rotl32(rand32, 13); *src = rand32; return rand32 >> 5; } /*-******************************************************* * Bench functions *********************************************************/ typedef struct { size_t cSize; U32 cSpeed; U32 dSpeed; } BMK_result_t; typedef struct { const char* srcPtr; size_t srcSize; char* cPtr; size_t cRoom; size_t cSize; char* resPtr; size_t resSize; } blockParam_t; #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) static size_t BMK_benchParam(BMK_result_t* resultPtr, const void* srcBuffer, size_t srcSize, ZSTD_CCtx* ctx, const ZSTD_compressionParameters cParams) { const size_t blockSize = g_blockSize ? g_blockSize : srcSize; const U32 nbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize); blockParam_t* const blockTable = (blockParam_t*) malloc(nbBlocks * sizeof(blockParam_t)); const size_t maxCompressedSize = (size_t)nbBlocks * ZSTD_compressBound(blockSize); void* const compressedBuffer = malloc(maxCompressedSize); void* const resultBuffer = malloc(srcSize); ZSTD_parameters params; U32 Wlog = cParams.windowLog; U32 Clog = cParams.chainLog; U32 Hlog = cParams.hashLog; U32 Slog = cParams.searchLog; U32 Slength = cParams.searchLength; U32 Tlength = cParams.targetLength; ZSTD_strategy strat = cParams.strategy; char name[30] = { 0 }; U64 crcOrig; /* Memory allocation & restrictions */ snprintf(name, 30, "Sw%02uc%02uh%02us%02ul%1ut%03uS%1u", Wlog, Clog, Hlog, Slog, Slength, Tlength, strat); if (!compressedBuffer || !resultBuffer || !blockTable) { DISPLAY("\nError: not enough memory!\n"); free(compressedBuffer); free(resultBuffer); free(blockTable); return 12; } /* Calculating input Checksum */ crcOrig = XXH64(srcBuffer, srcSize, 0); /* Init blockTable data */ { U32 i; size_t remaining = srcSize; const char* srcPtr = (const char*)srcBuffer; char* cPtr = (char*)compressedBuffer; char* resPtr = (char*)resultBuffer; for (i=0; i g_maxParamTime) break; /* Compression */ DISPLAY("\r%1u-%s : %9u ->", loopNb, name, (U32)srcSize); memset(compressedBuffer, 0xE5, maxCompressedSize); nbLoops = 0; milliTime = BMK_GetMilliStart(); while (BMK_GetMilliStart() == milliTime); milliTime = BMK_GetMilliStart(); while (BMK_GetMilliSpan(milliTime) < TIMELOOP) { for (blockNb=0; blockNb", loopNb, name, (U32)srcSize); DISPLAY(" %9u (%4.3f),%7.1f MB/s", (U32)cSize, ratio, (double)srcSize / fastestC / 1000.); resultPtr->cSize = cSize; resultPtr->cSpeed = (U32)((double)srcSize / fastestC); #if 1 /* Decompression */ memset(resultBuffer, 0xD6, srcSize); nbLoops = 0; milliTime = BMK_GetMilliStart(); while (BMK_GetMilliStart() == milliTime); milliTime = BMK_GetMilliStart(); for ( ; BMK_GetMilliSpan(milliTime) < TIMELOOP; nbLoops++) { for (blockNb=0; blockNb ", loopNb, name, (U32)srcSize); DISPLAY("%9u (%4.3f),%7.1f MB/s, ", (U32)cSize, ratio, (double)srcSize / fastestC / 1000.); DISPLAY("%7.1f MB/s", (double)srcSize / fastestD / 1000.); resultPtr->dSpeed = (U32)((double)srcSize / fastestD); /* CRC Checking */ crcCheck = XXH64(resultBuffer, srcSize, 0); if (crcOrig!=crcCheck) { unsigned u; unsigned eBlockSize = (unsigned)(MIN(65536*2, blockSize)); DISPLAY("\n!!! WARNING !!! Invalid Checksum : %x != %x\n", (unsigned)crcOrig, (unsigned)crcCheck); for (u=0; u