zstd_preSplit.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * Copyright (c) Meta Platforms, Inc. and affiliates.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under both the BSD-style license (found in the
  6. * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  7. * in the COPYING file in the root directory of this source tree).
  8. * You may select, at your option, one of the above-listed licenses.
  9. */
  10. #include "../common/compiler.h" /* ZSTD_ALIGNOF */
  11. #include "../common/mem.h" /* S64 */
  12. #include "../common/zstd_deps.h" /* ZSTD_memset */
  13. #include "../common/zstd_internal.h" /* ZSTD_STATIC_ASSERT */
  14. #include "hist.h" /* HIST_add */
  15. #include "zstd_preSplit.h"
  16. #define BLOCKSIZE_MIN 3500
  17. #define THRESHOLD_PENALTY_RATE 16
  18. #define THRESHOLD_BASE (THRESHOLD_PENALTY_RATE - 2)
  19. #define THRESHOLD_PENALTY 3
  20. #define HASHLENGTH 2
  21. #define HASHLOG_MAX 10
  22. #define HASHTABLESIZE (1 << HASHLOG_MAX)
  23. #define HASHMASK (HASHTABLESIZE - 1)
  24. #define KNUTH 0x9e3779b9
  25. /* for hashLog > 8, hash 2 bytes.
  26. * for hashLog == 8, just take the byte, no hashing.
  27. * The speed of this method relies on compile-time constant propagation */
  28. FORCE_INLINE_TEMPLATE unsigned hash2(const void *p, unsigned hashLog)
  29. {
  30. assert(hashLog >= 8);
  31. if (hashLog == 8) return (U32)((const BYTE*)p)[0];
  32. assert(hashLog <= HASHLOG_MAX);
  33. return (U32)(MEM_read16(p)) * KNUTH >> (32 - hashLog);
  34. }
  35. typedef struct {
  36. unsigned events[HASHTABLESIZE];
  37. size_t nbEvents;
  38. } Fingerprint;
  39. typedef struct {
  40. Fingerprint pastEvents;
  41. Fingerprint newEvents;
  42. } FPStats;
  43. static void initStats(FPStats* fpstats)
  44. {
  45. ZSTD_memset(fpstats, 0, sizeof(FPStats));
  46. }
  47. FORCE_INLINE_TEMPLATE void
  48. addEvents_generic(Fingerprint* fp, const void* src, size_t srcSize, size_t samplingRate, unsigned hashLog)
  49. {
  50. const char* p = (const char*)src;
  51. size_t limit = srcSize - HASHLENGTH + 1;
  52. size_t n;
  53. assert(srcSize >= HASHLENGTH);
  54. for (n = 0; n < limit; n+=samplingRate) {
  55. fp->events[hash2(p+n, hashLog)]++;
  56. }
  57. fp->nbEvents += limit/samplingRate;
  58. }
  59. FORCE_INLINE_TEMPLATE void
  60. recordFingerprint_generic(Fingerprint* fp, const void* src, size_t srcSize, size_t samplingRate, unsigned hashLog)
  61. {
  62. ZSTD_memset(fp, 0, sizeof(unsigned) * ((size_t)1 << hashLog));
  63. fp->nbEvents = 0;
  64. addEvents_generic(fp, src, srcSize, samplingRate, hashLog);
  65. }
  66. typedef void (*RecordEvents_f)(Fingerprint* fp, const void* src, size_t srcSize);
  67. #define FP_RECORD(_rate) ZSTD_recordFingerprint_##_rate
  68. #define ZSTD_GEN_RECORD_FINGERPRINT(_rate, _hSize) \
  69. static void FP_RECORD(_rate)(Fingerprint* fp, const void* src, size_t srcSize) \
  70. { \
  71. recordFingerprint_generic(fp, src, srcSize, _rate, _hSize); \
  72. }
  73. ZSTD_GEN_RECORD_FINGERPRINT(1, 10)
  74. ZSTD_GEN_RECORD_FINGERPRINT(5, 10)
  75. ZSTD_GEN_RECORD_FINGERPRINT(11, 9)
  76. ZSTD_GEN_RECORD_FINGERPRINT(43, 8)
  77. static U64 abs64(S64 s64) { return (U64)((s64 < 0) ? -s64 : s64); }
  78. static U64 fpDistance(const Fingerprint* fp1, const Fingerprint* fp2, unsigned hashLog)
  79. {
  80. U64 distance = 0;
  81. size_t n;
  82. assert(hashLog <= HASHLOG_MAX);
  83. for (n = 0; n < ((size_t)1 << hashLog); n++) {
  84. distance +=
  85. abs64((S64)fp1->events[n] * (S64)fp2->nbEvents - (S64)fp2->events[n] * (S64)fp1->nbEvents);
  86. }
  87. return distance;
  88. }
  89. /* Compare newEvents with pastEvents
  90. * return 1 when considered "too different"
  91. */
  92. static int compareFingerprints(const Fingerprint* ref,
  93. const Fingerprint* newfp,
  94. int penalty,
  95. unsigned hashLog)
  96. {
  97. assert(ref->nbEvents > 0);
  98. assert(newfp->nbEvents > 0);
  99. { U64 p50 = (U64)ref->nbEvents * (U64)newfp->nbEvents;
  100. U64 deviation = fpDistance(ref, newfp, hashLog);
  101. U64 threshold = p50 * (U64)(THRESHOLD_BASE + penalty) / THRESHOLD_PENALTY_RATE;
  102. return deviation >= threshold;
  103. }
  104. }
  105. static void mergeEvents(Fingerprint* acc, const Fingerprint* newfp)
  106. {
  107. size_t n;
  108. for (n = 0; n < HASHTABLESIZE; n++) {
  109. acc->events[n] += newfp->events[n];
  110. }
  111. acc->nbEvents += newfp->nbEvents;
  112. }
  113. static void flushEvents(FPStats* fpstats)
  114. {
  115. size_t n;
  116. for (n = 0; n < HASHTABLESIZE; n++) {
  117. fpstats->pastEvents.events[n] = fpstats->newEvents.events[n];
  118. }
  119. fpstats->pastEvents.nbEvents = fpstats->newEvents.nbEvents;
  120. ZSTD_memset(&fpstats->newEvents, 0, sizeof(fpstats->newEvents));
  121. }
  122. static void removeEvents(Fingerprint* acc, const Fingerprint* slice)
  123. {
  124. size_t n;
  125. for (n = 0; n < HASHTABLESIZE; n++) {
  126. assert(acc->events[n] >= slice->events[n]);
  127. acc->events[n] -= slice->events[n];
  128. }
  129. acc->nbEvents -= slice->nbEvents;
  130. }
  131. #define CHUNKSIZE (8 << 10)
  132. static size_t ZSTD_splitBlock_byChunks(const void* blockStart, size_t blockSize,
  133. int level,
  134. void* workspace, size_t wkspSize)
  135. {
  136. static const RecordEvents_f records_fs[] = {
  137. FP_RECORD(43), FP_RECORD(11), FP_RECORD(5), FP_RECORD(1)
  138. };
  139. static const unsigned hashParams[] = { 8, 9, 10, 10 };
  140. const RecordEvents_f record_f = (assert(0<=level && level<=3), records_fs[level]);
  141. FPStats* const fpstats = (FPStats*)workspace;
  142. const char* p = (const char*)blockStart;
  143. int penalty = THRESHOLD_PENALTY;
  144. size_t pos = 0;
  145. assert(blockSize == (128 << 10));
  146. assert(workspace != NULL);
  147. assert((size_t)workspace % ZSTD_ALIGNOF(FPStats) == 0);
  148. ZSTD_STATIC_ASSERT(ZSTD_SLIPBLOCK_WORKSPACESIZE >= sizeof(FPStats));
  149. assert(wkspSize >= sizeof(FPStats)); (void)wkspSize;
  150. initStats(fpstats);
  151. record_f(&fpstats->pastEvents, p, CHUNKSIZE);
  152. for (pos = CHUNKSIZE; pos <= blockSize - CHUNKSIZE; pos += CHUNKSIZE) {
  153. record_f(&fpstats->newEvents, p + pos, CHUNKSIZE);
  154. if (compareFingerprints(&fpstats->pastEvents, &fpstats->newEvents, penalty, hashParams[level])) {
  155. return pos;
  156. } else {
  157. mergeEvents(&fpstats->pastEvents, &fpstats->newEvents);
  158. if (penalty > 0) penalty--;
  159. }
  160. }
  161. assert(pos == blockSize);
  162. return blockSize;
  163. (void)flushEvents; (void)removeEvents;
  164. }
  165. /* ZSTD_splitBlock_fromBorders(): very fast strategy :
  166. * compare fingerprint from beginning and end of the block,
  167. * derive from their difference if it's preferable to split in the middle,
  168. * repeat the process a second time, for finer grained decision.
  169. * 3 times did not brought improvements, so I stopped at 2.
  170. * Benefits are good enough for a cheap heuristic.
  171. * More accurate splitting saves more, but speed impact is also more perceptible.
  172. * For better accuracy, use more elaborate variant *_byChunks.
  173. */
  174. static size_t ZSTD_splitBlock_fromBorders(const void* blockStart, size_t blockSize,
  175. void* workspace, size_t wkspSize)
  176. {
  177. #define SEGMENT_SIZE 512
  178. FPStats* const fpstats = (FPStats*)workspace;
  179. Fingerprint* middleEvents = (Fingerprint*)(void*)((char*)workspace + 512 * sizeof(unsigned));
  180. assert(blockSize == (128 << 10));
  181. assert(workspace != NULL);
  182. assert((size_t)workspace % ZSTD_ALIGNOF(FPStats) == 0);
  183. ZSTD_STATIC_ASSERT(ZSTD_SLIPBLOCK_WORKSPACESIZE >= sizeof(FPStats));
  184. assert(wkspSize >= sizeof(FPStats)); (void)wkspSize;
  185. initStats(fpstats);
  186. HIST_add(fpstats->pastEvents.events, blockStart, SEGMENT_SIZE);
  187. HIST_add(fpstats->newEvents.events, (const char*)blockStart + blockSize - SEGMENT_SIZE, SEGMENT_SIZE);
  188. fpstats->pastEvents.nbEvents = fpstats->newEvents.nbEvents = SEGMENT_SIZE;
  189. if (!compareFingerprints(&fpstats->pastEvents, &fpstats->newEvents, 0, 8))
  190. return blockSize;
  191. HIST_add(middleEvents->events, (const char*)blockStart + blockSize/2 - SEGMENT_SIZE/2, SEGMENT_SIZE);
  192. middleEvents->nbEvents = SEGMENT_SIZE;
  193. { U64 const distFromBegin = fpDistance(&fpstats->pastEvents, middleEvents, 8);
  194. U64 const distFromEnd = fpDistance(&fpstats->newEvents, middleEvents, 8);
  195. U64 const minDistance = SEGMENT_SIZE * SEGMENT_SIZE / 3;
  196. if (abs64((S64)distFromBegin - (S64)distFromEnd) < minDistance)
  197. return 64 KB;
  198. return (distFromBegin > distFromEnd) ? 32 KB : 96 KB;
  199. }
  200. }
  201. size_t ZSTD_splitBlock(const void* blockStart, size_t blockSize,
  202. int level,
  203. void* workspace, size_t wkspSize)
  204. {
  205. DEBUGLOG(6, "ZSTD_splitBlock (level=%i)", level);
  206. assert(0<=level && level<=4);
  207. if (level == 0)
  208. return ZSTD_splitBlock_fromBorders(blockStart, blockSize, workspace, wkspSize);
  209. /* level >= 1*/
  210. return ZSTD_splitBlock_byChunks(blockStart, blockSize, level-1, workspace, wkspSize);
  211. }