zstd_preSplit.c 8.6 KB

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