1 /** |
1 /* |
2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
3 * All rights reserved. |
3 * All rights reserved. |
4 * |
4 * |
5 * This source code is licensed under the BSD-style license found in the |
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. An additional grant |
6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found |
7 * of patent rights can be found in the PATENTS file in the same directory. |
7 * in the COPYING file in the root directory of this source tree). |
8 */ |
8 * You may select, at your option, one of the above-listed licenses. |
|
9 */ |
|
10 |
|
11 /* ***************************************************************************** |
|
12 * Constructs a dictionary using a heuristic based on the following paper: |
|
13 * |
|
14 * Liao, Petri, Moffat, Wirth |
|
15 * Effective Construction of Relative Lempel-Ziv Dictionaries |
|
16 * Published in WWW 2016. |
|
17 * |
|
18 * Adapted from code originally written by @ot (Giuseppe Ottaviano). |
|
19 ******************************************************************************/ |
9 |
20 |
10 /*-************************************* |
21 /*-************************************* |
11 * Dependencies |
22 * Dependencies |
12 ***************************************/ |
23 ***************************************/ |
13 #include <stdio.h> /* fprintf */ |
24 #include <stdio.h> /* fprintf */ |
47 #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ |
58 #define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ |
48 if (displayLevel >= l) { \ |
59 if (displayLevel >= l) { \ |
49 if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ |
60 if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ |
50 g_time = clock(); \ |
61 g_time = clock(); \ |
51 DISPLAY(__VA_ARGS__); \ |
62 DISPLAY(__VA_ARGS__); \ |
52 if (displayLevel >= 4) \ |
|
53 fflush(stdout); \ |
|
54 } \ |
63 } \ |
55 } |
64 } |
56 #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) |
65 #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) |
57 static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; |
66 static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; |
58 static clock_t g_time = 0; |
67 static clock_t g_time = 0; |
224 * Returns -1 if the dmer at lp is less than the dmer at rp. |
233 * Returns -1 if the dmer at lp is less than the dmer at rp. |
225 * Return 0 if the dmers at lp and rp are equal. |
234 * Return 0 if the dmers at lp and rp are equal. |
226 * Returns 1 if the dmer at lp is greater than the dmer at rp. |
235 * Returns 1 if the dmer at lp is greater than the dmer at rp. |
227 */ |
236 */ |
228 static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) { |
237 static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) { |
229 const U32 lhs = *(const U32 *)lp; |
238 U32 const lhs = *(U32 const *)lp; |
230 const U32 rhs = *(const U32 *)rp; |
239 U32 const rhs = *(U32 const *)rp; |
231 return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d); |
240 return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d); |
|
241 } |
|
242 /** |
|
243 * Faster version for d <= 8. |
|
244 */ |
|
245 static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) { |
|
246 U64 const mask = (ctx->d == 8) ? (U64)-1 : (((U64)1 << (8 * ctx->d)) - 1); |
|
247 U64 const lhs = MEM_readLE64(ctx->samples + *(U32 const *)lp) & mask; |
|
248 U64 const rhs = MEM_readLE64(ctx->samples + *(U32 const *)rp) & mask; |
|
249 if (lhs < rhs) { |
|
250 return -1; |
|
251 } |
|
252 return (lhs > rhs); |
232 } |
253 } |
233 |
254 |
234 /** |
255 /** |
235 * Same as COVER_cmp() except ties are broken by pointer value |
256 * Same as COVER_cmp() except ties are broken by pointer value |
236 * NOTE: g_ctx must be set to call this function. A global is required because |
257 * NOTE: g_ctx must be set to call this function. A global is required because |
237 * qsort doesn't take an opaque pointer. |
258 * qsort doesn't take an opaque pointer. |
238 */ |
259 */ |
239 static int COVER_strict_cmp(const void *lp, const void *rp) { |
260 static int COVER_strict_cmp(const void *lp, const void *rp) { |
240 int result = COVER_cmp(g_ctx, lp, rp); |
261 int result = COVER_cmp(g_ctx, lp, rp); |
|
262 if (result == 0) { |
|
263 result = lp < rp ? -1 : 1; |
|
264 } |
|
265 return result; |
|
266 } |
|
267 /** |
|
268 * Faster version for d <= 8. |
|
269 */ |
|
270 static int COVER_strict_cmp8(const void *lp, const void *rp) { |
|
271 int result = COVER_cmp8(g_ctx, lp, rp); |
241 if (result == 0) { |
272 if (result == 0) { |
242 result = lp < rp ? -1 : 1; |
273 result = lp < rp ? -1 : 1; |
243 } |
274 } |
244 return result; |
275 return result; |
245 } |
276 } |
366 * |
397 * |
367 * Once the dmer d is in the dictionay we set F(d) = 0. |
398 * Once the dmer d is in the dictionay we set F(d) = 0. |
368 */ |
399 */ |
369 static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, |
400 static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, |
370 COVER_map_t *activeDmers, U32 begin, |
401 COVER_map_t *activeDmers, U32 begin, |
371 U32 end, COVER_params_t parameters) { |
402 U32 end, |
|
403 ZDICT_cover_params_t parameters) { |
372 /* Constants */ |
404 /* Constants */ |
373 const U32 k = parameters.k; |
405 const U32 k = parameters.k; |
374 const U32 d = parameters.d; |
406 const U32 d = parameters.d; |
375 const U32 dmersInK = k - d + 1; |
407 const U32 dmersInK = k - d + 1; |
376 /* Try each segment (activeSegment) and save the best (bestSegment) */ |
408 /* Try each segment (activeSegment) and save the best (bestSegment) */ |
446 |
478 |
447 /** |
479 /** |
448 * Check the validity of the parameters. |
480 * Check the validity of the parameters. |
449 * Returns non-zero if the parameters are valid and 0 otherwise. |
481 * Returns non-zero if the parameters are valid and 0 otherwise. |
450 */ |
482 */ |
451 static int COVER_checkParameters(COVER_params_t parameters) { |
483 static int COVER_checkParameters(ZDICT_cover_params_t parameters, |
|
484 size_t maxDictSize) { |
452 /* k and d are required parameters */ |
485 /* k and d are required parameters */ |
453 if (parameters.d == 0 || parameters.k == 0) { |
486 if (parameters.d == 0 || parameters.k == 0) { |
|
487 return 0; |
|
488 } |
|
489 /* k <= maxDictSize */ |
|
490 if (parameters.k > maxDictSize) { |
454 return 0; |
491 return 0; |
455 } |
492 } |
456 /* d <= k */ |
493 /* d <= k */ |
457 if (parameters.d > parameters.k) { |
494 if (parameters.d > parameters.k) { |
458 return 0; |
495 return 0; |
496 const size_t *samplesSizes, unsigned nbSamples, |
533 const size_t *samplesSizes, unsigned nbSamples, |
497 unsigned d) { |
534 unsigned d) { |
498 const BYTE *const samples = (const BYTE *)samplesBuffer; |
535 const BYTE *const samples = (const BYTE *)samplesBuffer; |
499 const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); |
536 const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); |
500 /* Checks */ |
537 /* Checks */ |
501 if (totalSamplesSize < d || |
538 if (totalSamplesSize < MAX(d, sizeof(U64)) || |
502 totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { |
539 totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { |
503 DISPLAYLEVEL(1, "Total samples size is too large, maximum size is %u MB\n", |
540 DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", |
504 (COVER_MAX_SAMPLES_SIZE >> 20)); |
541 (U32)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20)); |
505 return 0; |
542 return 0; |
506 } |
543 } |
507 /* Zero the context */ |
544 /* Zero the context */ |
508 memset(ctx, 0, sizeof(*ctx)); |
545 memset(ctx, 0, sizeof(*ctx)); |
509 DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbSamples, |
546 DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbSamples, |
510 (U32)totalSamplesSize); |
547 (U32)totalSamplesSize); |
511 ctx->samples = samples; |
548 ctx->samples = samples; |
512 ctx->samplesSizes = samplesSizes; |
549 ctx->samplesSizes = samplesSizes; |
513 ctx->nbSamples = nbSamples; |
550 ctx->nbSamples = nbSamples; |
514 /* Partial suffix array */ |
551 /* Partial suffix array */ |
515 ctx->suffixSize = totalSamplesSize - d + 1; |
552 ctx->suffixSize = totalSamplesSize - MAX(d, sizeof(U64)) + 1; |
516 ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); |
553 ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); |
517 /* Maps index to the dmerID */ |
554 /* Maps index to the dmerID */ |
518 ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); |
555 ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); |
519 /* The offsets of each file */ |
556 /* The offsets of each file */ |
520 ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); |
557 ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); |
544 for (i = 0; i < ctx->suffixSize; ++i) { |
581 for (i = 0; i < ctx->suffixSize; ++i) { |
545 ctx->suffix[i] = i; |
582 ctx->suffix[i] = i; |
546 } |
583 } |
547 /* qsort doesn't take an opaque pointer, so pass as a global */ |
584 /* qsort doesn't take an opaque pointer, so pass as a global */ |
548 g_ctx = ctx; |
585 g_ctx = ctx; |
549 qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), &COVER_strict_cmp); |
586 qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), |
|
587 (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); |
550 } |
588 } |
551 DISPLAYLEVEL(2, "Computing frequencies\n"); |
589 DISPLAYLEVEL(2, "Computing frequencies\n"); |
552 /* For each dmer group (group of positions with the same first d bytes): |
590 /* For each dmer group (group of positions with the same first d bytes): |
553 * 1. For each position we set dmerAt[position] = dmerID. The dmerID is |
591 * 1. For each position we set dmerAt[position] = dmerID. The dmerID is |
554 * (groupBeginPtr - suffix). This allows us to go from position to |
592 * (groupBeginPtr - suffix). This allows us to go from position to |
555 * dmerID so we can look up values in freq. |
593 * dmerID so we can look up values in freq. |
556 * 2. We calculate how many samples the dmer occurs in and save it in |
594 * 2. We calculate how many samples the dmer occurs in and save it in |
557 * freqs[dmerId]. |
595 * freqs[dmerId]. |
558 */ |
596 */ |
559 COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, &COVER_cmp, |
597 COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, |
560 &COVER_group); |
598 (ctx->d <= 8 ? &COVER_cmp8 : &COVER_cmp), &COVER_group); |
561 ctx->freqs = ctx->suffix; |
599 ctx->freqs = ctx->suffix; |
562 ctx->suffix = NULL; |
600 ctx->suffix = NULL; |
563 return 1; |
601 return 1; |
564 } |
602 } |
565 |
603 |
567 * Given the prepared context build the dictionary. |
605 * Given the prepared context build the dictionary. |
568 */ |
606 */ |
569 static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, |
607 static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, |
570 COVER_map_t *activeDmers, void *dictBuffer, |
608 COVER_map_t *activeDmers, void *dictBuffer, |
571 size_t dictBufferCapacity, |
609 size_t dictBufferCapacity, |
572 COVER_params_t parameters) { |
610 ZDICT_cover_params_t parameters) { |
573 BYTE *const dict = (BYTE *)dictBuffer; |
611 BYTE *const dict = (BYTE *)dictBuffer; |
574 size_t tail = dictBufferCapacity; |
612 size_t tail = dictBufferCapacity; |
575 /* Divide the data up into epochs of equal size. |
613 /* Divide the data up into epochs of equal size. |
576 * We will select at least one segment from each epoch. |
614 * We will select at least one segment from each epoch. |
577 */ |
615 */ |
588 const U32 epochEnd = epochBegin + epochSize; |
626 const U32 epochEnd = epochBegin + epochSize; |
589 size_t segmentSize; |
627 size_t segmentSize; |
590 /* Select a segment */ |
628 /* Select a segment */ |
591 COVER_segment_t segment = COVER_selectSegment( |
629 COVER_segment_t segment = COVER_selectSegment( |
592 ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); |
630 ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); |
593 /* Trim the segment if necessary and if it is empty then we are done */ |
631 /* If the segment covers no dmers, then we are out of content */ |
|
632 if (segment.score == 0) { |
|
633 break; |
|
634 } |
|
635 /* Trim the segment if necessary and if it is too small then we are done */ |
594 segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); |
636 segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); |
595 if (segmentSize == 0) { |
637 if (segmentSize < parameters.d) { |
596 break; |
638 break; |
597 } |
639 } |
598 /* We fill the dictionary from the back to allow the best segments to be |
640 /* We fill the dictionary from the back to allow the best segments to be |
599 * referenced with the smallest offsets. |
641 * referenced with the smallest offsets. |
600 */ |
642 */ |
606 } |
648 } |
607 DISPLAYLEVEL(2, "\r%79s\r", ""); |
649 DISPLAYLEVEL(2, "\r%79s\r", ""); |
608 return tail; |
650 return tail; |
609 } |
651 } |
610 |
652 |
611 /** |
653 ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( |
612 * Translate from COVER_params_t to ZDICT_params_t required for finalizing the |
654 void *dictBuffer, size_t dictBufferCapacity, |
613 * dictionary. |
655 const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, |
614 */ |
656 ZDICT_cover_params_t parameters) |
615 static ZDICT_params_t COVER_translateParams(COVER_params_t parameters) { |
657 { |
616 ZDICT_params_t zdictParams; |
658 BYTE* const dict = (BYTE*)dictBuffer; |
617 memset(&zdictParams, 0, sizeof(zdictParams)); |
|
618 zdictParams.notificationLevel = 1; |
|
619 zdictParams.dictID = parameters.dictID; |
|
620 zdictParams.compressionLevel = parameters.compressionLevel; |
|
621 return zdictParams; |
|
622 } |
|
623 |
|
624 /** |
|
625 * Constructs a dictionary using a heuristic based on the following paper: |
|
626 * |
|
627 * Liao, Petri, Moffat, Wirth |
|
628 * Effective Construction of Relative Lempel-Ziv Dictionaries |
|
629 * Published in WWW 2016. |
|
630 */ |
|
631 ZDICTLIB_API size_t COVER_trainFromBuffer( |
|
632 void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, |
|
633 const size_t *samplesSizes, unsigned nbSamples, COVER_params_t parameters) { |
|
634 BYTE *const dict = (BYTE *)dictBuffer; |
|
635 COVER_ctx_t ctx; |
659 COVER_ctx_t ctx; |
636 COVER_map_t activeDmers; |
660 COVER_map_t activeDmers; |
|
661 |
|
662 /* Initialize global data */ |
|
663 g_displayLevel = parameters.zParams.notificationLevel; |
637 /* Checks */ |
664 /* Checks */ |
638 if (!COVER_checkParameters(parameters)) { |
665 if (!COVER_checkParameters(parameters, dictBufferCapacity)) { |
639 DISPLAYLEVEL(1, "Cover parameters incorrect\n"); |
666 DISPLAYLEVEL(1, "Cover parameters incorrect\n"); |
640 return ERROR(GENERIC); |
667 return ERROR(GENERIC); |
641 } |
668 } |
642 if (nbSamples == 0) { |
669 if (nbSamples == 0) { |
643 DISPLAYLEVEL(1, "Cover must have at least one input file\n"); |
670 DISPLAYLEVEL(1, "Cover must have at least one input file\n"); |
646 if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { |
673 if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { |
647 DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", |
674 DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", |
648 ZDICT_DICTSIZE_MIN); |
675 ZDICT_DICTSIZE_MIN); |
649 return ERROR(dstSize_tooSmall); |
676 return ERROR(dstSize_tooSmall); |
650 } |
677 } |
651 /* Initialize global data */ |
|
652 g_displayLevel = parameters.notificationLevel; |
|
653 /* Initialize context and activeDmers */ |
678 /* Initialize context and activeDmers */ |
654 if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, |
679 if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, |
655 parameters.d)) { |
680 parameters.d)) { |
656 return ERROR(GENERIC); |
681 return ERROR(GENERIC); |
657 } |
682 } |
664 DISPLAYLEVEL(2, "Building dictionary\n"); |
689 DISPLAYLEVEL(2, "Building dictionary\n"); |
665 { |
690 { |
666 const size_t tail = |
691 const size_t tail = |
667 COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, |
692 COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, |
668 dictBufferCapacity, parameters); |
693 dictBufferCapacity, parameters); |
669 ZDICT_params_t zdictParams = COVER_translateParams(parameters); |
|
670 const size_t dictionarySize = ZDICT_finalizeDictionary( |
694 const size_t dictionarySize = ZDICT_finalizeDictionary( |
671 dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, |
695 dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, |
672 samplesBuffer, samplesSizes, nbSamples, zdictParams); |
696 samplesBuffer, samplesSizes, nbSamples, parameters.zParams); |
673 if (!ZSTD_isError(dictionarySize)) { |
697 if (!ZSTD_isError(dictionarySize)) { |
674 DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", |
698 DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", |
675 (U32)dictionarySize); |
699 (U32)dictionarySize); |
676 } |
700 } |
677 COVER_ctx_destroy(&ctx); |
701 COVER_ctx_destroy(&ctx); |
687 * |
711 * |
688 * All of the methods except COVER_best_init() are thread safe if zstd is |
712 * All of the methods except COVER_best_init() are thread safe if zstd is |
689 * compiled with multithreaded support. |
713 * compiled with multithreaded support. |
690 */ |
714 */ |
691 typedef struct COVER_best_s { |
715 typedef struct COVER_best_s { |
692 pthread_mutex_t mutex; |
716 ZSTD_pthread_mutex_t mutex; |
693 pthread_cond_t cond; |
717 ZSTD_pthread_cond_t cond; |
694 size_t liveJobs; |
718 size_t liveJobs; |
695 void *dict; |
719 void *dict; |
696 size_t dictSize; |
720 size_t dictSize; |
697 COVER_params_t parameters; |
721 ZDICT_cover_params_t parameters; |
698 size_t compressedSize; |
722 size_t compressedSize; |
699 } COVER_best_t; |
723 } COVER_best_t; |
700 |
724 |
701 /** |
725 /** |
702 * Initialize the `COVER_best_t`. |
726 * Initialize the `COVER_best_t`. |
703 */ |
727 */ |
704 static void COVER_best_init(COVER_best_t *best) { |
728 static void COVER_best_init(COVER_best_t *best) { |
705 if (!best) { |
729 if (best==NULL) return; /* compatible with init on NULL */ |
706 return; |
730 (void)ZSTD_pthread_mutex_init(&best->mutex, NULL); |
707 } |
731 (void)ZSTD_pthread_cond_init(&best->cond, NULL); |
708 pthread_mutex_init(&best->mutex, NULL); |
|
709 pthread_cond_init(&best->cond, NULL); |
|
710 best->liveJobs = 0; |
732 best->liveJobs = 0; |
711 best->dict = NULL; |
733 best->dict = NULL; |
712 best->dictSize = 0; |
734 best->dictSize = 0; |
713 best->compressedSize = (size_t)-1; |
735 best->compressedSize = (size_t)-1; |
714 memset(&best->parameters, 0, sizeof(best->parameters)); |
736 memset(&best->parameters, 0, sizeof(best->parameters)); |
719 */ |
741 */ |
720 static void COVER_best_wait(COVER_best_t *best) { |
742 static void COVER_best_wait(COVER_best_t *best) { |
721 if (!best) { |
743 if (!best) { |
722 return; |
744 return; |
723 } |
745 } |
724 pthread_mutex_lock(&best->mutex); |
746 ZSTD_pthread_mutex_lock(&best->mutex); |
725 while (best->liveJobs != 0) { |
747 while (best->liveJobs != 0) { |
726 pthread_cond_wait(&best->cond, &best->mutex); |
748 ZSTD_pthread_cond_wait(&best->cond, &best->mutex); |
727 } |
749 } |
728 pthread_mutex_unlock(&best->mutex); |
750 ZSTD_pthread_mutex_unlock(&best->mutex); |
729 } |
751 } |
730 |
752 |
731 /** |
753 /** |
732 * Call COVER_best_wait() and then destroy the COVER_best_t. |
754 * Call COVER_best_wait() and then destroy the COVER_best_t. |
733 */ |
755 */ |
737 } |
759 } |
738 COVER_best_wait(best); |
760 COVER_best_wait(best); |
739 if (best->dict) { |
761 if (best->dict) { |
740 free(best->dict); |
762 free(best->dict); |
741 } |
763 } |
742 pthread_mutex_destroy(&best->mutex); |
764 ZSTD_pthread_mutex_destroy(&best->mutex); |
743 pthread_cond_destroy(&best->cond); |
765 ZSTD_pthread_cond_destroy(&best->cond); |
744 } |
766 } |
745 |
767 |
746 /** |
768 /** |
747 * Called when a thread is about to be launched. |
769 * Called when a thread is about to be launched. |
748 * Increments liveJobs. |
770 * Increments liveJobs. |
749 */ |
771 */ |
750 static void COVER_best_start(COVER_best_t *best) { |
772 static void COVER_best_start(COVER_best_t *best) { |
751 if (!best) { |
773 if (!best) { |
752 return; |
774 return; |
753 } |
775 } |
754 pthread_mutex_lock(&best->mutex); |
776 ZSTD_pthread_mutex_lock(&best->mutex); |
755 ++best->liveJobs; |
777 ++best->liveJobs; |
756 pthread_mutex_unlock(&best->mutex); |
778 ZSTD_pthread_mutex_unlock(&best->mutex); |
757 } |
779 } |
758 |
780 |
759 /** |
781 /** |
760 * Called when a thread finishes executing, both on error or success. |
782 * Called when a thread finishes executing, both on error or success. |
761 * Decrements liveJobs and signals any waiting threads if liveJobs == 0. |
783 * Decrements liveJobs and signals any waiting threads if liveJobs == 0. |
762 * If this dictionary is the best so far save it and its parameters. |
784 * If this dictionary is the best so far save it and its parameters. |
763 */ |
785 */ |
764 static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, |
786 static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, |
765 COVER_params_t parameters, void *dict, |
787 ZDICT_cover_params_t parameters, void *dict, |
766 size_t dictSize) { |
788 size_t dictSize) { |
767 if (!best) { |
789 if (!best) { |
768 return; |
790 return; |
769 } |
791 } |
770 { |
792 { |
771 size_t liveJobs; |
793 size_t liveJobs; |
772 pthread_mutex_lock(&best->mutex); |
794 ZSTD_pthread_mutex_lock(&best->mutex); |
773 --best->liveJobs; |
795 --best->liveJobs; |
774 liveJobs = best->liveJobs; |
796 liveJobs = best->liveJobs; |
775 /* If the new dictionary is better */ |
797 /* If the new dictionary is better */ |
776 if (compressedSize < best->compressedSize) { |
798 if (compressedSize < best->compressedSize) { |
777 /* Allocate space if necessary */ |
799 /* Allocate space if necessary */ |
790 memcpy(best->dict, dict, dictSize); |
812 memcpy(best->dict, dict, dictSize); |
791 best->dictSize = dictSize; |
813 best->dictSize = dictSize; |
792 best->parameters = parameters; |
814 best->parameters = parameters; |
793 best->compressedSize = compressedSize; |
815 best->compressedSize = compressedSize; |
794 } |
816 } |
795 pthread_mutex_unlock(&best->mutex); |
817 ZSTD_pthread_mutex_unlock(&best->mutex); |
796 if (liveJobs == 0) { |
818 if (liveJobs == 0) { |
797 pthread_cond_broadcast(&best->cond); |
819 ZSTD_pthread_cond_broadcast(&best->cond); |
798 } |
820 } |
799 } |
821 } |
800 } |
822 } |
801 |
823 |
802 /** |
824 /** |
804 */ |
826 */ |
805 typedef struct COVER_tryParameters_data_s { |
827 typedef struct COVER_tryParameters_data_s { |
806 const COVER_ctx_t *ctx; |
828 const COVER_ctx_t *ctx; |
807 COVER_best_t *best; |
829 COVER_best_t *best; |
808 size_t dictBufferCapacity; |
830 size_t dictBufferCapacity; |
809 COVER_params_t parameters; |
831 ZDICT_cover_params_t parameters; |
810 } COVER_tryParameters_data_t; |
832 } COVER_tryParameters_data_t; |
811 |
833 |
812 /** |
834 /** |
813 * Tries a set of parameters and upates the COVER_best_t with the results. |
835 * Tries a set of parameters and upates the COVER_best_t with the results. |
814 * This function is thread safe if zstd is compiled with multithreaded support. |
836 * This function is thread safe if zstd is compiled with multithreaded support. |
816 */ |
838 */ |
817 static void COVER_tryParameters(void *opaque) { |
839 static void COVER_tryParameters(void *opaque) { |
818 /* Save parameters as local variables */ |
840 /* Save parameters as local variables */ |
819 COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque; |
841 COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque; |
820 const COVER_ctx_t *const ctx = data->ctx; |
842 const COVER_ctx_t *const ctx = data->ctx; |
821 const COVER_params_t parameters = data->parameters; |
843 const ZDICT_cover_params_t parameters = data->parameters; |
822 size_t dictBufferCapacity = data->dictBufferCapacity; |
844 size_t dictBufferCapacity = data->dictBufferCapacity; |
823 size_t totalCompressedSize = ERROR(GENERIC); |
845 size_t totalCompressedSize = ERROR(GENERIC); |
824 /* Allocate space for hash table, dict, and freqs */ |
846 /* Allocate space for hash table, dict, and freqs */ |
825 COVER_map_t activeDmers; |
847 COVER_map_t activeDmers; |
826 BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); |
848 BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); |
837 memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32)); |
859 memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32)); |
838 /* Build the dictionary */ |
860 /* Build the dictionary */ |
839 { |
861 { |
840 const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, |
862 const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, |
841 dictBufferCapacity, parameters); |
863 dictBufferCapacity, parameters); |
842 const ZDICT_params_t zdictParams = COVER_translateParams(parameters); |
|
843 dictBufferCapacity = ZDICT_finalizeDictionary( |
864 dictBufferCapacity = ZDICT_finalizeDictionary( |
844 dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, |
865 dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, |
845 ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbSamples, zdictParams); |
866 ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbSamples, |
|
867 parameters.zParams); |
846 if (ZDICT_isError(dictBufferCapacity)) { |
868 if (ZDICT_isError(dictBufferCapacity)) { |
847 DISPLAYLEVEL(1, "Failed to finalize dictionary\n"); |
869 DISPLAYLEVEL(1, "Failed to finalize dictionary\n"); |
848 goto _cleanup; |
870 goto _cleanup; |
849 } |
871 } |
850 } |
872 } |
866 dstCapacity = ZSTD_compressBound(maxSampleSize); |
888 dstCapacity = ZSTD_compressBound(maxSampleSize); |
867 dst = malloc(dstCapacity); |
889 dst = malloc(dstCapacity); |
868 } |
890 } |
869 /* Create the cctx and cdict */ |
891 /* Create the cctx and cdict */ |
870 cctx = ZSTD_createCCtx(); |
892 cctx = ZSTD_createCCtx(); |
871 cdict = |
893 cdict = ZSTD_createCDict(dict, dictBufferCapacity, |
872 ZSTD_createCDict(dict, dictBufferCapacity, parameters.compressionLevel); |
894 parameters.zParams.compressionLevel); |
873 if (!dst || !cctx || !cdict) { |
895 if (!dst || !cctx || !cdict) { |
874 goto _compressCleanup; |
896 goto _compressCleanup; |
875 } |
897 } |
876 /* Compress each sample and sum their sizes (or error) */ |
898 /* Compress each sample and sum their sizes (or error) */ |
877 totalCompressedSize = 0; |
899 totalCompressedSize = dictBufferCapacity; |
878 for (i = 0; i < ctx->nbSamples; ++i) { |
900 for (i = 0; i < ctx->nbSamples; ++i) { |
879 const size_t size = ZSTD_compress_usingCDict( |
901 const size_t size = ZSTD_compress_usingCDict( |
880 cctx, dst, dstCapacity, ctx->samples + ctx->offsets[i], |
902 cctx, dst, dstCapacity, ctx->samples + ctx->offsets[i], |
881 ctx->samplesSizes[i], cdict); |
903 ctx->samplesSizes[i], cdict); |
882 if (ZSTD_isError(size)) { |
904 if (ZSTD_isError(size)) { |
904 if (freqs) { |
926 if (freqs) { |
905 free(freqs); |
927 free(freqs); |
906 } |
928 } |
907 } |
929 } |
908 |
930 |
909 ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, |
931 ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( |
910 size_t dictBufferCapacity, |
932 void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, |
911 const void *samplesBuffer, |
933 const size_t *samplesSizes, unsigned nbSamples, |
912 const size_t *samplesSizes, |
934 ZDICT_cover_params_t *parameters) { |
913 unsigned nbSamples, |
|
914 COVER_params_t *parameters) { |
|
915 /* constants */ |
935 /* constants */ |
916 const unsigned nbThreads = parameters->nbThreads; |
936 const unsigned nbThreads = parameters->nbThreads; |
917 const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; |
937 const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; |
918 const unsigned kMaxD = parameters->d == 0 ? 16 : parameters->d; |
938 const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d; |
919 const unsigned kMinK = parameters->k == 0 ? kMaxD : parameters->k; |
939 const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k; |
920 const unsigned kMaxK = parameters->k == 0 ? 2048 : parameters->k; |
940 const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k; |
921 const unsigned kSteps = parameters->steps == 0 ? 32 : parameters->steps; |
941 const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps; |
922 const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); |
942 const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); |
923 const unsigned kIterations = |
943 const unsigned kIterations = |
924 (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); |
944 (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); |
925 /* Local variables */ |
945 /* Local variables */ |
926 const int displayLevel = parameters->notificationLevel; |
946 const int displayLevel = parameters->zParams.notificationLevel; |
927 unsigned iteration = 1; |
947 unsigned iteration = 1; |
928 unsigned d; |
948 unsigned d; |
929 unsigned k; |
949 unsigned k; |
930 COVER_best_t best; |
950 COVER_best_t best; |
931 POOL_ctx *pool = NULL; |
951 POOL_ctx *pool = NULL; |
|
952 |
932 /* Checks */ |
953 /* Checks */ |
933 if (kMinK < kMaxD || kMaxK < kMinK) { |
954 if (kMinK < kMaxD || kMaxK < kMinK) { |
934 LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); |
955 LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); |
935 return ERROR(GENERIC); |
956 return ERROR(GENERIC); |
936 } |
957 } |
950 } |
971 } |
951 } |
972 } |
952 /* Initialization */ |
973 /* Initialization */ |
953 COVER_best_init(&best); |
974 COVER_best_init(&best); |
954 /* Turn down global display level to clean up display at level 2 and below */ |
975 /* Turn down global display level to clean up display at level 2 and below */ |
955 g_displayLevel = parameters->notificationLevel - 1; |
976 g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; |
956 /* Loop through d first because each new value needs a new context */ |
977 /* Loop through d first because each new value needs a new context */ |
957 LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", |
978 LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", |
958 kIterations); |
979 kIterations); |
959 for (d = kMinD; d <= kMaxD; d += 2) { |
980 for (d = kMinD; d <= kMaxD; d += 2) { |
960 /* Initialize the context for this value of d */ |
981 /* Initialize the context for this value of d */ |
961 COVER_ctx_t ctx; |
982 COVER_ctx_t ctx; |
962 LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); |
983 LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); |
963 if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d)) { |
984 if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d)) { |
964 LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); |
985 LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); |
965 COVER_best_destroy(&best); |
986 COVER_best_destroy(&best); |
|
987 POOL_free(pool); |
966 return ERROR(GENERIC); |
988 return ERROR(GENERIC); |
967 } |
989 } |
968 /* Loop through k reusing the same context */ |
990 /* Loop through k reusing the same context */ |
969 for (k = kMinK; k <= kMaxK; k += kStepSize) { |
991 for (k = kMinK; k <= kMaxK; k += kStepSize) { |
970 /* Prepare the arguments */ |
992 /* Prepare the arguments */ |
973 LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); |
995 LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); |
974 if (!data) { |
996 if (!data) { |
975 LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); |
997 LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); |
976 COVER_best_destroy(&best); |
998 COVER_best_destroy(&best); |
977 COVER_ctx_destroy(&ctx); |
999 COVER_ctx_destroy(&ctx); |
|
1000 POOL_free(pool); |
978 return ERROR(GENERIC); |
1001 return ERROR(GENERIC); |
979 } |
1002 } |
980 data->ctx = &ctx; |
1003 data->ctx = &ctx; |
981 data->best = &best; |
1004 data->best = &best; |
982 data->dictBufferCapacity = dictBufferCapacity; |
1005 data->dictBufferCapacity = dictBufferCapacity; |
983 data->parameters = *parameters; |
1006 data->parameters = *parameters; |
984 data->parameters.k = k; |
1007 data->parameters.k = k; |
985 data->parameters.d = d; |
1008 data->parameters.d = d; |
986 data->parameters.steps = kSteps; |
1009 data->parameters.steps = kSteps; |
|
1010 data->parameters.zParams.notificationLevel = g_displayLevel; |
987 /* Check the parameters */ |
1011 /* Check the parameters */ |
988 if (!COVER_checkParameters(data->parameters)) { |
1012 if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) { |
989 DISPLAYLEVEL(1, "Cover parameters incorrect\n"); |
1013 DISPLAYLEVEL(1, "Cover parameters incorrect\n"); |
|
1014 free(data); |
990 continue; |
1015 continue; |
991 } |
1016 } |
992 /* Call the function and pass ownership of data to it */ |
1017 /* Call the function and pass ownership of data to it */ |
993 COVER_best_start(&best); |
1018 COVER_best_start(&best); |
994 if (pool) { |
1019 if (pool) { |
1007 LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); |
1032 LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); |
1008 /* Fill the output buffer and parameters with output of the best parameters */ |
1033 /* Fill the output buffer and parameters with output of the best parameters */ |
1009 { |
1034 { |
1010 const size_t dictSize = best.dictSize; |
1035 const size_t dictSize = best.dictSize; |
1011 if (ZSTD_isError(best.compressedSize)) { |
1036 if (ZSTD_isError(best.compressedSize)) { |
|
1037 const size_t compressedSize = best.compressedSize; |
1012 COVER_best_destroy(&best); |
1038 COVER_best_destroy(&best); |
1013 return best.compressedSize; |
1039 POOL_free(pool); |
|
1040 return compressedSize; |
1014 } |
1041 } |
1015 *parameters = best.parameters; |
1042 *parameters = best.parameters; |
1016 memcpy(dictBuffer, best.dict, dictSize); |
1043 memcpy(dictBuffer, best.dict, dictSize); |
1017 COVER_best_destroy(&best); |
1044 COVER_best_destroy(&best); |
1018 POOL_free(pool); |
1045 POOL_free(pool); |