From 274b60e6e6571ccce5982e36ac82eabe606c1364 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 27 Aug 2018 17:08:44 -0700 Subject: [PATCH] largeNbDicts can compress and compare dict vs noDict --- contrib/largeNbDicts/Makefile | 2 + contrib/largeNbDicts/largeNbDicts | Bin 13626 -> 14034 bytes contrib/largeNbDicts/largeNbDicts.c | 144 +++++++++++++++++++++------- 3 files changed, 114 insertions(+), 32 deletions(-) diff --git a/contrib/largeNbDicts/Makefile b/contrib/largeNbDicts/Makefile index 082f0102a..026d76f12 100644 --- a/contrib/largeNbDicts/Makefile +++ b/contrib/largeNbDicts/Makefile @@ -21,6 +21,8 @@ CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) default: largeNbDicts +all : largeNbDicts + largeNbDicts: LDFLAGS += -lzstd largeNbDicts: largeNbDicts.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@ diff --git a/contrib/largeNbDicts/largeNbDicts b/contrib/largeNbDicts/largeNbDicts index 40416f050fa594cb085444ebb1ca433629d59318..c057a2b78aa551a2de831b6e304f8747a6ea3d0f 100755 GIT binary patch literal 14034 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 Wy#|t^{i|{0&vJJf{-%Wo#s33;RQ9z1 literal 13626 zcmeHOeQX@X6(8GiF|_0y%7;<;SWt`{L@&M)LlWZDm-t{Wtb>W2k3z!c&bJpIJ>OZo zdy0byGT0|&d%f00rHVw6D58ZTT3l3UYC`G~Ck_Y&*91rjAE6Df$A|cANcr&f_h#qL z=Szb6N7cV}(s4t;`D1>J@tkce7{iRS<4Ko!silTCRYtD1*MMaMSE)1v<_NM(Kduq29@6~j4 zDz~>&*c%ZB2vgjPDSPVd}BOCLu4k_n@#> zC7dE$T4wkPg{r0!n`4QH8cRfz9M9a}hc9&eYZpccm)j_{Pv>MIHc(Yt{hj_2jpW$j z2b9FXywHI~q3y;Q=9RFjZc0u^?d0}qs52F5dMrWO&pE`1qN<6wYHaBVC*x|bsUPQG zjpXtpF$yfXA}Uas>VZKXRl6j>}?Zc2$$NU^`0qoL?~{~Jyn7$uMM;YJB6~b zEN;M5me8d_Av*|nv0RMj0Pli^Vp9X!P}Jf+0k>m7zmKtvkkek)q1wPizr38W7U;hT zz8$r+(3DiaT`DZ&yD`4LOh#}vs)YJ2)cGkxpC6Bf=SQ~0BfwO?gG%$?f9TP%2d-ay z|DBKTcyq~$yB?kkSuH9%js7kzAd;Y9W&JdY;TDz_nxzGmbhN@*RTQHNF>|sj6rZ2$ zHRs=Am=W#>oFU%p^TMTAn0vXfbo}>@;8GUG!*g`%*kYJkk9V0Eg5G#)p^~?U5lxEz zJb@{?ZgC^vM!=1L8v!>0ZUo#2xDjw8;6}iWfExif0&WEUKM`0hTW|W;`q%hZ`#U@2 zf%ocU>l4}9Iw5BQU<3Ppr<+T(PF){C>~iyD$;VuAQf>!Gb zaO%7Ra>YU7Yl7DL12qEF5~vlRjzFCN5&=no1_BKNG!keOU?G8p0P?_7HS)mTW2v9Q zX;TVj7aRy?J6~W!vQ;Tt7w{f?(F48mPp96I7aiNYLY_B%t=0O~v1_eczdS~(QLF~V z+E}AleYNtySlx~O_5Kb1oBZmA-Ey}15G^^|beNOVZIF>HR%!nV`6j4$I_keV>X%4; z7pd=nIvS07Jr@oFGB$I6Z-1|Ww#52E$$WPb5e#Ow(j>pfaNnqGy=<@M)*qXg%I9V4 zbvpu>9`&TxBW2|IQ~ChJ_3T5⊀t=>WuXw%Gd$(c63C-seE9WFBkG=kvjVsk~?K z8+>@v7|sq&_B;|wIALvRsNRK}iuIZODwpA09WkX{l(E8TP^^y?>)-Y;RLY_c&DnH7 zehU!r9S!)7C|2GcAeHrNT7_agcF-Y*L12fL>>2zGY(;~f#NMbUTrE?~JnO65IlXoC zW6uw&JHaC|Pl60r*h^th&ZNnNY_-nCfll)fWwK;kwv@RwvNdb2oPDwntJ_5xFZ;eP zp~<94fQ$5?j5rS7Nh{8*-sZPn5}|(p6a1O_Q~Xy?*awgj{RVWw?+saG4}gkl}FXLFzKAt~;R&zFb|QY>v$vHl@jZTl3QX#06GXpPa9d<-@{ zYHki@+UUTpg8f~T(;T5~w5Onbg1B5QZG~s;Xw);$3Fd>$)(}ul+4!t+#Zbe9VI7jB!4`QjME~cvL}IA!qw}rDBAChoI3j*0gwIOhpC1u zutgL03Je{Al+K}Iy=Bh=M!+XlWS4wJ&e?vlZSBD`&og*7T*1LLFZUf1-48*62hgju zW9Dsgez&sd3;myp^{$+`%ASBy9@t$I%s-d;G&e~HG}|1e6P#`O87(4v`prNi zPtYP((coTJ8Igxk%OeZ+)*EL)E$m`9-=aGnAB%F>Ya{yfulZ`)NhmeUua-qYf z(B$iP@phEAPw{q)w|jZJkGIe8_F3NU=PljaeElSE>3-+yU*zpU-oDJ+SI{ci^V>lD z9r^lZ(4lVlz;bV4#PNSo?^Z zsAR-$f!^zjN^Q%~DXb;Bx*59gnH;YSuhZR-E^3xmu2vMrT{dafSfV#&qBWCJeM$;D z-ok88s85)Nbc?Bx0};a{*9%4fzjhMtFU=7_G-Am_NZ)c9d2CXQ%;T&(%Ra#%%)CiU zXnKe{3@?lJVw!Q8lrnhqL zqXFt+5$)Dij9Q+I$2GnL=2*FrY^@A$D24)WkEUN!6<${n#dCtc0ZUo#2xDjw8;6}iWfExif0{?Xc z=3HF2aSnQ)(W26a@R4N7>}A*MY^a4kt>vG_t6_ZIjkMDc8P)6Nw5T_<2A6lLrXGqV zuGW)1A}uwUO%`js< zNJ()`2l7y)Z7FD@x~3Iv$)(bwrS^g}W&|kdMdR1;<%SGH(@j-36C~RRS+pyjH24hN zgp$1)SM?I;#W(Y2lzR3Nq|_OK{t5^2QQJ7!tjA0ahY0qB;z)Q=*X;xiGi1&UuDq@_ zaK!=$S%r!(qGzc+-Ky57b)`%VzZ?irxP7H5zosx-+=FnlHI&AwV#D~3xsqq$lu$(F zDJFJ(6&v!B?n*TM^zkvu@TG^V?me}gXMa2ucyEG2d786PJz$3Mih=M&0@t47;0A%2 z$nSUsF8sRUDqxJGp}ed@TZ*G)xUUTVs0`m%h951%PnBWHaAQ86)B?+1E5o@m{81S` zR)#Aa=Zp4g%JA$mOfN9-SL}a58K$IfslFQXA@#<;wx?IIr=p&QN?*y-x8|p#dQfSH z_C;ZxD_@cPf&W;zX%Vq8CRPCr>Rsk0*Hqoapik zS;@68l{nYbksN>39r^SnpT?!*3Kttsu#ziX@;octYjLBCcUjzGA{tYp&b=*M*qJx= JxThgD;Xm-ofn)#x diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c index 749d9660d..536e45ffe 100644 --- a/contrib/largeNbDicts/largeNbDicts.c +++ b/contrib/largeNbDicts/largeNbDicts.c @@ -159,6 +159,33 @@ buffer_collection_t splitBuffer(buffer_t srcBuffer, size_t blockSize) } +/*--- dictionary creation ---*/ + +buffer_t createDictionary(const char* dictionary, + const void* srcBuffer, size_t* srcBlockSizes, unsigned nbBlocks) +{ + if (dictionary) { + DISPLAYLEVEL(3, "loading dictionary %s \n", dictionary); + return createBuffer_fromFile(dictionary); + } else { + DISPLAYLEVEL(3, "creating dictionary, of target size %u bytes \n", DICTSIZE); + void* const dictBuffer = malloc(DICTSIZE); + assert(dictBuffer != NULL); + + size_t const dictSize = ZDICT_trainFromBuffer(dictBuffer, DICTSIZE, + srcBuffer, + srcBlockSizes, + nbBlocks); + assert(!ZSTD_isError(dictSize)); + + buffer_t result; + result.ptr = dictBuffer; + result.capacity = DICTSIZE; + result.size = dictSize; + return result; + } +} + /*--- ddict_collection_t ---*/ @@ -181,6 +208,7 @@ static void freeDDictCollection(ddict_collection_t ddictc) static ddict_collection_t createDDictCollection(const void* dictBuffer, size_t dictSize, size_t nbDDict) { ZSTD_DDict** const ddicts = malloc(nbDDict * sizeof(ZSTD_DDict*)); + assert(ddicts != NULL); if (ddicts==NULL) return kNullDDictCollection; for (size_t dictNb=0; dictNb < nbDDict; dictNb++) { ddicts[dictNb] = ZSTD_createDDict(dictBuffer, dictSize); @@ -193,29 +221,64 @@ static ddict_collection_t createDDictCollection(const void* dictBuffer, size_t d } +/* --- Compression --- */ -/*--- Benchmark --- */ +/* compressBlocks() : + * @return : total compressed size of all blocks, + * or 0 if error. + */ +static size_t compressBlocks(buffer_collection_t dstBlockBuffers, buffer_collection_t srcBlockBuffers, ZSTD_CDict* cdict, int cLevel) +{ + size_t const nbBlocks = srcBlockBuffers.nbBuffers; + assert(dstBlockBuffers.nbBuffers == srcBlockBuffers.nbBuffers); + + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + assert(cctx != NULL); + + size_t totalCSize = 0; + for (size_t blockNb=0; blockNb < nbBlocks; blockNb++) { + size_t cBlockSize; + if (cdict == NULL) { + cBlockSize = ZSTD_compressCCtx(cctx, + dstBlockBuffers.buffers[blockNb], dstBlockBuffers.capacities[blockNb], + srcBlockBuffers.buffers[blockNb], srcBlockBuffers.capacities[blockNb], + cLevel); + assert(!ZSTD_isError(cBlockSize)); + } else { + cBlockSize = ZSTD_compress_usingCDict(cctx, + dstBlockBuffers.buffers[blockNb], dstBlockBuffers.capacities[blockNb], + srcBlockBuffers.buffers[blockNb], srcBlockBuffers.capacities[blockNb], + cdict); + assert(!ZSTD_isError(cBlockSize)); + } + totalCSize += cBlockSize; + } + return totalCSize; +} + + +/* --- Benchmark --- */ /* bench() : + * fileName : file to load for benchmarking purpose + * dictionary : optional (can be NULL), file to load as dictionary, + * if none provided : will be calculated on the fly by the program. * @return : 0 is success, 1+ otherwise */ -int bench(const char* fileName) +int bench(const char* fileName, const char* dictionary) { int result = 0; DISPLAYLEVEL(3, "loading %s... \n", fileName); buffer_t const srcBuffer = createBuffer_fromFile(fileName); - if (srcBuffer.ptr == NULL) { - DISPLAYLEVEL(1," error reading file %s \n", fileName); - return 1; - } + assert(srcBuffer.ptr != NULL); DISPLAYLEVEL(3, "created src buffer of size %.1f MB \n", (double)(srcBuffer.size) / (1 MB)); buffer_collection_t const srcBlockBuffers = splitBuffer(srcBuffer, BLOCKSIZE); assert(srcBlockBuffers.buffers != NULL); unsigned const nbBlocks = (unsigned)srcBlockBuffers.nbBuffers; - DISPLAYLEVEL(3, "splitting input into %u blocks of max size %u bytes \n", + DISPLAYLEVEL(3, "split input into %u blocks of max size %u bytes \n", nbBlocks, BLOCKSIZE); size_t const dstBlockSize = ZSTD_compressBound(BLOCKSIZE); @@ -230,36 +293,44 @@ int bench(const char* fileName) buffer_collection_t const dstBlockBuffers = splitBuffer(dstBuffer, dstBlockSize); assert(dstBlockBuffers.buffers != NULL); - DISPLAYLEVEL(3, "creating dictionary, of target size %u bytes \n", DICTSIZE); - void* const dictBuffer = malloc(DICTSIZE); - if (dictBuffer == NULL) { result = 1; goto _cleanup; } + /* dictionary determination */ + buffer_t const dictBuffer = createDictionary(dictionary, + srcBuffer.ptr, + srcBlockBuffers.capacities, nbBlocks); + assert(dictBuffer.ptr != NULL); - size_t const dictSize = ZDICT_trainFromBuffer(dictBuffer, DICTSIZE, - srcBuffer.ptr, - srcBlockBuffers.capacities, - nbBlocks); - if (ZSTD_isError(dictSize)) { - DISPLAYLEVEL(1, "error creating dictionary \n"); - result = 1; - goto _cleanup; - } + ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer.ptr, dictBuffer.size, COMP_LEVEL); + assert(cdict != NULL); - size_t const dictMem = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy); + size_t const cTotalSizeNoDict = compressBlocks(dstBlockBuffers, srcBlockBuffers, NULL, COMP_LEVEL); + assert(cTotalSizeNoDict != 0); + DISPLAYLEVEL(3, "compressing at level %u without dictionary : Ratio=%.2f (%u bytes) \n", + COMP_LEVEL, + (double)srcBuffer.size / cTotalSizeNoDict, (unsigned)cTotalSizeNoDict); + + size_t const cTotalSize = compressBlocks(dstBlockBuffers, srcBlockBuffers, cdict, COMP_LEVEL); + assert(cTotalSize != 0); + DISPLAYLEVEL(3, "compressed using a %u bytes dictionary : Ratio=%.2f (%u bytes) \n", + (unsigned)dictBuffer.size, + (double)srcBuffer.size / cTotalSize, (unsigned)cTotalSize); + + size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, ZSTD_dlm_byCopy); size_t const allDictMem = dictMem * nbBlocks; DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n", nbBlocks, (double)allDictMem / (1 MB)); - ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer, dictSize, COMP_LEVEL); - do { - ddict_collection_t const dictionaries = createDDictCollection(dictBuffer, dictSize, nbBlocks); - assert(dictionaries.ddicts != NULL); + ddict_collection_t const dictionaries = createDDictCollection(dictBuffer.ptr, dictBuffer.size, nbBlocks); + assert(dictionaries.ddicts != NULL); - freeDDictCollection(dictionaries); - } while(0); + + + //result = benchMem(srcBlockBuffers, dstBlockBuffers, dictionaries);; + + + + freeDDictCollection(dictionaries); ZSTD_freeCDict(cdict); - -_cleanup: - free(dictBuffer); + freeBuffer(dictBuffer); freeCollection(dstBlockBuffers); freeBuffer(dstBuffer); freeCollection(srcBlockBuffers); @@ -276,7 +347,7 @@ _cleanup: int bad_usage(const char* exeName) { DISPLAY (" bad usage : \n"); - DISPLAY (" %s filename \n", exeName); + DISPLAY (" %s filename [-D dictionary] \n", exeName); return 1; } @@ -284,6 +355,15 @@ int main (int argc, const char** argv) { const char* const exeName = argv[0]; - if (argc != 2) return bad_usage(exeName); - return bench(argv[1]); + if (argc < 2) return bad_usage(exeName); + const char* const fileName = argv[1]; + + const char* dictionary = NULL; + if (argc > 2) { + if (argc != 4) return bad_usage(exeName); + if (strcmp(argv[2], "-D")) return bad_usage(exeName); + dictionary = argv[3]; + } + + return bench(fileName, dictionary); }