From ac7992896077695b576587431a1e254693d690c8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 14:11:55 -0700 Subject: [PATCH 001/145] version one complete, can compress a file given input and output names --- contrib/adaptive-compression/Makefile | 26 ++++++++++ contrib/adaptive-compression/v1.c | 73 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 contrib/adaptive-compression/Makefile create mode 100644 contrib/adaptive-compression/v1.c diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile new file mode 100644 index 000000000..12607edff --- /dev/null +++ b/contrib/adaptive-compression/Makefile @@ -0,0 +1,26 @@ + +ZSTDDIR = ../../lib +ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c +ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c +ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c +ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) + +DEBUGFLAGS= -g -DZSTD_DEBUG=1 +CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ + -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +CFLAGS ?= -O3 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wformat-security \ + -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \ + -Wredundant-decls +CFLAGS += $(DEBUGFLAGS) +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +adaptive: $(ZSTD_FILES) v1.c + $(CC) $(FLAGS) $^ -o $@ + +clean: + @$(RM) -f adaptive + @$(RM) -f tmp* diff --git a/contrib/adaptive-compression/v1.c b/contrib/adaptive-compression/v1.c new file mode 100644 index 000000000..ef8062d8f --- /dev/null +++ b/contrib/adaptive-compression/v1.c @@ -0,0 +1,73 @@ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define FILE_CHUNK_SIZE 4 << 20 +typedef unsigned char BYTE; + +#include +#include +#include "zstd.h" + + + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + const char* const srcFilename = argv[1]; + const char* const dstFilename = argv[2]; + FILE* const srcFile = fopen(srcFilename, "rb"); + FILE* const dstFile = fopen(dstFilename, "wb"); + BYTE* const src = malloc(FILE_CHUNK_SIZE); + size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); + BYTE* const dst = malloc(dstSize); + int ret = 0; + + /* checking for errors */ + if (!srcFilename || !dstFilename || !src || !dst) { + DISPLAY("Error: initial variables could not be allocated\n"); + ret = 1; + goto cleanup; + } + + /* compressing in blocks */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + ret = 1; + goto cleanup; + } + { + size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: something went wrong during compression\n"); + ret = 1; + goto cleanup; + } + { + size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: could not write compressed data to file\n"); + ret = 1; + goto cleanup; + } + } + } + if (feof(srcFile)) { + /* reached end of file */ + break; + } + } + + /* file compression completed */ + { + int const error = fclose(srcFile); + if (ret != 0) { + DISPLAY("Error: could not close the file\n"); + ret = error; + goto cleanup; + } + } +cleanup: + if (src != NULL) free(src); + if (dst != NULL) free(dst); + return ret; +} From 00b5e6c512a2c9d8bc0db321ff1f79889d803c33 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 14:18:46 -0700 Subject: [PATCH 002/145] continuing work on v2 --- contrib/adaptive-compression/Makefile | 9 +++- contrib/adaptive-compression/v2.c | 73 +++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 contrib/adaptive-compression/v2.c diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 12607edff..b2c47654b 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -18,9 +18,14 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -adaptive: $(ZSTD_FILES) v1.c +all: clean v1 v2 +v1: $(ZSTD_FILES) v1.c + $(CC) $(FLAGS) $^ -o $@ + +v2: $(ZSTD_FILES) v2.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f adaptive + @$(RM) -f v1 v2 + @$(RM) -rf *.dSYM @$(RM) -f tmp* diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c new file mode 100644 index 000000000..ef8062d8f --- /dev/null +++ b/contrib/adaptive-compression/v2.c @@ -0,0 +1,73 @@ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define FILE_CHUNK_SIZE 4 << 20 +typedef unsigned char BYTE; + +#include +#include +#include "zstd.h" + + + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + const char* const srcFilename = argv[1]; + const char* const dstFilename = argv[2]; + FILE* const srcFile = fopen(srcFilename, "rb"); + FILE* const dstFile = fopen(dstFilename, "wb"); + BYTE* const src = malloc(FILE_CHUNK_SIZE); + size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); + BYTE* const dst = malloc(dstSize); + int ret = 0; + + /* checking for errors */ + if (!srcFilename || !dstFilename || !src || !dst) { + DISPLAY("Error: initial variables could not be allocated\n"); + ret = 1; + goto cleanup; + } + + /* compressing in blocks */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + ret = 1; + goto cleanup; + } + { + size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: something went wrong during compression\n"); + ret = 1; + goto cleanup; + } + { + size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: could not write compressed data to file\n"); + ret = 1; + goto cleanup; + } + } + } + if (feof(srcFile)) { + /* reached end of file */ + break; + } + } + + /* file compression completed */ + { + int const error = fclose(srcFile); + if (ret != 0) { + DISPLAY("Error: could not close the file\n"); + ret = error; + goto cleanup; + } + } +cleanup: + if (src != NULL) free(src); + if (dst != NULL) free(dst); + return ret; +} From 0887e98d4bb9f8a62b29d0dcb9b0af7dd732aaa5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:28:59 -0700 Subject: [PATCH 003/145] finished main portion of code, now need to debug --- contrib/adaptive-compression/Makefile | 1 + contrib/adaptive-compression/v2.c | 285 +++++++++++++++++++++++--- 2 files changed, 253 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index b2c47654b..fbab219c3 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,3 +29,4 @@ clean: @$(RM) -f v1 v2 @$(RM) -rf *.dSYM @$(RM) -f tmp* + @echo "finished cleaning" diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index ef8062d8f..f85ccfe19 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -2,72 +2,291 @@ #define FILE_CHUNK_SIZE 4 << 20 typedef unsigned char BYTE; -#include -#include +#include /* fprintf */ +#include /* malloc, free */ +#include /* pthread functions */ +#include /* memset */ #include "zstd.h" +typedef struct { + void* start; + size_t size; +} buffer_t; + +typedef struct { + buffer_t src; + buffer_t dst; + unsigned compressionLevel; + unsigned jobID; + unsigned jobCompleted; + unsigned jobReady; + pthread_mutex_t* jobCompleted_mutex; + pthread_cond_t* jobCompleted_cond; + pthread_mutex_t* jobReady_mutex; + pthread_cond_t* jobReady_cond; + size_t compressedSize; +} jobDescription; + +typedef struct { + unsigned compressionLevel; + unsigned numActiveThreads; + unsigned numJobs; + unsigned nextJobID; + unsigned threadError; + pthread_mutex_t jobCompleted_mutex; + pthread_cond_t jobCompleted_cond; + pthread_mutex_t jobReady_mutex; + pthread_cond_t jobReady_cond; + jobDescription* jobs; + FILE* dstFile; +} adaptCCtx; + +static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) +{ + adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + memset(ctx, 0, sizeof(adaptCCtx)); + ctx->compressionLevel = 6; /* default */ + pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); + pthread_cond_init(&ctx->jobCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobReady_mutex, NULL); + pthread_cond_init(&ctx->jobReady_cond, NULL); + ctx->numJobs = numJobs; + ctx->jobs = malloc(numJobs*sizeof(jobDescription)); + ctx->nextJobID = 0; + ctx->threadError = 0; + if (!ctx->jobs) { + DISPLAY("Error: could not allocate space for jobs during context creation\n"); + return NULL; + } + { + FILE* dstFile = fopen(outFilename, "wb"); + if (dstFile == NULL) { + DISPLAY("Error: could not open output file\n"); + return NULL; + } + ctx->dstFile = dstFile; + } + return ctx; +} + +static void freeCompressionJobs(adaptCCtx* ctx) +{ + unsigned u; + for (u=0; unumJobs; u++) { + jobDescription job = ctx->jobs[u]; + if (job.dst.start) free(job.dst.start); + if (job.src.start) free(job.src.start); + } +} + +static int freeCCtx(adaptCCtx* ctx) +{ + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const fileError = fclose(ctx->dstFile); + freeCompressionJobs(ctx); + free(ctx->jobs); + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; +} + +static void* compressionThread(void* arg) +{ + adaptCCtx* ctx = (adaptCCtx*)arg; + unsigned currJob = 0; + for ( ; ; ) { + jobDescription* job = &ctx->jobs[currJob]; + pthread_mutex_lock(job->jobReady_mutex); + while(job->jobReady == 0) { + pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); + } + pthread_mutex_unlock(job->jobReady_mutex); + + /* compress the data */ + { + size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); + if (ZSTD_isError(compressedSize)) { + ctx->threadError = 1; + DISPLAY("Error: somethign went wrong during compression\n"); + return arg; + } + job->compressedSize = compressedSize; + } + currJob++; + if (currJob >= ctx->numJobs || ctx->threadError) { + /* finished compressing all jobs */ + break; + } + } + return arg; +} + +static void* outputThread(void* arg) +{ + adaptCCtx* ctx = (adaptCCtx*)arg; + unsigned currJob = 0; + for ( ; ; ) { + jobDescription* job = &ctx->jobs[currJob]; + pthread_mutex_lock(job->jobCompleted_mutex); + while (job->jobCompleted == 0) { + pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); + } + pthread_mutex_unlock(job->jobCompleted_mutex); + { + size_t const compressedSize = job->compressedSize; + if (ZSTD_isError(compressedSize)) { + DISPLAY("Error: an error occurred during compression\n"); + return arg; /* TODO: return something else if error */ + } + { + size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); + if (writeSize != compressedSize) { + DISPLAY("Error: an error occurred during file write operation\n"); + return arg; /* TODO: return something else if error */ + } + } + } + currJob++; + if (currJob >= ctx->numJobs || ctx->threadError) { + /* finished with all jobs */ + break; + } + } + return arg; +} + + +static size_t getFileSize(const char* const filename) +{ + FILE* fd = fopen(filename, "rb"); + if (fd == NULL) { + DISPLAY("Error: could not open file in order to get file size\n"); + return -1; /* intentional underflow */ + } + if (fseek(fd, 0, SEEK_END) != 0) { + DISPLAY("Error: fseek failed during file size computation\n"); + return -1; + } + { + size_t const fileSize = ftell(fd); + if (fclose(fd) != 0) { + DISPLAY("Error: could not close file during file size computation\n"); + return -1; + } + return fileSize; + } +} + +static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) +{ + unsigned const nextJob = ctx->nextJobID; + jobDescription job = ctx->jobs[nextJob]; + job.compressionLevel = ctx->compressionLevel; + job.src.start = malloc(srcSize); + job.src.size = srcSize; + job.dst.size = ZSTD_compressBound(srcSize); + job.dst.start = malloc(job.dst.size); + job.jobCompleted = 0; + job.jobCompleted_cond = &ctx->jobCompleted_cond; + job.jobCompleted_mutex = &ctx->jobCompleted_mutex; + job.jobReady_cond = &ctx->jobReady_cond; + job.jobReady_mutex = &ctx->jobReady_mutex; + job.jobID = nextJob; + if (!job.src.start || !job.dst.start) { + /* problem occurred, free things then return */ + if (job.src.start) free(job.src.start); + if (job.dst.start) free(job.dst.start); + return 1; + } + memcpy(job.src.start, data, srcSize); + ctx->nextJobID++; + return 0; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { const char* const srcFilename = argv[1]; const char* const dstFilename = argv[2]; - FILE* const srcFile = fopen(srcFilename, "rb"); - FILE* const dstFile = fopen(dstFilename, "wb"); BYTE* const src = malloc(FILE_CHUNK_SIZE); - size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); - BYTE* const dst = malloc(dstSize); + FILE* const srcFile = fopen(srcFilename, "rb"); + size_t fileSize = getFileSize(srcFilename); + size_t const numJobsPrelim = (fileSize / FILE_CHUNK_SIZE) + 1; + size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; + adaptCCtx* ctx = NULL; + /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !dst) { + if (fileSize == (size_t)(-1)) { + ret = 1; + goto cleanup; + } + if (!srcFilename || !dstFilename || !src) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; } - /* compressing in blocks */ - for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { - DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); + /* creating context */ + ctx = createCCtx(numJobs, dstFilename); + if (ctx == NULL) { + ret = 1; + goto cleanup; + } + + /* create output thread */ + { + pthread_t out; + if (pthread_create(&out, NULL, &outputThread, ctx)) { + DISPLAY("Error: could not create output thread\n"); ret = 1; goto cleanup; } + } + /* create compression thread */ + { + pthread_t compression; + if (pthread_create(&compression, NULL, &compressionThread, ctx)) { + DISPLAY("Error: could not create compression thread\n"); + ret = 1; + goto cleanup; + } + } + + /* creating jobs */ + for ( ; ; ) { + size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + DISPLAY("Error: problem occurred during read from src file\n"); + ret = 1; + goto cleanup; + } + + /* reading was fine, now create the compression job */ { - size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); - if (ZSTD_isError(compressedSize)) { - DISPLAY("Error: something went wrong during compression\n"); - ret = 1; + int const error = createCompressionJob(ctx, src, readSize); + if (error != 0) { + ret = error; goto cleanup; } - { - size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { - DISPLAY("Error: could not write compressed data to file\n"); - ret = 1; - goto cleanup; - } - } - } - if (feof(srcFile)) { - /* reached end of file */ - break; } } /* file compression completed */ { - int const error = fclose(srcFile); - if (ret != 0) { - DISPLAY("Error: could not close the file\n"); - ret = error; + int const fileCloseError = fclose(srcFile); + int const cctxReleaseError = freeCCtx(ctx); + if (fileCloseError | cctxReleaseError) { + ret = 1; goto cleanup; } } cleanup: if (src != NULL) free(src); - if (dst != NULL) free(dst); + if (ctx != NULL) freeCCtx(ctx); return ret; } From dd96efa9efc20b298ec152daed9c9b18d641e3f0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:44:22 -0700 Subject: [PATCH 004/145] added print statements for debugging, fixed long memset by changing to calloc --- contrib/adaptive-compression/run.sh | 2 ++ contrib/adaptive-compression/v2 | Bin 0 -> 467088 bytes contrib/adaptive-compression/v2.c | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100755 contrib/adaptive-compression/run.sh create mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh new file mode 100755 index 000000000..f9e276730 --- /dev/null +++ b/contrib/adaptive-compression/run.sh @@ -0,0 +1,2 @@ +make clean v2 +./v2 tests/test2048.pdf tmp.zst diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 new file mode 100755 index 0000000000000000000000000000000000000000..974745ab75ce65419904f0cdf236697bc45f05ca GIT binary patch literal 467088 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
>M___i%!>umw~Jd3_HL^9|L!ZcYrQE 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⩔ybu8UsnrAI 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%0A0lBswVoEpIEG!#g!J!0Q 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

1Jy*JLZ-VHWG!S`1Cj!| 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 Pn>WobB7G$Z=z>cCDk!J8 literal 0 HcmV?d00001 diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index f85ccfe19..4a171bd09 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -45,6 +45,7 @@ typedef struct { static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { + adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ @@ -53,7 +54,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); ctx->numJobs = numJobs; - ctx->jobs = malloc(numJobs*sizeof(jobDescription)); + ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; if (!ctx->jobs) { @@ -95,6 +96,7 @@ static int freeCCtx(adaptCCtx* ctx) static void* compressionThread(void* arg) { + DISPLAY("started compression thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { @@ -126,6 +128,7 @@ static void* compressionThread(void* arg) static void* outputThread(void* arg) { + DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { @@ -202,6 +205,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 1; } memcpy(job.src.start, data, srcSize); + pthread_mutex_lock(job.jobReady_mutex); + job.jobReady = 1; + pthread_cond_signal(job.jobReady_cond); + pthread_mutex_unlock(job.jobReady_mutex); ctx->nextJobID++; return 0; } @@ -209,6 +216,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { + if (argCount < 2) { + DISPLAY("Error: not enough arguments\n"); + return 1; + } const char* const srcFilename = argv[1]; const char* const dstFilename = argv[2]; BYTE* const src = malloc(FILE_CHUNK_SIZE); @@ -228,7 +239,7 @@ int main(int argCount, const char* argv[]) if (!srcFilename || !dstFilename || !src) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; - goto cleanup; + goto cleanup; } /* creating context */ From ff9ac637d9f11e9e8e574ccad6989aafb8018a37 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 17:44:40 -0700 Subject: [PATCH 005/145] removed unnecessary files --- contrib/adaptive-compression/v2 | Bin 467088 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 deleted file mode 100755 index 974745ab75ce65419904f0cdf236697bc45f05ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467088 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
>M___i%!>umw~Jd3_HL^9|L!ZcYrQE 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⩔ybu8UsnrAI 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%0A0lBswVoEpIEG!#g!J!0Q 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

1Jy*JLZ-VHWG!S`1Cj!| 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 Pn>WobB7G$Z=z>cCDk!J8 From dd447bb9a73bc4cee24c545f35349d88bdb88287 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:00:55 -0700 Subject: [PATCH 006/145] fixed an error with number of jobs --- contrib/adaptive-compression/v2 | Bin 0 -> 467087 bytes contrib/adaptive-compression/v2.c | 32 +++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) create mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 new file mode 100755 index 0000000000000000000000000000000000000000..0c0e194ebae05845c80f8f24d2534a53a56c414d GIT binary patch literal 467087 zcmX^A>+L^w1_nlE28ISE1_lOx1_p)?tPBjT3EH85kHqGHg)U`1q34iV`RX>@HOEK5#KG zfXuV*0_kF40P$H6${83~7#fhd@$tnarAftbq4;=I^K_=Ng3Vh5RRQBexeOdo^APR> znHQgtnp2QkgvEUqpytVe9Ld1I0OEtf9cCV;`;znXiV+@0ci)F;5ceGc1u!V?rm-@h z_!n+4gb^Q~TwGF=nU@aXGN7BcWdp=KGd_p{5Ff&VkSJ~i`>!;wJTosPJ~J;ZAIwJ$ zzc>3J=5av%3*w`j0Wldug1m8q5tL3`977x-Vqo$B#2f|zFpq%&nm%BBPTrWKHi z`1rh>_~Odkr2L%tf|4SJ_;~E*!Hff$2htDH3&NoIlm;=NI6gi-9}x?f?h}Bz4`v>S zk8YkU)GQDcAD@$m&wUC|^KO6?GcYiK_{ipg{3{QY15xqu=;;K_zXu`Vw*jOWgbxx* zCn<>~i6Dg#j2?a)pyt8S8i-FQULoa(kEfrvtBWVB47dPs7efLxfn0#(+X(>>KFFUC z7Q`J&3=9nhAm#;DaMTN2U?AR!QrxWLLF!NkB&aEX;6L4<(;IRIz`&qi zT3n=`lbNKSQkjziRSQZ>Aa`UOjGa)LYuz>HV6N2nE9J*k7#P6*VBq0m0AVc{AF5A- zfuRAUZ$SqnT%f`XAbTtr7#KiuAU`tMTw?*1d~mySGLwQUi%U{-^_=ulDswWEAUf5c z=3jWm%Fw~gz@Q1`N4#QXSOHNoN{)uWXb6mkz_1K~P>;^19*u7nfSR?PwI4h>OJ8_& zhCcA<^u6KJ>3ZR~>lu)@kGq}#Q6AmCA3T~5vUqg*?(pdL-Qm&cyTRkQ>kO#e4Ug{7 z86F28F?ldv^yu_m;nD3o!=w42fJdkA43BPKH0cFUX$PqE43C2knLMn0FYveVf?RXl z^~d}F|Np;~WMW|00cvx09`oq+J>k>qdd8zW^oB=w?F^5D&zU?Je|dC*xKOLRYcF_c zUiav%z2MPZyTGG6bcIj1?+c&q&<8%9A}TNTJZE6==yW~c(OrAs#r#JM49!0n_*=jh zcDo+%=yd(@as?v;gGYDmhZh?d85lZUCv>}>;os&vq4^-A;P5yorgS* zAN>FS|9_9((h0|0Co*)qo^j|n?$A*?$*0@(gHPx27gsu18IHSlfWqpyYY(Un_vmcR z`2YWZw`)(g?-_{k&Grlo4Bf6Bovtn2u05RxLGJHn>FO@+X@1Ap`SJ1tW{_M*w`)sx zX;0@TkM7zPoh;M4U3;1jFm}53yxj2r|NrBz;1+$iYlF4x3I67b3=9lAK-P8pHguly z=q_Erzl}$%v$qChHJH5o;NTC&&ejT$fNMj?!H(VnF!N0F0Y>dskUEf9AxI3&>pbMq zUAm&1Wm;!z30M$Jf{g-Ou%er18gnnmILj0K&DtOzffa(ivmvc}E6Dh?P7|+gurSE! zV8+W$VADVvyOB&|W|`K>GOgRS0i@8i;iWPtn4yv7(e3)fqwxqR`NkgRmv>+Qr)N+? z?ymjN&EDzyqTBUFr|XYy*B?mojc*J%K#7Wjfx)r!fJbldhwuOY`*id2Z(?Qe?dJ69 z^!?${x%b8Q|NlKYw>|(qwxsH-q^!0Znd#8r18tUFhJbf{Kmlp zr*8u&Z@rlC50u{bMu1${8u0)BfBtQ)L12y#IEqR)fRgWvpDqjx9-XZYV6nX*>V?{U zXvTwv4oJ|W)AhtlMkWRZkKR_0k>KK?J9L6acWFcSUa*PacyayU(HZ)}qqFouXYC!Z z|2kbkdHutS2c8TJh9^5tHUD7fm}(DlPb;W72v*bW`@*C7;2#gm6a3Q-L1epHL26bo z^0$JTk}r2KFfhE(^@ci35fnh(wI4jXOCNNH-g%+o4Js5$e|U6$_h>%O=+RmGql4|m zKQD-KhzX#q4UG-3o)2Ew^qhj~X+gK*AU5U5<}JXc;3Xrdkc3(N;*JMW7}$ZsKoIN| z*B>6%t}pl}9qi}@33s)E$Q3ZpgPOs}R+(b8Y8JvOsBz8c8vl4=@$<_wpt$b^r<506 zuFx<87rMt?PkaQK$^miY4p1ua=ypBf(Rc)uMq>}Bfq@>sd<)2ru>2C>(d{aLEzkIL z`ksK4@2)pII$d`_Jq49qs7^a61_8jpZn8GHDJOcN^u zN_;fFaZmu~+Yk5u|93S!;JA}Tfq}ub^OR4w?Cv!vez*?``K=GY)YKbbs`mnzI_S}P z-lg-HPv<3%&ejw6|Nno%)WphgycJ|7HvTG=h=IsTU^y0f40|Wo|6P@QhI`=vt#66mi zGrhQtBxivl$MRx7lAHlV4&;1~?x|o?n5Tj@Fi!=`H6LOFH{AAWfK^Tf+3W!l?3}6q z5dl-o5WSsKB_M)e3Tjg4Q~`(}n1Y(yIh6w<2=;jMA!d(*ub4bKr!qi9A)x}{bx!^9 z7gU!+C}x=82Z$h;f(yQY2!geDPJIC3fwgx|y#e8YHFQqB0O5f(bWS}1=IsT=<_rJt z|NnQk9{3BcIS>5>arS_vr-H-4dMYTi_}kFqTALq~+4dd)dtfU#R3IVxqR$pw)ouVA zvKK_X*nJ&Zya|I#v{sOyM<+PpynMt5HfRRKAaF=_gX83dE5x7`5Q9L}iy}0GK!PxX zWQ;)EmIo1Lvw{{H{}VjDyRzT zIwl?JmJHC7Nz%bRy|ahv)_;OSoJ{HJWZvn1G7ffB*kO?bd|o1}9^{Zy08% z1VRi0g#*Gc5U+#ng(`l-FawDNVi+h`5Qc$x9c(XdI6wjkhc_|9Y`X(Ewn3qUFbu@& zV0+OGHLL|^AXP$iV+|xxqNJao61G2swECQ0L0m*!`VPNQ<3SxqU zKqb)2{NJFOs}IqZLIfxpN_dKjyL6sJ!npGe* zy{%Axg01X^)S2B|!NtyAP@(0~*$W!p@aSw!05w25r$&IOt)QM_C!|By4IThNbPhoZ zUr5>@bz!QJi&e03-MwIQkUDc9HC?SB5?+FQ0d?jQY>^XO_g;|c9^GL3yQhLWlrQ{1 zA>6$cY%aWT0`WT7UMN9zBlqpF7{QIli2F8#9B>v?t6_J52Q~-K#jXD3o`3)UzXWxX zpgLb%utEyRDsVuGLmB}fFM3#myV?Ap9wA7ws})4TL(mkg5{rkW@Hn9f)IWka0TQ!N zJDPEtcE%c%B(cW8%k7}_(%TBj-jM9tJr$H{VYw8V%zS5fbh=LP=nU=f=qzpM+zT49 z^5}M*;nCR&8fo(AuAT7Wh$T{3RD#0-IyL|rn(ROx8vx06wSq`^U>pG@CqZjyY=Y*f zK!$=xCZ>XHd%+6!M(GTAu~j>xgYCtAD~Jx{BvcC-(12(=X9e;-y0#TiZ7oiEg-_M;2heqUqTK)q`QhW^Bqqqt-AtOv0uBWi;r(i*sg3VNeDR186Lt z@UV8>0gh#ma91mcgnJ%3r~`MpI##QYM}r{7!A67N8sC{?@iTNZs1;PN!K%sbso=IU zYV`pcg~dJE_sJB=Z>3u7ej&!e;uo%w535G#XdkF~@M4Z3xJd*m!#i6GKy8;UhzKZmclUxs z6c2Xx=73}hATpqI(G3;>$z*_J!VE!uFAx(X1ZwX<`(4oSb)1@G8P9KGen)=px$`mkr`-1aG)VXXl!-2f;for#51`hj3;K#K=A`;M6i>{@x)EJ zWR53(pN{4k&=4ZrGawFPJn?@H=6GV`8v)RqBK9es4=+5Pf`$}#@NaY7(doJcX_6H* z9kT#D9djJqhK97PKohA`!EIUiRBMMvcQ06^@!%X#yR8$#1JAjlDQ$o$Z9d5Bfm

0;n-1&!K) z8};3;Cz=m{W&&T{0?qM)+wP$BaJ)6<|NsBs`CTxp+xG-?DjBq#s~fDYvo!=fvkFoP zF}$0liy0~f>P>Y+6l?`6_~_9cI-!%Ls~c<{XwntTcsU1b21vO_x9@~bMyT1YM~;Ih z|3HrE2D7YP5AZiPgQtDLeZ7Mo-BUp!#=nh)tFsl<@#+SXV5dyzX6foY)_jby)1<4j z)dOq?NS8y$@oq2|q-rXt9q-U#;|0-(O|Hg^5n?;o!5|Y}=7HLg-L4akgPg|T(e2s+ z?P+%VZqfEV)BKXLv)2Y3n4oF`r09672|O@CEJR=?gS`V%*9jh0?*^*`xh=E-G(o== zq?~yx$T;vcJ-Et&46{PU;k&^qKvVYMDNxX?J=h4ab3vM4c7puw3Z8U#J<-{!1LA_o zPH=4osnbAC?*&OgBe@UkF3`|+=RuF`lO`U_pb6Su&^*BLRvEZwKrDo39Kf1DVjvGp z1^_ zM_RY<5ua}G(gL4u-;QqA6E5AMXB?XkF?O=3xO9f7aJ+~DtxrH(Ls7e<+jWa$$6=R_ z*j>k5cQS(J&^$W9lMz0h$342Yf+h!EG^K-zW#1JZ;CW%_GzWM+0I0YHO|e1N12~2| zh6Q^xz5%Tr?RJ%L=?)ce+-<_Zz~It(+o!j5fls%s=p<0BV(-&g`@o~Kc1CCE6i|1v z)3?P3vM$1>6I{BvbWY6yPjG{hGi2~W+jUB3YYI57K!Vz>5C!1U7!m|No!>n=uX%Ld zdZC>Ts(?V543yX)`ODh1g}+&h3ARR~7bFW_MbY8VWfSJWzwLaQW9Nx9m(JEGkR?d+ z{M&rO7(08xSsPrMYJ=IGy`aS34Pl?^_L;h%vlpD_e5YuGr7k~!<}=qR+K~JTEI7$9h)CyF z(4-n9OIw38Fh96s3lRZjr_dRY_9nOl1t|t+4-n(!QAEB0nG1^}W|yf8I$fqNfFua8 za!@LGc^lmGWv%Q7C36OkZr=x>iG)^g3%BtIC^bhr#yQ5v9)8i4g0h|j*ZLOyeo!hW zb$yE^sGZo&`>7A)4C2fwGAC9+6$^^UbOFlR^H&5w{D0WXn9H_Xqpzh^awJ%1t|g`3jx5H$fMhJ zfk!iVIu%kDf-6~%PH;Z+=&oJxBKkIX(G19CkZkDD4bHqC-H?VFBsubLYxM%NqZ`yM1}`)C@Zy~s#Gz9THaukH-*(WWyY`1icjya`=0iU{EU)uVf{1@q z`~Y4%1PYZGUK$WREy|!U0&6J!09riM?R)3NTpduaH1tR3fzD$djGsI@Lw~%Ggla(P z_WtnbM(9e%qU*6b9&5}nH9(w>FjE*)$xBeF4{i*B`pGXoszPkQ;`x6d&rd`3yapEU zuG7Zw?#l(ROp3H_=pQKKwPQ0O2a5@ZK`{qfmcw_)n?=mj@TUt}vmeANo> zKH^y(1B%Sp!{GH?&2K2{zlMJX_a-6hVY+rg96;0cFrj3vhso~(m0xsO55v-hR@#BO ztne-$h=b_A^2B5IUqQ2Iy{#Yi!`8z%z}Ca8?nLp!eo)A7eE_DW-T+g*7r+$idYJwH z|G)SY2Wmcm%;aHU-~kPWf)sX71?%qa1?dK@hXD)jJps}IT@Pd4iRPID`;phffHX87 zac}?)j=Zps!|cD}E5Ez1(W3mG04iuHSr20_1j^YoS`YJF8TWda(@1jQhi9C*!3H+VVE=z5rL$a)z2E1)GXB(I0@766Yig2o#?x>44{)JcKscJP{)ZpeC= z)mxy&8>naP(T%bmh6!R2Xt2_wdn@vK7*mKr;59GZdqLETBs7CSg0R&mJ0UA{Kx3R9 z-CL2@!@Q9My9m7IrF$=kdLfBs5J(VaPyxgs(2yvo*8^S_16~~TVll)Z@S2zIy&&qv zh0Um50|~+mQim7>3ZoZ08b72`u?T@n`c-2L6_rpye+++Iy&S4XK%wT*y%#j~kLnRKh!G$O zgb^U#i#1Rq(ASC(3YsUNR%|yoFc2n!X69c+;WrUyNKJy62nrj7i6Gt!4*VwK3^)gf ziJ+iDmpbU8_fuuVetsG1Q(L!5JrH)!UIo^ zS_{(DyA|R!Sn4?r-ctkSVa-Do5KR!zfEPP<9_WN>0(l#2u5pHFf;b<%CK90u9LtM*gx^()$_VgDDQV^Yx)Y}c!wiPiHz`Bxzphi>f* zkM7V89OD_Fy@@ZXpfS<{oz+@N}plO@O| z6pa(HXyk=z#AR75rXKJ{MN}u5W2$=j2a(chC%h09#TgLT5*TQY9Tt1egIcZKC|-X# z7m=b|VN0JNX$glRU04h`08Tuxg*`X|{vxJEkKR^L!R*luSxnhY`l_Bk!WaQ_0=&}( zdpQOgBzX}54NTaG*9njA(gPmdp&Pn=*SuH_suj9JPau-Vi5Kcn4JgS2MOP^nT^~gt zPQz-A3#JB-?phQx6)}~djCdV*@m~mH1ETN&nGm|eqxlfFQ~@2YLa}6z1UNS!dlzNI z3$`{2k|eO1P=&<==!h3|6%cg93&q|en7TZADP02eUJxTZ2o8UGLlY1zmQV&V=ZS+u z2W9wE3aSAmF`($mz@qC3G^(&#V~(i-efU!ZQwd69`0(PB08Y>2Nen0sUoVE?U6jNC zTS$eH7*I^e#bN?9F+i6LK@$UtuI-q*pzFI|4gJC2GXDdiGXB4D~evI@q!w3ASOr%w85i!FNkD-j^GiwUI;V_MT_-9&+mdp{h;fG zq?SP9k*4FVOW%NPByqja{}Pn3O3*wIt=0=oDM1^e1P!r5V+(z~&>T+^##^mX{Qw&4 z>?Cr$waJsr@z&$TXr93{-g@2x*Ldp>kIqu)UT5D2KAoxNJ8XN?mnWlr!SUf;M(D}jeWalZ5@1;LF4|e*# z0ngDKe8BZSDKSk$=)b$A*WDj{MsW;wmrD7Jqp(*S=uj zhfVVQ_zY^5odZXu>kaTmZTK=I@b=El$WpD~mDJGlCZK!NL9>1@3f(~I&i4gmL4@mx zmo*@5&9yfe`CD~BOk_Q-5BRqkfVH`yYCG`K6{NPi7PO7|#)~Y_pkuG;<3d)3PS-2l zu2(<{Fiie|rr^NqY(Xb$bb~fqgH94S=hJ!6r`vUhbL|84 zFnI-bit7arYu5*eHB;a)Igc!b7A9b)gC;d!#JYmR1azXw575T&m!Q*1Jeq4SV1&sF zk8al+kT7}h!U$E{j+dabzdX8YA9!?^UU(4(+N~P;~UV<>m8u|+K!z_`@`e&Xtl)g`CzM|ao-F&p$rml?a-;wAK+wA304J-n7zM2rPH3~+C2>XEqV|c zj9~Za_63!B2Ru4iR9mj6-Gx*Mvt6)#bOH0u|sL7MRp z97qd@3sS9p5Y^zN6No%e018sb`YjKf=|6||>0cXE{B`qw$p!^2k?G$7BmD=UoK?|W z`+$+Z8FaLr;kO;2J{Iy~)(VIuCj@zhniK(V+9C_}iC&ywb~R2G;L;z$00t!-MhA|3@C(t}pnv zd9Zb!@HqGYRD*r+U}o~@WP*ybg2gpFxFPsS%K2NfY{#;vmdf4ruM^&UXcB+e_lc+e``Oy0JV@h zkH2VzhcGkX5`okgouE})wLiMqA?tY{UOo6+q7hW=|L=DFbMOI&Dg(oR z2F~Lk&I=AzhW`vOj)dxe1`wz7N9#^}mPW zaZo&a9DJbRu@fW$vd}~Ega;=~TmmZopMe2IoC6{bat5;a3y3&K7)AXH2@kMm;0C|Y z@Bn)VAz@76+H@{H;m#3gJzT7!Fk9#!#ci?XW9RYgW^$+M&*q46bLIYe% z3V?Enhqa>ue^Vd>1B2lK@FL9*-A)__UoeAnad(jbcrHrwrw8LXkIo{nj&4Ve<_GNH ztmXT`qq|bzg(7%4Cb;B0?)nE5LN7t_11VV@z?wX)9VPf%Ap@zd9}rol8R|CB!r7Ob zKypTUNr+P&jKCx*U9M8?fSx{ z)AbJ|YbapL@8HwrK&cs;zCD`X7=X)@&e9K^p>IG#TivB^UVu(7_ULqd04^%KYd>`H zxOe#82g!m&Kuf$G`L~rgFuuqDox8ow^}a{*5d*CDf!42sEa)u#a@_R;C|5#Eu?H=V z>@MVJerDhKt@ESD<>%V2Uz#7-cY@;l@_P?RS_0(=)cs$bwLdybzjT*=0U5(w&e2)U z0WN|N{sE`A7xsVt|3}ULwhRmmX`QbBUP6YsK)V$|)ikJr0+q=x__qZx9(=^XcrmT> zTv})8KmP5m|2&$HD1br>UVeeR01fRYFQLu`EulURUPKN`$=zURzI!T2w(}Zvn-Bjsk#fkX z0~it^<&2$E!7Jy%p`Z2utXfBVN5t3a#n!8S98 zPUyJU(TfN|rtVf3&~}4^%?BAfd%?^D;KLW;VKxn-!Wz7Op1%pS^AfT}hS1vb&ERG& zt85~uSqoZQegm|`6>?f(;}K9gi*}5Gt}fRx!)VVcq;-A(CEK)4*KaR0exhcJAN<=( zzr8SGU|@Ku3Q9kqrLdi@FVebwzjd4fwbD8SKq?OY<8bKs1m*`YLNW!U{z5W4t=sh* zC~Ks3vV*b=v_F7UzJSgG?DTyC8ja{Kee&Y&@BjaIf=brTQ?LU$L8C_ChK=hB(6|_6 zkJtf^&SNi}OhKLpo$&|SQWyGye_Kfx6R5%c;u(1MA2j0T`+|R)OCvL+DB_og)YrJ} zj|JHeN{YnTf5-%6Kg4bq{%t;8%#O`J89@%)AC5b4fC^D?%M(n1%W#M< zCm;jpcp$j_Pfzh-CsFq3Xfh9uNzfW2pV$ z!Q5T|Vxl(lAmfdou^=pFLngOfKX^1-Fz~lP+HlY+z0>stxcLaG<2*WjUx57pN=hEA z5iy_@c94b_q&>9*~#6a#@042WT?JY4N z&w@Sj!{d0n11i@8l^cM{1-S>L8)A}<2m|P>avlh;2TAGi_6`)@2PB?H^Kk`_#^VBD zuN`~QX~fFlYIwi}Tz>xWXg;Li(G5D9Qp^K%_$Y|mc!+}spwy%?|P!ByB(v zJJf%m^!)=ACodqIZfZf%tbx!D;wHp`4wPr$04ei0-fn>^3rhVU&q8HAP-Q{607({< z6JXrq?G7-tpn!*TKTz8zu=XzKd|1$6cxNeS81jQh^D}l2a2FHYlREDD17zk)(7sQP z#@ZL4RL|dxA_ZOy*IoPI1uMAD2aWK$KJe(=0rDxR5#|BQxeq)#+d*Cc3nH>Ctoeu9 zKJfs#ua`B@7*w2bcy#ipcyxPkcy#)xaCme(2zYe5s0h5+3~D`tTRxcWFVOx49A&^2 zaP)!70I(xrXEh&p{qYqvWWE(9-Q59B4V|FUv;&+PI(;8_bT@!gLnpXR_Q9hYQbvJC zfO|nIYd?4#Z%2x`4<6n7A>Kw$p`HRD{|JBz=NB51j0_&V;F9)($MHsx22j9*8|R(p zJ-TrP!3POO@VE{{vFi_y<|6^n1|NESgXUkb=7M^VhhcViPXv`jkmIi>f=VIq(cRsh zpb`jFDuP|qZHe1OaUd7LOvC4NQ;0o1hwOy#U1{={_`+|iZ)}I1}JK)ZDC zZVtaZqJ0kQ|Ic@Zj%PsXqS_BH<~oCp2uB)E=m7~Fe=!wA!8%PC;|ZX&4$8~TwJ%r^ zITU=jEa)U_@OVtGsG|WZgGaBa6o~4s{ovB&&f?PH`ka58>+|LZ%$=b58`tLtf3P+` zVC;0g0}?*?gRRr`Msw{Q21ov>2OJwd{C8}40GgFL;F0{pqxm>%x9bDP&VydeHs`us zZ*(5>V7B=LQti=Md!fVkJbw!)#F}ewFz`=3VDi7i^?aA>dC(}&36JE9pm`4u#%ms( zrQk_ql>QNx^W#CA&_KtsH`hKu@*?=4Jg^Bb$3R11FO`|W=Kz34DN%<`tF=L85csTT77x&QrLG+>EdTxg5AFei zMg%*#z?tH>Bghn;$WEhVB8e4*p_le#F=rx&y*t1#`AQIBcDv z8;-lKVPIh3IPMC*aOAivXy6m%G*CIk08-Zch@<%zQqlJtRP@O+F@Vl3VBl{}0EH5G zSK>j);dj0VKnH#YF|N+i3C*<=q!F`dKS9QTE*Anf{-Pv=38&Ql)Eub9CHfOk#>ZDHhZ7Xew`%eny6 z73=nGfRECGTVyZOK(ZjGb^G4n-zLJ?8M?#+#BDwRYUdt!=?0O%;lT_#eE;|h(1H5= z+fQ`*E&(5P)O?)DqqFwFOXz3^Tv^&5(3}KJ4d^U~1KqA0kQ@l!R_xIUUI)tG4nFm) zmz4+XMu-$_Fd6L3=GrX`{Ln!?(A5A?w}PfHXS@tUbK)(~=*0FDjR!$@5_ly41a-7u z8lovW3Yv$4jC0O-2@WFA6e-k`plaHq({)C3?Ft6|R!)f48K5cb6^*r^G{AutN1(mx z-~-JdLo(f=D>^}kS%U`lIl93i`k^yG0DRuG>xa$&5zyoTsAvS2)Uea>( z$nN8=4?re?=J#3on?OVUpl;lYE#PCL?{vG~L6mNw`@ul!IMC_d9PwRu1riI2ai z0OeJeq4y{DU5F4mx%n6!xzPa`V2Y(YdenD*ln;*#a3FOFqM99kh09OsiJs=_b<7E>p=)wL1wT(f|Jl7YX zb{~B9-vT;i>;XDHKAI7lN5JU|cHEN{$eiBV9iZloM|bFj7gr#9Acw=Kqv?Sh4#Ni0 z11=NbbHCsw!8=f}f((X-H)!P;;=K9Z&>3*2*@Daf9fk-x^ANQ76_ND2K}RII{&)$U zRiEI|e1I8Tp`qFaYV~-4)PkfDHbRsmPPuQcox{N20(Vc-iM%`ZUg3*c@B=(HkD(2;)N28KuTk%HL6usH=Ws?)gmk*Ibi3X;_=2$+G*egm zppzX`2Xxmy=yV75As;l`Ga?oMfg7pNLF5O`7R(44@FE9LB>`%(fo>Rp1rKO!91=c| z@)Xj4wf^=0KmYa<{OzhB4ZW=7{HY-pN+6|DVFt`!i>AS__xa$ef{8G2?nu9M{8Xx{=W?<+9tw-w&JpdNtLlN8Y zG6j@Oj=P=!op00ay5`^u7S;du3=H7rXs7Q1u#gaxx8tQ5R5i!}U?~>W|Nog$Rf~a| zcKq7{Sq?tpV!Qy;>d{zx0Cc$@qTv7zzuBzdDBJ-r$>Dh&EM3D2YC>LN=5GbfLqS?W z?jSdS2J~3hfCo}QE9HG}crb&O%z!SmsQuv6?JnT~n!<%_AOam$1KyPK!r?k-B?tJZ zi2p+gJ<(01<%Gq0wJJ5!o8sLoIEd`Cw6Y_P{b2hF@JrH;*@la(MgLs-yT9?)&xdkbze;!s1-c}*UWBXd9p znB4pY9exMbK*wDn%c2&5ds(g@JfQIinzwBH0UAIAwZFj)$wmoq47+|f-T=y(pdzTb z_6ie!D`dJJTumx?utGu=q~B4(@Ec?e0{prwP;b8RI4HD16L_A?J3wZ>_#zALhV1|+ z8}Jz>;EN_+azh*iDo$V-c}lnM6yy>GbjF8A=Ty+W3jFOdpj6b$$_&=l3zkA@xPvYq z134QsO3uHH6sU~;;YPoezz47n=e|Wrs=jXb6!BXh))_^VE zI$nYXy*-Y*g701iM@sf*)OhQ72`bk?O5pMK;|FrQb$}`oq{auhqysIB0@no|jkO(E z3QEw`#IOk?n2kk=R0A)BaX>h=P@Mt^;O8DRj_RcOy82WZVx!{-p zb%?>o(Sy$HX}tj2;stJdKr<=0C((EWR2hTTZan+*|Np#y|NsB{@&EtrAOHVf{rCSr zD6xQO>Hq)#^Zxz+-{3EV4h9|e3cc?PG&2kuEAZ&71>cJW9uNfIMC5zJ19Tq}XuTt| z?*)%e@W8+c(DEu!Cx;Q&9sJt_IzzXBYlc>EFS^_H0rWV% zY{uh+VZ{yJ~m5 z=>7|J2k2rMkIvd1FG17m9v~Ni41nFj1Tnyd33Qa=7F|$#_22{0 zGPoU}I;8m!qX&4T#gOqsrz`T(st2G2Z@xP`K=&6~`yPPTln~?IGBPmi1hu~$cYxYj z9=)te;4%y}Xk&EnA6qB*z^EPk+l&tWV`+ZI+*!H?wCxPcX9DwAfcPFz`$6lf!Ie^X z>5J~#4ba->fKPYq4qwe%KHae|9J^3LfxjpBEm@#{{}- z*Whzc2iQFwsP1_RD)~A=8wP70F?jz(Cmdga^2f@d4B#^=Lc- z$~V#A!A!*ZF;IR2ZGi4}Rq*K!mGJ3~74Ybm{p85X;L$DZ(HZ-}qZ52im`5l0-1^R` zpw$YHqf}iw!DnUqKn~&a>6{B{(fB}C0(7>5))9DgZw1%iu!H2ldXIq*5`WL^(YY0L z<&Q@v_&gN;cJTV5Ue@cN)X)uXA|P7S&3i#+BX%@_hU385I0vj6+zjL2#$gRUPZfTL z14tD(`M7~qLHq$}W$|w_c#+!(YA`}Y!R=^|PD_vD;C4PJw|jJg@0NNA3LX#0nRl@B z%Q_jM>Od`L@XDy>y&w;xx)5ylDNyV|3XcaEF)rGil6Lh8Eg%?k>Knc_J2e!Q)5EVwK1DN39EBJaA(EJu6 zzQM~?Np93CBhWk;yrEP3fqz@+bI@(NFPwxxNf+E+0Bw^2jmUuJCqZKjnCS&HU)${} z;A(iuwevD)84!4z7Wh_9$WApMQ0oI6oS-xTN>v~aKoY4BD0sm`jVvl2$6ZuF4I&1g z&e#Jlz!?}aCyF{B+V}>PHaxmrC49O=K~wdJp&95s#h_~+eL6!y7x;qiV+4)tZ18BV z-NC@$0$PUz8s&ry>B7#Iya_Gja+beDk)LX7Ax@BrOfY+btm-XMddXvorc zSJ1S_0`TrPP~QU-e2|+aUxHS8-!uv@S14Ij{WJX8?8 z4=XqhY+8p$cWuWDH8j&Yz@|ZpHCNEy1&{z}j|%9**$0pT><2G%LD`|X_5xB&f-5}G z?P1_N(e1kdQoBQj$QqA;k_~u<9W^~QzcDa8;0T&7szqeFPDs)P4V*#Gwgm0B0GF#C zpvy8rSHS!Ot&0U0qG;of80FQA$M?X64|sqbY0(sT)dDz4LU#XyPTqvu^CAvhxqyc4 zYfrp*2r>Z_+u#l~s2Bq;8E^o32-QEJ@*7-cfR`;{lmE~SUc&?$wfX7M{E{8gRq%ZP zpT7f@a2}v*Gf?_z&`I%^=8z@JpccSO(CX&n(B;dZc{$jUYtWMV&9?vl|AX+M|NsA=`~Uwx=n(T~|Ns97MIZ>HglFR$(9kt}NFTKdxB#wqth+!f zv=HlzKxVwuU}Ina_5NT91e*R{h=cBX?RNdp-2qz21siQ(2hG8GbcRmgZ+{LNC+lVX z4x+k!XCO7QKm`DNDgtcDVX&gw8!sN-N1j@U28|1WJdD(ZF9nT6ZGo(X11-qy05$Wv zOFLei-~pGmpw%NFZ$Nsoz8{V^g51Xjs>fh!EjnErUIwx6`vRK3p>vKOJh~e|65#yB2{#8Ze1qo1y^wazcaLsx3+IJA%%ROY zLB_B`uTCSOp$8>|vkGj>k`xeIj78sswd z7ug^?AXU$a7yF(=g9BRWgJ&0BGJ=$Vdilp2K*mEtA2bREaxi$n2pak?uY*qi2g!r( zo(FZPYHz%_^&FhBK?BemUc{nV0J%IEI`*~!DMVnCvlAL(K&zz>z$S1T6+mNf;DP2A z&`z)&Alr6=G7)$WqbukJ_z#^;AeX^|(UBQ6Yzx}q2<`@TyMAciQ3J}ru)V185C)lp zhzn2>g0@fKD!_37S`Z43Ef5b~1;{}NGf;wO2e=at%2yy?!SWR|>Wy#w?V!yy9=)uf zb-A$XAz-BpT3hM{=&ptrpru_NovtfhMuKtzbRh<)mEzIud*MYDXfO~Y3)z_pTEhlf z4i|a@Bnxt=Pj>?-P+uH`DHMX&QqXaim!SI+z}Ga)eTteuATx0fKvgMnu!5(BK`DlH z0yx8gZ(CmhN|$F&GcZ7|R{&YvyaN3{$K5C8lBKlp%B)ceR$>wi$e)OaWZo>V|9-D)p@hxyf_IS(+ABf z*WP#`^B6S&f-Wup@e*`-h6i|g0&LU_(jd$RX9!S!uYlxt=%nh0?hep$VNj%4cYvf2 z5%?MufyQus&~icwvhU~^C`dgT4?>c}`4@I;K+DuzAIFGUSsXrmt#LP#gZgKKASmmbvB1)aSO4?OVk3gEDU z4bOvW63_}d(Eia2FI*tb0gaG?oCE3xf_gs=koDsj{sk>8*nAOO4L}a%2z!Vv7Q;cI z4q43L0%1O2;BNsBUqf3a;Fd9X{UW&2UVGsM4~j|0U4MWlNkP>Yq(=hHXyB1!a6=T- zJ44&giQ!(z3OZ1nBbT=?3c|om@*giRf?Nk$eaCklR0!N@ti5Bt#NZ8o3x*b3n3j29 zEv_#hhut54aRqdeL3izk&UQ#bto;C9vkvVhznBWqcLCJVd7<$DIkaGY^nsZOS-t{l z{ezaTz*_&Hw0*n*G-L{HQhe5Wka;0O!$HNRoxRbU>==d0Aao8o}dF^ zedwG34TcxpFxd~C6F}~L2^yV-xDOOl&=TbXBo;wDY)Klj#2h`o0>JwZpzCHkL!aQ^ zir~V(?VyX{A;@Ng3y881Y$s?n!46OugEu2w0BHKw=)Wr(psl#zEInK?Xs(eV@F5pepPG z_$&ob6S4ULy9Z(=7&LfZf<|vVK(TM_dIB->1WwsmoGI`AlSJpi?!8`|XV zJpSU(Ul!2*!2=$R2SK^Y1GMj=({;m6aDa5zg6;x+0BXFy2n2by^Y{zRTgVXya|}4* zKqIjzNgcAj39>)8x%LaH_g}!g52~|4^~DQ_>#)Q(XnYkiXb4^(h_ZhG(m1O9@Iv6i z|Npj-iVw699ya&^8rT5m2k^Zv$6vfW52_?lH$jo2ku~IU;>}s0Nqcx12h={ zn!$kbG1_0SV29Pd z02Q1!_*=k(r{IQOlMT3$0O=UN;06uILI#UK+Q5mg8N3e)y>Rveb(5iEsi56+AmyOK z0Ia3E0mOwgD;hyt-$0_r8u*HqZ=IXB*T>FCejw8lT{?GjLk&uKfYoZU>17 z@Gd_@M1Z-7hyZgD5dr2RA_8O*!uKE^%=cKwUpq_RbcVj@4t>*l39{1w-uC(P|NsA& zpeRFB)ZMNxI$fW<1XXN1Kz&sZ2NVi)Ahzn&@>Do4KI*4_to1+bE#q2wk7Q2b79o!*%>upv4r(=kO|B6_lKtYfmsB zrn10YJ&2;(8!tfX@<0Qe(BuXx$56}T#y6nVMsVvvF$P0+-7akDRpyPv)gg~lycyxp2pTOIBK*_7Qb`1l6i#=!(1w8Z& z%Kxw-G8Wj>aVPlRYyNgNkg{IZS>R2(y`aT!h%snT55*K*ceZ{2Wf4%KXN3$dAO=rB zNuqlaD0zYxnVox)3@YDXD@e9oLJe5ZY|@XHG9cTUYZox^x0piuG>`>g+Kmu%K+8Qq z7*|D1;7ADK07pz`n1F{hXzg0-3(%^vHc*uSHU*S0!K=z% zf}6FVS+UMm@G7%zDCPUX1L_OVGImhIP#+Y+gq=MDh&}n(^_y0iL@$_CgcV8=U}Ng%6tl0gZ-hegp-6J7^snw70tfyf6xGENJKy zbnN*DpYASjVc2=-g+64{0c6f?(*@Kt0GeX^@lt{dJR}dAVgya;ftrmS9?Y(wacqzU zpn3o_Mh+@{ASFI18H0F`O=}Dogn9dn23?Vm*8#&%sCLV zLE+v#5ge19$6mOBkHdv7+W^mbyqJ6*8a$xl&jWI0DcF(Uz&qyQ!;4sk!$A?=2w5b+ z!oa}a23;rzntK7I0*_9}%0F=OWncjfx_Wf>f)@jV0@9-!EDiF4M`t5g1IWPc1_;yl zgNJn^sHjJje&Bt3@i1pWZDwF%;BUsX`2=XqA;e||CI)cE0SiHG2C<=YI-uzFfZECo zx$?02fPe>NTEzo;2_h&MI~zMda~2Rsf;P5$Sc8@GH-THH5OL5BcMogu!7=>Jv%#ec zSdj-aSPV8r08-ox+u048tp&TH5iA1Uzdjl1xPjzDkWLI}*3qNamX#O0KURd7m0_0` zC?)ms>hrQPyh#1X0^LynxfL25vfZ8FJ?@P!L8gMT5`1hB+%XkoXJB}_9+Fo)|_oklGH zz;d9P<15&6kg$L)6M`?k1I;Z#9nKFqA>IQt?F35K-3?$zF@xtbz;V&p2x@F1f*UkF z_fi6)9<yMY9BicZb<#D_L$b5)xCOT=dcrobJi7zVBH#@p&EWeN8Tebl4OfuuNWp9k z_A7sDJk+7hJHWj-{uc0Ib)dn2&;ceOTdf^A_?w_>ia#I)!g1Fhpw|9N=xNjsJi0vu zJiu%IJp^FAGw_jrpx}ZT-wF*FnCkzqgHew+fb0hu2+|9pUzUNLR{PK=U_sDkj8@PkqM%kA zSTQ6bz~Z30NjlJ3QZSa9!UDo3w_WHQ=P30V10W*)Qd9*p#cd(i;8-mDi%i3MCqkO25JnPV?v(A9V#)Nnx6 zSjaFi9B<`;2B!xk6kasRFfbUN>^Rl@gQ;Wc>_4Dwa-c&ly1@#q!I8p0=U_)KNUEz9 zM6O_jHYvfga=m*WfL5ROZUuXx7t)(b?gd%naquCl2jhuuNDAz1o$%-X{}+qEm&&z* zHFfubguq9Bx4r5gMBOb8pU4lW(`C- z0u=_`Q$bE_e#O%Kh}kFkkmtb%tUip#eY#sgS>2}_oThxbr-HJxPxo9%2A22eoC~>y zIiAC#b1vkv=6C^*&bbcYLaJWEqjN9lav_h-R-_(@N9R^hFT)g1d^)Fs z?$GqNTIYX?LS%wui^+tk_G01*Tm%?#mnwpKs{!Tl7_@$H?^3)7%I z8wL-!nHdn3PzNSJcwp_;tsw32?Jl4`K6nAlJ5YTHg_AaDaqnJG7;^Bp>4RHE zkYEO%f)DN-dUSJoG#}*f=$;Bv=F$8Lv|rPs^V|zpkmhbkJV0X;8W7zOji9v>t&n){ z1$(|5(uRf(dQJtm=Ft>@ya^i>h2{#7`@!MZ1n$+ff};f*40h>>8+n0rA&9-Y15l-CX6L9F)BJmt{|c7z9{=it%V3pTzRqS(3@bPbnBXKMvG zTl0Y;p?NRJ(aikKIiQw&Ysf!HEz%8UbsqGv?gh#7H-k>|gbPAk?a*byg(L)Wk3$Dc z2wVn%OnbQzv?{(EmP0_{@lu%yR_HYE1;rW%fAckvV|rPuK`p~x-YFoedn!n-+hk(r z)&OwOfIZ{TW$6L&k3$ED1@fRnS0y7{5X^*%G9g4kOsFU`LKMV=*wW$A4UTW{(bJ44 zKto($OJRwW1$3t-NTl=l3sp@9$O)bYT)JD4+~%2l!9(+c2jf9#z(WtJhvo%vDtTcE z$|~J3Q#oLz6U1`xl@Og^cYp>lLDqGH2U0wm4}J!Zdx6e&_ke1L=J4haP}sqb!2!D062cVlfN1vU^pNoAhS<&wY4~)4DfD`F{|`hx3$7tC>ejq!Z?9sgyTv6@?SCr40eUgtuYRQB6 zYe`9u&bgqmYLD(nP%R0X`0(hC1l5v|2^UWZkIr7u{EP?eVDPP=;X;p2@CH<$PVnfL zPiHS^)2mM>_?!)&&bgp-HbB`0l(U&z!RB|ig6Hv|qv6cpDHaddNDcVta1ZDhN+);30r+@B|WU&aE4w9XdwY1}(Wkm);`Rl3~P;`S&_x6AsZvq{e^6BmcjjQ=|PXz@9v9)9! z=$dfIh&-&8gxtmq4s4HZgBLO?;93&o3PddlRs*ReL7Qn2wIoOxQY|SD(%cP+2gr#y z9?;Q%?q0A)a4iXm_g=8)yTNCi!zw{&Es3fC<<qID5m3?E3E79+y%*I;&`#YK<`5&n zGR(aoAy6?4PFWBh#A-+_3CRiIbnVdznX>H$@3r=@?ggjD&ejZ2@zD;gB|+-pwd5Cg zEeT?Ef@(>SJgk<43PM~BsU_h;pvZ*Ok{}^)EeT@4YDq|VfQR@|YDrL}AZp3bM=T5; zy}WrKs(UI(4qQt@=9j^qaRAqn5X(TdBq->iwIoCkTuVYl!Byk`|Nk92DnU%BD5z?L zss%A2wt#9$kUXN61o2>H0ceV^8xqZ($6x$cM5-l`+=g6Bf^@@bNswh85bZC7L0JVl z0R&3=@bnDQ0Iem#OlWb?4L-vNy_N)<2+iTJS`sV@hct)5LU^mS_@Dp(A8%dr2ht{N-V0*E zM;1UF(8xm1SfM1VUC;mk zDDYbsfIR@EdO;PQ2ekTwHsqlt5NM1AT-t*->cCpmdm$qekZxx8RFGjWWUqpY84%lp z88XxaA8~>xhg@QfreKgB{0PC$6j#m1dpVF`T-u@dqLET zYwJkF59SCngfP$rYFDNt__*;x2DnO2ftW5`vyB}}0ftJzDdqFIC zss(XCsdg$zJ<@m>SR6ba2I?1fgSCTGEl2{KYCU?veHBph2Kxxo4uM7vDES_G5esrk zC#cqbIwGQexXwn5$#Nf(P3Z%9d(kBHM1)vcvkfRVITI;~m;z&wBBU%t8@DZ&p zbTyz6EvOpUh*la{4cPTBc5xyN?|cUxL(~p(87wrX9_VNViFScW_{dHaXxD7-Uhw#B z?^bY!37nXl5B%>0GhPIN#>WnTHd^k5rYult=g~bC)DrXQ2Csec=>|77eIQkqPxo9% zLYWITwsS7H;n_VGGIBf@QUuNg565=S1-0Egy61xPWar!hPy}>>Zb}6mFHs9VUIMgw z6?BGxPiHJ>rtO6XGo(uB^xfbAtsR&l6?~^JXp7wo4`$yL9-W|NLcR|?nh*Z)==7Zd z-UrpqiO@R%JZYy5J}Jh7@gwLm9*=I{1s?#42SS4245$EvETjS*u?1d^2I^$Hc6fBVc6cyDdI;98JNVndV?(eq z9W--V+5s9S`Tzg_i_r6+(i|LZA3*1!{r&&{Wf90^m~zk=L7^SshN3HI>Cy&|ZjkQK zh8I#0ts6X;eH);M>o-7+g)|5u>CG2(yjF(?Xhzc7cZNr2E2t9UhcyC0YMA+(K__+} zZ>{+csTRAzEN~+bB+uUrT5}H<><;aKv;aZMK?P6f3rO=1#0M8JAO@s>>2?Ke_Wba2 zK4|U~H01BmycZO*9Q@6o8CH*8)}CA7hTl>U)eSx#8{F^%Z~N}{?Qj6K`g~u22Sz|V zP$Lh-1=Af4U7+S2n9t+^R$U3YgYbnz2Z#o@!Mjbs@d0n$fujesuZRU4ke$b0Xh|S7 z>R|RFwdj01zzbi&ha7|U%6oy5Cuo-fxD^N5JmuN}X}^KZ1}#|x?U9IEboFSET!7tX&uIH$x{)JHU5T9%KY% zVo)>8^#wxUMTfx4)1YAr$Q&^!7l9HEBw0cSDE5Qzy@$jRXgU~lni8l&@7@cV9eW|L z5?azj$0b05uv$zPl1;!uC{4+iDWKyYVO1HZB=$HC?hS(Lh;QIAITm9cL0n{La&MZSZ zF|a(1-$EpT$0|uXdQH^257lgcjyiN?M5$ZK&ShFkJVxVE&X@_+1v*{_m2g9 zluc(iN2lu*$YOlh`OmQZuk^h?GZ3`g9JElVlM%MH3VgmDXzd4NmCc9Ya(`wTXt6oO zt3=$N>CyP+0;qWJ`~f}xuXCyhXxbG_Lf7?lUWX1}zOdo}Ev5YP@BjZ7`P>W)yFmNN z_k&hZfj0QLUhi%NNm*XtZ@UF*azjTqI$b-CyS9M(HQ?4#^8pRe58!8HJ*Fi)rUOWbQsn^!$EU5h;y7deTL?>v;W;a-W=ecg4O^|aB zA+6=kQ{6n%J9~XVUg-s?>jaivf28%O;MIcuj!dFp3s|e7zA*c!SvJZUfa|;81 zixMMf2pm*5L3)vpvAoV+(D>JjBrXPqouI(j1rLl<-K`)g%M<*~kOdMTt&r1kUxKRX z7kXIqboPR3HAsKMqnCH}ITi-UNxhnfI;Wa|JktvH%&~5kjiB1IwgYlDp>``s614gb z+(&uwh>L-t8?2%8qQ~V29^HGv{a?o89-ZKGr(bO3gxnkezJa27FGx27e+y&-R&B?N zAkbJy>l4s@I50U`@H94B zvI-~T5Hoa00zx%|z01Jg0-kw-@R<2qp^L>kJh~ksJeprHdUS&O=r18_ z*de~^1|6sCaxelahnUU*j|DV?$8eEvpu6aie8__tG6VvhPk`9R$PZpe3{L#r4hf*f zt4F6}29zHGG7a*N2dFYfLe;X)~f(}gU-U`Y^FZw}~Q=Q=1eeePw=(1?g zY(F?&Kr>C?_9SGs7CegxYW}})V}~TQ7SQp3kPAYY!HvY`9}@g+5bxD?ytog#Xry%u z$cSE6r&FLb1d3ZwMxM}l0us^u9*~6($c_UwyIy<(4M%i>mjEC;4zwfy<~R@=x>x`@ z#R*DtE7>59gXRO!HK+1Oj`9LKYR7gQ7@vQP);mXTIa8yjkf1x&*~&}&5|^g$1{G-9S^L;LcK~o5ahH-~|$p;iFF1AIDw4fC@_Ra!rs6JCDCG zWrLLAzGuLV08p{61QP-k2B6!(EMD+~tnan0I1Vn;A0C60>Apw0ea}EjbKe8qz9&G@ zKfTj;Pq*)pPTw8fz6Y3n_b~hJ==9yvTzdquGXZpQ5yMOHbttuyFM=&^=&~GP(ke(#CE#)yG&B6dixqSL*AiHv3=;DI70R_MUdXdRQz93r zT&rDzD2{);IQ;GZ|IXGLP;m^FfE=(^`=h&b$&2rx_+SS2>sGu3&!zXW7N1~Y0EO5o zSo!;-yL1O=s|~2|JqFUyJrz7t4k~;@SG-sZI@Akv><^@J7Bs>C65;_@&`8z~ zk8VgI3z7g$#ehlx(27Y{e2eecL@j<03h@Swyx~dau zQ>SCZOVBQKaB2k={h*_~z_-SN_MbuuQ;;rD*^F)q#I2o<5gxs)#>YVYLhwk=gYM81 zumZI91gP``EpXv)gVZpfDBANP1T?nQ30`V}Tsnc4UVsA@)LjJm8oC??RCM~@c)cDsU7O=e){8JJT*Y>)0>>3iWtGpH5xgMq&lk~X&>ipU=?)WNPWK@^cc zAVp;81<>(^;7eh`#t~{i;#``9@f4S_`&-sA<+RX4MF1?;DHv<;n|=w zPCyX?Dgl3ZfXWx}j_czu96|m7MH-|8`~hlFg04V#aRZclK%oXt%Ai%_uu*N$A(jU~ zB_LSCA5g;=bY2ru@#lKOqZ3s2xjyjd1QmQB=eU8EQGkaB8jpY)%itrs5&e14e&-J_ zK&$w{MK9z6i`ow_Z2O>l1v_hhz=oARyeOXxnn6ImOAO_Fs~w;XW{^eV{Cod5f0x(% z0b2hKswu$h$qm6v+Y#r5gU=&?oKXQPZM#dqyif(T)WGAPpyc=EMJk978kd5eXn?Xm zc?bBi3Wxy&?z8te_?{8lo%Sf^f!3enyU(7G|3Lad9`We5ha?LJj&7bEoxUIVx4C`- z?QX}hKfkl|3rHKt|KKD4ko^y;#c=r_rN0YGT-|0Mb3D51Il5W4ce=jd-{$(d(-nTn zH+U6Wr|S*=?WHd|LFZh94&nOY(a8?!<$_McLJ5x@;9JLt3SVqti(wvUJ`QwlyhpdI zflqI!fUDtcm(JUeBUMWufcKJj`d;wqbUon%x^bZsG^XtO!jqZFqtkVRXE&24vl9pC zd_xaVfIzB^1>lo!z)M*`JFY?F?+g$Ppl!e#Jhh_)JQzW{9Y7GBYwT z7#;w1&p@Ykf;Obrc6h)~CGP~aAwgXW@Ew*O%w8NG*1kLV+rcZ-U^4-*i36yG0UR$R zw!^C&(BuJV4(a7AkfUH)KoO07^5E_^uvYZR18|G3yNJU>^PER#5olTkGJ61B;sdJo z!Qn*eAhbs}xG@4bt^t0@0rLHh;Ok}(N7aIM$bhfy1)a!I`r(B-$i1Nai>UNnOvxAcZb^Dz#O&e9v;9sUsZ1918P&3ZMR z1*rm`YlnLOuEGxRUIzHNwdn3G-wHlJ3U>Sl>iHiYjc+!9q6sv<0=m7n^oK|DF%1vU z6+fNdL5BdpVEg_5KWHGt_X_xY`{rXF9-ZG`d;ndgw(SJyEOSt))O^eWBzo)D|NkDn zrp|jnjb__5yP=KdGoYK%z#|^MC%SzvfEoeQIw4JIa4VYG_XxA^fll8&;D#Hxuj6st z73404m);=P^|BTmKyE*Pioott(23>~UQ7eo*&RB;1Ke2L@S^M|bfy5(lB)gjLgfwU zP@*1i=4-A!!^q!$0#plwj&MS0ByRBN3yebC(2>HDHP^a8wXcmdQl1m!v82HBAp7NFyyJHcx{ksD;-{0+&1U^x%y zop#{1;e!{SzC+^x(l$K8?0N#VZ8-52sK)@hSkw9--@sz)rdGA|2`!XxnfPa@&v_>p+@23>E(2Cl{+jkg!j#@h#v zPEg~`b%sad5m0gmHy}{zXGD9$odYb3-o60kcJR4P?$G0!aO7{K`wU@6go3K4Zcgy6 z5uiE@R3CTy9`IoH1>GtEn&v;?0iNFnZEXfA1Et*+oX`_-pmRPi)j`KVgAO$61RcPA z;)Qt&xcvw^wtvBks9tC~09QcZvq)Tjy!-;Gh(SG;1s=@@nL$ghAvZ_E3Ku${modUALqxl6hq?H0O!`gKPys-f} zjaC4B1ycj)PzmV0(BLUx=t5xrcF;Z69=)tT!7T=e6xzAZXuFz0`}IND6SSE7Gx*Lu z*9k8nXWoKpV^H-9IpH6?8@(5z^w^6GNWe{a32x_M?3F&zCD>is(fQG%^BDiOOD|5% z0fmd}gqNVL`;bfpI~Lj=?9$o^FWS1H0R@dy(9tMAUMhg4L8tFRPtgVK0SC|H!j6UB z-~lS6JCDEES_m4X2MwNqPjP`Cr~(N}#6Eej7rK365!hS{8s-M2w3lE)8`QBxK8PA} z?I`G63doVY$6t7NfDRPt>@5M;oV6=h5NQ{Db~7~iPP{N`1m$5+p$0iK!nzft1#vhg zST*#*El@%LUCatg2q5+f_&#jVDqwJL{sN@g2P%5O+pu9r_rWqczdVE6zyJRW|NZ|j z{_p?)`Oy1qzzs7{o(8ozAnk`A9wc5b(|O)Q^ScM*FK~U`ISssB6Vwa=Rn(o+zzfbm zT(pZ*I!|CNUmD*W07oq=-wja30Xk9bhezjL(1KNu&aL1zMx9f^Yo)tk%hJIs%%Hmq z;0xHndl#Dbf>d(wgI7g)90xBw2Or{_$il#|19WGlW9I>nZpgkK&}Jp*79Z$6d5s4l z_bqOP*f14rCS;i#=rpBnh^rcpLvOGI4Pbz_9d`GESTB0*L5UkYLhRAK7c?;ZVpj(= zErT;Xl6x+KhD3U|LWX-G8@YNx*$kqa89W*f9e6(u9u$VmEkYa)*+2r?Isn?&t9ih) z^T3Mj`n31H?JtLUPACDuNU{)aCrDDC_VRXZ2^@Zy;H$soV|NN^I9I=5Er9)7^Dz% z`yD82XPKKJ$Fw9IiR~2q~S#-Xs0f0 z-{W4;80m{|tvLM23o@!3ys-#14q;P8%?Efqtik(_FasWPtUXu~=I`c%Jm3XF;N3~b zTS0@bpcY{BUJ#3!zZX2N0m+Ro9#(@2)EH0@Lqt59kF$7mf>)$A?*)l7@V9^(*4?cj z4rG1TzyJUFo6vJ2=mtDJ0I$FSNxsa3q!dU2(F-n~ zKz(y?N#g(b5ZbQx5ZftDTrfLulf+9}H43bwnu6~ysqJOV2BVh_LYTEW7Q20z~t zI-d0bw4mU4JLq(0P#8dXpmUvJJdfsj0i>IbK*BG<1JB?Y{R)m3`>Q~mk~fXDZy=|t zf@@RQfv+W?P7he60|)FB@xCh1aUY;l#6h-#Z1iY84mr%ayAaep{@~FaDDc7^)Exrf z&|drC#jR#&+JKo3E@|N{I*`P~C<9K621_ByRo4_jPM?F)(xmMcP!yDk7l>2cRNAPRJxGg8|Easnf$ z2<)!i@j?uAs+sGGZr2sf5BQr8uy(qF?hNJU?}Nq3d(g_bd!S}CC@Q*rR~&rF(&@Y6 zxa$%Y&>;i9TRwV)$Vv<#>~Lb?Yrdi3(#Z*sNLA@ zy5isqfliQ@moy&`=yYB3avI2sppXKG0(0{ZX2>ByV5^uR&N%pj38se$q~{`NIRPlp zn3-K?fR!w1J^+&G28{yxzOZIQG`Zh`%%2V0_X@tJV+MaSr1QC;^I-Eq#!lY_od-G( zflgISJI)fu!0_@KC^0}5%^vV*KKK*V3u%MwqXf;&EdgJce9?mubkgMkkLE*vJS;Cj zhQYdBmmK^7THLwABl&^{<3-S&vE8mqnjf%tx`HkY+3`XN)cS{o3FyvLaF~Gn2X0h@ z?k|79*!+S~+jT>y>y&QS72UpTnqM+1p0IYE!ru%UVn-6|be#Z-<`<02u1i1xV+cwW zU@Zq9GJUNy~T2Agdp}BSf1Ai|l?HL}> zcAe1a+Vc{0_+&Sj+3niX?YjY_b3?c5gtX2>-LA;N2kPN{Nb3|S2N~kXKlOlP!-xNl z4G$O{`L`YDbe+@fx&h>fUeGcHkdqJm?{wV)^6U@BPS-Ur?~)bbphSl^#9u;JUmx&j zKJdo_lx8(gcpQAd+v23xh6I>2_TJT9?u7`=XN(6!!<%F_Hw>DWKjMX!)ft z>a}a=<X`O)W+zx7PL0fCq;BE&>!vP#ilObwAjb9It4G@1E2hWRx ziy|-!-0}c5Q2CodCy79&%@4j{gq$S|KIedc8w=OLA1n?X#~`wxk#lep1hUi|G~14; zq4@znTmz_t29LL+=>ipJU`M48QFF^#Bk<P0Sao+wy%GnJ=~z#DWnkZ17$<7 zDtMTJ0=xMK4}TB1Ee7e4fur*XBLl+@a0LlIg97A^J{NUgZMrN?995$O};9fSvF%_W%F?&3nPFhc*R~tak;~m|#ip{nKDwpmurl zUJ%KHl0?Do6+yTcq{5>c+*|GB>h1+`FF$~saRNEHK^yEI$O?+?y`XgzF9aAF81{p_ z+j;Qv^G{fKvm*aaL}<#1DVP)&7&LKoo}vUVd8IsG!)=7 z5Zc4&1TE45=V?d=1eF;c@J9Gv(9)W2q((S+Wdf`b4&s1DMHxZO&lgAMurNT{qoB$W z&-}9oq`e1jpTEfd3Q5eZps4`Zx>`^P4<2RF_WjU#0)E230OUR+{%x#BSJ32qG)Q~8 zv-V4O?U&9U;LQ;)B|Sj%NS&a&CRc)r$X?bhAPO`Z3m*;aW!(sxTLYc8IRPZu%eooH z2c16Wfy6I>$nSy4CqVdzVEh0G|0Im>0O4PS@eLsSk1#%XP_LJjcL#U^>Bmb@q6CfC z!}tsk^;R(c2T-@Am(?A{e*odf!1&-!WiM+Zj1RhC%%hieB8(3j0{7@;oetxJ+Snew ztP5a#PEHKIAYS)^{*IXe*INFDuJVsQUvT?%{#)!DFJmtO77T z_!7BZR(%*BJcilJY60Ve(ml8U^yp^&e-vUUsLK?4ctYgA|Nk@p{r_JHTFv$E|Noi) z{{LV3@Bjau|Nj3!`S1V#o1mpi|Nj5~`S1UK&j0`aOaA}=U-SR}|Cay%|M&d=|9|BF z|Nk>Vcd7mV|G)G9|Nk@p|Np=8|NsA>(>+f9|Ns9csD=Cg|9?=w42D7H^?}lRXYHTn z+CTjKZJ?{)j=TN=EiHi*mA-#2KLBODx4@p z+9!=~CNM!uUY~AO&Lf}(1%nT0G_!LrX!{mu5b*E+|D98>fJo?>a^T}MJerTOcyw-s z>_di7om)W%n|1es)Pf4?-aVk>(ZHHOB_ikus_s@0+5CdBvvteg|Nk5Jg4BY}UPBbI zfB*k~2MUe`P=97G=r|9g^7rrm|KJW)GQ2|tHa@NMR9fd$(BW2T$6Gi3{r{hlp&KmG z{F3qVgS5^=X`NHo{RM5v2AwbhsOJeipChAOHDV z?cs7A2fM+p?mX0d=zr%_kOw+J+cvxRg8bd#vz-ZIYUi=E<1MA2L)#b`Ui!hcqZrM< ze=EpAmLK?gKs^Ykvq6izQCyMMZQ=zr^k7=&7GIE|;4}&m1Qo~N<4<0KPK`yIDem10 z3J_59;qU+dFZ`#oFt|X1-?8}xqf2M&lE44|!$c0gV07sQpZVu_@FlY=;|a&+1I#X+ z;8XaHx6Xhb=yn`@2n#6qK*utHA{Q(G3&s85Emu>Zio07utnOaWnS0L&w&B&@SO^tstuNU|RPca0I3~cK%3nY(Bu4=F-^$5$W99 z0}f75a_8R;=5)5!fZ}Z{$nd=&rsi>nj(^>)sP=S}bTRX9Zw2e#3zqr`YK(xb_32{1 z{6HJ*$TY{!15mAppohYxxpuNlgouH|0Hpb)GN==GycKljB*>qj>KweGjrZMtv@x{; zkQK1%9K`WxJOV09Vh_LgHid-&QvMKHZ{^VO!SEYseya9IhwFFHygzs?5oifcXYB(p zA3U-MT1in0x%$=FR12pY{ zw4MT7Zh~&Z=q!B$9%F;p^1!1LYy;@#J{FHo(DhuPODk(nbo;&m4a6Mjc0B?rvs@2! zyB+`qmg}D8+Bb}d`6f^zd07sMDc3h?phOUQBdwDQboW!*aVI85hU2a~K*8DVy2ILa z4}Y^OSmh3|!V_u7S-Kb)x?OjGdK9iuL5Gf#E~akRH|S!_-L5;p4%qQ>JvgddL5qzU zAk`F>@w3J^;2T{#!LtM&om;`pwNCK)bRM1HRO%3{3nyd+-K`*H2&26i(ai>r&O<`%#d1EVvq9^tu{rx0vI59KZb)V` zp_`5DZf}@Y&>REIY-9yp$O<5{3`k}(dvrtg&o&=o@#q9IUxJn$gDVZh@iySC3C#!p zfd>#lO+@G>hHjuh#Wr8kri}9jxmJfC%8*JITLK@M|anBkzs>oD7q6$BwG=mxKG#~m!- z)e2|9l_dB$G>>k`Av554Dag7Z@O~o?$n@|_FL1F^+WchuD)Z}_ge=?L3Q_|#&WjPnJkY=$*t`dvQ0IXQ2&j2* zStXcxAlYv4nKLl+m{81vyKe%_Jn#uQQ1jrjmyl&4$LYY#WA^9{-QfW)u|jvetOxlH znjAnYx;A*g3M$Yr2xy=iTuPk*Wyjhx4Ez|u&H=LtwCw^T*g;pzfYyeBk1_2AZ`<(b zt!;p~1zs$b?&#op0owlsp7#N7L_oF+?yET*&@cq;)_~asm%WE9+uaIMgT)?jGaP>V z6IRdO2NjA~9Uu*}0(7_#h67@eWxFBg5aIBw6L{MmGvw|Bln90=iS_JI&+34p2pYj~ z*^kJw-K`)s*gOj^S`@*B1b6`$%1R?hapD18#nE^K)XoDf@j+Q%2wJaS0cs-mg1b^a zy;DJ*KA+yXpnjxBx2(h#P^(_rqcawCdOcEy-=lLXs4L&cWYhxOZSS56I()vn7ZflrUc3W44%8I_4Vi=bU!bMJ;1(LxabUSla1RN( z6BaU1(!CYbzxC*z3JM0$q6(0&yTRwbzbO0yHWuV$M2~9DCQ#Ue21q|ZRw;nag6D@F zjt1g*G#&v(9_Vni#qHq3(a_IF1Fs*&Hy_{l#=!wJp8y&n1&iT(foLkHNqTR|L; z#v>p%#2$WeuZ@KPV}75c^GK$(u`qy&HPAE>=%S35YM{go+DrmIa==jnTrYMzNPv&Y zfru%1K<);GTp$O#q4B^E@I4%m+f+VybXN#~_h&)`i8_x&=HvhW`$4yMXddsZ9X*eP zqVq^DyhjSlA$lIk9#F$>Xq-oK5!{pqpS%N`HyN(ykvs#{4&Z`-k(l#Hkk6k1EvUt@ zAFs>zRfq3m&5~^lp!PDhU3vW5TpvSr<{{3XF#wm@p!L5*o1X+K&^kh}2x!b0v>J3WhMVE@ z8T{K=U-r;seh)I92?{*$s3hn@CGcWB~45;E95OHvO3Q7G7h&V_XMg0p24^;++{|s=0Uubx! zGJp()+4RD~LlvYAq@wc&Mtc#u{_zLWe%3*~pO*;tZUqgKf?5dJ_VZD+KNdDo2c=>2 zb&y$@gW#kB%I2NtU#Qo?r{&Pn3uHd~hevZQC^LatY2cL7dHlugT9_WB@oSiWJ-`Ns zzJS~YSo`AtCFt$nnEQu5cr@33K%7+p9xi?fYPNvdQJ^J=APYe&LP74|5yb?Wkca0d zkbOHq^QMT!AfOZuvK?G{)qVhv*LQvgtw?A9{3d1&yP1P6dr;@$VC5>SST)oC+F4YTgSX8Tnfs!7~HkHOwB60}Z;t zsy!fQ$9Qyt?>y<83R(wp9J~M?vym%}|ASbtb7VRptCXPYb(p~mm0;_dAZw)r z_?uRPYkb%$C(y=N&?@fkUXbC92RBxl&uctbe(H5=XD?{c2TU9y0vct9h;;UXR%$@l zjW0pEAfxV(1tHM&Qjj|wyQhNO3Ei2{*?R;u8x3A~(hR;Eg0XWiXh@5{6*TAAycZ3L^Xl9R+B(n--p|0;xfe7_0`oTLxG#ja zL0Vo|f$c+#c65V}7k;4(5=1c^foP0gG+0 zn8k}Uuqhx3$T$YXlrF$#LtZuLaU(|pT1d{iVybblRhc$TL2Y;Ku zAJFH8WcMq+3r>l*$F;c4q603O@qb`NE;}2KxbaVVh1c{@#10>$P|!7cPog5n34%L z%XZzSL(JfHnUFw$tol6oK(HGe2nS#CbDqdNp#S=cIz-+By227X<_cbH`4YU^2Qusm z4igV(I5h`@TBeXXgMS-G=1EXcBZn>2iih0YQ^D&>Ux{*F%sdGx@<9TKum_z92b!yW z30f%xb>a(tVA=1@-oCTFhv5$iXeiJncbJgpm_qwJRDjD3flY* z-gw$Mwd6l&DGg{i6BG)^TR}U$A+;w+5#-=s=-FQI^Tj|LxjmY}yKx2hoA@DBCq(c7 zsOkir(AN##%ZXZbf(}K4iGzI$KD-|y(%EYR+C~7;1+6+k$If-{1?d7E=LD7pIkbB! z$dMkMt`jfjTp_J&gRVkfc>R;l+P&3IZLj{u0`Qn(*Q^ zNbLBFk_b@Z0!ekZg2+zSIWJA&CV(;;XyYp=W%7U$Ib6Wvg%wyQNVK~ZM1t1Tm(J-1 zyW)i}$n;i-yCG+-Ln;T*2zmEj$QC)UIAjlN_g091d{=b3F6k^?&|JHKu@iilGC$Nm zOA!89@j@SLI`|Z0sDD-?b~M6@ zZi^RLV4Wb*Zt(4z>Zi9nG~n z7(4g=0WUIv#Lpgte-6Be2b&JM5EB+Z2VQuC#E!qX5(csuB-IT*l)2OO4Aeg$6QKS% z0P@e5zyJTk{Bxk&;>9emPLODKD~RlLJ=0x!rW@>v7i+%G|ey$+=bK^xn*z`9b2SMZK#*1o@*zp&j?JS+GAgOLJ+3ETO>K~8^ zQ2*Qj`3E$<4D-*8Zi^SELO~$`672?$S9iKT=`MZJ4R*zgyWq$}ayOEHpg!@i?giya z{x;CIoD|;OYs?fK^W*yTH{Gm;tMvK<6`n9o-Gy zl?|<)K%$^q+(4E8L1yR~s!%~t3W8Qopz?#i{RO1h1&KpWJA`NU51qbmIzwMH*S=tc z1^XLBuzz?V0S<&GNWuQ$1q(>*_>0~UP~d^2x?4eHr|TbRu!Bs12Kxt4u!E*ez&-#C zK6hKZ2nOo}iFSjJ!0&YZlj-^gR0uOe&QArC-Cz=2?|}S;QtyBSUK%~ZUV_$* zz*Z-K1U#S@dBN%(kQh?E12Ux(9CY1a@*t?*0SO*Nu6IBJ(2^3mkpg54wB7+p9t71p zAVw$H#hG9dToQv>P=CDOH3oB$6@k1A;lmUOAS;3hLh7Av@VX+8#v`Ea9_Yw12k?1g z9*u7Vz=LahL8XjGC*(rh&Z*#hw2zgsnuP(9HJbN=G6*Ao>r2pm9gyP41A4u3H&{9J z078#W@Cq8xNe%x%l^AI42&n2h4qkf!Vu4Pc1kJfYj?9Cdj>inSJhT(CeYANmXorOY ze^V!D0TiUSUGfV3?PA>{;#j7gTliDbaMk_c5w$>41SLw zsAUU^43L&iaKjSD+X*)jys!Y&s`2OsUkM6zVRtXcE8SB;J!H`8Oi;rP+>q=9HyxY7 z-6}@V-hObI3Tmn}gEu2E@vGy5fIFE89kNGa9|>JWQ$GkA2if~FIpWi?dP;>FHDuvs7}k8ZFE4~SVF-CIHH z#$an#CkBJnf%+;?--73NU@-)4Z$l-}f&tWq>g)xbj|o}y)(qYU0}2Mv6d->qBp5*Q zh+qKed2tV7C1@=tEEvGz$H3ypU;GOII|(EYx>6Dn44~ae&|rWX2@M92R!A^_mOsOS z0U~PgViUwHkQ8YA0b!O$_g0XRpx!%ZDzXa_^w2~C8u36422e)?doY03wRM8WB|4{q zPA6yvpHTn`2GHCsEEquJ7KmT~>3MM-VkKx!6BZ0$@%>=&<1fDZgM$Gi51NXGI32W- z2O11eBcZ_n(h3O%(3~qQ7$Bk+FIGd$0!e`u#~{q|=-vu4(xZDX$h9w;A*lw*x2VAY z$s6DUFVRv1Xps+=VH1x|@H{ju7@*@OkTW#F>;GO{fLIBd`-KGq$gCGTz~aYWeDDJY z14tgUQwHL6(6$g2Hu&2v{02?@Ldu>m-Zfa3j|x;U9eia1mjl~=@S%`L_f!b;l^#^C6TA+_12XjC z(Y+O9-wO?hn?dVj5HcVOU+`g*0Y&kPFOXz`st07*i(3epm*CjiaWp+lO#k&2by%?r za|ERDW`;NkE(p&-*5Fc|zug-wy0fYpd(Vy(M*uy7g4^TCRzz7K%fIXU}0yF@bMRCykN53tspg!ktyg< z0@O4}e-C^wCa5I`TE-3=N&p$vZSkTFYzj!CyA?!2Oz8%Xm31EU*o71{r#y@y=hk*Y zDCjA+2Oo-iK(1rhr9o(m9K?h*`#@}1 zST^TE+Pxq#$llyt@bu5W{*VWb*yTpJ#?*uJ0*V(7d7x(wM$mF-P(QXFJdXlNFb7{K zqM43p%kZy9Nz~aYW$auiz zL5)ywq2k)`(gifs0W%L;I)TiClui*K(@;b$UR-tu8w8R99pVQy$fFyyvUD%VkuNrS zf>nS63mOfOq6F0a^yuCTIp`jX?~zAyJUaIVfSb19@)JCq1D!ga@InCWr&bS;>%cWd zckP51zrnU1e_;%9A4t-p8>|L&GUiLrVX)0$V<0QPTqi)h=m0VVA!PC5vm4m;AaRfG zRxkxVUZZq|M>p6>FD^oY63H9Tsv7D)@YV*1$5CU~bw#J|lFm@@=nm-e3?%<8VT7Gs zS-awe0oW_81|X+F{kP(UG+6NXi(rWRK$0HaU^SrAc%l9S83Xmq3W)zSK!zZMEM5ph z^n=7fw?shV4|G2529NH&7NCadM|W@tAbA7Te~?6sE&f2qf_8?2M|(Uv_bMRy4>VW< zjlTmg0w4h>0dgADe+OPzg9VSjD0YR|4suv`D@ct;r|Su*|3Jn-J#zr!KLL;-2qB9X z#t{7=aZtqwG6sCWAn0&Hu#;X0Li~s14HW-@J>~(uzPZg0dIc(|6oE9g!08&^)B>jh zNK*^KL~3e*LlDx`f-sSqT438DO)Ur$si_4TQ2|8;$jX7XZgoQMErE>wqTI~_%2gnjf%3F9xLoCLSAnzxKp6ln$=~So zz0w&99x&r?h2&+>Ko>ME+<4&vaS8`08lh?7#tVJ0;PDsf&fv5Fk_27g4T;MK(6j(D z1{(1sV;D96*@a6~Tb|Yhmzv12o2RNh+ z1z{4=h5{>u~tE)5q@o9GYds&GglUHjpM2uSSs zi@A<4sqR*g0^H+WKS0{y17H>}lEFGbqTQ_^60+I{OXCRa%}&TYyU?3pAdMri1Tl>x zutLZUNYKU+C;`D5M?@8>2P>n}Gqi3Mu zH0b$WkhL7gUx0RefENE?tS5%8N9nHp@uDyqu>u9EAG%%(alV(~0myc9#QHYS`Y?#R zB&Ply;I$yfUrhM_|35+dSO5S2AJhBxFLD zFS;N#pv&XH=Rbq)7da2w?*Q6+(e1n81#=V&gR9|5k51PGE}bVlnvW#J9tNLZ;nDa8 zbczAQn@CQa0dgX!B?fgOX#I{4$c5iQMCb&FGdp}bT^nAoMX@k=AXZ13s|6>?g9Ootx12sA!;~`tY?(7D;AG93tiap5ny&z{ec7n!I zK)VfGm>o4-Ivq72`yoL64v)@OQ1j+xI_S7N$gcI~3JnJSmgk_uQ9&6JYLiFvK?%^L zGsw~+jm}S?ZecgfQjpo&P8#6D&N|P(h=AA)5(R7P>;<)_p$2tVa(Hxu1zftp;p_p~ zqXFIe54w&Dw5bEKyTYRry!+n;axTO1R?tWrsFTzT9&-Z~bfAV+=Ux$T6AiM!2y_4f zVs;-ieEH(IEvOa&HKiaMY`XV?g*SkNkH4_AhRJrfg4A?^w?{y`2%P?wws zR6`?+S-kjc1u_LB(cKCnA*OUg4xa!!@CAb{I8H#}(&-2b1!#?d5<)!C5P}B~Xjr(j z^$4ip0NF6o4dFm;zVB=WomX)jJX8${9njb{XyO$qSU^e;!2%jcf3d*^avpV;J8WQjU98gY&FGmB-&%T%mP7k0q11zbhm=YPOvH6TR{^_-Czg4xMYQtT3}ws z7Azv@!2+smJHfN&9^Jhl4rB`5qq7$@K-t*}T3>J+yh;EPETF+R&@}KL&>k5`CITrz zq!y627fs;w@CM`;XlelqXM==~zxZMf@(@V2yA?!2=82%e0yPa91R!moV0i&D3R%qJ z#X7JlAc^i)5D77*dn;(jwR`V}KmY%~*lo$cun(zV>U4zpA2V#U3pGGz27nHU`OtX? zRAhDbLJIO;P$oR?2s%WH0X8_<-3u18xvQigvx#4l} z`e#URfQ}Pn?A&_*DLA0>pwMA^(2VDc#b9^r0r>$M9AM!dknr&rqF|4JWV>5IWG8rS zHZ(Y(ra`?9(gq5S9U!BS#VlT&Hv$C*NTRzHL_$n~EF|s*JMhIFaJVCR8!0%T0pVfY z3o42D+cx|LtB!OD3QbuMK2LWmG-x4wf$mnQ*$}=6tnjl2&m-_RgKtZO zGz1~-%&nkw3A(QnT$oJ-^FWRF;~t%-UgVjA>nLy;*$tCJYHxNrYJg7m`~zCk0GdGs zH95P%n{hi^eL(GwbC7l?)`mG~y2Yate0i@&C-@l4(LG7fg9!^qo56Q(3};#2@Y+TgQT0WHOxU0@P;{v18bOrOz}W&n1duC0S39Q z7|}2XO>;rd4+k~Otxdo|1~LE=P#~R%hB;`02kgPl))k-xv=B0(08)h6Fb9c(TiD&; zxv6eM!yIZUNC8sAe7`Z+ZjdNgTPNgR+HQ!G!3}fJ^#(58;BfZn>;KA9VxCZJ<#oP{Vuy=(^;+bC5C}bQv==(}A?V&;w_P86b~B zGaXo193*`F#R7d$b^*zDw}ME>lms->K}~~ZFpxG-rkesX3R%qJMHJW+kVJPYh=iEZ zy%jXJ*bR2zi*!SHxIh}_piqEjJTMa$LZH?uNB|x{prK31Dr=8!@Ljgu;Dy^BoxPxm zn9f$v0cgj;_a8$-2Q&!E*tvHCQm}xOAc|&?win`tAdmNe`~nRYurLEi`1p%XJ(z5F zD@YAwJO>&qP}8760MZ5umJX0n$YK^RoWZ7mB)VHcB*YZR*+ku72fhd}KnfO^*Rcf) zsC9}GET9p=&Q{PmJJ79}pyjR*4&*xW&Q{Rv$;ZJ1)R15S4OcOC?rlK|7LXD|uz+UA zUN9JdJl+8E3p7~3!k`WBoyT7k>B3~YTS00d$BICM1!@{J2te9E!BPV<3R%qJg$~#h zkVJPYh=iEZy%jW?+zoc%3rl^ZV1aoZTd;r{rYOOZ09rWM3b};_x@s28f!sXZ30|>$ zycIP42MHE1t8;G!Qm}y5*CK)iG^zIDB{(KaKz@M+3t0FPNci}RXdO_nfMmN{K_p~u z4jL>_)1W~B(gq5a0+3P2Viqq%!KQ#Dx?4dc#FXx>pgFc~umfKx=^+~Cpb`esFbDY` zGi)FYbI?+hPI$u{#Pfh;G(^K3DhOVM0&AGdKpN%{F>u2ivJMA)E-|=a4!W4c19D?L zxM2=i%LA1cfGjzJ%ZqgOf)*cj?}h3DS=kLL-+R)is1DYi0c5Uc}upn!-kGpn& zqX@iSI|m#;koCYl@a|vjgcttc#FPOFPUyh)gcsHzvEwg}Xn~>yB-PytB0F7YKo@d? zOn{DKPXNV63P?L#z~V(0SSLudyA?!&j;Sx5(Y+VsiWhUi@dWWnFUTj*um>@_!KaIX z&dC5bAwf$yJU~a(yUy|G_Fd5JItMgN*Xg?Exa$gt-&Qbo?oB}Q+Zu%5HoPbX+Y|$G z9Mo?cUc`gMj=y-J3Gy3As=F0LcDn9>`VC|P)GHf6ev1HUhYMJ|SO?Y#676mUk)5tP zx=VL-gI)3Bpf<=@NL*t24YZ~U-EW{(+aBG%2fAJNcy#(+=yW}E-1P*+ZzmW#_l6+( z?F_^%O02kb(SRCg~y^W^&7|ps8=q4{N@AF4i~U^ zaY+N@e2{2&D~RlLz0qBIqZ{ms7f->FisT2Bcmt(RbiY05biL!z?fanH^^QlU?}tv; zH^*IHK>YTCv2(8nlHWi}gCW_u_QQ+aV4GY(j)VH`!;9r0vEwfcz%B$yb+>{@+>?+UEi~wYHR#1j z;FG64;7!`uTHw+hT)IF`rS{Ogh}2SpG-)lsOS9|=LkMGn5>1BrL`g6a+E9RVPV1R4*5mI^_{ z!D=~}!D?V4;BkED>UdBs!{7c9>`JhBw{Ht*X+x*)6wuYS-M&*mx7>C5E;;VH02BtG zJtVMY@=Fl8bj6F0;6w+in4yamYgfFu3lcm2B25*P9zjx&1H?LAH$ZbK$OLG@1@9gK zEg^?3lLze{(E;lOi9#2GyKcyI-2hr6%?z%dA(eDDmhCff_$ z1xU&ELU#Z(*w`|;JD~eCyM6C?bh?5jwUJ6(SPDfcYyH8A3DnwzrO*#AtU+SOUmQ^a zrBIMmH~1hv++{7O=?Y7spt80LtP>>K4Q@vxZRBv>0^024x&@RleYYT|N>|W@KA;1|fRh;L9+oW!U+@~kM8RnaDk=aG?RMP)NkrY?b}#4(ECvSf>w}2U z-vt^92aRWRP6bW)90yO>fXW&V*h-yN(2NCatpzi9TF9dlyp9027^k}zG;!tuy=ogg zi`HG>0iLx3nd#B|LjgW(2UZEb=@qQ0JJ16*YX_RX0-g8?cFge?aw_1;2V?-G$^q$g zVRrOz>2&ls-U?dm1dg@NR#5l) z&R)JS17C-(%Rsk*#ns@~*YU%_pj)N{~hZ+fK*+5pkf%kcUI)uaZw&@7Dx)T z))`@zNB369vO7pOc%>pJV77uo1ALV(II=;9b9r<_n4qX_KETo43!3=z=mZbJ!H54r z<8j@Pxq!}A&@^9XZwfe!K!bRYFaonYU>!Y3;DFR10tck;#RHH-I>G%fSm1!gPk_ab zzhHoP2-H6VZ5M)g2sAba4IHSE(7*v{g{(IM^*>;N0}-`&u|*CXI3OtxNDl?HRSA?# zySIXj^nmvIdO#7<4GSCrScr62NWhqoz!8844rpz8XD{gJa8Rg&QYM7c*$SG%?(78} z8h#u+^9>0c&=fXk;V7gh0tpax9I$vFSp4{lV=~~t0X5x0i$x(G0_`G#1`gCnXyAafLIMZW42A^`MAYI% z6~ru%lt*_fn1alOdq5_`J)qr!AUOtxoshB$mV98rqV3@UEqgmpcAkT*=!X`_pj>#| z!2?_hLS~dbnhz*=ScA)E{&pi!Jpq}Rg)WE&O&Y>NyW4>Sbb=$ud(fQQ?I7UM37-B! zr~$RMK~qTx5e3jrF|e+^pjk<%+u)G~nr-jwJp;}h;7Ryy2&WUW(X#grIJ<);#33mf z%<|~mYXGXhpqsrwY7mhI+NSy9BP5k+fcjd{rO;sUn_%(dF9fB*kp_|n%^HH-2R#82 zygqR+)JSNgfwV#*O#x&eim1hlqf%hAKvE!YBh2#X-U^yn2kq(u&A!fsL@rV&K$iwW zqGvB?A`v}!KvUkGy`ZUa56IrWs_JfJynP==R43LcOeMDT#- zv|n6;I0bY_1uRX2#dm?lkH7dN3HB059#m&Tyabx+hNfw#k55}=R=4b4C} zo#6T2&R)0RD z;NSttgQ5kr6ALoi3=JNrk?d_c7{!U2-XkU{|^cpz<3>sC;S#@|)|G6u3r0yOmi zDty3m7tq2C+|cysbOhDNkno0yfT|&g6)+J{u?tF)uu>E@2?#1{JwVlW_g1iUcQ0t! zi%0iVFb_Urk_mD)x*SsTwbRi9RDS*jO#;GNu-)LcY-cNIj`}4#p4K{OSO|JPmIq{) zDCAfyXaxWs4MaZ{3p`u~zwQsr>jsZ6!CUJfQ(>)jusm{W9V7|yHdqr;lpX$wVta!G$7f@j@A57Dx)zG(wmKy8=3f1TYhnqQR|o$oXC1X@SmO(1}=}!V6r&fH|G5py|)f-ap_i`Jk~rNEm@x z9-Vs^AO#Lc4I*$r`d%1`fjvJ1U|2sC>N4IHSE&|m;* zg#^w7kbx+o7BAQ!W`U$YJADymd31vhsq*LsyY|IBP$0qs2Oc7j);dz)2z2*C0|zuq z+Sw}t8er;f1#!9|9LU8ekVBS^w}PfYA%O#Cd35gWKnfh_2}zJ`5qm*Xpf5y4!Jcmb z`2`v{VDY~o2X-ERVG6lS9wZOyB|tm`n$3g;4%A3!Fo3i|0;d9GAd0BP3((*RXaE&t zNOvnpg-2&A$SlzQU$Do(u6=P36o~M^0Tq+r5P`JT!AwZXk$?vdc$yP@+7GDJ3>pQ2 za5^DFI=!Gl8jNX9kIucIvK!V~2dP1%9MFK!iyt6|bhc)I`~nReu=oS8`0*DKkc;O* z@}RyM$X}3YN@(Cfjf4gRNGl|85ZLNdW_CWP{ zKw9gdr9uc1P-`8u-UuNAYORA7JRw9tt##0nDd-sypv(o%)X)?SnpExV1>Ls=3OsP6 zfjN+|Q^=;p9|+VDaNGlKH_Y z9wZOyGlBz};KoG{kZI5u1DWR0ZSg`1Vh~6Q)FOf!1l_m@a^#C2kT`{g95mEHJs&U= zc9bFbkT6(M25(sepAhBS1KL2)={x7R>kM!J)Xo6;-T~};aL1x{4q}tvf*0P9#ApF> zDI|y2E_h)E7CinUj}Po=kR+%t2yyEQ(1KrvX0S2PT}}%i3Df{&2tvr>g&ssdNF20D z5USs!yL1I;qdus(=YjYYnkK*=1a0VoH3{}YlLklv?$;fi-~r4X-M(8ud-6Jcj~sVB z0P*VqkY6>B{CWi8*Ap+|A(knCoC)>oi5FgA!Q(HQK({P)wt^%<4S9%LFF^eYG6w3g z6A-^jfDAzhS-fz9=m&{|h9RN)J-SOTfOgt~{Hg$nc_c4E+lL_Ed31wMe?d#__y?^$ zI`;}7`4u#F4c*Z8;YBgTG7gY4p?>`UzV8Xt@|ng1j#rQ*s6Pa8>kp`3LB>Em_5tEo z29O~LA&VDb5d9!=PfhoR>?H@*Si!rBR*z~|q` zI^PI9;{6Y_!v!>F%*fxm7BcVwZY)A>9y<;`4FHs^K~V&%=Rnn0CwQt7S){uc)aZb> zay!B8A-Dny1^y;Y$j&~HGH9U;TD=BS0vSSqG~U4Lz&t=V9l#gnL3M!&WyqW=xB=(^ z*@Fr;2fhatYzAZxDuju&2Nl#X^?>X_g)ou!pn}bX>_LSvk@lcM7Q*d?P~gj(UsysC zA4CWy1G4aiJT@7SbuU=4$$%_-@fy^5fu4~8zRU``19K<*)B|P@3D`15s9ZOL2Xiaf zeuzd;2OQK*29;da;PB^fPlg0KsIl4I3p%$HdgmO;jWJk51KMPQ8DbVl z3R*@%TVarGtl;bRUf6-UNRW%+LFX5NEHp+7C~&YrcCtd4MC@b*D}+qaLrW1*u)b^s znGY8R?PLY55r7}K0#XAHda{~&uuUe=1{rkaBd9_4!h{nv{|8z*4BKP^7M2AGAAhlo z4JO;&3R2SvUNi<>v;{Q{vZ4dr(gZCx0ToBEy*D7Ex-DKLfK35OK(4Kan9>b43(H1W za4137+jc{i-9k3Pf+dLA2n$x&-3tmh=o$|Y6Sff+#P)zDZ`ejykQnxjuwb*`*#pcw z_yDvK7Q)275f&^5k5N!Z7GwczBP>`JvJn={gqn`B5f)hjdTTi3ln`Wz9mpGDktN_d zKpSDPNI=f}ei;X8_d*J5*A>TImw?9!x?NX*s~GS(4N!Ks?+QfG3_9!qBn~Q+L07VS zfOLSH$*v7hHn@QdD(oRr;MOsM3o3;{?O%^>*9J%t?Azc0FMq);Oi1|)VIq~kpjI%n z{Dm-)%3rXFkn$J8M3ld-9Uk4a9iYbPi$kC@TsvU`;FW_f)}e}lOnxy7RSaa}i#Dhj zv`}$f;el4X`gTD4&)~uA+Tj7V#dQWauUorL;BOZN7pAT=x_y^;bh_eg|G{?mgIa9R z#?XluX<+-otqsUQ1KqVJUIc;#kH6>y??nYmg61Sag-WL@?uH+zSq@t}a{|<$_k`#N ziG%vOQ2mf%vGl?VEl|zg2|2(3x>K-3lT>Ys*~!V3uFL9Uh&aWZ>K30WP*eJBT^i2&`iUq_py#0WGRrXLx{; zO>-zTJg}BfC~?5;(G9i_+xC2Tu!B}h%y>B$k{vrdz$FpX6y!1pD%9-?QX=aCi)`dV z2StejTnR`Eq=les% z5wN}D9=)YsKo^X>SRf6)OW_6NUI%`8$a$EZwST&6{~UZS(FnS)=YO~BpMwuLz*l~7 z9tUw=aHxW=`+#yJz*l{69tU6a0okA4{08hE(EjrV+k@(02Z8Sj?5_RrLJJfLovt5t zfUbu?+}O}t`T^`DUKA%m%mdv9F|_YHL7Z0riWt~w259FU@b6=ts>Z?qzEKC#tgHR; z;x^=h8_;#QovvSALN4#9{qf>3NZj>Hx9b;}X$_!C2Xwy%#`y^_6`1y^!t9d*#R}*= z2Jq>0oyT7^L&~iWuv>h7yf6e6x}B~cKnKvmOaz~w+FARjv-C%1=?~B7RJww1qd~vFr1_12;ek%XWh&jJ zA3QqGzi0sEY?z0QU4M9ht}sHJ#l^qg^@qn!#5r{*!1CR$KOljVhPdAZ_u;$WkC1+3dC-AYoyT82(F=$qydQ7N8pnU+}j;a)9d#k8TdgTA;Jnqq5 z`r(BDNC7DIK~k3iDET0*rvrsQ#D2&fKcMA|{4FLB2Ym2ouHazcZ-MkyK-t#==D{ET zFM>`oW&q`!7Z*fX7$Eg!^AQ1fG6RQanN-U~9D!J~64Xxx>*1=MHi2Ag*91!HHc4`^Hpw3i$bb`UZcAN;jC*-3wCP ze2~%d1b_QEP_+*h2Dt`QrgcvRMGgOc7O&gx?4g1>O9nZ=zr%_FcWMvc%H?f!-W5(Ee>NLW;VZK?7Y-@(W4ujO!)VU z_%U99$iQs_r#z^Y2N*lSUU(@D8qEhsFUWZ?e|DbiJk{AU73AvUtu?>@gKipQI1Zi& z0)-D`O2`@&Qoyr@Pt{O;P0&h`vD}?aj+X~aOWY9 zZg2#5@Q6V~n8C5H3{JP5U^NhNYj6PZx5e$YJ?#L_$jb;dE!5yv2@knb$r;K{k>7mVFgL5>C;U9=Ze26VD~hf0Cs z3gpL^$G{;AGP%1KtQI*LHNRx+Jm1+0njd+=#K^$V-3wxB9_;J|O~^ql1|=$}|HHb$ z5}<-H1{^OSWd|P!Xt#p7d%+g`0In?DE)S=x735Y>9VVXaX1R+kphQ&T+ut%YD zk)Zg3r~$_&$g-Dipk^N=k%6Q^`?A5s9dED|+7-N@NgVhUydVx}j|>Z_A@U-cpM?Rb zzTE+eKS+hlzxRLhcX`bppb8Ap{%L$;Ai%(|gF%3S!Ljoo=mz2U|Nr}Ri{7_Hu4!Jp z2if%8*$E9h2OkH#Y)7sMWZv49VJ1u{x|1=7ClF8$MdjFEqR>5qf&n46C=cYf>q z0IF!4YyU9tw}4tCpoX1Cr|S#wu7n*7ObiU5Hl^)r(fKLwVuKfVIGS8zs^g*}p9hYv`8!y1E63~U0N(`VIn?cnG z=-NwkW#Ew#v<3$}zQEZG+>-4?@qkOG?-lS}ft{fzJi0?qfVL)pY35LnKX-Kd-njgr z({;mf*EyiHb=-9ZgaVJ29d}&-;&r>;Xnw)k?YrRe17_DHovur|T^BSTVCi&S(CxYb zluTXMbh~cobX@^bc!06fbxU*Y8bp34NFT2hG8$0LaS$HYzI;Y!rO7lSp%M<)fl?)6F zJ3w^|M5xnu3XN4M_|53JY4e`jJ~=yqKKzJC2c2UoZ2l7lZe zpgb=|kM2;=Rt_dmc!8w^I$f8%EJJq=GsrpWAm=cH+zoOeGpcjIVa3$xy5QvrP|fK& zN81(IwQ%X?+64mqeJS9?MVtrBNcRB9wif~*$3WtBPUpeqgN&WNbHE!Ex=SDM?`H|? zbe+)cy5isi#^x7{+OBIlU3;2qCou5$f|9%80d3a_ovs}(*+AX}GrL_ox_#GZ9_;j8 z)9pF|9B&i4T_;$(uHf(C1gq*{1}OvyB1KcT>lS9$1>iI^!P>Qlf6l>%hm8E&4t9qw z&^!b__;vy#{}iZTr|S%Gk^;K{bbTc_%b7C5Y9-e-AnSTT&FOB}EzJl1gZJpHIQWCH z({;tmyP)n0$-#Ra>~@mU2O@ZX|M~x)*o2q|Nu$4 z3uEl|UC?}xz0C zpqy~R19XK5k0m5HHZf;cyu20Xg9N*Af|P*bXmI|;BNy3C$b=Da~4z+IPLFwxfg6CsDTFRIzoDf0T|;aX!Q*!n0-23 zFL*T8-nhfcz`)-u2lh6iw(#ioz40Ot)D!?!6`)1^u7(GY>j{topKjj^FThizC=~^$ zyN^146YSCW22|7=9&p?Rs^eWdFMAw642}F39-XBRJUT-`5q-g>)Afu`r!Oe|fcT*E zBzJf)`(6O;WCtY-mu^NE$c}bUFnDzP9`NY&UEtB}yTGIIAOmO?rqg$ZN4M_`1b2c* zx91Wo?b?s(CNsioVsq2nO`wu7KyfzSorEW1F- zfMr*=YXi7G0m&Tl=&qg7%`zPa*V(8_O6%4k0D2Q;?@P9@f^8{lQr4n*0s16DRI zKrNe^YtJz7x6A}3!B)_57ob$w4Q5%puHkP3U2zWKf{U9O-7H<*TS4kNKZ1^IWk%t4 zpmG;5vvhT`bRlV904n>S4dCOhAKrq>Mm~6L3APnf{(%{YM$K{XR&h|?foz`!Ijp@8 zR1Jb9zz$mgb{J^t66~-UV3)wT6HvJjcfw^~fE@|u9s~`bf{WA@{B59{-yt@Ft$5MR z(gmuv5e@<68?X~zCWCwemT+AIQ4I1a&SvBqkS|@=Kuh^GpyH~NvD=xx z=eRcI;Uf$h-0$YC0UJeJ__Q!U!l$$LOK0ha=GqVF-8GPB!C}(v`=R+II|To z3^aC&Wqt!Zu>hLidHJ820WwsMn4f5VW6)Xq2Q=@{8TzKv_XTLo*`xU#W9P@t^DnGE z{Qs}*`T}&VqwAke(0F$3hwjohFN(kuPfJ*E^lA51MNqu<|!U{P_UX6>F}2zytI61JFo+w=3v^cLV70-Kgye(0%V9 z|8zoTF+G~!Gj{&#{QhFX`~Uwz+1K?$ckP!KO<*2Vr|X+;*ALdNZ}^)buKNJG+7jZp zH(*aa>2!V3?FyX(4fW`J3Nn8u6Uck;KJ*S(~g0GLEHCD=gH3V9-Xx(K+x3J;^9vDBz0&R4;Bol@sNU)JedE&Y+TnQlflH_BmgBB#KqjUg zcU=YAJneeJrQ7w53uuV@g=6OdmrmCu$6YsoRCK#;uy(z|-(C*xg>L|tn-4&R>_HD} z-y8g`ppG4=Qw37{@;b62P~i%>8yD2U2Nl{M(vGuuF)+N8MOFqb$d~Z9gGX9jAr^oY z{{Ii}j=g*asz{)cP}}*}gPN6~K?(5n!k``d&_g{gze?-8meyIih=0H9qUIkQ{JlA# z=5Q~o04J!J@!jFUzdv+Qx5jPf1Wf~IUhuGXoxwjH(#QPBzrS`7vmm~*6F&c+jm2!?-~#f!~%8KKBRTBgn?}8uHB+}zO!}MDVT@w#2Nq;($T-+!<>bPZ^jwDtr4evxvi&|yItRa>pRF4 zStIn6cg*q#v_9YiT4qT*?t1Cp|KqMFKxqTyxbD(7ou51~gY#AAwa(H59@(Kcn7d2g zG#_K^{MPx=<1$!y5A?8-Zr3y5ba)G#63#%;Qke8ImAa zAt;HR0GBW)x?Ld#KvP$D=nW6eQ=o3l%P1sWAX#hIJy?ulWaxG!XcVXhfLR1~z`y_9 zu4ljwIP+5K|NsBc>fz1h2imSDI$e)=bi3Z@4n5L&&G3@P_ix>`dpiGlSbpGd10PqE z?RtZ`+xN)9hm4>Z6i_KJ6|@)&luWjQ7#`ohbsp^WMGwIeB>#MX%p?8dZ}mnJrO=n4 z@*kRvVSWS6yrcWA+x0}Z?-j^AS8ErjZR@%Nlv0knZh=ss?mPo5-oX=6NO5HCx`n?D zG^z;-PuCqF5>&*1oDRB*8e{?@Xdwrn9DybD9as{&$8pymAWbjVf>uQ|*WO^@Z_Nig z3ha#L+9M47ZGlLE1PwX{{ysUdD0EN&=8z9*-6mdX&BqwiI!(NgLIdJu!%N>kc7~pL zxfIlBf!PeY*4_YB$I$-!1sJ>A^#y45`9S9(@E8>1 zbx>6csR=?4G}pdCcl~aV>s{Y~=XXOdfG2!iA0T-DkVl4IKnC->U0*P}?g4vt4`@=d z^a9AU-L8AUWx<}87m&&V(99R86}P1mH0Kq%1vJ9B1KG?6{Otx{C+z6<-2;xJ1D&9$ zCD%Qm2{5=2%Qb}gN6^9#)b>HFKXB;yU(Gco%4i1*oJ3O`05h$L_)S z$;0v@|KtNIj4u4!PPlaXo`Lk7!JQj$YYH6i-KC&q8V^8K638JhEI>&dR4YOT#!g~Y z1l}Hl=+!uYf(+(9*!gjYDIk~=L2dxGD?A{BUTA>KYs(a89caQG9AnFC^WGT=p8c3}UD?yS$I=fwOcr-s?^Z-lugS^(e z7hD~HcPhDV05uDn5B>)Y!SQbk`2V5`WP0yj&=IeY%}kg|!of;vJ3M+J6-zg!k}F)G zkuuOQ1*pLTa{h}zZUzR(a+KcMju)FjJU57Q@Y(embf7YR7oPwr0ekZWlSilP4NxuV z(foi3;$=u<5A0ZoJ(!MFC+r^9+H+z=${tQ+Pso8h0ZLGy2=nOO3bO0PO4QJKF%!yvaRJ1D zWr4;cphgJx{v2ri8F+o1>kp5EFW5a8k9#y82hE>)fESgb-ya1UPd))!tljM@;nN)| z;J6z!80*n3yB@T5v|HMvGxh^$WfFK?Ve1VrHT43R>OJujvWFf_^&a^7|G!V?)E!{C z)(s%4yY&Hx>TF#BqPiheXX^qa9&;je6k8oHKXzH%_U=UkVc|Nry1 zfp)iotH5q>cr?Fc0B^ zVgl5~2Vb&-hyKw6<}9f94~{O6?yVrZx~GD?{UR1LVAl&?`veMtpa1`Nf)iZ#R!{(W zKum?k{EGyT;?C9#NFacy7ocNF!3DiBxY$892&)mY5F@|=Uq|A z9?Xz@3d^p0L755?a*(v`(+$p2KHa?)KmY%KA%7igS_H&(AnL^&Mh3_NY)~8117n4J{&I;e(o9eY#sg4g`gh>vymL0T7pfs23n}z)|H0iYihK z{PGQKAlOBqg&Q8tdqJi$V5Z6>u<@-RS@iIOgl2ayxG2;F-8u$JiXP1`89|A*2|dw5 z)~usz>I4@CpoK(W&vAj4vUY<8Tt7etq%A;&P%GpN*H#e6qwxr+bdQcb{6g?GGXrQo z5p_5iR9)a(uH5*hzyj2UWSu7p8o+^#qdzzf3X-iiz|_LJiQz0qz`CL`ry$GaS}LWK=wW9ybiMH;6H)RPo1Gxz#S>jRs@jTl@}jD#V?$T z(`g?(I?X-6PJ?xCLCfL6?g7Q2JIBEn%$)~1kApi%;6_)yga`NnO2ZfTz*hKz7LEPz zVD^*n==1~2bvtr2KVS!M!T0^(aquBCB#7)hF2DEaw1dnhAZ>R5Ek}o?8NBx8;jqu6 z+fxE;g|(joG#`SNHgr2EfL+?@C;>_h-5wmx57|3GtJz&YcpQAd44&lbHUf26F2D2W zG=e$|z4-_a4^SQehX82nkr`<9UUz`Ri%xL)UHjt&Xz?x72-NbY`HcX$z0`SZ7pT^U zhUAAA{GhfDXp7S~PzC}=Rhb01b7)TN++zjZ@c`Zs2i{!Y4GMBk0nmyW z(9BUK2Lu1~Lk*7@`L})YNIvJmh`zG~V>$%X-38|da9RR&dqI6x(87xD+7~aTGcYiK zd$XPA__v?v^t}T*@%ZvHaQXr#_Xi%J)%KvE=q%>|8_|4>rMdPFgGXoS1^yP$uo>9H z-M)9g6H%Zc6;KxxEC}L&nm669XFQTmcpQAlB(4GlKquOF`~CpMTJsSH za1Q`AJfUfYe>*!^5+lBPS(kofW&kJQ&QeI0FZ}?@R@b{lc646wP&@`s4gnmXOz{lF z_TV<~=rrgC6%MXHm;*RE12{naZ|HOlEPZyCes}>|>;bN2A)DSoJ8oP*bRO(H_ree~ zp{R;^X@ZLwb!7nsC!22H& z0u~Unf-1s%ZX(K*!tR7Q37g02DtonHjz zwL*@Cgiw%M3_-{AfaI84A^KWD$6Fq61s!h*nx;M8D)I0Cf6#2INAq5gS%^*^=nO?r zyu5S+b&7hog0y(_?uBfNhFISXw*JL)NHZLI@Fs|Q5d!P1fhL|j@M&HJ(G0Tf z1Y|e(%F-80PlBBXI!V@}doPH3kp$~_S%I3__>4y}DH>uD*kN}Wz>8=gPS^_$8uWnj z=tfR?ppZKC66_SvRk9x4dqLC-&}v+8nF{K8`SgO14a61lm7Uw+zTHp%qML|0Q zphJD3iM_iO#PMi60xAV!55G8b7t|R*s-HmRE&l#f1Go*u8V9xoR(^mE&hhBn3OXgn zqjM@K+B6Vx5<-U_m(6SR>Kd>{^}d3_Fa2Qnz{XMop2wSrFN0iD|j z5-J59^#mFN@d8!!M4Ajb_{9TeGKlkn4`MPXU|;5eqO5r@$QDNa=?7F8`L~_$=$;DV zLtJ<89cZ5XyNBg3{z+iz4~id~e=_j5XoB3(4R#-BO9D9hIyhhl1c5x-368s$+>8ti zkaz_(9Z=#Gw3zqhYj7xoLZbNqXzdMG32DA;BKs%rqK|7!h`1FD^zEAH|(7nAry>mga z=+QeD6c9e$rpH-8+oH^TK<)g_y`XBnbE^-?B`CG}@m7##P=G;h8V0dIxA%b*c25OW zjvk%&eL9bMbWQ~oz&@R~JUV+p<)2UIN#D*3KHU&bH&j+c<;8t-a8U^9kmza zZp8QtNCLbW>ME#i2DeSRdqLiX6cn&swIF5SwWsUA+af{D6p)XPw}NhA2G#uCV3sx5 zQT*+(;A8_*)d{Y&A+q2tkf6Zso(j?i+P8Y~@&jgYZ414-8loC3-FdCEWh+=U*l194 z2Qywy1Q$i1mI25KkXqRq>~j8QKd{9hIYfGeT;bgfb{e>}16{D)aR{OjRAOxfMRG?- z9}`3fY!k?cmka)b*8A)Pt-|j-h{K4Q2+9?y2A^ zqVsz9R*)d*I@7=Z|G)TfotXi0kvPQNphgnR-Jlf)-QcDb$U##<4uTe;Y290VK^8(* zFF@E3m0)**40*W$oRdHi3lclfc`)sGi!Z1J!oZMryp``Cc!L$#|B!Z5Gq~Brz~2TM zolNTntAN~n-gzpmvn3Ry6~^b^-V+M4pMU#SkU@;+(mFq+b%M3VR*=yzXMrk{Zdi2>O16k<{Viw-C%9UF0T~i7kl`{r>l_RQPlFpqwx)BQ-Vh~>pZY=(E7V}fk$WQjLy(0oxT%%I$b+_I(!k|0h3t(FF#vD67ZE;a3VYux|;{}~uP8fzOs`!?XKFjjzzdto-vY~cj>0>=rU z!NtbfDUeDZ+;cwe3Q_ukm4U(V8+d~!;>4Hky&wx;s9yyo$i3i|a^0mfxmQ(!Ow}Ju$v=|*M z)X8!xt$Qy>s2gTDXaoeJKCQFn6pIIn!C)b%4oIRw(aeP6UgT@JtRYTr*8-Ov;G4Zb z=LLWqdz|GI0|SEt&A9Btvyf^{}0-;9aRGVbINT z$|D)9(gPAs)-0!RT4MofE#j~SB-h;vA|ac9yO6eW#&K_sY#IR=mjTvkR-SkI1awd4N`=G1g*i@g1?Q{rG20yR64(V^!je_>2UsPWN<$v&H%O0KQySIX*J-VlY^u36`$js1OI{~pa8d99hWCWdj0P-cK ze?g0LAmIZKGk#ELb{|No~QZ}kI}cnsaH6CmDghQu0w z8>pF?)(w`1T;K<)hg?E8z&QNdeL^qrZ{G^i537MdZ2s-NAPQasfjBU$|3mC}IUU?w zK=Y346#jP5@yrm#aKAWo!Q&q;2J#;ph!?uRp`)aW38DqzwN<#x2VJ`ZQ4G-yuh2_; zH^3{l8s81bY9Lh_$gv0uR)We8*9qX2zbhaI_wg$I0!1rQn;3N1JiJW|;&?P30o7Bn zhhJ!&XJ$Z}KWKd8z{tR0cwi?ew>Wm5@agRZ9pvfLEqeARGt$vBkOLXPhro7D1zmsZ z1KHPi=!N=uP(udPFM=cx5X%~T2|9nfKe!MDiGVx}?$mX&bb&l`{KZ=-(5ZHyYYe(u zK_sX>cot+gNV)~g>IV0PKzuOc<$Q49f{X*L>ulZ&VlnWyL7Gb-4&03HsUR+>O?j#F zA~VFaP6!1v^gpzxwH0nC$o&vQ75JNtkeuEP)d@Bfbd1W`FW`a*YzOGH3J~KZ=ujz0 z&l_YA#2Q#YSb>(+gFS&95Rij@A$@s}FOR>tA<4i1Vzr(DTL2=#egfIodA#{JW9J^Q z-JRh3Sq%?>y6K=ng6^qcrPr)mzJh|Vqxbkf&~g*d;yQ@&kp461%#4$uWr$lr@oCL+ z&4GXVafgn*Ak|&1AaVt4zdocJ4>B9l(7d0?DX83m{}7kG zOavuU@E`@mX1JxDEY~1G?!9swoZ*uyVE&oDDU#%~+nfQ~ls=w{`EYUp(R;n7*Up|f;NXXpyhLVD1m z$PN74LXNX{hA!z2U1A-&g1;TqoazRhfw1T0b!O-m57;RQzDu-ySAZ6(&glm2nxE0_ zIs+6Wp&LMD0Xt}edRr)H91Yy@@txBhx}>{w26!Fi21ovFB|q3TKS55$2QLzxTJrz@ zf6YV8p$j@g7j(O>=r{;6@3>F5>juXTpC9bqt}{TDjq8k;Q$RH`I4NOR*a}+6hfsO@ z|NsB{K>2$Is6_5OY5ynI{{G=_mJ)fw8;9Xg@A zbcJ?lPv^JJ51k>c2f#-L{O@#~@Nx~z8_l%~82I~+fpS9af*0?^ASvCoqq(+&k-u#c zD8IRObRO(>={?Zt+S2XX0ZD04friG~7Ld!K$4P)fys@?cNd&S0yaf_9-7Kd%!Hbr= zT^ryc2Ols(E-HZRsI&%eJ>+i(dkWO-2d@?c*$X;npyOo<%;BJH)?C}c)6tv$|Nnpf zcF^=yH^`}tHN6MGE|_wtV=73ls})4T^AIGKIzWlpwc{nIpaIX~b^CUJr}kh+B7m%X z!3f&1)>yL@Vqhz{B?hts6dc{59nhkz)1~(S*lLh4#AD!E%Aw&HBc!Y$bk$>W|+o-6E!$4J1jpywwj`v0ZNfCL1U%h-~vZ#?_353hQ`_> z;IL=~b@#z?p!5V<_>agaSkf&x6@xqvRtfXWf2e0>fWz(x=l}-L0VE4v_JGP2*CQZd z&~R6G=mvBxU^!PLEs_~GfbKHy=Kb;sRFuGKOGvMTzZJay1Kh&)XgmTcLt+oV z_;(zA{Kx~>@j3H9q12Y(rCi``q(mN{v;HG!3#n-Y*lolepOXM`9r|`3(0DUwav3y- zfA9qdXnAMzaSo5hAl3Gx%LGIyhjU}l>Z4DL+u53OFg+Fkkuw2Q*^1^;%F3EjS5Izi_zz2M&#(!}(_5_F$QGiaKh zUmh}_4_p60a6OP6NGo`8N-15|1DyliQG&%j+OG!!okco28&adVJY#yDi3p_yWHt;F*z6(Gl zBIwYnZr>B29WkJJR44FUlK7J#=fGrJxDU0;r{E_8v1<^}NKT1f7k1zuYTYDCsf zc=7fT@$O87X#DYVKB(jZw@6WUGq;1pLHjzI4}glg32B|DJUWl_Z~ycHQm(sB!0%kF z!TS0KXwJnIeC+-UI})6`38L}GOHdo!qq(*NJy>sm3UG*XK_zPk!nvUNZ}6r(yrGNi z+#NpMt_OTH55f|IDa7&ykM7!r7ab3wfec>zfHf8E@aQf*;Gy{urvEE=IkfAKmtZZ; zwGHTw2klt^UwVLWB4|S+q^gFO<)CZ?UP6Vs9;f+@LRu$iNz{+DPS30Wsc~*Dn0}t>{HQx&^-L7X` zxb&my2qu{cZW-7E!YB&&e#P$ov|}~IzuOPy7qu}%6WjN7knEa$2URt z+;{snz_xQA2RCcMM>ZV?-+2ONHr6g+kN_P>1#M3~08cbXfNo4^t_5`;VB+8siUA^i z0g*T!fW=>c)?qf*9$;W#Vt^k|_TUq!>vR@;K+_JyfFkHL$^S36fraZP@a;aX z4_1Dle+L@6pm`gQZakV@!J1tkywqg^O_V{;2;TupQJ|41@E*?29|$LOyY6x9 zJm7io0h15oanMFv=vLhWFj37zFV61-bt%F96v#f!sUXL9`|fc8oi5c0zT@)+&mFL< z!R_Jhy&&qv&D&5{gHE%7c62ZecyZ}ANT=(AmsOx@4-$Wn6|Alknk^XkTNZ;;21@*a zwhuyYiNXku7mVQDf~}DIrNC1Kpe1;qbj-XJEC@Qt0JIbtbn_rcxcLB&2Ph3Tk&svZ zfHoI{V)zJX4KAp(z%UIZae@XBU@Dj)UB6BU1@SRx&l(~jdO%Jf1)UoKi#zDC{@tZB zU_S2L3m*LL{O-}c6~uZWwF9)cxm5tcdyQ3ZB9OJ>& z9qN2AtbFePm+uBX-Ju#j-LVQD-LVor-K7GK`$75DqqFt_Xn%x9XDH~jy%R2-u18!t zeGm9_mhSNAjNRbV8N0%#Gju_x>m1N7Z4b~EYS1$8Zr>9w-L6Mmx_!ZwlTUZ-29NI8 z6+Ydi3%XtBKgft25;iA zfw&$e@u2S?z@2{T?$ba0`~siv$IA@1n#lCSwFNo-cr?EuX+Kxz3D61dFFt}6eu2jU z(8lKo9?uOLzaw$|I?8w+Y`kG~zXj;zH3jtj6a3p)P4|!Pw}9>2pm@IpQr|AE+w~i$ zN0-*g4%*HF-~SWf(G9)+1#N%@eEvqK>jB6)Akfi^7vM@3G^`BT0~-oDqWA)+!wVYr z@CL8;cHQt2(pmyt=mI(-&-Vpnhy+Eg{5x=y2{e$g0d&L~=rGt9Rh-b~7P#F3ZkoCN zcnMl=-~s7?fW=`ac7P^Jz{Aqat}j4G&4aq1ptFg=w)tKFtwjcJ%|+1!nnplY*a__* zHy;5V6^v2dBkfl??h0yHFdTP%0xF5YC;PqO?*}^~+4Tv?;%1vh2L7q=&1zu5AN<>D z8kw5^Gw@G=Nc>X#$6flOv-Cx`>l4r{iR%;ag23k57YzJU519N1Emj6^qJoT1B9%AC zUBO+tzhMb{E@|kC7su9s z0v_b=&<7r!U7+cC&^Bj}#)BXmz$f8#cC7&SR(xN0G`>U+~@TckU|3Lxi`-+=0o z0FOd~Wx-7N5!9eu4^yN8QFOzj@!%T}6V(XNs&DXUN#nsapwSFeSqqScZeNgI6=d1w zhs++$KN%ePryOu>`0(Gc;Q^x~|F#dH#fr^8K`Gz@e+%faK964852u(JJbF$4o?>R$ z1zKfDR6N(L3e;e^|Bx?>>ZXXhl!Y0pP)VEa5W+>|ECm9^DQS9^D-f%Rz_UcYw1H zcsG6nI152aln)-=6%sGnKuxz^kTTE_?e-oRZuArY6(<7Vn?PPp25nUXopR(0y2TE6 zk#hi~5L&T7J%hbIfb2KLS}G*M{nG%>E3A;r1C9Vte1Phx7ocTq;9`UEAUN`wJ1RhN z1B!g+jsO_5dj}|XKxOQUv@&pvJ@8;W2)+^xG;s`WxkAcoJnjjAyQf0pg*~K4{lTNz z9+W9wKqovw_JYe@S6rnzsH$fKT>zkfYCU9p7ruWSbe!&OP}G3;1S8H{0M#d;`R>N! z9-#6HG{KG19`39K9evpu`l37ZP3xu3&?o#YpkXxd6f3BPe8Jf1`hdR$bV%Dv&~^cE zJqh2^6Z)ak%K&tW!f_{1`vkG2TpK(i`ho$m`zQ26XV(l+_(Eg(CG0?jKj4r7pFj-C z-l*dR9?fqGKSj(9+1<}aYkJc$EzsmYtpgUv5fI$fW1`aa-q z35QL$g8h-&>3WC1WgUuGTBqv`{ua>IG=zWCJ6*5vw@iXfID_?Obh=*PZ((7EPC|pl zGCN(*@VAtqh-G!Up5SkJfg+aO>3W2}C6)!DR~J*ERetHOQh9!D?6V zw>YyQsRi9v)9Jc|zhx7$s1NASCD#S~E&L!8+%{i4%#&I>IN(|3;LLH<5aY`&0U z2c44t0-Q!r=c7Kcfet}&{ov8OV+EGzyUYeQ6V!<7bPR!&YA^SISM|R@ul|?fHw<*P zb93zrF6h>>6V0_Rq@n5X0_R2WHZxdF{T-Bc!obGWet3}$b4BfkPWK47E6lLFLLRIQ zrqIaE{w(!y~1E^I9>4tJf-o15h*OB@VVj$Xwn1}7B@k+62ihF1@4L^ z5cAN}MK4$zPFLhXbTK2jf*qeL?7+s=es~d#?1~JyE94;Np}T?|tPQ6tK&3--?F$x| zD;jHGJZENr6_i*L=vMIbMeT%=3m0Tp6u@1<4>1qs6VR%U51n2maQXM3{d6E#qSPK0aQX8v`6=KXp-}xb zaQXG1$^%qiVlP5KwL)|43nus^+YfN&t_01VfKrg}ht5Wn`X?U0d7uU;IHe;yw`&ea z6RZZ4g}E463HJK%6KF>{$n&UCbQ>bfh;Z}|P>FN|E(~c>x_;>HIKaffVD0;Yzoi1! ztiWj2g7tQQs(DZ_LE11cre%WKFSQ?zw}VUo4X=aFK<#v$@S-LQBm-)gHG*3^AWhBS zh7VGb1~-*W5VhtDa2eeQG7BCvqEIWFcYuT$5&a~vgT5o&3$liRAJI#^=#hK~(shFN z5M=g)dkDApF*EFH0h!v%tFWJ$;YICyXb-{g8@Sa5@f+wa;_^JugoW#mmlL4YV0Q%Q zf&j2imzA=z=(6_C98Y7eD4f9kCA-ieS?a?s%UIbqA;-1ceN&S>w@s zqyRDAvjem?7Q}@ezY04)s=M|_V|@s?B!Jup7Y*`HZ|x0m{~mN>MtA9t?y?Y{Zr2~a z-3_4gD?OWEFoLE?d^!)kfV5met#Z(miB|-;`_So?0B-7bdS!IGe&}>60F6-`cLdu4 zIhG03;6m*$AoYEl_kr>hQnv}*(t@sbc;Nxsj15{J)!7Jg49Lrn-VCTEhiHFdSwGYr z`k{FjHls^HfelH8*g9-sFllIaOXf4^w2|5up!W0*$Sw1rgS!~`Ta`g!2Quh*1E}E- zbr-nX25NkOrrQ{yhBa1zgM+^nGG+$q4S7UBmUDU}K$bOmWPq;T>JBLA488FZG!)~} zTYJC|bi{JE?}zTf5KvgZI1jS27j*9)XxI(3V#4=B_e5}U0aDcoDtdgnT|am>9sqT; ze7ZS&I(bxlx;;32K!Yzn-5vrUhJZ_VfP_nDh>C<~Hv?!iM!~b&fdj_m<<(5`R>(2bcW=Lvbh z;_rv!jxUVhn;bh!|9EtUe((U@&Cy)@fx(sE^`K`m=qduxsl*>VK+C)!J^u|JowY0Y z!ACZMT>}~?gbao)0H5aR0lRh0bpgaVpcP)AGjSVhZ-7R-_@_axD7Xl^91OgG0xIy4 z@e^3cfq?<0<|8BMn6hTuYsW#AX$xqW@kMti1H*CFgCG%bcjONPe?R1&k3Y;10WtnQ zr~oKmdi1*P@aW}DS;@@cksSI5bk)UokIes|^HMw*e|ltJlJQ`?2=4HP?tqL~g&y$e z1`Sw+p6D)==q$Ye4*zcOb?GZGJG&l`p0P`}gM>?`i;BdHDo9gd10w9PjJFkd^tu}O zfX3XQd9yPV)V~2;7VgMD^`K+J$N!EE4?%-2C&8m|Xo43FK}}wtPFK*uT?Y`|CQzn$ zsR=qh1++{DJQ{ZZ>^H2VaRJTX(Kyg?#RqVD?8U9uU{x19z-Q36fY+%s*Mio>ID@hW zXc;odB2X?tA7X)q&5P|Ii@IwsypRJ8P(y24&;a#I(EX#}%ke;iP@n_6xCxE;&zi>@HbBcFaNaoHumi@0dAb`^lBS{choNZz ze6b78f)r{ea)AdQbMI^f*$W;hhLqkQdyxwQQ0)X7rG<=t!{*CC;|q|YzxId6@ph0m zKq&}%CLN-@hmJQKZwD9fV84Skfa(a42JqSFNbBo+T?LvgxcDbQb9%FdG(YTO5>Dt4 zGIW;($c^A89jG+|3bW=47B+;&3Kkhuort6k*7XnU7S|8W6&%RAIMD1v(*^3PL+s*V zM6!!V3a?$Dy*dzG0?bId1laNF0?)H_gIp-W0@u}8Ap&j`pacq{0xsQ-ka7z&v4|3#b*{%UimYnc>BvsnAvx$ax;jpzg{K z2L4vaK`@Y#0(^Zh7i6Nsi^HQER91KibQef;7D>F62aU5I*a@nfJeq5NFz~w^>~wtt zK7Nro^aJP)m}8(_7oDLGJQ{036F40F&ColGAAp977(36u0IlEO2|75e`2eHkK}Znp z0P#Cr_q+tx4Bf79#YaI^2WYbmXuC`&c;e?kH>0)d9sV}RwXdM5paY$Zuqy39w+Dwc z2SgHdGjBHsdf?`f&_syL5X>xODocNPrhjclz#l0lL2g>~7FK#+{7dnE~*jpP(rL zM*$bmEv%i;lQ}{A9=dB!cyu#@mLqv|JAxZb93I_{kO?ig=`T3J3m#k#K#OruaG;m3 z;Jnam$ps#5?S!0G$lnZVPk<6AB+y@ijv@eYLB&-YG#Y*!X8;wDuxS4QE}kG~kaxTO zIPSm#N>DFB8*{-rLFH8&xQ2wtf{aAc%fSXczZ@*ffu@&3njc9o2b#S+jL3R^7dSkwb-JDbU1@W?qqi6|O5gbt5*i0Qj=S#IqX`<)ExqBvTzbKSx%7a?!Dmb! zouwN*j=Qe-H zW`WKE1@%>qgRa5?O>Dl<0`(YsL2Dv#G(SOgN{NcZi;bXi0=gstwSNqnj{vpBF(%JJ zm6DH&KzD!ysP_h{B41?x{r`U#IH6sHh9@-LT<8|)ybkVMLP8W&cv*f&PnieMQsxC{ z%G}`rI!B~*2P`hST@Sz#38=j4cD(>f?~go~L7g;kU*yA!1)#I5%+8@VVzkr%2&9z^c`Fp|T zW3uZP@KA+IBWS-iL$~V}@XCoVFK_(+{~wgsk=v&Zur(r(4QQfsXMsjyLT_}l&Ys20 z0QPU_2ao1mpdk_lSOe@}r|T8au1jPA(1l9i)4P4TU0=9#`(6R>dgu&20IK|5LF17J zx_wW)05AK6&b5L94Ano4Zy=MWp*O(ur=Y8(LEV!FouF~H+6x|?r6+tkT@Sc)`tAW; z%hX(D!iZ?ifd^PzSQr>~fQA{6PA4k8;L&&tG;ZJlN-D64V#w7v;KjS39qcb+dq7Pr z-wBYrP#t%I7Se-mBkwLf;n8>u6lNZvyBEQ`9b7tFV?e9!n?ZU&H%@lCu5syh{Q$aP z(W4u5Gg9e+Zjc*6H?utQ=yXzm>^N`&9e2IJqti(MRGRs8htBZn4C3(UWCEw~7a8v0 zLg7U7KG5y5j2@8la1aHa&;S4b!H4yL3tZ49%I;23Zva&0f#kYdK_o0WgUdY7T|y_i z+d#`IL5F%=gCzR~kM7bF-EJDtl5i@h*aBa;#T?q;!3^Pot}f}E3fZy{dcmW65(lW= z3BF2b+Yj&aNwVE%%S1ie}{%=j1K(U z{&^%HM`kX;ZTgr4x|o&;e+HfMkiW!wrn zvdRN=I{Z`+>xD}fc%=7)OSdEF)ba~1onRWoR{`B^@6zcAqEC2$kNdg+u4+M>d^|xl z?*)%eP@@~n2c4h~;tPOc4%8oj>IR+64l+#!$_LRPz7CWRqCxxsC?7d zV9XQEJ3)dB9-XbA9RU0-plua9Kr2pAvqSeb$buA5vu{6`2|3=!1DqYe2ln`NGBY^t z04-&4LDvge>H^jaW_Iocuj&Wug|Hx761c%efY!=j7y(&J12zK8L|;np(FtBf|Kdak zs5oln0M(z}t|z)X1i*>V^+G3%b)tC(NQ{9Wyz2&IvHk^*?rjoaJz&eg%+9^wgJr?) z1+iWvL2UT}Drb?#4?rihF@gs5kj~!+-3I__OS}M&A;1>-fHrc1rWe{kgM*;1HuzLb zFHol$p6Wh$G#>yRw-4D4)_MHJn|4sT0<8n_y#YFG-Ldn8WAgz97f=Jr^}-8KvlQHL z2Wf+}m_P$42A~ogG5!tO&;{N<4B7%*`X#ON1ON69FXjg_Fm#uGIrvV%q2nWH*<^=5 z6Qr6)9iIf%Po1?tj=6ppKIZyO1*FoW+aA1l9klGByB@SX8G5ZmJ;>|OQodfp19JZx z=ynFsexV;8-3%U@$2|`IXYTyj>3arx_h+~332WCg{LQz)8x~H0n}{zU2V{ZHP(Tu3 z125G)V$a_OD!jXEkGz=u>HmN5b@$B&gh4$?@VdHQR*@;p4Bfs*I#2O$;}Pie1$84q zhn#>fFDiY|!6Vk~3SqzKwz>8q3~UHY=tc*RSaa=xdYB~#UYLR9`L`WxKA_U+y5l9( zlJ}EAmK=atvZLE|2f~sI5KF+7>WP<yK^^8}QEU2gg{LltC@o z<_geWFi67^A${Q(3zLZlxTyqge>J`V4=?qy#sz|Ig5ymOU|@jMM;@JfLC0-+bZ&*5 z_c;}GMh5)IVu)b(Ua(+iDC7cx&e|8y<(|;BmmZ*rkj6uhr7WPuuiz#QXkYCG@OD7Z zE)dYxMG(y#3QFNSJi0@7fbaI`4BY_M-VLg*z}Hg#aOw7vaOw120up!W_L6bw^j!eD zuF|F3cZo~qImoyHsIB<}d}~N&=mgN6^eD0&AQ9005H8(*GH^W&5EHw4K=}u9=Cn)a zN$~x};8Ry1(oLW_D;G$^ySerW=qOC^d8YiXhdiJ=d%9hJxO6*MxIj+l0!4#Mw}Xuf zeB&QT7F09;aOreV0q;BpUF|ysG@9nZzYVm+>4azU2T1?p1XO+le{&rV1A_;2+at({ z$HAL#a8gN4qyb}`41>J|Kd7|@B=TuPT+zlq14>4*_&H4ZTf9F)tCcbVkX&t%* z(uf7M4Ll%+l)i*^=|I~MK}Vc=K##$OoU7>34c?{J-3v0L^Az}2PH@5B?YpG;C8IW2 zy!j`y1OMb>(0cP+H~79k>)Hhl{L^8A{}g|M7j~Qm`38K4^Z`cD5N|{rs67JSOa?hy z^@Up;14DN&=!kO5gZ!=2K-n6+h0xj+;danDl;ER6p>E#+D$GGeE959q__i_7UDXE| zJ(>@&cy#xIswd78>Z3Z;;|na6<5C-U~VlgoVEuG&cZN2HG~M zdBWr14-W9eA2WD5;l#_8fB*k)u6@AB-;@ViF$7j_?Ye=#8M3ej8c3b4C%St<>8JT1 zV<(v5(G1>&$iUz75_IMfxaFq}J~^4VXlZmkA!7t)SD~k<%22@6inw2E`J{ z6W!pNx4Rc)2rNy3>R(uz0*Pa!DUcxcH01_qDxP@39Rp5GAfq~4|9}pu{TU5POduvG zF-d^E0X7INF@cUUhB+FPm_U3F=xOVn&@{!54v>rf(}LV$OcD0vqyI?Xv?Kz=Sk4Le=gvD0O%y%7vEn0{}0-e z13h;abaQm`A;!)Novj~0DZY6x$UqkUCeTL0?pBbI)?gX_J{53Y16v47P-x`{ByE9H zGJ^2v?FAiX=5f3gL=m5+K}R-wbhm=3gAbTIy1~XknjDNSpk{CD4N#n@B0L1*duW~l zc?NVOpGP<3uut%{FyI}iofklL;Bk-6UeMvTFJ8TZhXsfa2@4jGcF<949?jrmqgnWy zAP4G!jI;)yS;yZ8TKx!f94vEz{fF3meFEAUdU^W~IL|Qgw}M;wpwbGQU6ep=eh?2_ z{Nl+j@4@K|l$T)H1#~_j~iAo|NpR*2P%R=e2?x{FaHH1eRS6 zfJC50Fi0FDyMP3-XBQt>cDWM)&MqLMI$Mu`qUKZtD7%1|pzIwCu72$PGOmVvGS1vOBR3KDqzQ4CIU z&<2a|3h)LbaB=6+{F2E7(sbzorRb?365L9G_Dv>q_kx6A$$kPT;Xx;SR$xkbbhm<3 zcyzXc&iIDNTl*r~4Go|xR6#~S+YK|Ie2?zd4sb$jt@!u+6om&aRDm)Kzxtx zRxs7s3OX_!4r?ob#4!p#kRbNLPa5pf+7mDC2ZOw|7i3gtYY8Zy zoDBvQejp~O@Oukxc!CW=EBrE$91SY`KzxtxRxs7s3Oef^95A3#th*OvK2qTal0_7L z3E(sbYAb+PFJgkh>vvo_&l#RXYJ9qMgAaLg0UZbjjwTn#Il?ZTU~$la@c;k+hm?VLaT6TwiaOrjm@aPUu@a%l(*%_$d(H$V+*%>I| z(H$V**%>I{(JkQ7?ZDyL>B!;PDd^F8%BAz*i|Og0wJ@ObL~3`uh>C+Qhyf1*!S;W; zfd`*JN_-!9XoBX<9)MSqFuU%6&T@S??qC91y#gNW0lCYXk-r6WTm^LT;s=jzA;_sp zpFAMl@DJUff#lE+olY{K?IRF*$SstbpE^Mu`VXBD6&|4eI(R2u7-;ly17wL+9fybJ zMGwaF9-Va@py2EVUBl@60Wz-n0m{>MlxhCK!QTuzW)w6_*zExty#CPXAp=f_pc!=! zX3$tFXp4|1$n+20MKYbAK&!k!!?n|oJ2X584b^@JD+PJ18=}mm+erd6A_yJVodj0s z!oTgfi{i27e+>LBpyOA;nvl&0-JJwJpa*mY+K28yiB8b4Fn=F(a`{8IheW6E6v(yn z-L5M-4|e)a>2{sc>AIxbbqUCqosKdnhITS~fR|Ub%m%GVs0H0%4?U>{bZzDf@YcX? z-y5KltU#wWy~xT0M|Q_c=!Jh6s$N9oLR5gJJ3Ts~C#X5cpeBnMplPg^`ru><4L;^Z z@Jg9Z2nFi)LQ=;M*a|98Qh{vbgPk4<+dl;wl=}f1x9fHf>2&?lSo`HUXbE2{cw;Si z{?herC31}5!_XN;fLucp*=-3xz%`E7Oz|K=I%&-6d51#A= zXZmi?w9J*xi`}7DE=wRti*9+k7v^zi(pWVJUK<61>=ytu}F$1zv7M9;( z`wh|dYJ+ape4!1h2f_K>^@4}B>j}ujENCzleBKkN1MX^g5~S1vbR}kY=mXHKBq(;E zMa+wpzrhRWAOQs~XVBJvA)VI&nuY8{JtzgV5CnBgFLYKCzT^{U0@R3iUJAAvN6g)Z~`*cPt_;yE0 z_;f}~_;yDL_;f}K_;ySAb{BE@bQW{?bV?t02F*b+7@lO)?{b}Q@*lG2{DepHMU*+G)lx>ua+Ta3Yg-3VoiWgS@z~Km;-~=60*6I2KvVlCKEaB4~dce0cSiq+n zG*%qD!?QD*!?#n|5t@v_79Q#>UE|St%?EU#qVEEi&V#<47koP}f(pwQS&%jd$br$& z`=R0Mr+s=|7a%R2?k@cUsrW#LV%E;^0gYd{PI%FA>Hq(Ipghz00dz>Y;WrPEJ=Z-t z4|#ODc7V>YdvP7K;{gLpxr~ zfoPfl&1#?)5q!N2xRQq4s|qfLUB7^Ib)M=x_@WckmWS>rg{cpKw4dNBUqLJDI$=j5 zgVG%&R*{M}P?7rL9QYD=kdtc7yjYKB0%&#fkC#D^I_X36jya%)7JmzTB`jpi1gH}A=2l!CLi=D1pUTg%7 zH-l@Y+7mBILQn%A>__m0dEf>+X#YLDm3IQ%K=Jy~rs6F|ic?Zbz z4E!ySE!v=V?gdbEb{>Bbt2y8d)9Jgz2i%awv5v^4J3_(* zbi9~Hr|W_j!k57XETl|S`-vL*p#1ydWf;h(-L(&p&oToY>*51i{@Crh!KXWP0ixE` zfY!R8+80#oN+4=o5SPQFllcXBAq`~XK57MvvR(z^Ur=!i^Dol*UwHftb}49XZG#VJ zW}w?c!L##$XQzjPN4JNBXD6tk?IGaV=^+4Wl7KrM4ji7H9FE5wK!qCv=!CHwoi|-N zW0&}Jmd@02Z=mf7M;ctNsDuDXq zp!Fo+?JJ;s3Ozy;T4X?`W_oKox=UMNr(|{>eDN7{s$8#Y$8pysprR1{P*qS34GKlj zfvP^;u`3`gE6}-k3qUO_pKj1urqL2U-LV}$-K7(JI*Y-P?9(X>J-Yn`sDJIzcm(84 zu-B3HCp5otFg)N0sy;xMS%dZrfu=E*&@R}!ZsRgbTU2k-|UIEo;&_*ff z0Jj(5@k&U86eaz5G`>*)4O{oNGW`Aj-_`J><1WyeJlD?4=)G$f&{Anob$#BY^Bnks z$JQTz{{Mf$kpb$#vz`YXi~+mmne`-u2c8V>W<3nyfv1hTOMie0QqaCOpKjL&E}-k< zyZ3@k0-clY(aYNkRt+k#_JYS|ajgn|;S&fx&Eo>-c3Ni8A_-9Gj@I7r0JZlaEp5me z1q+W(8x@c43J#CX8qjey6#^gzcriL?;b6s8(4Mpd3eB}&7(mTiNYe+BQhXo4Izyl{ zd_iR%sQv!}e6Yj`$fyFy1*qW*THny?dIQ`Nht$!aj!n1k576vp)G5gE8lYW~plHNd z8o@RkX@Ytv;K_Pue+G7*9{2!eh(FvsAcae=I90IkLWC42r>@YpD%&j}vmY_2`Q zz~2g5yA1C8gBI(6N?=gv``+;AbmVy11S#1-lg~H6Cyfv|W96A2xUk#-K3NJpZ~|KV z4Z2YQY#!=CR1d)0Q=LGAZjkXL&@on!142Vjyzos0H7X&am%X(UptT#w7y*xN*2)Hu ztZPHJXn6xOLuV*xomoRSh=OEeTq```eW@4PYRn9Vpc9^YeJ6MvKL$D<*zgi$c&uK+ zr`z|2Z?`80sBP&2I*}T58f7QA&jiZkpgxmNH|V6|XaS$@*a<$}r87`FO|FLjT|0k! z96t;)*Q0YQXhznj^IYdt(7HVzQ1#vm8bSuQKyP?<-t_1M&tt!M`Uw<(;JaHqpw}91 z`UEXeA;lO(4s`M;c#SoDZH4OtkH#aQlmI{b5;HwQ+q1o{7eJl56R=#~8TzKX^bNRq z1!{R8da?ib|Nn;HJbGO(c=YmqDgfniPznwG&{=xMqw`vKDCivNGoX!@E}bWRx*2>r zSyX(w9XNbI#f?w5g8+zu*bNEln1WTgs3^dcX~2{jytsJc|9_;WEoem{1FXGT0CEI= z#|j<)|9=;#Xf`|vQtZ)d>rlYV0NQE`9&LhbUI1H<-H{&9%?se|XAB^hV{@bq$dQn3 z7>NB+kp8O&BK*nf62Qq`!rh~HE2OK9^fe&p5EfEDBa0_qYf!b6ruY$*&t}yVo zg3n=tVc)4_k<#$kH!?)W(!n5%J$WtDjwG%u#uY;~$?hc*c z(dpaK>DuxlF994|pz9j8fOd|%?g2HhL0UoW6c13FwDXZirx$23=?3t4J7|eerx$2h z=nRkU(ghx!MI0WTT*sXttv%39M4jh6z+Fl=&=yPBV!*~DAm4#{qtNy@=sZzSdj&B# z4Gr+g(=7nHv)ZLQf&twT0kpaJbG&vfXmVD&=1{3A)r?B zizf#_?MB}PpvubwobW+wqd{eu?}cvH9i6U_GaGtA^PZr=jM5L?Z8Mla&7#r|KHcEW z`97diy}C;ucy>PW>?~3M_4+(Ji$F757d$(Q1VD2oKHae=d^@8!JUfjHZ+G4U-IweG z+Vs*HIsshy`*w65d?5*HTs0m6b+n_gj+a6DKj54Wsbb)Lx=!CSu&yjQ1RK;a0CnSgUV=gZQZ2oRe+do- zNc-LcGW_=9Wf>?wKpWAIH$;FY#1PyBWNvrE4zLXPq{9qw(r|s?alD}eS<&%^1|;tB z2GE9TkW)Zgw~seWfyqISMfCtx&`3vTL)-Trjc>q%D3Hj|I=Rt=+ zK0p4#$_eag>~VhrbPOav?5xKvmEaf>tYl``6$6Q(-IdG?FE&S_#{CNh(9}$&)}PN5V{o?CWWH4+`Su+6Nr`O`sEFK@)%;%~(%@JO}DOfQ}1+ z9xQSgRJedn$pJTD2%O{LyTG?w5L6KRb_as*K;iK14io?#6fWS}9SIo)@&y&uAu0;K z-2q@F8otm5t^sIA;tSz@paKQdHz)5r4?fWGJlPQM-eBNw(E~AiYgd4Zl^1THQ^~ta zL4!{VJUY$6cgb~@b3j^BA3Q(>E9g3%&e{g>7K~ER?JA&6IWH6=Ky{Dr2iUYXN>R`y5~U|zy#ESX$kTbuqZcFq8pQX7HN`nPq*&|-%c-3zi@?br8V_yfv;aF0P+O`zTxdV8_?jRMFhZ0)Ct6i_1r)P5>UL1{ld#@l`Z^`Ag> zG)m!tr{I8&HGrliUPyvcHhA>7_QDIDC*aZpUSwVX5Bh^b6?Blz1Qk?kNEYa8Ch9fOGnEM}iX%IJ`R#zPJUw?hD-7hxE+Q z!xP@$-2tBc0Ywq0_5T6X4Da-P(p?HVdHMx(x{2!J1 zXjA}^ATB`M=X(O;KF|#=u?L_D0%o1>2A|H8zM#c9t}9+>f*cGfNarg<`#V0^BQlNkX4=)EOfR;3Pb_Pg*wq|;E27uOVoB$OZzMUQ%o}B_HE(0x3 z0fpW{u*=?ohL~Y4gSRh1{S9~nu`?9Z!+HVU>j9dew+=%(PUAnQOJxNrSwZzX=qx7C zM2WQ{x)RXEw5XH(kn^-*T^{h+u?YJc-+<1fKz2f>?;VgYK}%vl_n&~avViI#<}eQU z78eL#zyq??$vMT6TFEGsBC|fzbLJTA#vq)xu8#M?2Cj6k-qu$e@!DgNi@~twJ-X0OB6_ z9!!k0;dmeh?Jr8m`r{iYp@%|E_vmhbv`>9McxX3*8nK{> zZgAXpH-M55c*u_#BGV1JSO}blLD}|zhvrYvAa$!CsM>J_4Zlu6R^18J1ip$8Gy!~| zx%LhNe=Dfx2DJceA86T?btA|X{LP@F3y!-k0T0(Gf{)+4!-z`w1)zo%nC)SCh`$LuSOZxU0Xc*M z>T6I=05!3oTu^$3ZU<}L3o@O7zr_Ju4uLzD9UhRj4^%Z$oPa##(+wT$J@~@*0jLQ9 zzRc<+Vvz{wWDBTcL1*6~$0?frz1QISJ-Wfhcyzi#ZdFt=L#^2xz%{!d=$ugSejZS? zcQ%5$EFhLgcPAw1LqB*hcR_kWpnZH@pg4vqfG|TpcvyFVN*Mk&Rj`l2N3Vl4gHiz0 z#}ETRjZ<(UfrU|P1WX4~61Hvxn}#PpFDnEW4v{&`47->>SHSl28Wk}!y!h<{&ClQ* z1hpFE60n0i8$p2uUNi@~@4gi>C+*tc!3>A zUj{?^APrFCKwUquB`BT350G9UsH6l-c7wZ!U@h8>pw1(F89k`+59+Cc*Gq#Hba#T& z73i!S<}R?>Zt#WA;KdbFLEFnfOHI1L`^{clV_`d@@kprXDXJeZ+&LMZ4G za%-?We+zQPgLaES=aGSIhDRPs7Z(vgpc59518DK)|Nmb?gA^JBppNiM=%JyX^TAQJ zEQ^_8mo_BIyz-bCUU+yjGrT+xYB7Pv=|H>kHBW#B&^$UFL5CNB7GdrHtp)&<@(>|} z6To>Bx{>;&2bPosD-0nE*PHi(redI1%0r_DoGu|dpkQm{VQqlMBcMtjI$(n`9tax0 z01$7ZS&%Y4#0=ME{z{lCZ>pdVhe}g&#pw=FA zsLi8$5@=4_qxlu%i#%|*6uiaJqth3oc-bsy@y-dzI(gq0kj^fsmF?Tz0PXC)@P(}2_H2I12%7pwtVP=ZU5mB? zx)yDQM|bQ3=vp-B)|eOaZ@{H7sDAQ*UI2gQ8nhIKOh!OvF*{v9yo?7OcnbDDd{86w zMz`;k7gb-uhJX$af!@>kq8-f;upGz`&^b|{1mFQLbY6JC3LQ|*;rhX&@dzmCf(9bt z?b~3F<~J44r58TEu?swUV>^%=yP%0K*jT(zXXt@W&{PAsHx0T%7j&OAD4X5@9Zb>b zy8(1m1Za)|)YJ3m^aV914nU+pS3Q5|_TA$H8Y#W?Vk4xx+yEY!18p1VWbyzlN(S8% z4Q4iYG#{w|xfH#>2VO6+6SRZB^AzL+Gswy=pU&f*$6iFcgVHYOOdU{OtbO5O9gg1q z4+a$$y`dMN^HtzF`o#-@FQAZyu3b~U3XLdmDFfQ^2U->h4d4%m00uR=K;0SQqOn-JTw8iY1RXz_ypv*K;H|Xqn%1`beBG`Ex9A21%X{KP*G-kCY*k^I90bO{w~c$67p!>43shFvNU8$3WZOo7<|Zf115{sHYz zb^Y`5JY`Vmkj8hR$HR6TcyyYmcyt$VcyyM4R;m;TfEWVsF@12a z;6?cwP{KU^B3=g^$Bz6{q1X7puktwnI(iJesR3Hwih!0?^qQK1+zzgJdU+otGc&xn z;S3F(=GrF={H>t1AmFNy6*R=-0ome@^MqN@`MD)35+2>)nFr9`74Tey259pN=t3w1 zQ2dpsSiA^>-n-k91j=aLt{;v!fDep;^8`Q%8O(#EWe?~T!QCDT9^ zu7~Ay{$|iA98g^zo#3MZUh2Y*c>|px0zNJQa-LT&=%fJfkzU|41Hi`{Z3T50pm)8Z zoH};{>KpjMb5}s?Zn}N1fHrk?p7H=6Ja+;l=8;``0($TqXxAOc#my*Z&*^~rd7#k> zu(zP`13i8YB-h;vBEiL2C-nHa9Uk2fue-rdpxc0Y0v)Iw`4Y6YAAH~()KIiT=u|)! zL-xFY(;(;^I*?d*D~Rj_A9c_TCZU#hx~}PVT?0Mo2{b?)y2FDxbb|-zEI82VN}ZuI zz-JIZFMzJS@nYj7@bn<){3+12w#du%z@NKxDGuO6) zS(l%AFqhr{9qIt`2&l3JH(NS;K^ynM`PQQweBRGSMi0=U?c?As82ALcbZSY_Po&AJn0Qkfxuq5bg zDKts&QB+__(3w?m$>s;l;9;XFxJQ!q#)G>t_hUh~qk-Jm%lj>snc+pF9kf^lU1|>X zA?R`n`0R`C2am>skTMpWJ3$lNYwSQ3zwZkV&?! zhYZJCL;nB&4>B8ah(_~X5TBX97kmqMFY7AMgh6sINU&R^!y_529AsN(NTWqJmy?{c~8hl3|c;7YRK(}Ka$*6OnuR*H} zdQEv_m>G86g2dim(LBuF18A4JU!UpKweU>LccUIyWcJAp4&{JZBKa%MKTRwCM&}MM$n-}G9Y!$wNDuNTR>~_4Uz7+ zYych0`r?r#c<=~xZVb4{A|lidMu9`^aRf8Nu1}Cq`xU{=@S@ccGt?l59fInf1_;x4 z1L*8I&^4zJ4?<2}1S^0reRrTLfSl3@Rsdo8E`TWjk7&ZH3h+5&kctGt^gZEm@F6qE zDpZ9Th!_DgL5nLPjwwLMfSCxH3WN-piI9mv$bgxk^I0MGbwE=9XcQA1wh&7ogTAhy z{_qEn&PMQvG1S?hz+`rX=>rb|LWi}$J<7%-pzZ@`jvRU)aj&ZZ=rH(ZOF_`OMo@1H zzJK~fH%F)Mlb4`QE4X#n>G}lR+Ua(E(&_pKV8S02KXezdSeLV)2uN6$OCSj}R|zn~xy|)3 z4(RkV7t3RC8Aa%6<(U0_@cK6R0!`=|Jn%LHGsxs)J%@+pzs`@HzHdP1yusIQeg|E- z>|ze8XF;ns4={obF7W7Vg4DCFKRk{%Al0)U5cTX2L_OOLI;r0GN2j9j|qw7~KQqKf@-+!0pA{y6Rku419ff0}DwF!T3l2So(EbD* z>njzI?znyd8Y%_vCKdvj(p~$5e|zb-7fzr7DCh`2Xz#;|v~J&T9j8FUNgVQ3zTil5>{?quz0kr?7 zbFT$x?`h{&128pJ15EWQ{QdvmrL$EAO!Z2Dw&=hDp;ZJTC;&Qq27K};*lXQjaZqTq z@_=PP7Xc#6K*FSz10>m01?wS$Re(EBpp)=GmUmADJEeOo*eTt6!A|K0JH?~37kn~T z=Tz`1Tb)}Wr%~-a0CrL9p1=S99|s@y1Uhg1cIHX71ngccl6i}UzgJF)hf{u6rA59N&hDY~SP~gH2 z&6)~6Sh8~~=mbW{L5LpR5HaZK!JSh<2OdHNyPH7AEOfT=fJU9*>cEw0=TyjndtlAo zO`w$6*$dkH)p@WRya(*^18}&5&cuV72HKeF(K!`z=G|0~3p~2Ff_acIc>@VmkPEw= z6gpc$2itXnN$XaS9Dj2&xG-r2>FMqTNrO-7>D~%*U-wkdS&pEI<{W8ImTCo^Y6m)s z4Q%ROkgvLV+PkNMv?)&oISYI#d-q4@S>?8D?Pfm zg7T41cPlubfkxN1egF^TpY!PM1+iYt1la-740RYNV}RQ3Aa`4Xeaznmsv%)wonW!< zUQmY92D`lZAXDc_(80D-L8XlcXfmJ`>?CHeS3Ru3N8|BB?%)F%1uofOArH!T{OzDZ z{UxZ22@zIq1r;*TGZaB(Qa4LiCpfAgr#3Yo_z%gYFF%9a*$sA+M`x=8D4);aWMJ3< zE#1IHIR0`IeAr#*RL~)J9-UjkyD~fXf=-rm>11>UGCCEL|TL02rD;1BHCb) z&ek`7kjhOk2b4Uz!Q#+zGvpt*a`^ZEKlCK6)Z+& z+Usm(0d1e`hKRw-O-MfnEC?$%L8oMTz|}#^P0(qVP(fI^`Qi^~Uwd~eh=!J%Paq)x zwiZ%ffsdZ-o(gh-NB34R4^nP|PELdw2r4%(fISByty@8I@NyHRr@I#<0WKoCw}RZ) zJr(Sm&f_mk#K7g|39!wr5DI+iC%D`MX;YpGau&4Q1o;D8K!RkThevvJgEhc~!R01M z9$IdKoa52m3-T4D+ywas*&Q#|i-JmzUQl3mgUU@8uxCJ|H8@oum7AbL&Ty5RpdG`Y zSa047O8xM16Jic1Ny5ubP~z(D1xbJd1$5p#Bmh7O!lQdCXtyyxq}+sxfYJq8xd{^T z=!TwMJr$H4Uu*+;q_Y*|keASM6P)g`ShW{q$BV7trB-`EMs>D60hODJL3c0rf|y9< zCP*H%+Xd9V0yz|X(lFRckM6CYeB{&J3eIQXa`Ohf+yt>+B!cXKl$&6Ofr1FMhzDel zH8=~v%T16xXe%iw{K3b|X@gykC^tc+4W!%zJBbRR z5ahMe1>K+%XCOzh!>&|=m3pA%D4{=~pd;(8UH9-eL(axvT3Er9B-3yWcdkl19I=F@b$Am}sRM0Wr&@u=-N!T5_09F7&&M&Q9;L*Jm zqzXLnW({(ACwNv1dL1(OI7VzX>;-9hVFhw+ckP1C)-#}TM;mk?Z!d@mDtACVVd!pQ z(5ciQ4Uonf==LS>M1xN^Wc3rc#5n*jaX_pWkli;2SUjNa0fmM~H`r|+)?okfw}BV_ zK*c)2V%^~BRBdpOz)Kv^zEsfijRhXeQ2nkmFbf(`C|b9Ik~B&|0}@sS&$>Yi8qkVN za6to-=?0%Q4=!jx(fLvkR3O3@H-Z~zpabebt+vJ^pnf)J$rkGTJM_NGZg%K;e9(Cf z$ai$YPeuX_l7kKfDt!T3S;~0P!`k-&e={T+K|_(?yPIEtPc=LNI;9P^Yx@C+13nA^ zvK#}HSwK5qPrUE}tv-Sr0`e?N{pSU~Cm&<+^~(20;gK=xPde+xH4+Up#m`4qVzJOM;dMfCj5vx_vKn`kwLVJb0W%1r&ZS zR)WG4wn`B)+Jtr14-1U*Cyyt~`=2FM|x%O4;kX)j#5L1T``Ui@NZU|@E=06LrT z0H|PW28STOJZRJbdcH&B8v#(M>-T+-wX61IXUycZ{8-Yr1{c zTz=I2im?+^Q-Y30tv%5#(%u=m#N)UtXg4l{$8lHCj12=M6@aer1`Wc46P^HQ0tF-v zx+`uf=%zo=P<}U<)p^~6@dtFZH)uIAswlV#03O({?eNh20lJYR1yqfNuCSg8VnEiE zgNBVjmk_)Jt+D8C1xaYTu0Xz10d_*Y?~iUrP_GLVw%xuPAZnFeR~&r6*a^Dd9JIKC z@uEj3_@EIFJSsqq(N6GP9S}2`4{$)+F2}(oEGUgaWWa|9bcdekWa$E(jM(jZr}I$r zq5qwscbb1Nbo5GtS3-aXJ6xAQYs={eA>v)FAX)g?)(N0-&u-TZ-M&kdeODZO$k++G zc@gYn&=nYv3e$Clhqdbj_@yME1Gzz+5ZHS>Zr>$nRX1n_3~JR45efYOtGYp>;h>_(gBiTY#RELr z-wUo^AvQrL?LoWBJUY+4NZ|y}&oRQ(Lmc^X9w@JNyEb4r!Sw@7C8P!6!3IMOUH@V9|hzkrXq1qXKLDaH%X z6a>-Wp$#^q^WedUOx@tbt{u7rbWz3i&R$SQ_Jv+2Xu(M71CMUA&X1rr-g(HHfES{j z|NnRQf^N~!JlGBH!dsr;?}N-nKq3Ir0PgMu<&@@wOb}Or*6uz4_j3^8Y3=#~+VFS` zn-75bu^SwL{M$qr;Ud1E7-zKn#NQhWI_%qZiAOg$BY|$S1$z>-j=39@%t1%{@VA45 zvbPn~hzE`0{r~^}Weqrv!A&D@-w(c=9F)4dJy<$@m%IcmIE4fpsONxe_ynAWgH%G6 z&%+x#AR$nf5LBLUfXs~rHRvGAkw8oNK-Dm~xd-8NgCh_U6U~P>EH6T15Zp>`e!80{NT}z zT(E!Wo(L}3LqP@m3vj{y5)F5fxWjQz{g4^?uavL;#3$6@6$q&*Y z1sCF=1~s&62wIc_k-;d$uRsg&E7%Hgkcs>_3h{SLpmc?^5Qh|G z5+ziKgK{G5;v`7t9NPZ^Y4+$2eSubpzwls&+^7U8#KGE`!9@YI5WnF8DrC>Sm=CJ0 zK#HL1JHgtp72;qsK>Kl_Dk0ABU8~cP}WdKnl-O9;ih+$OK4{4pIX(7j(!gSf_`z>kVkRdJj_I zfz(59>*0qKsSpugQ2662Qo%|(eP@76Jy1S?7pb7S2CYa1r!2^b7-S>{TovFhQlW-J zid1ZdgUU}>kqR~(avU49HUKS#Sn~2b-XhhbaWAM?Z(w9#fQ-iNfGj0|ED&yd36khM zWqAS8ngL5fhNNGDHcUawZVz3svV)LX5mMiEy8d|yItaHL+&%8@1(giV2RSTHz{ZV0 zk)jPAngEsDu76&FF7`$&)AwjR0xE>Tr8DOG2?Nl6j?U6IouMz95B=%9-uw!5^y-7o z&^!F?pi14Nmlbjbpzi~ZWRVUJ#zX%fK~n%|%;n7s(4n%REeLH#e#QK6uoN%Ntf-~)X@rMLF7t|AM5$UjFSTxjw{O*9ZK~O5jj)eZjvCbnoT| z{%r!57a<#Up>6^#n+Ao?2k;V9@U00*Llfxte}a2{pkoiZYk$1Rc=!K*cj=eT<1fxJ zFfeo;fL|GkIA07!J^0Et(2)tpUz`Ow3wqWyXf-X;d2h9lvp;Kpym$d>$wAcfg3=u! z^`K3FptBe{kH3&5Mm?zU0#&bzP5lngg?yd0e>zKlWS0KfSZO{_+x181!OQPDv9E91 z0p2Ox3HN}Fq0c0p#z`{cnqzz;>mhi$7-U1#wXYYpuxCg=T#-g9YV`uVGhyf(( z-=CbabUVmq5B~MFKhPr@yuJ!_WC&!H8#^fOK{0|O{%=9!-yBr@G=k231YH^dsd-#K zc<}Ez(0TCSPv%bFKQDbjNegst;6eU9C!u1XV+me>t||bRTHtfbK*cEk-UA-ZAMD{; zK|Oy^DbRV4fA7iWkMa=R-L8M285zC222Y)1NniZiary^TUm-?{Amuyg+>aj~kaGTp z2ju413y_msOCLbeGiXGs`51=>_hO9ih{8&P&k5 zShwq$PS-izu5%1ecANr_AcBXVOBXcPDKI$j!>`6U-syUzo2}D#M(4q9*BPK&Qw}uO z$uM+;PUmj{cm7;Khd+YGx+fp%2%X;LIvsLO0_bY_&;{LG%`dqQK4k7V(c!wgxk`qC zzXdcpvIDfMx$~e$W9X#54t0cX2Avrugcwr=A79VR#K5pmW2^pO zaB)}1&cM*+x*2p3W`{eA$8pycpj8&lwR;%&!TXN7Tz5kb13cvc8c_xh42E9lVBzX^ z1zi%|dCH^t6*GAAYG-Hzyk)co)Vb~U?SQw8JbG=*^}&mwuW2(g>?#KBMeF6Q(r0FP zQNhE^@G=Wj0lD4*A7TMI;0tt>GibIDX6Z}lw9N(3hW&2eJ87U(N00Mw|MbEabbbTq zxQXTipbB{fw6W?6QCWHiG@&+=4cr}D0U2y?UD55j1$1OL)a{Uo(N6Gj5&Z3-g=-$Y ztiQmS9U=v5-g)%eZqfsLLJ;JMYKSMc>VeLG<7Q?62U-hg2M6dZJCIjgSAaHpB8M9f z)5~hG$q@H}n>URwL8$@ZqiWD$+sHmb_B%)i*zcX!UK|2VH9!=C+Bhp-f@)Flg#e)Q z89=A$bRK{47oms)bYhSPNCT`EYlL1&0$MfD`tSe$W&i&F2d!@~{r~?zsH-&T|NsBH zv9%{5_k)3pDU^04bY92>H2w}+#NFKqs_mdhN?rgRcLi?zKsG&t4q^rEgI?hQzMvX( zcp9jM0?KW3K*R0LyP#JmfaiN4JzuC7KX|Z0hTTd*1M?R^k<4=T-+##U(%=jNIZ?Ys z5_DPzXlmB?fQPm34tSnY`~bQJs=4+78(Q{|0`+J?$K`^L1qPLI&{-<*nZk_~p!iMT zZ+iua#Sh&(Ky4dP+1|YYG(!GjZt4I3;M*ZO4|aZl&J6c}PG9lqcKzTBItp6zQ0IkC z*N$%24rN!6%!lqSu(zKve&}><>2~eVc5MN#iwAWC8eYhNmQ8>LU_cYfRUDlM96JyA zFdp~ltl|J|-T;;K-Bkie!U9m??jjk@W1U4ZaIOT13p!)!L${NHwQB>U#|3ixaR*Si zGC+pbL3a6m=nhoSJk%Me00~UcNsyqqIB=YLa(I9)p9E>_F3@2von!flzYTP?5_rNI zU72?1oKEm009bv1j5U}833bT01!$0_6MP34=&mr3GHVYRq!srbpzFatbb3fY-2&=7 zxGwN$KEMph4Ia%enW5nexl#qPaG~>*2jm987tx@?s1vk}pmxFwzYqWZgX?P0dVk1$ z-mX7hs)Cd>*G^#IZ><8CL^GNXfL2c&0PTx_^zcCwtRA3CA({^|gC{v*2T}Y0m#UzV zQ;+W21>IXg3SX#$TDh=uqiYv{F9+!^1)W`W;6*PxXmP#o0S}N#pzZ#Ud5f2z_GP#0 z3{daA+jj!OX3#1Ph|wU{i#he6&X4Z|xOT`fAUhy;*nw6BfHq7ZU1@i~gBg5{4ro2W z1P^A{1sORW2KfebM*aa%f;-k(x&?GO6gwpCfG!{dHJV>SE)oP?tl|NR#?Ip}I9V7Nx_!5R zQc`z-LgOLO*a2uLHxRT}88rIP?YpHrbc;uK=?CpFg~nH4`3s%C8z3e_#vokRyaYAd zx?Q(`+^+4Y02zV+Z`$ql-Jl6RZwY)@H2C}j&;&NPEcy>>VS#v{nO@L|;1^!(g!mBF z+e-koNzvP*3ZOP?XX%$0cg{nbq@YWCK#kb$(l0Njg9hdyeeK#G-Jq+CYF~67e*uaI zQ2({_*b8u{9efHKsI`kZo&h%BqqFqGi)xq!An$=J0ENSgsURCbr2s_4^#kNek>f9H zK!a|e6E-@Jy(j>cdJs!c-4E%Cfx39zwLdz|V-KUY_d09;bl3hl_*|m#IRgX3|8Cbm z2On^NyCj^)L7W#H;O+>NBLVJ;a2^MDL=f$HaO)C!{xZl4^!6xZjuYM|?CuAZ%%IBT z1H?=FK{KV@r60OOzr0uu@kLLZL#L39t3Ts_Gc?lZafmCeWzCYk=rCgtY_XE8F z4e)z(?*xsrcbC2a85ILI%J)Zer3M3k3+N^ZglI(c=#izMY{Tj=R1ACHijHH`cCS_?tl~y4&>)X!bw!M>k8Cwd)7|W>D`1 zE_j@!3#0+;t!~#3-M()?C1dH2Zk}nH2RnJDfy-3L=A2H~H!m0e|NkG9JW=Bhbly9p zd){38hmn6WbRonC(D4+_KNRbHq=-j&iH1gTGbq1K4Isr`ec7UkvR*;-acLS(ucj;_h@azBo z7Y|o3F*xo3^%)&^f=b=y9}KSit_QopvM!bf9Qmgoa%_0S=*qwCkV|K43)rAukU<`e z2O))9Zv~PdbO&i?Zvl!RctdPwZw87Wcq40PZvsRRVhX26XRimcfTw{+XRiZ_hyv)K z2NV$vkIr83!L8lB5Rdn2K;*#`b1yU)6p#cvdnF(|h`pX19^F$ROd-(Hpo2e|Jvw^@ zP?QLGboMf!h)8&JPX&u;g68gjfX>A2hQvVkR*3PC80ZG;^XYB{DS1)5oC$Q#%Lj;Z z5cR_A1+)zSo|^1#1qpg|wyppzEu0KmYz7r#ZUr0L38A`Mp*{i&YM$_D{=vfE3m%<4 z4%Q292LyxkH}3`crJlbTGN9WE;(_`D5Q91oL3%Y)K?ZejyqpgzKpMeX80r}q_?z88 z0^ML;9tU4=cz{nDf$Q(!08Q)v`Trjw;sKhS1)1gn?gW9YdFc!>1tffo0d%uAhy|UM z2RX>2@g>MQM8AXiB&cBl)(mc2LHrFaA3PvFVt`I(EC8>sf~f2~2%Zvx#&BmV2RP35 zf@1fDVL9j`pRI2|Vc!d0n|Hhw!~ty%1Fb~{D}}~8IKTLGH#jgcFuYjt?H}k??*|}_ z-K}8C8XO+{t>EGo;v6RaHb00bE&lxf-vtU6upiL^*rR(dD1UT=-Qm#jk$>CA7t1+7 zn+jt7K(`Wfw$AwV|Nl!hP^%4+bDMX7k6+<$0auBT*m(iEz_l6F{XgCc(h6!Hy?hI5 zb3okVz~2N}_y%!tH^gTLU$TP7nn5D~9=)cy;>--YKmh>W9Jd2h{5y6Y^yzKg01geX zQ(wGz{_j7;4p4dI(b+ly;~I{Rip>T|tO@ zG{0j6t*-v_|3CjW7Q@bi&3nKzd5o}2HNgSv(fk6TN`$fVLiZH#Bo=593xws-4Y3V! z;X2sj-l^dEqu#w6K+CGPLM-hCNx5``Bl?B?zkmNddZFvN_kv@myBCy?e7eC!f{*4& zNW8dow}P~HZtVe`Y}a|NyBES}Z2%RV8t=gA;RM)MdqLC-(NNS(3=)K8;&Z$V4Co=- z3)xoE-3wO847S|^nrfj51RQ|fy&!kINP7m00q`=p&ejT041i(-k|BDx9sxN9ma0J< zP|wh#dn&Z}1VxDlBrU;G^`*~XU+e&B>}~~9cvCgVBz&nFRKRzG-2qM27g<56Is%cZ z3qX!0AytF4;!f3I7sFFE3v#M{F2c;P3lso5LBmJrVc)$MZV zUXYYacPrR6nZJoh)gYBF-K`+)om*32sT#s)jR2+Upx5B^umJ3BFQZ<-~mZ~9^_JX8bx?91nnf!x@R1H$;(%lNu z-nrEVmZ~9)Rs&F~u6Y4Y4;5fv?FCUUGW}6gHAoPas+BnKl+|E`=&2eM>fKX84(aX% zx#PvI2e24`rfLmP41i(-nyU8({04211J&6FAFy@zf{LJn4_O#bboYYd4Lp9{amb^4 zE2#0(A?DG&71ZWF-Ue<0fLj>g2Br_B2?(vad^Jz_bRK$fcOkfa`2rLa-FrdQ3#k@p z4<5a7Xa+jdsdujdIB2$l`~hvsL2F-FjK1Jl#KhpyyH^0B5R{Ek6~YR%7xzK7_wM}w zwtXw8nS-hj)Gz`qHF>cOq7W1i9=)s)pvf6%ns0u|2s-}^)I8W8a*n0*8fZrws4W6M zz_S%(f+PPn5suD_-69ordet3l82FE)X|EZ4d(;wTo=SR8U8Q ze|yO(HYD?*1&a&+HWn_I&VwKe)-GgXc(G$P77HFPWMXLE3+fax@VCf z3zN>NPk#OX-wh$Rg4mr?Ret^d-?>!*lssc+GcoJ}b;0&Qdtm&o*P353cDI6LL90+e z!!F&eV12DJpo9dva`uJVY$k@CAZ@$g+Di^r1>lSC2sTzt-C#Q|zv{dM(hhR0HpGy; z1xyStLGHwFC90vYpnG9}u=Yg>h}{kDmUuM3XY%OWdjp)xTEP^wh;J4H4Q)YEfJg6E zaDwe+mE;Bu1Mq$VQQcEP>V3Mw3CXpKN5G|{_d3`hm;}gnsKhlC2~c)`N?iT*|Gx|W zwy7YUE}fTLx7057$^|R#yy(&`(&5rM^~5hw!*?o3%?k&RpWvv2 zWNrmHula{Oe-m;xkwr4D8xoe?;7&OI_K<@tpx6Xyd$DXD6T^#*GobDP?Pu=>n+Gww z`G*958?vd#=OG-4VJaKQRFIQi1b|G9hM9_NK-)Z|%=HsA1`hU0cQ438ovjj}4Ad(E z%0MzRu;zsBR*4lfM=km)3Ivn>;)<5ZUvLQAZtJw=m}y`X`p7i%G=Yyg?k-3k)&&~63uI$Kvj1R>qB zz2M;+%^x1tU~&E?P@;qOV2(EkfT~tdL_o$mz?Ew{=$`b}CBHyBix?Q1_kvi={Jr31 zTFGE>k8bejRd-0EiwBAb_~0I}67T|Qkj|H&iAK;~I#AekL&lUqi=@C~D<0iYidR<> zGyum5-fL-j(WA4K1C+lZ;nEG}fY$Y~fi6CNVfv1V0WyBv_@)4~1g&!~Xy33$=T^|R zO^?p0C%|@11qpUHfmS4RwjKe^iyZjVd8qjXvq$ID9iV-lty@5~BW!B;y#i=9xfdh@ zpWlWwG`d?sCV6!Cf{pKn7~cs#N5P}BcLr#~9pqS+i2G9t0=hhmq0I0Dl9o*3k=5&4pp9KcnDF`-%xfOIyhDT>B}M|W!kNP#ujcnAI|$KmG{LuHcBL97QWgNk+cg3gtI zOapBN9l8TKfyWxm=5GTHyLY#O&gAI?TXY=E2Bp&OR*-Y7TS0nY>Odkebs#oK9V`aH zmRh%h{KVh<9Xxry7bMmVj)v~3pi@d-+_?H5bcob-kIq)mK|?Svb@zh85ah^1ol`;Q z41uaykYBrdL1%w}x88zI81jIepwT@Q#Ob^U@ht=BNEIx)Iz<|r_kvxCbgSb9n3c^3 z7`wp%-ucl(^E_zqyYqPGRM1$`3*%#;@w{G;;~_(I=Q~^1fHuTR9RszGKupU+j{H-= z3u+wsw;kx-3eu-|416jwXjTn;0BrYEhzW-nJ(>@(cyw=taE~#1G#_Jugl^|?&?-(w z(0L}{)X{m}qw~~@W>6rVgsiz}1s&3a^He7|k9C6&#pwi#fJRzD z%DW+n4i=pkq0tFZ3L2tDiC4&c!MVpV~*0qRlEns=}@5D!2z zYr88G14B3XcrUOO;Jnxk&QP7#JQzQEG#_L_&cq&_=Q~?_K#79u5-6EKvglNhNT*06 z%&O)C|3K>qK-NLlJV5MhKJX7>mJ8&>EDunw168grorhlRx`<>RNU&3+5jCN2_M!j(4NrESho+V8UXa?(a~_)CJs5v^ zbYAaltpO=>IRr}DAg1L(1+j0CnQ|}m%7&sPkMCk1sAB`Nu*AZc6h=AXCsepusb}OUod-i9(chG zy5v(w1~G&KTBaL|*hz&AEMfV5N~ zrDc@_;#4b;#0#sZObi~~y&yK|-XxG_W-rKL@SUxo;e(fYpd(>%v`--CtMr0STk+_e z3Oak}IQVE2&`>U@ssf)L0BfIsPb=vLpUDDipMcIbfvJPFPe5nDz{EiN%OOX{fKKZI znbz3~I%^EHi3Gv}oi~QyNq_{QR)E?kA`m|42$$}yAP%g3!U2&0o5c)1M#-bI6?6ca zM>m9mv`>D3!UJZWMd#Kp$b6el@S$zT!H1xMVjr|m3S_QzE68}X_6gXbu=WX<)7=W8 zdO@l|RsXTht)QdTAZNi@gW0fl2k4}?POwGC!Kc1~tm|$CIma4&FdR%BNCc)1#0IH@ zwNJpdS+|1x1aF^!#JabFqM>^#=uDay7H7dZ1=K!)91#d}Bq$6)jy%*k^$H{uAo9JS zXa%=Vz^8_FgHOMKob(54pMcIX>&BuB+&%%j60Lm#auui|?FI)pO8X=QRA5cs3oa%h z$__Gu%VW@(*o&&Yp!NxfiQYZ|>BDTFKumzQPas@a`vl}Nr1l9obwJuDkI#V0Rd8kA z2|Hd7qW1tJ=*$vOBIpJmW&}$T-BZCy6Wl%lv%0r}kL823Pe7xJkoF0P^4qt zrq->XlnZa4fI{=d#66%80H3zk*$Ntod{Ms#REUF^NbM7_qq=*+HiFN7fVEG+5}n|< zg|$z>LZGs^yBA~yw0#2cHMo89_B6bG0(Lt*{eqRl+9yZ;{{IiHvcM)rqNbM6uCI;sR7f?pY z0#D3CvglNh2&8=i_5-AS0x}Amtzjt@+&%%B<q@Qc$sLHP*WJ^_h#_kz@-v`1+iJ{y^I&5S<`_Zm=+-eFADIK-wqZ(Cyv| zau?{_pU$bE10q4~7Le<^!R-^UTsK(a#jHEv_6f2gMEeA^9N=XiWJw9u@e|PYQIF1E z$o5n4l7r)|pp!sA8#-Y7L*e5m&~q<9dSLAn9?%vR=!pQZ_6g|V1h`)C_zCEYK2RG3 zq`0&71K1-FspVeE^~$=7Fy7*?I@U2N}=}=D^w~pr!OMZSZZ&kdp`@ z2NFWsCy)ag!N(zh$4~Y^%maG?G=2g)knVWvmVc0OHji$IbF5oI#-p`Qzz&7APrw|g zYREx}pfchZDy!dhrExKPP1T1Qd+ct)OHK zZ=Zld^To%_pb*##GP|>t2NWjvH-ids5EH3=0+I*yK0q;p(mnx8KynYPeF7GOv`;`* zK-(t}UxV8xzK7w3HQ4P?KX>j5ZJ&U2p|npx3cFfCB)l$ywogF1psf)wv%3|f87cy1cDI5(0f{jX z6WTrj$2vHVK@O+w1m`l)xD}}Q>4qe_Zm_2y?GsRRLX?6V56JNfUK0szpMcmeq`=Vz zv6&fCSU@5Hme1ft7}(X&%nEIvfU+If3UFTR24^Uw_6Z^rgWD&6e*gdf;?x09GJ#~# zsUQ(Z`vhz_qA18a{;)00y4{`8(frw+b5v)nxOVc6|#9CK}7omY^F8%WZjP5 z@4x^5=ZCgWz{M5P_z6g1XD?`F=L_wP@b(Evw7VCi7NvaxUO30H5tOt+OiT3k3CI}K z_6f-9u2v8UPZLP(6Oc8~_6dXuYM+2@#hTHhK>h{giOyEgMl6?ZaKdouY_0kC|Gx{Q z0Cwqw2zEmRVd(`DyYO-oGJXQG6S=r`>1=%giYKN0;8GXTJ^|H}-BUs0koF0r9D$^` zZm>IG<0q##fT9;7geN&+v`;{39Nx+RaXMR{{QmzR+R6Y`XApsIu&_rr_&^Bo_z5Ve z;5tE^PVkCK6rG?&q1|9%MEe9(V?x>|-~y$4D=61M+9w5|!l`>I#Fyaq30SUsDoEnR zugl=}35X3EcLn7^MEeA^p7G^7@bYi0^E<9TK({t}bcVh-?)n6D!~}R#H>^Pdy1o{4 z$QpPmrxbLr-4EC#4rt#9Xbn23`2jl25VYq4bm*Gv4v$Xo*=!(s1;{i|Goy5gN4M*N z?$RaD)&=M~R@WUK%&sp$cfEnPGJ%^DkOSF34T;hgh+dn{(uU)$pyRDT6}1OwFPF9J z4Aho`>juzTen!w4ji7x(pgT!tfO71yPVk1N?$QQpFoVAhGy~b~+RzC$@VF~zeLF+9 zYl}7bIyjhg3rrexwj2YrZQ#1W!`iijzxgX@Dz3YBLwD(w?$9SMy7z#SC8#j~JqZqE zJ9x{_p-%9bSfGIlu94G?(-k$;z-cZk)RvKKqJH#Kso2d8c>Z7VxrghAWe#p852+pLuO1uH-J}o zgQrVMH+X>V@dVA4xV9h$B77%!Kx+9|kndr&Jm?fNnDZcMpcZuNA!y&&3J*}V4my`_ zD_9kzP6sb$1=r~y)(eHT|Np<744xAKg^{)E68<(#(5!7Y_=dU{3Si&WZs>%pV-y5= zs~5CB5LBCkS2}|dM>phpZTLJ0WcS!hYj75F-2fgj>FxyuH>COoon#8GyC;G=#*l&x z>_)I*5QUo1i4V}FWsr(Hbc2U==nAwd8*Hd`=nN!bSbcpMRN6z9V|Tl5fL7KZ^(d7! zNL5!Wh=do+pfh5if%+d3sE}LrAk!Qm6Fj=XN2!7ugU~|?UxI|Y!TT^F7pir-ws>@d zA_H_?Z|Dker5w5fA_uN6kz)nCP!?JvgV--RR)dRBaG)SI<86TM$Ax5z_M6~%OZor* zKiELu1CYdwsC>cM1zhcJ0VT>{aKF*_M)y<@w-YpH0gjx19-wWCkl6}|KzAwV*vu#3 zN*271we!#mQMhi92)u>`$4W;>@A}`Mx-}DYAVO^eQpdk`Luc;_knLAj!fMsf4c)#C zDD~+Qkb*5MLG>w!iBX?|3_z_Jy>?B+v~OhF5~2KfpC$=md}M z(iM=p4|E2wM|bE64{*o4bOorN{{R2~7c6JNwH{m=QRRWx#5NuQt(gFwLcySR|NsAE z&;S2V`|$ri+fOX#3pKv^0GcZA+zYBKLF+3(D;GPbf(j*%?x`R_`1*?9p!AJcU-27s zX#pq!!?)gR8$;GtfQ*1`y@%EykR1r!y`YlVqZ?v;XDfKxqq7%0cF;K$wD#gS_~d66 zaLEUehE>O{pktgt!-jwU|A&na>;ccMz|?`Z_IY$dw_|_>AqVMoZUxPlL$$)HaPY1b zm>6W91H2~+I@8h#p3wAw%xijdLYCjad7#-ns9sP7zZEoh=h58?GN2pGf!6epSt6)5 z=2nmgL5t@ht_IEScz`SZR?vt!+&qiUt)TU;aK26FR?x8Yaqu`Zq#Xcpjy2eL)Ybq* z1~!fWn!@yet{v)z>^0Lo(Rr+MYXB$^!NyvH*|2d0kVTLI?c=Q=Hpsee@Rk|tR*)W; zI*OgFeI#^7DEwu*wiN86Qi-BPmWRC!NRa5s?P@KI81ohB6TR}sZ9^Ft0P$YFv z1)cW&!U!S_8eN1te=jI>p@+Y6}{_f=hr@!6ZN<1l?1iwm@957i1i`D1o^eWNoKNd-qgO09b=n^G^d^#Q5zX zBXqkJbaUmhH=reMrc+-qG3jz-@}oR?zS| zEUF-VX0S1^ZGMns**z7UaX`%>2&;Q5SRbTC1S+c`voIjm3%(`)|GxwsE7lDjqqPRl zmGigRfR_*N1x4Kp>usPg04E<0NE!P=10n<}UOg<2^SAPXl|!rt9c~8M!{pI?5VVmI zoC6`pAx_u?9=zZH-82DCJ`gQX<*+u8$RGIR3fOzrU~eEbc|d~~Ag6g)PX$dABgw!T zL(@UkGi>kzWC*l91k#1l9s()sY6X$-iWNF|0n!B>ssJ+~i`$_hAag*s4S_rXi3m^- zg9k5O`h$+t0FNodLQ@d52)G-(z3au^h49eq1n&lUv3en>ApojUKe(M)YpVjm(!!W7o;0A`X_>1 z4@w`nQ!>O5r~xD;2(Xnn6GS_X1Od{8k|00|yIMgc;RFHF3{4PVW;Y~*q9+K{+yS~( z0aWrq`UX>xa!1j6aP9ytjRK`6SVo8J1cc14xOBD>l{>%|;0%m59DxDSg%TJbg=&H#L3t7qdCU-3K`e(wL1!z-I2aFF>$Zcg ztnF?E)n8yMz`MO6xeU=K0XK>u^)0w(QUYo}?_Bf$KeW2;1yy+6TS3B|CZJVSp!(MZ zVya8$LD162<^xQS`W#dM)PZ}bz2GcA6|4)?uYtH3GL{H306a1Qb8hp2f1rJKptcCO z-UoGawif&btui|HLJz|!AQ^~Lz_9=xi3GJKK(;d=+YVL;U%3TVY&{j!#_H(J2PH7* z$RoIcgEaC8QrOv>0SbqWb3k<)cxDtN3fXvs(o;$SDeIa8Dj-2jjGhw67}TB;$m*_E z5D8DRu85UeAZwr_j}Rtkc4_ke}P&o@G(db zr?WKz(ftCg(0~YZgM~rUA0XF)W>-PM4c7_cbhd_|>jVjOgN5N8x2d3R47l$GDKopb zg7YL~^%uxP-BUrk(jc8Uh+Own&?x8&oBiN^9Ej}!RfOoud4Q}wqYCO$VedVG7Atvl z_JS5qcyxkq6g}PwTBHD7{RNhW_nx4OEQ>Y-U_XN7} z5h?`lJwYaf!E2u&hmM06k%RgyAk#X*w?KlL)({?e0Xlqzdk07ma-k-;_tXO6gVxY> zZv}B+y(iG^i!ei&Tfys{AUBqRhJ8SqAiXE>(ruV|7M)vDKvFQiP3Kn7D){5z`wu}y zJg83xQefQ*G9In>1a>H__XOs2Lsrjs_kvV|ORrAwI_hq)vDRQVtXBlG2y)RP+xw?+I+FHF!?|YVQdo(Y+NEXD@`8{|Bx71>MQ%(Y+TU z0g5EZnv@q`mVu=~tB+xx?cNItU1;yg2IT#2$n}!ldqD{U+(rX0h3*Eg?f2;33tH3w z7B+#X2I+?^mxeXnK+7XR)4br-?;uq$3DAQ5?x|2)Ag%x#2hQ-w)`DAjpa8H2tH#lL z%D4~iJ%!&!^q#8kGcmmQxf|Ad0vUnQdjbUsXz;dsFGvct_v8X9)-Fy1l`Xvxb$Y2e-yh>g~J0_nrndxDq&?>#}du-+5MdZYpzya*K1dy-!Us$rnLC-4GxXi9^$ z(ZI%Z?}cVF2yZGl0y(dt-TDOAo6}E)>A>b4M_&pd(sD0->}{j$XU?d6G#_I`w*nCs})4T zD_dyq38V|!8U!;TYo4JZAagvrTfv@yLfPhfk&y(dt11~p$C7lX4bbV~|^LgY;7h7?E^1aV+F4Ybl9lG7l)CnSyF zyaiHgJrxr5(B2bB%L^t@0RkyGpuHyrkc(e~3Xbl*AT~Jqp}i-lb3wf)kZ#QA4+gm& zls-BkA%`U;!%9)G0VE{|unwFFLLNtg0O>+W5FmwJtss(cf&giTCI~PSM}mO%o}j@4 z>OFx{0<`yplsilofpZ6__XJ8!@G=>E8#=uA1iBNQVD120fHN@Ua0CWO7fN7&6n3?O zNWy^u(hLm@FcU{$fa@@5?+F|t-M!#K3T0UdSfUf07NET+Z~+OvfEaQWdndTM^njIo zkP^BZTu^{#Zon*vQgGJ>az!+x2n6q8hW4I7>=&meg7PFJO~QA0C4*QmVO|;CuDaV@7&4(ZF7H^hwc=R48$ogLm{mR zQ1NqU9w?xowu2SIdrx4+)>A=ktd3rGPy&PYo~L0qsZj=?4{%ASOod31kdv?+IjeS1X8wCt0N46UZ89?+L;L^`5}CBGzp{ zawK?N=^~JSA-$&`piM21E3jQUTfabfpi~L!0YTQBf(2m-6P$RVTSg#x7fTb&rL*-1 z$cI^T(PIH5j1&vq;KT)uR&Z4eI;jS<8)fSk(4P3tV=vU^BK4lYjkT#DDR5&7+CBhh z3XkqqkY8YPTj%>gQ3?^lllC!sPavO0XDeubGjz5Ke62fJpc_njKyIrCO?d4E z1vgwLh|}5n2DFJBvg-z-6C}_L7Dn`*KwT0@?+F}3-CMzV65M+N-9zmGS&0hmJ%Q!A zr-CG2{M!ibJ%QLTMTp)L=wODIebBu#ptceE+!J`6XeVU7RwsD%%yIBSI_TO|ur$2) z1X_s(>Yjp5%YyFhJp-OphpaP&-OC4FJPB6^T0zmd6|{Z^Dg^I6fll3mi9zO`Kr75Z zeJqfnovqNNm0+r~bp}Wfb^$nKp;|ZCUU2UTbVE95lQqa`TR|LH?+LUb3DiCZ>1Kv3 zeeG-oUH$CQ4WS^tC&23u%$GR1y2c`}r0#gTK zgVe!#Phd-}TS0z8?LC1cy0?Pj?8ViY;I~8y$}gdBy~>(t)qOg4I&L%1rF+a zfzM9!BbPaq>udQYGrf%Kk0QmDPBJD_51GWcq{UWhtq?+J8z!i%a-aPJAkM(aI+ z^kM5gLCk>no*-OU?+Ii*Qtt_zb|Jl|hciGm47B$I+V=*FDoCRpY)m)!a66bp_f+t* zPH^uD%)(2@ifKKj!%sqivFD7-udrzP^vj(qPh4-F7QTJm1Y*6_MPCg!;;A2Bx ztbquD&g%d-pP}tTuwy{2W9ViC$lMdeGvMBn$P94%5Y&4DI|-g?Ko&vj7?;jgm4EQL zC$NduU=JbnkwCpCuny~~pxlNe1M58<2OYQo>pg)Cf%cw2x=`AOAcbA6AQE2LLVHgj zUC`DbnAr{9Zw3_snFHDx3QF^khyeKqx%UK0=&;a)_MSlL{e@#YJTyC74}j8`emkgf z0Aix|p1}5kdrzS33~IhGgL?Cjy%wN-&mij|S)^N}y>lvP_ZF;x1an|H4Yd8K8=Q9_ zy(h@N2SnZiX|$dSDpKKdParKXj(|!CNXY^1J%Nt+c(E2D3}S<$AKH6@Iv3P?0_leK zo{*wn6y$nP`sjpggoH*vBmlv>p(5R|or;j1=tLz5u$4Fy#C9AB0;CHiL4Xu?wSq{( z2?C@UnjpZ;Ziu%a{y`)N)Z78;J%LgJwD*J*Jl{YaYe-uSq#2%{d%?1FQpQ zU~Iz?7$99JfdNw3)e0gB2L?zpG%&zS9DxC@!=Sw2cW$tZ~+Of zEW5$wLnpYp^njIokP;eJ@`2`_KqVhUDY$EcTm*uTl!5l1KZ2#tw4t38b*I z7j%~G3)3c0-3IPGfkeA|L26NYPg6hzBwrJ#fCMoydQTu@PmQ_JTS2ll%OD6(eb2xjNTK->+n7hh|}5Hf#?H) z8qpAeZm_UNcQ2@$0q#A4f*Ymosx&O@h` zN{@inxx>0e;1%@Xd+#BOeZi~hL96Ijcyxm9vH)Gm1717-1Jv;;o#FwyY`b&{w7&yh zJ--6Fs=fhqgE@3@9q3vQkTsx%^;gWo#2JP$6Is$!?x$Sf^O32K<(7HE`Tqu zU*ORVa)xgQxFG4=3R?5t4K~CY%!YMPK#PSt!Nwj3uTBOP?%k~*o2^?xdSL26A~1Cz zHb@<`BLi{?WLbW*ASZm;P3?m2(km}Sd%^t|&^l-EgbnUh>)vzCIbWI2tSlzLXRcq*Fn%utpMOjj8K~?x`Som?&t! zM)y>x$q+khPe6JoP7g|6SA#?{R>g0Z!MwAj2VrvP);H&VN{UBXofW z>bibVM+2w7 z19cxj`XJp0ctqX=ITX@&0EGrL))}GYI_aNcG1SsUYS2mu_%Y?F82^9^JK|OP8P}%|%dw1Ca(d#gU5~2T)-FZS8~DFXmT(@(DQjVK+^I zQxoX2HBh)hcZs#vg0mVpr5|7dU+fMp-VtqkaCHQ3)q~EKev#e=Z^ZeY=msA=4Q|1? zZgA;t1sQ`h8v@CErfndXfOf}#4Cn;4c>`i%w9-LlptjOMmUgv*NO)r6gs%__1=UEP zy)>ZOhy}9m2}@0M9ON}f3kqS# z)|v&~;HU(h{{`~NycU#z1)bCl3s}&uJIxcIMtJQ4mu}Z9om)ZYDZ6wYdy(0Kn7jb( z#{r3hn+c$F8al(HyA|X}*qnuQ87NvHLeSgJz){`lI^$&&e3jR|Z=h9PAb-Hy%OFlC z_#9~HJO!vt2N8fA4-MOu1C2}Q0bihq?F63yjiL>4Ff_cG4cnX3;L%+=1Jb$%?~Lva z-2s`HD4pTaJryMMB4Z}FT@7M;fTR(PYS2l!9*svp`)$B`b6%bK|6k+Q|Nm*v|Nmd| z<^TUskIttajc+D^YKdOfc}$>|5$|$F(9J`T8?yF-4ei_tYV&(^P6h22hT7`U4H4`H zZ~6D=4251OTMOFR3My8PyFLI>;KNrN4?&J14F%nL2EMb^qdWA1M<-~X5tu#z=0k3s z-2wJQ^C8e}w4mLLKRmjFI6S&bf4Fq}Nw{>L^I-hs0l8cBhf8-?52(iK>;>)6b?H3m zc=>@#r|S}sr7n{uKU}&UEL=KWL9Kca+s38Sb;5C1 zkbw*y-L5CVcic@m*zl0ig@4;YkcemU2M@*%9+3OzKw1<)4SSbP2NjSkWE0f~@KBj+ zhfBBX1Q*a2R`A__;44^rj=O@+jb-R|oq{AWrPH%vS{M#-f|2M|bH7a5FY^ieu*m zAI;+)jQ_xUJ8t~@|Nn(?5opsYh-rCYxUffl=?PF;aOrkk;?nKA;Qs|s`xDf3RaXF%!16 zw-X$69?i84Ed0&T%I<^*WaA@JWe3{NeBx#8zyJT6YacN3HyvVuxe$^VHt;vE0`Eq4 zJptMM38MFc3UAQC(~JxZkeGlS%UFA&v-b)pY_Au<3+)r#zEe<2Y|!}8i){s<5*x(C zD6v5Xz{4MOJv&nPgGy#l*t2wF4R?cv|No)g18@f^8{8jb1}D!}(AHXyY}W>6k8be2 zwT_)99Ged?x`4K(fRCGb@sQ#F|4z`w#obd+fQGjmz`A`KJPtl&0$nNg|NsBa3!oFFk9%~2k9m0U?*BiSmq7;!c!0gk z0@4fa2{i8o+04S<1e%!WZUvcV-3pT7?}O}{ZUxDEfV>RpMtMLJ9w_-heZBE7ICU}c zw?=_`4B%2`1Ans*j0euw$O+3FYr+DZi14B=4|l=>4K%%o%>yMY5ECO|fee5rtjmZ_ z=n2TE9Js8&k+81U!V?y>X$htdK45~REl`smJQxCrBTxoGi6hX!=!+)M)l-n<1>!@( znh_M%;G>(G!CfhM@&XxY-3pR{Coiy_ps>c9yuiJp9O9ZSNNAdy<6u#(3=mYT~USUl z4^3Vmmtjd>L7)ZPpgIDay}*P=GpvX@0=l&mBne&f1*$wetV0iU^iKcx|380Q1UMCd zZi)k6Zw;=_n861*cr+jT=VAE?rcfWO5Oj|uSRwy5r~janL_qfLncFJDFV@ zz!iF@>j6l(gr4xQ4&BhvI}H>l?T}sV6ObF@Cpy71B`^MGA!ok{DCIh63h%|eEKv3X zF)^|qXjl}Q{h+7pf~@Om1(EPjnhX!;P_%400ZTpWQ1$=6tKk8c&SQqqBO*Zi*j*>M zbRK}F6qjz-DWEnEB3vM?A5fryLJJZ`pa5dY1_h97g9kIHG6fHyf@;SDFK>d{#8KZt zh1e7Ze&}xVDV?nyAaT$YM%}JG5MDP}4Au$;U6%}1J)yI;1w}Q8*9{iKX#DQ*=q^0~ zY5YP?V*xcFLF3A$2cT!KyvXhYH-6#Lh{i8?bh_~fsBw#O|1iA0yY4$9gGVp#_HT>~ zj*xp;J1J}Lg4@j9p+7uE+q=gpL*?Gkpxfx4T<({bu-!R-F;cH-B9`{E@BzG5A8PI0u={YQlhodo zL+Be4?cH_0xDytrk^7?87nHC-Olr1w*<;}ei~RQPrn~Ss8fxv`?|0x%C#k)=AE9rw zy*r@o-B52(bB@yXuD%y?_QTTNID?p65p|Nn9Dnq|=9A&+i|G;E2(RPY+(?x_&PQ#nAZ{JSAk z=Trt1-VcbCU?t2`LB@J?PW=E81XJKOzf)g8M8J*!t^Dnr`T!yVp|*k~T)M*~TtGVj zK7r1mgh)fgCxO=QcJ|u*1fA)65xgF_^$N0rCeTt_m(JD;D1xAc3ND?kXApwTyFg;h zpc9mS{{QdD?{c8C^#nv3;^iO*kItzFP((aDI;ZY{h=5$v?W6z=u1+TvkM6w?eUNbN z1_!{4S5Fy1OOwF?Wj*!A&;S47%Wpv|TtMLmT67HZCP>Zk))hY?dk35Mf>_M_&5#9{ zAP#s%7T8{wE{pE1V4;tlF1=5>!MZ?e9l?y32mXT&n}Dpr0-5Sz-3khH{^n1q3=D?f zcE~U=FmxV*ug2L6^5hG?Y$k@|t#hE(dvy1Lb#_k$$ExPV&U2kxXMnv9k+ALsNg!P= z{-OC8bLZ9yyh=K^)_~+YTMfWlPCy*%R*)9{=5lTZhUUE>5oZ2Y@NL`&0K``x|ZECD%uBqapAjLn4^tRHlz z9VmHsxAuT2Yj9HLZwFnr)VLQU#2~=Hz~5vD4igZ+v$X>ro}H~NKSB9<2{a-hQ3~qd zzWk5?3K>u!!aNFzMA(V`pEN%}bbEBSf|7D~FC@ajNuYZx$lPwQ{mt(|*#@+!0kmKn zwE4L=29%OJr-C+`b#C>~|Wewhg&ENVT95WzG zJi5U<@U4(u%7OzAQQTqzzPm|FkbZN>;-Kqh3f2XTmUj`E0RwB z^&3GY7b7ULz(se%FG!Km-3nq^gKgn&_6O+$i-5}wkeU43Sh&C(kd+?YK?&U~U67!< zeCgl+?!BPh;F`xf!8F7$kSy4Ym-9jEH6c62A%5zFxC|WLP{($Hz4`JI*k&Z}^RI6N zxfrtl{5aS=P;5i2um+oe;a*S#<94qyQSNO9M?Mc^MK|b(aHQbxYy<^=_f(Ldq0!zs z0UXv-Aub2UZ07_}ynt7v9&642_5Z&x#7uksHaU6 z6^w7)7UChML4NkM2r}($=2z2&>WV^v?JbHQW)G;x19O{^w2ihC9trbLd9_wb= z2n|o>Mo^;U-?kSl@zbMwDR0}4Q$gxFFM_rmgG03}qg$j8l4V*&KqfbUOr8p2Y98vG3fd9aycbj!Fz~m6 zmeWH`ZT=y`-vbIP82^Rv!3T`YtswQCVDeb2-LL=uRhq%&Nj)swLH>9ldjTHqDxIyM z!}DK)jPmGZy<7+icaS1jxPxT7!D>8ud0T2A;cg2FcMt^&cZeTB2d(jM+Y6Qeg*!;p zqq{7i8yfCBARo><4+?h>6BO=X7w-j;-C%b^!>fBQxM=L&3Mv{sI(tE;mT9$!ipe}=zWk}vcDa(+=;bj>}`*E;&$nFKH#BeV-Bw=MQhI_-$ zf|3!)IDGDH2B#TBSq3r_>Us}YSq3u0qq`Mk1FS3qxd$$URF*A=lw}}kcv%MGyf}FV zo~ML6TN#kbGW$GGo&qU?_nB59WuHE1~!VG3Za~X5v3~&VtDzv&=L1bs^ z6mXje)IPEXH*5LZK|7baA>zFt@y=5o*_UKIn8B@lYbO5Y9k4AF-C!No;073fvk|DM z2aABx5NKCR_DLBJ=3Y>{`8cR)) zH@MH%-3#hfflj~q`TxI9cQ2^h(+xJQlSSpl<9mz@ovjPN?QU?Ffxi`0Vm0pt2{G`u zii4`Z?p9C>7_yfR%zT;i8MM(BqNZgQI8;F#P&*$Ks@>quRkzD27Fe5C35S}-no}&0 zR&WRo6(Cb!?P4r;u$+Q4b&t1VX(%5L1vL^O{(^4o1#voCfBb;oSqthoKm@wM!VtTT zw@!ezySu?GYj7CxHy3e%Oa#mEx2b`h4B}vV92_%W5Y~Vi_MNRDfo?Dfi(HUya9IeB z7nf5k5c^@V4${Wo{(=+K)4|r32W$WC)7=Wv?$~Yeqxl`93na-oHos(c>7EL0-Gh$J z?d%1esO!-=6?ANC=T^{(t1g|rFTl$pB;~yh;RFiBctgIR+|KLB@fq7!cjv3epM+NH7ChC4yw2E`wExNZv%L5|PB= zRU%0Haj<#F?ggpDa4$F{VO1iAdtHcfZ}VP|(TFM$WG2-0@G24HBxpi{Rf!-?a3Q2B zk^eWic@L6?SBW6bi)}}dtHd{;%n5B~>ZF6JM35p_l?amUZUvDZy}W;mAXOsY@Bjb# zx3z*OSd|F!GQ3IzOMt3GkSKDM_yUwT&5wd255xpji69Sk?gf$Etst_q6_i}NAtX3c zg7aZ#>l2XP-$y_R8^nY)??Jf|)Vv3s$qj4XgTz2X0>A(Nhd1v*V&LXID6e{SBRB6K zfJ|NpGI=V932NSh&PHzD3n~i`&3lju$jy5&AKJVJsqbtBk;hu^L7MlV@&w+z2XS85 z9f60tN@wdGq;UU|3JQ0SB3QVCWV^v?JbHPT7C^%NE~F^}qF~_;@(pt%w0RGf0EIh9 z6uEhS1LVW~he6>EVuHdQLmC<`8c5qua_mVx--vJ7-n7qlz`i6P1|kWuim48%bx z%PxSN%>i(sgt{JHmVulEx~B+~#GpeJAosw9kjgT5NLdDwhL>d^&Wl$E;CV{8v-J#8o=Qjp z;kvJ#vLK#Z3)UqND^W-F`@fTK-*<2k62 zQ2=eWHtz+o;L{Eu4rIi=dn%X>8t?&idpceE1iD*6LSQRF4DhsrM|Ueo`nUrJs9yvd zK?aL;dhkG6+h7@RyBpRq*d%C?4^a;*@kCDDpujpceTJ2xYJ$A4w-tkv|38 zd<9+h3MukIEbCT~T38VZ5&;+aAT!}bKFCUxBHwfmsMG)%2P*PGbT{}qSx`WN8PFmh zBm?ymtjI_5CQ6ZyBn~g~LE4Xl%|muCNF|1Q!66AN@-f`ox|0V(q9AVofif))87FT;y`umq^c2Zg7OG+D=6tgO7@otkcl9Sc_@$0R?y{!oxOKJ$*^-O_el+{3@$XRU-C~@*`2Spx;3Ce094|VPZk=?Bzva=PGT)V*}G?PtXW?(oD zuKW;nGL~wT5p?P&Xq;y%sP^>e-U_M-J(`dG_vq}E09|!DRRnY!cjs0P5Cyrz8D9IX zK&t&f%0MaU_=}`%pezaEgR>;)j&W%12NFYMNs#gIED7SEWXUBUUyE-8Wl0bdlqJC? zL9--8JuFLt90$viAWJ+T4u#cze;|1cq6d~GK|Itf3BEiQq7#-Sk#r(uNzgsx$6Fcx zz_KKWW!(x=3rpf45pb3SnF-I5AS+R_r1@4*tb&XKWl0d-4K@}OkYENhOM+yeeu8C5 zByXZ*NhEQ2mIP_XbT3FHhI_#w3Cofg?rq;fjC-5+f{aFFNsyUP*Tb_U$VndEtsomZ zp&cHOCb$q%mV6J%k|1e#mIQHL{M?M3B^MxN$^1}ImINt+Wl4~1cPohW=;d`tgk;Hg zkSqzJU|AC6Ab6GpOMtQ@NEA6s&H*LPs?DIl05L&X66B%Iy&$r?6-0Knf|6@@D=3dJ zgV~Ub32BzXTF1!K7NCYMN-x5iiNAdd`m_Z|75UQ^Al;n@JrM2aIC%KxgX|qFf;YQP>Suj@1>g@I&r~HCM4P^S4@pkIet6v-9o9Ml z4+?@FjC#4arl%+R>NHb%4r1wL_pKb08*Y$y}l=r0ea`Jr$%A8ivp% zb4Vi4C36sCF_+94>JqVJ4m9Ke2|8wQ-nWI!8A1{eC^f-m&A=fCUO5L*4z624gC{R{ zTY|y{B#$M@f+Es^f9f%ZhG&cp{M*iHo`B3MqD;fJL9Q!ASO;EC1hEGtMw=@^0~$O+ zAPcdiSe{OBOAVsY8qDTz!u=icMr-B^n*!+swrF$-95^$;qsExPP z1=P6f+-m{4(6Y1D1WZje08?{y{`~*ny%i+Ze2lU4duMA4DB^3^{QKW=xMQw5NN0C1 z$QWp|5Wdh4B-Y&vHoAK%$Y_twR?uDdoxL?6O`THhczZa&Vn^2 zL6&%Q_kt9_8j~qtSA&HRLn$BysEx@8gfduT5=kdgV>0ItXpk-g-k1cjtXn~9VU078 z2)Ho`G85jI1X+pFn0&Pg)E)sD2Wm`$=0{?cqR}e4R1_>I4{<$L~cyhfJ$EIOrVlCs4)pr1Zzx!WV>5Iq(?9B=P*cP zGVBj{CJ;oy8j~O|!yA)eiJu}~~-ovonc z+6^IlK^YS|6IcS$`*8&*VS|{knLtpk1kD5nfU*m8p&v*LbnfmSq?tgF7~)vlT=hYgPLLUg!sMI(#M& z#Cc(|0v_%vovk@Y;r_}K6z(8Juy6;-c7xS;^zzOQfrPs%B-}w1EZjl9fzJejB|za0 z5=EW~%mDdt>vB-IgP5Rj2f4L#FNo|0yBiu_-FqRW*j7j>*1H2#Xztwt%Hf?`SAeK) zuo$!i>}*Xz>KKERfwJK77oN*OWf_PMF3S#pA_p>71rkG)Wgz3>Wf_QrQkErvoc(VZ zD9S-hP+0~x30jsx)Wga$kmF!w8ORb3h(lpz*&Kx15oH<31k|!@0zw(AEJMIS&34X$$|X07i1i$ECbQqtst$SfCMw3Wf@2Y z>L*xPhU86@vJ6QaUY3Eh9|xNUDa$}sShs>yVz?I^lCZK2!@YS+LCFYY96t9pgVPM6 zECZPdbv?W+1DWB`-3qb+R+fR>0~bOn%laW@8AuvlmVr1g9xQ?9DdEo67^FNE<_5}B zAVsh|1(NLstMTaNRS$yXsXj=a0#UF$1#%F)ECWk`@)SrExh#tSCC$$?D+oZ&f)xZHOFX)JK?-060SnmGU?D_7 z08)Tj5POxT32N{RYz0G?;Mk5LWkeN`|!wUkC8KC>5K=->tN60{$;6g|R!FNbO z0Fs6m1R&0f>kE(zf&ip~z}E>>5P%fH3IdR9cPohW=;f93gA@ecAO!)4f)xZHFT)E0 zumq?e0Er?O1U{g|8LfBQxM=L&3Mv{sI(s8P32ko(s37Rv>H(s1&_Zd znFlJ%KzwjnmVhYBKw^lp3}igKECX>+$}$s>vo%4^?gcSHWf|BcXjukP4=c++j)RqD zAWJ+T4uzFvHVC&P$}*4%sAZV}LK&Hh_H&_L4gvJAv|!953_r-VCO4UqCwqb(>;ffT{=6iBuktj434H^>u`r}X}UJ7^#Z zmZv~ohL>ev2~eH_i6WO}I-tbaGaD2bASNhJfjrc?7escqg2>KRP;%{V1?3TDFdLf7 zV2v73q1D|ABB9NhcO0{vvo5sDJ?R!3D$#L;(R3Llh7ogW&}P zh=WoJF$>h#0Wm=Z1lS~K0Rd4DDxHC~-jx2#_YY5K;j#9a2Dmq~Qewi1XsrbmRg; z0jYpUumlwlAVshO0wmkr3L-)4A6+2@#570&0is|91jx(q0s<@nDj-0j$OVK9C~;;_ z2L%R*2`V5!9_ri+BD-5bWM?ZVxpqUyUQo`2Hg+UHdi6nidqGTCV+WKgL5-aWpzH!| z?102Py21G#-q-<&fg3xZyo%b`5doQebsDI#17d<2I~kxmjKC!VqOk)q0lBdQ=0h7h zAoZQCAo5si+F$Sp4#?^7#tw+{qG%dC+(BD4k-}ZW0u=5bMX+!e_yJnF2qHatd2c#F z!aWrd?jQ;l?jYa58#`bLP`HCcksCWaARm673JP}+6BO(`oxMLm32pBeFlXxv5Y-J9gO-4utsF>=9gs3m7CioNgQ65fwUh7n+GY&Kvr0{f>dI-7aWqX zvJAt$YbF!p-ez!`L6l`6Goh}Bmt`O)d33jeY=Dj6fZPKYLMqG7L&`FcG`uVWabCzx zhUY2JR!yWlHN^y!r$CBed5Qs?r$D4fFK?;?Bu|}#}~~-o#3nPyTN&c8O(;}GFW2=RA_a#g4!9(tspj} z$pe|kg3Q*z);~Zd!l!^{Nx{>w%#baWCsC$jLF*tu7g9ne*FbZ3kO@18YRePktcTcw zw2c$g_JWwziE1-=J11g-5QiH0T5mk&z}8CO_9cOBp35J@w|Rmb1kOev#>=@6k+*q* zW?AuW^8{@l1a0&Dvjnlt6SRqv0k)h1!~spRW8bw2i7=2yyL-VZ!E3iqL07{wyIf;| zPQ*gar~oT@!3bWMf!G2Ho0`QU3){R2Ni1Mrg5vunzYM6k3|jsU$>$&zYzO89Y0#89 zNQ8li9hg79!#8?@!=e==&uJ22U#u}qM}|zyJR`x2k~di|pJh@egza zQ>zG=nkoRM=JNalO$39)pcBDgKt)AK-#_p~Fgr+RcQ42oXeA2oMu5b+d%;F`PX!qb zI$8!~U2hCXQ|HtG5Y^q90J^^ub)hdv0Vok1f5FoSs(L|uaMhaux`zxhc>)qcRJ|ZW z;8ib(gHrW=06FyVz?I^ zlCY{5!@Y(?xwm;Q$Y?~>3o;YxdU(|fa*_umA;GF%kS4egQq^nz4?NBQl7?5kAkK@$ z-N;q%8&GKhod}lH1y#KuMX;(DB-`BzB0YL}Us*t^UNg|`gWFm`6s+n6c^O{yf+c=} zPFDe^j57F%FE2oeQ>7aec_1dJ>IHeIb1#VOZUvE@t)S%E4Iz6$857!#cmmSPB#YGU{GX zd4T9ffJ{K{Mu7RyZUjhuXDf(2*2?w|+>HP^9o~%qab9S5!NVQ2B@`*#A8Lcb9i#{r z?svX}x)C7KqnCG*86@0UA>j_9VBrq(4ZIrxmH>r2NEEpnaRcPTHJzYv2Qfk64svVf zUJ%(0b~iMbbYC9+yfsDiF-ez!`L6l`6Goh}Bmt`O`Ji1#!Ho(dCWCGpw!$s6?DLI=T^`yb1t2|pc9H+ zI;XAywRL(yV&L-iMPDmuxD<5s#d44)$i>>|bHgB0yCJ4_gH845Yz19z+S&UDq^Wc2 z2N2Z_7DF2@1t|a}>Eka{TL1me-V5S`%LWe6d5@5G4oD19Hh>Io4 zl1`+u;SIQ*^8#KrfLPY8Ahoc(1`+|64Ine&Wdq1cl(OMxGbnk1j05F05Z&Dh(h3Sl zFaugPfMlR9gOv?P-b5)Iki_9-14#REuz8Sn4#*1YR**^z_ku$bRyJU`*OMssHiIJ{ zQ8s|ggt{JHHh`Su0ZB-(vH|2CxDZm=a2--MfTZDN1BmltcN22iZ~&=nFjN7R4Io9Z zvH>L9-3lT-dU=@)AZ5cfNZA0QU}Xcy%kZ)RECDJTK%&TH!yZuLv}poG9*7Al8$cfF z+zTSRTR|k`Qs{2*)zIDGObO11ovk}SdKo}^dqGTCJ7@X7|NlYl9MH-2u%jpy5&w6V%QD9S7dL7gQD?+BqN-klQ(6KD3IlF3Uh>!pky{ zl_+JIZapYkK*oW}G7#O}3epM+NH7ChmVsoTE`yb2NZv##%aFw3Wf@5Oaj<#F?ggpD za4$F{VPzSHdu!{6ac?s?@)2bj$V{l~;bj@fNuV=(KuHWb^#gJbTnMQwHusj9wGQ2DU zOMvneNEEp&n*&Om1+}2S05L&%3gn^Ay&$r?6-0Knf|6@DIFB%c+0a}DYv+Ipt?pJ3 z32otIv4aM?KpQe4u>=}!L)lKyjhGt-w|TC4bngX^Rf&9We#Phl*`wgt{DRq~yB9iJ zoB>X$DgPm(3m(T?K{s)LjPINp@*h0f69A@qeLw@FU@_=?@eGg`duu=qphGSlQ{5q1 z2xJ~K`}4P)fgG3%66@{-+2PSW6=c3gr|W`F-#H$gp%Xm1T^GO`J<^~(Ev|DwiTn5q zqndyJvuo#oiU`*gFFir0_}0#0;BPenA2jE>09G=%E040xmf*f1hqj|iuwx`>*1LS+xj+fg(;qBT3 zau75}yUu}dz$t$k!ttc-|>2zJu?Yg477nH7{t%nYf;zJn>dH|V#+p{)my z`p#Amd92m#KX{Y`8c*=|FDn+zTSR!S05JSNC2> z5wI0f1o+!r@bkM}Pw=;H2j`P3Aa}qDf;}J( zq9C||QV{rV0Lj8i0kBe}QsBsO*8`w@(CvD_+I0^s7lNlvLN~y3;SH1`zzyWg+9RMM z;7GUY0g%6454;5B!fw|iAj@Dy0N9bx>Ksx8fJNX%0E*>TP%S@!!}615pdtX9<;}HM z5Jdnup5R5m5sz-y8?Yka3WS3w0$kS%H`iW)=l?4&Qp@1!Q@FFW0F*vq`ClBA|F6Ij zCrGLrtN@h%mBEQKbRGXT*LBcj>3ai~^J~|2g0lS$@GAp7TCT^kUnkjhTKl@zFW}ZAkcjX(DSIkVrY7CZy*Ql5^h}sns)}9h;~mNQK`s^dA4HV8OEz>~ww7ycgtB2L4u19HP4g6xh&P zVY;`1)OkSs*xd`V6cWYFFPI%W54_mi2D&B&e1S|i*z=vouq*C708;Mq@ZW#v^)V1* zI>Gy=p%?OWf-mfN37Uj``D-Sq@0kEU-3`QI=I;gF9paJP3gUJ9v_63{VVBwXG`V#5 zg5<#Q3SzuG1ZwRbKlmR+b+>}AE`fN)LlbmPTLkD@7s$RFkM0TykM6CY+hITre9(#W z%UQs89zhg=&s6hZ_LA`E^pf!C=FQLtNpgC0hko#|ya<~01y%a3T%ZPF;}Ou!N71o| zUzD*hF{Dih_2_(x=RPD^ed^wa1UeK48YUj2_aRZ`KBQYUpo9*&IBIa;hxCte#NLNg z+5kV?0knX`1K)i}o(-VG9W;La{|`FcflBuwg}nav-?8(+h`0|aA{#Vl0@_gsx>3c0 zhW8=8%L0wGf^N3~^`5{N!n}mc8iB+RBdwrIW?gNQ<*TJt+_q)H)e{ z_aPn51a%rf*XMvnT0u1AvLbLZ6?DrIw4no%0nN#RuBIBu`;df*axdiWBE(25=#C|* z>tS6vs@#VJT6xle^**HEoS^XsP;!HH@lftVIwK11;!$`X5?cl+^2oan=~Ox>VbkJ1 zqymu1w7n0BHyz%u1}(Bjy1V5t2PoWe-iK5o3<-D8eMkJ;T0s=FgAKj&2|DfrmH>_W zV7(8iFAWs#^tlg7DGgMXfi9x~mt~-9VW4FhNDNVyT>$k_V0XKLI4ETq=n|wC*Hb}J z4q}4JGO$U|StE#g=pb<`=*At`tP#i(=S zUa$lxPk}^{N3TGaAic0k21Oo-2^vcUMc(LrNHWkHn>wA~3+6$C3MktF2rQJR_I*f~ zOA!m@K|@dskYn3M??VC)K!FBAAnVLwtMNz9eMpt@pyoYjQ5fj{6VRXy=uRWF>rO%U zk5GIc5`R3XN(5a-1FjN5*TO)nM35MwN(5ap18?4gI4D&j=n|wCN8&(LB8Um962T@x zt3-%;Sd|F6aR*i39k}CR-#miPhvr(D#$ocl?b8-_kBnvM7ft<_aQBdL9P-(cO8Mp ztUY>JW&SZTbc65kf>ntiS@?ZOZ#f}VBIv#&^6x`Zj{!v;5o9uL z??cjyhKD<7@%xYe|6$?&9nk2c^gdU4rz2H3U@TgP5QqA8Zn|$cLzh75ShWcVI<6 z$P(!7JFp@jbW;&j2vOvNOh7I2L3bBHmBEU9B%Me_KIpz1%=?8vYGFk_k@q2~27^is zka3_Q6hsg1`;bb5h;c8y?n8PWh+O1@?mB`sqhddUihNK?fED>5S@?ZOdjA<2Iu3PA z1>JYVzpWKS!HRrPs(=^yU4-!Q#@*NB8@Xo(6!j8t5__ za8?6d3j@t+ATdN%16?u$&uSnJN>&41g7jim04S@0n4qi%HVK;5AnIXR4RqrUEUSSm zf!@9Y%W9yTil9P>tOhaxHLHQ{E`ln9Wi=$7NLdYZ-wo#dLLjxUtOmMq2;5TynF;SH zf~-X8Dem$I#TLjoP*wxcgZn-tE~4DqOsD&hYW$G1+6JW7)u;EMtOiO6u&f4>h2MvC z_%|f0f$lrv-_{DEU|9_mMewW!mH=fnkSKCi16_jj;;%0#@<2>bRs%)epuG<%$QP6) zL6^~hvn1$R7-*IRi6OEi=#m+DmIQH7vLxsdq!;WUU-yETpezYC37RD#>S0+DbmI;z zOM)zc-o69NlAxQ4phAc&2{HjSOM>n$f+~Y$NhF;}SrT;L4d(qqAhob8N#uP<>OP=Y z1sMm*k|26;--lH0O^kc#bsy48FXSuP7g=7QzyL8pSrQa^qxT_sF!zG)Ozj-K4{7v1 zB%}*yJDo=FLmD#oAvs0Dm&}1r*TQ!nl2{~Y$sCpMLsC3K#FDwu`;b73`I$V3xY(H% z_aWsv{{tU`1-kzPdY=Jws}uYfEYSTU6yJx$>I`a3f-a*0HzqqkXQ4wD`hmm{jY-fY zGw{YFh=bCY1YLskVwV%Be+OcM8k1m?pp8k0dRSw!0CX04>wnNXdGIwq(A#%ljY-f= zMNlC`V-jQnYGV>~cM((>tTBnC6R9x?y6*<_ej$)rSYr}&;}E!i2Qm}hzXMr`(!aaq z2x^aji~}_$L3B6x+9L2mKad&Fg?=CzsLPM7ft<_aV)3KyFNe?mB`sCWRh>8j~Oo!WxqxS@?ZOkKaNXlc4*K__wu! zC|F|>*?>?l5_Mn7Ki~Eq;K_*Y7>3v8l_V92=yASEs15mi* zybr1SH6+|Y_Z{(XYXwoTa0it<@R>lc1Ss4=qR2CWpi7WmEVToLJBSHt#Di`^qTPK+ zmUf`B40IU{xGV!*3j^&KgTxSJ8R(K3cv%MGpp<2xOORf?vjs&thzTmoz$QVep=iO;?Cx)14sH9SwD-G}6M z7nG+!DFIfNq1=Zg{t}X>K=&Q-Z)*inusj8dB6wK_mH_1`kSKD;7<38Ji$H5oV1SsQ zJOv7rA#oqlEGy9RCeZE$(ETSKG`tVVzzS3l$o&2PA6yWCu7!aX1Rya)K>)gB23`<= zI4A`H=n|wCPc11p!DDxgY>tg7m_}926KJCa53)1q$)^A@P9p(&Rp*O=h4ouV{K7 zlD`=|+|lksV!Z(hcbxYjZF>X>chG%D{M%YV6fE39Hy^%&nSqyOAP!1d2D$|4g(S$? z!}vZVOA}DEfUeI0l?@2)8{ZzFh~Lc0&C@CqnTfsz}nEHeOK5db1R zdU>55K=Ksmz9ar^tsn}Pr$FftUY3C+KzRxzid>d~E3Yx?~1kK!7+X1qA34q!&L7K#d&`6V%uN zn*?p_K-9w;JD?kPV2vG+CD7Y4L}LeJ0%~IibaxR{8LY8`q!X#J1G?`9 z^L`&#_-q-prAg zddLL?=&mDJV<+GOsDJ>a1Xuw9l7-)gq<9BXK!EN$;@{Q^qF@CCD2m_(1Xu!8K!8M% z3kc99NH3!GK!E{bf(i&wpb&o_k~~N+P3}WFstaoD(DXi}Bwcv8gSKiSt&EU34+?jj z_aU9U2?=-5eMkJ;T0s;n+(9=V!5ceZ2~fC$M3Eaipi7WmJk|k)JBSGichF5pw7UZ5S0;zgIVu-32boC6p>IHF7s$S3~NG~?3 zfVvSNCa4<$HVN8|fT)LcBS1Iqz`7A2OQ5&!z`7Bjn~I=9h;9VP1k`Q>=lJ?9mr(b-iIWu2oHDAmQc{KhR~CKFCGGgJI?!%n$AMP9dzFj|F%{T1#9wvN*;JO z0xa>g%QDclFwh|pkQkyY z16?u$FUvq2l(GzT3DS$F@}MXOF+pV+*d%CK22l?y%Ro2oz{)a^C7`pC{vqx>0o_yt z6+)C{AQMo_GSJ;cP-U>P3`r+aSq8fM2J?O)kXl$-M&x}+tRVk^FVO*&Wgue*_kBpA za-d`cawk6b((68?gYpz8CBVuu&`n6)tsv5)mzVD}Bu|0vJL2Eg z3Zh_n3Y03~-3YJ*C{KYzk-HI~OORf8$btd`#02FjP@oLe`;h8nK*ObnT{`A22j31f z6?E4KP3}Vyk@-ih`;bmbgUW{CdmqwEDNynPU7rKWYan`X--l#LlzZuQAJS?`C-Vt*#Nrlh<{rvh=P?3pqr22Wdm3OR5pM_k;?|qB}gxHBtek} zVuH#BP~;JRAJPX2P{O9geMr+lCQqg5eMrU<@Nh@F59#GjP`Kl~4{6pBNVtRUJL2Eg z3Zh`)4l0e{?HsTKDBMA!$n6}^B}gx}h=al%!~}&q=q4oE-G}5M4l2t)m(hURIiPD{ zpzR!x7^0m6x?~34&H-^y+Bu+0kY4;114TKA32NtnO@g*_AnIZ59MFwBuyzi}63}5h zNO!w|ZYqKbA=)`06HwbZpu3Bp%3$prB%Mg@9MF9?nD+~T)WX_1pi7OweKwGp@ID*J zN|ZjEEXaT0OLRbG8OYedeIHV;C@2|$+=8tx&-M(f(R%uKul1c0tL#j zyAP>W7}Nlw$$dyl!k}wV$h!~enGmR07$Wx}S%MskaU~I*?nBxm2)Yaf{W2!dO)SmS zybmc<5IHA;t~+`Oy$|K|CPtk1Af+%R!M%;Z!_XR*1kjVRxHh~n= zY59upEJbcmaLt4ENRMO$R4=H93B#1%x9r15#1yRr-Cgnb)GJa4%)95}V zNq$g4K;C^wclkgC!Jxkn$qeMofw~WACoiZ7pzVD~p}g?)iFO~->2;v|kNrNR`Mbc0 zlhpf=UhsfYFA2EnMJo*3s|Lgw$|9|rT|Nl=w_f5(>Fo5oN zqN#s&8{qWM0+1AT|FnXnu=^*=fS`XG-)sO~INQsrm-_F&M=!5=%D?}PJHU5Kb?$Wl zjf{701=~fZW*|Isrwt!=oE?lW*w{ zmu^29xSj@(9+&Pe&;@NCoxPyxM3>H!j+Y;}bhg%jZxsvu;Q~pwE}gBQ3v!!lpD=iI zPX(#;;CDUb(Fwke(WBe-hfBAEg-d5^4pU2=?=ypBfVeLAD zf6Bpzhm0=#+YW+6Jd;0oFn;jpbUgv$d04w{;BT(uVPNn$-kN}U_c2%vaH-x_e z%J=973wuEBfcEGHU;o_Q3o@khlm{c|el^etO1JNl=9i4xVDaXk%ntmMk2y3vV|3u( zcCH(Izp8cZ0tf!-Fu{L{KbwCt@VA_XSa!k#?1kemG>SlF zfS8sC`CGvkiGpuxwRT0gy~Crk6*QUr66*FHP`*buSlFYp)c_J35PO;rus}zHJem)( zfD>HzR*(vxZr2$eoyR>ow}NJiJvz^MboYW-FBTU5`|knL)(MV-=Gq4g{H>ru=jB=s z1_p5P1SULsdqKmM9>-fj)c*%xSv|U;2?JaLfl~Yqmu_&Ha_Q~`C8YnL5Cjb=f&v=k zN^7tc{B5A?6JcTyYr&D**$Rnguniua;7hPQn)iZ|1`B_)0xUeceGhnOp71#Mg99A2 z%&sRuDeC3QzyJR?*FIq6Z^~nXIvi{&IF*I5fp46J1X8E#iEi*s+Td&UKncmCc`xWX zTn7G@m*8s|L1NnAblwTRFTNX0BF76TO@UX`LgNLLra*kqO^u+!q_Y(?gbIo!(B+Z6 zpg~qxn&JS7K+_aR93xGE1hJ*YEQhloexe-Afq~4|NQ;`|Ha9CP+|fxL5WEM z>9-XZZKwdcZ|L^}! z@D;sNL3|I*Qy!pO&M$PfegJ82-U~92g}=!ReB1-bNNcbRf1e6CZGbHVU1SJKYlu74 z!Kna}w!kSFL3s4`z5tngycI+dpQayxG483@fTAaRWB0uscYU3_5KWlat^yMT=9Y&`-B-&r}J>;htfvWpzp8(@Rbvda#% z>;mF@bhm=3&ek2ExB!JGDBXAWg3L$CE+AP%b^-14fn*mD>xCxBNg!>Vt)NLXk8TKc z@BtGf$E*Px4K6S}AZZMgN>DNlXrs=HiGSc}48(^d6h=@&0Zo^AH17qO4$m|oQ>|M; zGVn|Tk|#0KfL88!9B&0t#Ah1FqKj5A1@RC_KT4)q0E(Fdzu_JN@u8UpwD<$GFcFls zv1J<2UZoeU2(2JKBrMoLVKD&|n$3Gbmcug*$Vlr}kPJN2faGCe(GB(=mQ1q@bQck* zC5Tjz!0V4(S@-C6ozUG25`rcB37~`r zYWsS0yRN{L^5|{_sqpA*1+7hj$Xoj&+6@gJovol9D6pb_29)p74How3Y_0hB|G(jZ zj)NZEQ^D1)b?AYP-uA!$|MRywGB7Yer9h^8SWg8hhRG^J3R{pS{%s!rJs@kG__qc8 zx4h`UKjoN1$5xOrU9BK;1tYXIVU5t`(R|>)2c!goYKDq{E$fD)d2q=CX1)vqRhZD) z7}D4TS0JDe1@+NEO+Ro{cyzby|ZdN#Uf>_`pxcLRM zOXoq)&I2#fGC`#T#0+Mz)u4ujN2luna1{b-NVJ~<$1kV>3y$3GsUQb+UWCSM4mf5( zBsgY~+MgAm=tL^~KzxtxRxs7s3Rx8krn-AU+F_|60%Rbx@B@is6n-EC$=5@FY^>)1@0cyzkO^0-Ehy zx_d#f>(U7phsPbLMFA4`=my0tI8tFz35v&gU=Kqg5ZXEfyR*~vz)K@gKLC+=OFsYq z?*X}9xf`6qK)t0-Czoz$uO{?Ir;`Vy8C`n7L-UhI=Ty+TW{+-AkFQF?qr3EgM>kmL zg;h6b{Gb;u4ZCT%vlYBWxA6$5HyV4GU*3fQ(%**nPy52a{nP28)asuyf%DpE|Fqj- zw14{l|9|Y|1A70oBN<#wAoWknKzHEv4sidJ5#%gT2?J@%j`mL(7`j`L8{D8y-k|HB zS|@@N6R7_P?VoBUf*RZd*gxHo03YZ8^&>q<@1LrDgtttGNB=bR1KdNT_D`h{T1We* z|6xOfpdKVf6AdJYy$FW)PkG|N*#*>pg!WH=fo`7b9l-u+XDmFsfP0AK_D?^)g{QIM z(LYr{c!<>g>C-pxuo&&1j`mMU?Vlct0+kZOu7AoO3GxTn&S`7E=c{;DA0X(1KB@)=|`RZY2bZ$8XF${(}sI+50TnGbwy|$ z?VpbJPf6{cs`-FQiDB12&G!O%3%P$93%X2iAp56pJZafKjRSj_-u=^Gxuo?^gFPDG zcyKZ>>;NsE-pS0szyMj?>(kBKJA;wIx0}YMk1Sj_y* z#^9w2AP#t!I>=|;E?pMgTfssfJ6(F8bc1z)*3y9)FAsoL40X4Hm3k<6KveJl{iY6b;2ryO)_cnEUc$L3?qom*qTrb6^%D(T#s z0Fv))y#PLj6U4D@1!>`L4(DNDXxyfPlpIzRJ_pqgMq)rksB2E;F#*(3QD9ep1uH! zfUJawbhd&nCVz4E1p`C(RuI$j7=J4#*h;XusD1@!sm@l=1;&-+dfa4Dw->o@6 zLFFLmcu!DXgd|hYK;Fx9iJ;I2DMC$@{8J7!JYwYE_DSe1Oc17vFN z6p*IQsU0Ay8!XnmV-CpU{4JnW#@($T1)$>P_zSVifB$Fi1@XZrhAjB?|3CD^5Re$+ z#1N3d@DoEo9Mls-e*FLc;`F7z|GVLLM}tj*o)`jA4?8ggRyPR&K2PNH5J4|Ju##Jp$v9nNDV>=>BNv0a2419tpdASK`iT5kXqP@As`WOSqw50 zeqsp7N|X~rUSItCzk4spIM4|kAR1yAC?LV6La+M+$w2)CyY3Ikn<&@)A&JAU`vYk| z4mJ-IV-PE>!7+y6UT{dluKUAquQgHbZ3ag^4}TMMaU;k~sOvpC8$rR}Jr(364@g4l zoUj0t#il}B4o+L06P7SQH!B=#&HwfPzc9p1d;T^#kXo<>FVlhZ;NR8?%9V@ofkd2w}SGj zM|W99w@4poJHYkM)+r#9XMjwe3Sw#=>YVBT%H(@NWdQ?!t0n{7)aD-|{5_z+g4)vj zLipeVM&?$K`c5!;tkv$<|Nkn@;PRv%7VaQ_yf8fv4|kQ$)(J@A{<;7Z?jS|5a0khD zgVlKS^3JJ-gu5*y+(8uV`~r|~;HMvfB|xVifkcr{Kk5PbaN9XhxPzFWa0k11FNo|0 zyBiu_-Fv}BWA|22(Foc-0E*+iXF%DxbL#;R)!lmm6gWGA(+Sq4%D%7VvV zc%1{4WgtGdEV}`U9LR~aATdN)1~Lj>mVr1ZWmyZz+5gXiq8!8ooqhy130jsx)Wga$ zkmF!w8ORb3h(lo~)^0(#9dY^*$OP1~Yz0CYtSm#)iBy*D0heVv;AI(zW!(x=3o9Ey zBH*$NWG1{U16heumdS(sw-;m_s4N4~-K`+4pnwE3pk*0I2I?nRS%&0Il(Gy-9A1`z zv>ykX2Pw-yR#>-!RARUn9FnlI48y(oXF$maWE?*CHiOd)qAUZM33WZZECZS0(cKEN z0aliQ+yfUvD$ABb$}*5NyetE8UOYSv&r`ykt)L5TVJEqT=YsMSND(Ygfn>YEYCL** zH7X%_Y8fO?fhbs>0yzj?mVqTec?u+oT$X_@LVb~R8Wb2HCMZvVJk$w}%I;P$yA_mN zyTN&c8O(;}GUmn^pay*_sL<*Llbx+K;5HJdy<`n;y7RXyf{JX2IJm{tc?x>1IH>hw z&BWil16J5~gLPQ9f^_pY8-ewKn*X3Q1lj?WeNx7Q8Qf?(4r*#SK-7a|!5Uxg0WXOM z>F&m~sndz08{AgMD#y|dZi8W!)ZeT;5GOV zi@^>BH4I-)2Q9Gf-V5q}d30|DHE}(ZtH`>g@L~n)cR`P3ld`BZv|bb4eO0Tb_#%uewqIX)KoQr_M@8jf>`iwBZvd( zy>w3nvq6;$C}uic`UJXLK|)}6gBY-GBS`wV0|zJ$V0O360>>7}A)x*dD7L!6{nc)l zQ!Lit9x;Er5)L(uHK$l0t?dvTDnO>d+TU30U^xY8QXg-{(jd1D1NGq`;nf1}!+}#v zXDjHc-j_UJ8^Qexus}DMgxGbwwE`Mw-C&k=E68sC<|ZzXiC{VYHZ`yTAP%O-!665_ z)fZ|Fr~}d23KHlBldwbs(hV+S!SM|~tsbNly@voxT_8FB_7|L>epoA(J_Fde?>^nF zAmbdnO@1`LV|0NeZO7)9%r4zip}h!(-=JQ^A5dk{IrRgmj@3(Mo6ZUpEM)%ze#-BUp(pm!rcV%@!9Q@g>YdUUp0fNe7Q4H|TsssW<9!D49L z2#^9$pdEj4{urq01@XaEufuP6Hv%MvsCq#L!>e8p2c_x--Fy6^pgBOF(;ue#SQrDlyy(4oO(mi{aicM~HE6^InkAh^iN4Ce-!t zsu$!W4@g3SRlOiha3Q3sm;X1o8v&ArSG^$4i?k!iRc{VbbM{UusOkkNf>pgB+3r>l z>Cwx(wh&VF^8E&PBR~|a>IHcjUiE?{KvgeD6uIgJU0MC&`C(9CfS91F7v!PNy&$r? z6-0Knf|6@Dgal_wa6as8O#!tjSA+ESf|#&w1SnU6x)B_p>;mmZfW$yU5xwFef=r$YVuHF64?r#Dy`ZuH(TxC^fZUA$^P$}ckowM65P7Wi9;6!q zDo^0u2oUGRyF>7BSLtj8-C+!C@+?UKg*!+QEZjk|-C#AK`*HFi;eHp=IREJugmCc1>NEc>qdZ-fwJK77tDu1Wf_PMF3T!Fkpt;QfW#1G8OV5e zSq9>ulx3jXU0>`t2rA1!Oi)<{HVImmLDa*_GLYk7Wf{m44~Roy-G~r`+Y#LekO`<| znFm4{tSm#)iBy)wfXlK7cv%KwS+|1J!pa7a2)HZ*nF%k;KvtrZWj79hq6K6es4N4~ z-K`+4pnwE3pk*0I2I?nRS%&0Il(Gy-9A1`zv>ykX2Pw-yR#>-!RARUn9FnlI48y$| zM7g&aoMsSZ8OThi>)~Y?$Vs5XBS1+EIwS&e4_pYTEOUpHWguyISq9>~n6)3Cr-VCO zLAUn8@{~X#C{KYD!SWPHwi~R*qnGz#4kS;xLCP`^1I-Mx(QZPbVhcDT_fzA#9SqaVrAjZp_uOKl{n-!+}MbUov){>^$L-eL%*8 zxfRszZ?52A5K(P#kE`=Q z=OLv2U^kNoWD=}1|r5>NrvxpxML>h4|f z`~QD5C^3wb^xVz-94b9 z7{mk>#bA@5MKMG@tSAOK4ptO{ECIFZk(!k~2)84OVvq@_MR5Z{8LTKq(uq_QPXQOj z6W~QLh-KXhQVT2EKqBCx7-S~ACw1NT>%zzffAQ`Bi zU_~*KH&KdWByo6A4AOoaY#yX223cX<3Q~#TUT{dliee1+&fP_fdz<%yj7Aj2ATyz^ zhZn^lCwX+Yf^6u77R4YfxDHYjgD6;03~~^>C~4|VPZ zk=?Bzva=PGT)SIAd4w6vhLpH36TnBDVD>3NhtYsCT<;xFGVGjs0Yr7TJ^*Dn)QKRF z0#FJ${(^TWD64_^;H>rnk<~zAh^z)O1fJDE9F(jEzE=Fu4p3GDF+o`kY!Wo9LDa*t z8pv6&tOl~gqq`TR0M_a`0(Lc62$9u53Q)7!4umpTRzuQ>l-16Fv)T!GRs*rDTS01J zNf#sn&T1et;aLr2B}!I%v>g;%Amcz;4Mcahg0zAH63l>RHINL{Pq3_pUh9nNp zY9Q^$!RA4-8psOkR**^z_ku$bmenxaYebZLoA-i@Mr1XRnNZimvl_@rpmTXZi3^(5 zK$_q}NLg(=B&&g>;aLsDd9h?0a#jOf84k;8QW2o622upeY9QI}RuJjY%lkSRlGV0B zvKokjWi^nO;aLqV0m^D1QRJ)!x=Z|p>NZg1ftaAI2J%qnUJ%*c3L-mOLCLinOhPl+ z6lMm7$Q1~f~8 zWT1Y6Wl1D&qGU-Vad?&lX+I7&50WK8R#>-!RARUn9FnjsiQ(S)n~8C6^InkAh%5;* z6Y6?+mIOJ;qq`Mk1FZG~X@UzOWy$xDED4f^XGsv}h2Uo7ED5@*81xR=2K@UVb`nV%_ zgdO54G#Sw9J_bmmxO*yC=S3u)osO_63oLSs5Cfs!gdA#k!4BLL02eUOC0lK*Anz@< zgSGQf9MJ8^0qLAT%tDcCu4G~6Zv#!MLL{}p6C>ag>=1tIb`-(tZV9}41hDE6f#^YT zM7JYL^9yFI$^~BT2A^vN4mITPZmwiuW> zt(FX+c@9w5mXW^&bb=mU{|bQWuvP(3*w}*SL@+|Q+mQv)`2!h?-uZLs66ge*&Mfb1(^fux(k5)4ziblzt2hz zlzTy(?q0AXz~O0mg1=t@ECXK<2r>b6K_Ey3`+~ryvY@g8+%1PJ|9f%e09XWMHAJMd z6?E_Ti#-QG3j#q*(1O6VvLMHR&BeMP5OkR@%+KHjeVwf!AlI`B`09h7@c^90Ngkm1(gWX|6P znWhAZK&*pKD`RA_=1S1;437}VLJUdxQbULXt-)iqftzk4f4toayY=l9N5&@H$x zj<5RrzvFPnTy>Dn?p}~F&_*tQOFyKy3KHw?1smNx6=XE%{6CO&y)__Bol^@yRCj9w z=v+_K-YQ4|D4veLh+hS28iV-Yrf~t)qz?;S(4ocJb!T10FUx=&%HH|?` zP}3M}60~UyQ4ebxgPa9x8iOqH=sF9jSW^=u0&W_E%!D_MK~|zPjZId9T2~&o_c7SkoBfWq8vVECFg7gG7;=#-N*#Uz9Hg1qO%- zY8rz))VUW#cDI7a&Q?%z?S_!Opo|HfZ@dCZ*w!Guy&xuRz7dowLGz6PpzH#jZv=^X zKyoU4*&;{`Jl_b)tElshpo@=RJY4qofA>@n6Exqb0m`U*LFEBrz7b>s@_Zwh51nrW zsqbtBk;huq{(zS)f}9SYZv=5()GdRDyGkecl3ZAq#KZ#>?jS|5a0gwX3%^MBX%Hmb zRUzRHqF~_;@(p~x5i9`;caSLZd?V=g+!z0sg2Eld1cf`ut(|*8WH;E|(D3Ts3n|66 zLQ1jT9iW7^cMB+ocWzw)qPoFi&=Rn-6?8K!teX!~2FilRUrb&KD$788a9MT$6giOj zMvxeyECU%2FUvq2l(GzTpXdt@kh6P1Oi)<{HVImmLDa*_GLYk7Wf{m44~RoyW!W5r z+Yx0M$OP1~Yyv_VtSm#)iBy&?0heV9;AI(zW!(x=3o9EyBH*$NWG1{U16heumSrpf zMGMF{P+10|yIVn8K>-P7K+7_a4Af7svJA1m!7^hdTFy$nI7U+1Uz8uHE1~!VG3Za~W*D5mab(gIgKQ ztspjJ6yW744p4Iza||5kUW^x@AnbhtO1qs?Z-6*kuYkzC7r+gh)-#}nO)p3YT$sN2 zvJkY03bYsFI7mN?i-4H{&!G?maXZQ#uYUJ!sdCFotEVT~G4t^_q|K7i^?Xrl%s2D;JU57Htk zkQlg81InwYjT+D`$1iw5CQk)1L5-Rd;6@FoEI>4BKqeqJYQTJGqXwkDvlT=hYdr>O z)PS50Z`6P|FILWlhr3E=E9hQaSOFYw4+?jXB3QVCWV^v?JbHNryddFz6cX+r3Ks4l z-@qF+U zWf_QrQkH@4nSD__2UM1Un4q!@Y!b9AgQ$m3}hy}ECX4IQkG4g4T=_!aiFpc zM0dA>w1NT>%z&0Pytsn}Pr$AnYmt|lHP@V#b zB9~>L+izc7n+XaG5EGQAKpyJc3nIH)L1bquD7kjGg7OG6m<`Qkutp82(CTglk5zN?G7Xy5;UhR8vF=`w4IbT7p{M^J z02$i52c)TUD(G?$&^Bny#tujUC|w_aF?R;2fB^Bq1qA2@6=(qg58Kps%&cejFA)~z75unY$h0T&P;GvNgU$V!v~qG&oOje(2< z6%ZgAVi+hO!KOkB2#^faPp|?4$(twz1d=$sfBl>Cwwu?F=a(ra=k_5CtnBKwgFy5MT*V0Ra+4E+9a+How?96%-gC zCa8b_d8l(Qi0p0!k)7bHgCG|Ob@zgDCbY3r0ZQ1FAicdHCakdo%9WtT4(Q&Lm(a!z zNX(-fobTZyI3O`_V+WL1Q5!p;E1O@)gG`&#zxv>N0LmN9F z^_}1=G>)~V{RNNUfSe9*?0`5g_Dq3?yGmzk0aCaZnSsI`qzD%7AlYuP8joIHWk*Q3 zr$WLVM8U!x!caSJ@V+V9U@eA82pl}B%Kz4`+ZUfp{krPx+T zDc1V~REq8W0xAePw}Ng2^XLYPK}*2S)(oV^4oDd&3m$)QaWbeZ1M$IS8R!l*Xjujl zLzHD8&BKrPF_$I3yJ!OAiuok(Tb6L47uI*|`jHh@^xtsu3qvH>IlF3Uh>!pky{l_+J| zvPqz50T~A>%RqED_~JBBK!O?2vJ4~x^%JZtL-Hm{S%xGIFUvsMkAuxab}vXJhI_#w z2`kGm-1}`JG45>!rx`?91~L=sdU#m|a*{`PE64^|Sq5?sTnMQwI}a(#K+^EC48(bn zF%h1pggaXkkn+?$BT$|KDT3uGkZd}~~-ovonc+6~Sl%wRUWRDx_g1r=J|t)O-WGx*{s zNRtOLy9Jr8gD!JuhfM2(H>ZLpL75>NL{FkjE`wHeJOC|wfJ8B9?hZ0x2T^T#f}GVI zTaY%Tg4$jXvpP|225(M9Oc3Hw17A^&#~j$o58S>auxWMqWB8_3kb}V42*h}~{{iU0 zCfKG`&@?Pjn^w1-U|{G3Z=8hf6@@Mi0qvy(Eg8VRR}>OqAdhzUf>nZ7qMw4Uxo38{ z#sZy)^#@PNgO$8s1TQl|Y&?a`m3BjHN0o(b8HFSkurERJ{gPh>)LaHFvWFxB5DT{N zbAmKz!2n2vfrx#d5unTjnYRUP*z9Zt33P)=*y0(GQQ#sDypI6B?-O;OCz~|Z$y~@x zuN_GUGm98$Gb9q45du;`YK_`MkKt;vA9>_#6J4k0Y_{v^rB?|9G zfQ;_$1smNx6=XE%pd65Oy)ht7ol`;AZ+UcsZxln@cnVSgN(9GWg!O=`UJxH#^=5#s z#)6EQg2WJ2FX(nMc-0Hypj5p9ASZM8fT~^)6IAtrO@dav5cROC7vwBh)eEu&w7vcx zVw}MRIL803{eKFdXaP@RlPp{Kx3w$E4d&^9K^D21*wHqWgro7 zx&)aCuX;gNqEx-w-JsGNWE`mK1<~EDAg!Q)1T&yjFGvRJCs@^sS7fBpm^@6k? z2b+iNUXV%*_ku$bR`p`Ix4MfM_creZ8I7oVL1sc-53hPbPJ$*RSk(*C1Q$Z8dd>fV z#~DD<@TwQYdGWRrx$5-+l@`!(hGcC})eBMtt9n7Q-K`+fqnFpj98&d~{R2+~gD6IF-Hs$P&Na@FerN}PF}puhkzK~*ovL!Em;WOplw>}&-k*KP>e3(A<#ZiEX+ zuMtRZFNg{2Mu2i9s2c&g9vs$<0Eu}(aw@zV0TKguBS3i-wHx68GWkXaXd)QI1a%`o zcQH5b1(gSgZUo2#8Z{MHT%cMua4 z?jW~z?gf$EV0S~qt9vh`6x#|Z#d<+kaCYtm-OcIIxwQjQ9)QH4C17W(2~sx#qzse= zkH6?@2bEAZOcyoZSmzg32QZ|5C)~z75 zu(AOp0xruyX2Q!dkd-K9S$rEPT0q8u$}$iQxxo_@kl@=yp=B9J2I?nRS%&0Il(Gy- z9A1`zv>ykXhwNUEN(}dcLlRb&VYqjFD>3eE2B#TBSq3r_>Uwxt267VUEG|$*37!7| zxd$$URF>sK$}*5NyetE8UMRN0^OSIBs}540nyv=QQy@jKJOz^N2CD(h{}@5?R30Qx zfhbs>0(lu;mVqTec?u+oT$X8o66c~8P+)+VpgaZgQ0HC{+1&~vJHdC>c7yW>Gnfs{ zWw0?*P@&b`3TkCAw}RM^Zp6#GI46R^?KWZ#*Z|$%+}V2sl;}IBf^I(U+`0wa*69UZ zALY_H6?FA*cP~f`T)w`z(F__c1s$-l9HgmxD#!%%xnYo4H^kI#u&Ex9JIXtI|9~`g zP6gi#-VMID9Ic%LQUFTQ$6w?%gUSXFA6zzY{D$ml zHX!LlDjVK_%LdRT=8(JwVp+F>)WY%_NCaFqfXsxK4InE~$_D2qQ1Sv92PzvtbayLA zD<~kr3~1Q^l7YGmRyH7c6QyiG5{H)!AnlM#*OA=|QiK&y`b|Njqb=YTZ9g^yWYmBn>YcK%5t>jmTw#0#eyftpq9? zK#E{x14y>J6-0XU^7`mO%7$x@vH?WF$_9{^;bj9@0#r7DM3Kt|8BpSEYXAiXhzTki zKpyJc3nIH)L1bquD7kh+NN}bE=floc36NedkltPp6V}cF4r-GDtfIRGz@wIUvr9-g1B#PY5;Q{%O59GtWASNi>!7knlBD=xvhK5)7UU1piy%kh8dUW=J z@3Gly0y>?zbE^i3>IRF!+c_La?HrIYP!>G?Vo}}S|Jh(|;IhmCQI>(!Aj&e3@$j+? z#6c;`SU}DWuLG53ASS3R1Dgab%OL7uWf{nEu(Awf3Fv4Vq_PZj|0q-l(ar&xfLfLb zAiM}G%aC*;m1QdbL47ub|FCr~AeMD2NG+^v0EvLhGLV_@vJ7M;N?BH33(5{4<3ME@ zi0*C$X$1u&m;o)zKr&Ddz{)ZtZ=#fCNaFCa45a-y*gQyC2C~As6{Hfwz2K09m1P+2 zJzhhMdz-8u8=A{t?Ho{{)!hmrp)H&&cF?giQ5(LFT+q^{F-L8tE=kIv8u9^I}B;Ef(>(BPo!98lsu{-UPp@Bi%DIiMoK zb;V0hCI*J)+BppTttQ|Tfn68CN(R>jD8<4Tkh2`BK*a)xsd=!|cg0I?utPv9p#_5L z1pd~&5ZgSueHTEdO z_?yA|s$4t3*&L)7md!&Kpyct%m4E+t*Y;>0@2u_VcI^Q9-nHZ9cF;hyYY)gl&>ZbL z2f_iT{Amcs!*VgI{d4%6t-xx)_J>ZuVYwJq%Xj_%|G&9*4iA45Xkgc)+jRoSYFOS3 z?E%Faw1p8m2f_in3gr7^t>xhSSUbm_zYQFtwR2vquRzX;A3!H9ih<&>msM4Yk)hjn z4lEmjq`F%{q(?9BPgO`ZEQ4f25CzSKz6;=a5G?xBqq}qgat3?@N(Pn{pbQ9NY942< z?E!~xPp9jOZr2svy`XdrZ9TjIDdqtw?gcSntp`w&2DKgvKo!(&X7G8uATf{bUQm*T zw;n)Z;MN036t(s61Z482a!~65#00e-JV0T)7nGe4tp|_^$gKx3AKH2VsqbtBk;huy z{)0zJKu(9Z9zdKI{^juSRq1Sf0198|=oG6YsH6iaf(0>1wi~R*qnCG^G9-vyAwdkH zpg|1vV+;5k;=N!AP)P^%V?eh}U-NOs&el61A6_T}1vH2WD(OIO?F8S7*9~?zG`za^ zLW+Q`kRrhMMrZ96kIvE)9^Jk-phZS!>y2-q)nwpg<9Y>@6^_3sE&~+=SHK0qgO}#u zg5V0GAh-c92yUPh1Xn-~GY2`W7sLb=1P@;R1l@bl?Rvwb8&(jU;BVay&L>wu?tm2p zdq5mSL2v`5An@G)l7*E5V5LZ(vw2SE9t+x398>mFDx1W%iUZh+^)8z@DeK(YJ^s^uqeST2my^5)tr zh#~+SPw*n(h)1{U4OkIy1;RlT0j}$Xn`^JY^Z%6>>r3G2Q@FGB0w{gL^1m1;|6hS6 zPLNbLSOF;iD}obe=sNywuIr%5()R`|=hvK@JW^ zlVgGHy9DXe2JgG<1n=4fo%;j2?E!M)EhHAv^y1zQ58CtGIt9Gn7i=?@9r=zNkbRR_ zx2v;2+E}oeOpr-1cY@aGS%Y23-^R}XYRQ3u2UJpk7Xd>wLME_5eHn1b*nt`W;LwB3 zjH`pnDzFH;pO_({VDja0a0dir2FLl>&{?^lu>;WAv4{hT zAx>;Q$k+*x*u%NzE^qJ6)eZ*`PC=L8@PZuK8)+3t}?xx5R<>z=JqwH^7_#6*-U_ zU_i2vGn6}9LBic&5)w@igBbZ+*g)Zo;sVfW%TCuP&3i#EW#DfG#UZ*|K!FXtl&5qlOKCShOd&j9s3S)fNDH}3_pnE87_H=}qYw}N=xKCMrnOz6Ewojy%2 z-Mt_=aJ+&TFAsrQyT=dy2T|Rv;62{32)p3X+4=*twY?j>@5ZCMLc*haE9eFkkQYGb z&3t46UxWow1i5C4*-OHs(@Vmmn|G5gNRrc|JM@Exi~;_hrqw@%O)D=g7P-DVi1XM@PP6rKAftaB79_Rw1m(a0m$PGr&9%?J- zMkd(UHOOMnjPOvnFKc%is2c{lUI{dI4Wc2JS%Dk=pgXsqO-GOn)KAcEF8H=9xDdEo zc?r}WLl%d1dBOKLQRTiYZlc@^x!Vdcb`6Rhs8eB`W2)SjRhx?3V+P%Y1@0zz^s;_t z1C8i_QUa{g43h0`1(6=Tyhnt)AqhB))aWS zqurOan-vu9IPc5K5rBj{=sqm|ZLJ^*I%Lrcx)}>T4+oY2jiiA@k>}w+mt?(YO9q8I zhzS}=1KpI>NwfR1q>@2p8R)Via9IYrHV9glfy5B=a2JrvG7tx)ECXGV_2PUIs4N39 zL1h`(BxqR%Q4cH2KsPeM$}*58(A$}y!*1Z4te`^h!MCX(6HvzAdO>$vL6t#A;kJVA zz=8@P#^S*DAz|J>1yT!}*8<&01su*BycZS0+gpf zqR8Wcpi8n|7$txr55xrJDNy8%-j^iVG2!Gm1gTS3F7oy08X1KlLk3Ax#dxcjm=V?f;q&~iS|{Wu;pyf15h zG^pwYT~-9HdO_C)L91Sn7^3P0T|xw#-vZyjg;Mo`F3EZk5)G<)K}=B93pNQ_^@23R zC%RfeH!{JhUXUfA1s6yc8-Q-If(jw3UXTf>RWImnE2uJ9)r+JPspIL0M1+IEQX2Pppkd-J^Z%GuW^adFRs(L~6;Jz>GNF*`trPqB~W|7EMFX%2TST|zL zF9wG0UQkMaRlOit_=uKbRQP~wpI`Yt9n6E1h0C*5}>LVB#K=1f-cE=u{{D5 z7$7F7>IFp}@%Lp_gY?qmzAQzM$+W#MYhO4#+|lmKD)|WtcbxZSsj@)A9dsWS|F%{T z1q*jjc?R!BfF(fT4iZJ~Mu0BKdSM?93U?3_6z-s#vS@c-*5xozSq8eS2wawdt_^~g zWgsy`Sq8d<2ws+fI4ETq=#s1#9burd48#PLWnhz_Wf??0tQ!HkkqK6ofh>XE&IId5 zfNrva3L(lekO`<|8R%{+s4`ethNKgzECbz#gn9oING+@^1KmgkF3Uh>!pky{l_+J| zicnCrfQ$o`WgvQR-%Oe45O|(KyD#g(S5Te;r36@6hH_ulMn*`U0^Ntj zzpWKS!SWO+ir{4#SOS!%K%&TH8R(L%7q5dsfdOKI@)RiYM(@k|54oolwcFrCOqT+- zQegDHtpDgcbjiOj%Oem}6oW1+0vE-gYlEOgF-Qzi6oW1yf)~Xg4oXoBx+Lqx-vCfi z3}S+cVz5cjq8OqcRuqG7WP%mNAWNXPGr@{t&`nlQAw*FOG6A(H2HkB1RR$}Hk#r&z z#i09;Fz=rNsf88Apc|>cMKQ=scu@?p5~V1X1^Ewr$r7k21{piJ@5{>d2bDe`cj9v| zz3$7p?}uCzgYLqDH7i3ufQn*JN`Mu`AX)f*S!#b67&;DhOa1Qo@gK%x76S$BOwSq*eq5jd-Xt_^}_HINu0tAQ>d zf@d`l2PLb4F3EZ^$rqH>Kul0p1DgcRY7q6XtOmM~36|ABmOyW3f@L+(O;%7LL{wQiY<_Fpri|; z2lsthOhmbtUiW2{cq3;u&|O%ttoG^+D64@|0xYY6Wa0N^?fwDDYM}eD__wu!C|Fhl zMG-u!fh9m$4J3-3)j*eIz4+<{iaZb#l+{3yH)!w6^6~;@Nzi3Q;4BHcHVB#}L1Kt3 z3A%&`o+UvXlq?ClBdSBN6(fhLg|0n;xtRGx(EBc+TjAixq=D|Iq4>TmeS1*T7<5??xM|!0 zIx8P?Od3cG(KH5KLIiIbgE%NnW6&j8FP_+en#Ld|sA&u~3EDJvk($P!`;aj2p8~0cHH|?xQh}Ss zAT!}jV~~|7O=A|2|G<|lfts2iW4poET7j1>g3N#}TLj5KT?SjWh~!P^9zDpiMI>?f zvPF<~O!tCRVz?I^lCUlJ814z3$68W`o=`2Hk}PYZ}|!12v679)vZG zL9+1svUp!Xn#Q2}u=uyNf+$$i802Mm(-;SRbFi+@`yh=PSXsN{jq zH-aTV;SLf-o^J$QlJ(-e6)4<6Oi;LkZpxzFeObv?pt5Yspa1{CWf|z&AZRxqB!(!< zK$j4~%Q6rLr7QzolJ!CYC|W?*D}l-e5Iwl>%j&Qo#=Z2qFYA{%JWrwB zmsM~Rl&3(+4OW(++?VC}6q2Vv_hIpGYXwoTJOxUJ@Ujdn0m@S#QRK1=bV=5WYI9Iv zfS8~>1&X{OabMOEGteR`&|VDC{Wu;pye})t3{()v{Dl?-plgGm1p!D5Q4oMGA%YhK zAP!1F0Julx3hxvR*iYoZSmz zg32REK4>3MGMF{P+10|2lsthoArrtFTL)|Qq_m& zDYW~tW?um1DNss)m1QXRW#!y~xGgosIdcLf(i(*NzeiUq8?U2fNo@h z6%Zgxptm!@3JB0mR!|{C0Rb`rwSWNKZ3R^ZD-i$xo`fB@Zv1#9e7odFdPpp*bBAV9M4`?9=mKne)Z zeOUb4T0s=7(gmdocw+}F0jhLCqR5RM&?Q+fTD3rd0b+tGT~Oo^e_xg-NH0z9%X+T~ zYV6STzN{Wic(|k8m*sRC6z(|h%ldp367Hb;u=uyNf+$$HgKoxxH+H}hpl}C?A~$wG zmt?)*1^I9 z3|5vQ=|n2aK=&bG-aiFW3oFZryf3Rv9h4nF#(~N*5Iwl>%Q~h;jC<*IUzUX$JWrwB zm$mLVC{KY>0<0`Uxi72!G9*ud?!)5W)(WCvc?y&&;AI(D0+gpfqR3?#=#s1#J5@n} z0b+vk6e#jW@5}o6|3971qWcfKw+Xbd)w&flb<5vO?fbIYViB8GLAP2l@Q>b?1q*lZ zM$LmBm=}COA_R269)J7jeOaKJEMD{}LMDPi_v1kCyFi}^2Hj6X@qJlpilC|&bXgI& z>IGdJ1g&~OVu-32bO{l>>IHF7s$S3~SubuYfT~^)6IAtrO@dav5cROC7jz>Ntm*|> z0==CHR`t3-?va4#f!!kk;-OZ(pu4T0I$>2Wl1`+m7jz#I=KWJ3wXmuebmJ|mzpWKS!Kz+R6v3-rumq^;1&Jb8y`W37Uf9WjA`ip_RlT6dBmTZD zCXim5+?TaN7SxTP>3vzQvhZ+6yD#haK2W&hyf16bNl3VZ?!)5W)(WCv;SMT!;N1wY z1Ss4=qR8C{&?Q+fj>&++9mE8MJLsk?+TE8GB?BtUK$jJP%QEmSSCAnQkQkyY16@J{ zFUvq2l(GzTN!AN~kh6#JeOda_plAVIuLLT~K=k0gFRNaP828fazO0Xu@H~ZfUsn2V zP@V!MH&|JQa$lCkaY&v5-G{}$trbMU@)Rf?!pkzS1Sn5|M3Kuf&?Q+fiX=gS0b+vk z6e#kB>U~)&BtXNZpaV8Q_v3ib@V+cN2~gPpx~vFXHgJHB@Q0KQATdPQ0J?+-UN(R@ zC}jialB^e>#X;>H5EInS0hkvJ$18BMkB%_>v`1*#I(j zaNn1eEC$LBAa~+(FTL)|x+;oXHh}KJg0*wJw}Z+CP)dN64Io+geOWSxA!P&TJ}mxi ztsn|kHh`iCUN(RwKxG3+6uE2wU6S=8OcWFtASS4600j#1_hm_e^wQ+MtOFvTb`DMN z%Ze3&hdbJRS;E^u;g0jZtRn{?;SRbFi+@`yh=PSX=w>W5VX$*5<`?_pi7A0Wf_QrQkH=($$Fs< za&|9>2`bCLCPB+Gh6zM=x){K1iMd-G>D_EC_UI*RgJvjnHHR zFUvskpi4WpLQ-2KzDG#u3x&?IRfv?Ix7j;h7CH4z7=$} z7XJIP;!1|peOU*mgM8PE{l2WGLm){)_hn6(&VX@WmPhj&2anF$4<4PRFFZO!A9!^7 z-f-!3z2eg8d%?Bygk$qd7MD)fGcKLJC-_@T;dd6go@su;+Ua_Pzr_<-?7*+)7fhY5 z2l$(!V8Sm!cNaPCWC2~>)m{6+qr3ElM|bE0(3O=g-L6+&Jm+U%@aS~?@zR@tfx&Sn zD=u~WVd|8@>Oi-zd0;bdI#iuUx9bm&<|7WVhtrVn2lHrr8Eaj(VS|NlKYw}MuZ zdvs0(ZRqjn>;>(vaOrFXZP#$=?3MWY|35qsT1EbXZaeM;Z}4exg!!)Ree{z7gQ_vnUb>+a+L-EQ33 z3tAlu73}T=-I;dzL1!yy`D{0sY<|Jm*}4E^QTJ4^eceGE9-UJm%X48O0v?@HCm_^x zJ4tl5_CWX`h23Bd?CQA&hy+MIsze1u0%S|KlT2r834{;!UiVgzK^~f)Ixlp#g4Q&4 zgGp9@ z{6$Ue-~S$+t)O*=9*{MIpqmN*{r}(1)80K5q)mA$$XT72Ixl#1?*;jzlcybGAgTtq za5L!AN082!+2D&QLC%3LR_Sz9@aWzP@(r>(UVN_s6~n!t!0Z-j?3@|{jwujn4NevO z(+@U0WaQs=uo=AAl!1Rfgnv=-Lh}y>{+7F-$T;2#+NuJI_2#|c)X(1nxycb?4k&%{ zH)TTt1(dkDdqEQ5K=J5o4FHEHI6-)HPX+P#xA8D}bi+hI>4JZo36teV{#He>A$vhW z9^G3(YC&^UfB*k~@ekyYPOw8>&IGG!1*dx~R_z7pd+|31BPyz2NKxc6swbrp}X{Cp@~Rf=U~Y zPEfdjox}_?1ymUEw}J}pmms6SC0lnZD1fcO`HsIGEC;%k9U`m@E@V2v7amUql}X(! zU7cVJ-QdjG47#}!luKWJMv9rvR?zOBmvcB77)U5L4}fxH=hPiwYAa~S-vcrX@6y@2 zmyC?(SZYAfnvt0cDg{5DCt|9bh$6L25i8QN;iD_Ov{fXhwL8X1pni1)g;f((L| zn-O5wfk4!&~g(r zISD#-1yqnULCQ_gCOlBN38JCpCTKbnstOwJpt($s?x`RbcywY!M^D{{=%gKTy8>UeOe*ZUXZWAGzs zpyejWAK(HKB;(PItN|_zE;m8)&~g*x9FOi^P#8hVO^|Pp-SJ|7IjHpL1zQU$H$ltS zx?4e{bt@=UAeEb-h{IKG8bH&0^ImZ3hn1TUb3jQFUT%UCS9dQ+0vsqFovon7aiGEk z609H|wA=(OpM$1Kv~m-aR6M%3g4B9+PX%Si7so&z>1+i#;P$oIt-LCJi5V-^ssIP`43)hf*kJA2^Q<_1!pg?%Ms-!XzCAAZi1b}3^N7f z6wGoH6u{Q4pnQi?Zi0lBTS0{kwA=)hN#Jr5B-0JfoZxa36xpzH6XZ@0Xt|lf!azc~ z>AS+CGjxGRXXy-&&e{nsovuB{T{}Q+?&GeYoXFtO?YqJQUOrZUv$^jIMB!Kh%CxN@ z5?nZf?o9XS4qXAwp&4NHU@oXwOn`F1Wnv7NGZmzyyA{NNm5HDMSJ1tUV3Uz0Jit0a zSAYvd7YJv8NB35c5zx{QG>zHa3L>q+j)IkjAQ5e_NN4L6*Js>$yaptmL5*FmQ$LdZ62P2RN&Cm)_|PedE!2{6$wWsN{0p;L#1jwGTiAR_G4p&@+&ts`dh? zK=R!IlZNm>Ws~oYm-E1$-{aBk`vY7g)qa4O`@$4tXt(bJaPv(Eq^cD}TDOAY0;w1R z1sX;%v>(!ZllTv*dYfxo7+_@(#1K$S^EV}dz0?Yd((Yc61lVI9ovjMs_y)&>NB2|^ z4_XF+;>M#pbOEdYk_MYl3rZLuRUX};GhTRuT;ACVvi#))uzV{hEn>4_FG$l1uVhfs zQ@fzE^$e)ou?Ahm*9&5T${kQo*rVHZ0kqHoX@C?up))+Xw}MisPj@Rg6@yD0(5W;Y zouHN)i1p%s(%=7Joly6H5}!vm*lixxVE^&AxkKCta<4}xSggAjoP@za0xxkwCxD9^ z*99KTQ2nkmFbf(`C|b9Ik~B&|0}@tl1!Y)hK?BO%;DQDu(+y7g;DQDeoi7E!Wk1*{ z(1OMTvenw7@d&7&9eWt-mO zfI{Y_2RJAY;|blqS6sSXFT4PW8=gcR8F0M;8fkFp_Px;Qd&Z;l;BgiekQp!jq=0k7 z52T@jH1zw#J(}Mbcy!i+^R!22=!;I@CoY|?4?vj??99&48_*1S1C${zcKTic$#r`v zboyTCcD-QjdWF9kbhElE*i|6AZh#ze%7gI&WEkXyOSkJ2kIrK+e6#-kXLh~N>3ZSi zVNj}WK4Jh0KGgfoF~SdYMf&lk6(A3RgCz9B@g~rHs2~B5%Z@iK05RcmbC7t=RTT{U ztzw|TfyUYg3@i)`{Ox>Tm98I-H-J=vde0u+t{*&_D-sy^rygwh_#fOGyXcX8$OGK} zk_V~pwOzY{fx)BKbjJz?hFyAKA>LCf7#Lo#v_c0wj=SCg+1yxr2Xx>Yf4dUs*f-ZZ zpw(xgpcqd$&e8=^(CvB$bdH+qotGE?|Njrw)eTl??fZbg8Duzw+a3A>wqy-rLN`kn zNGnSh!VGXgcDsH!?ob0V9ORYb4i%v2hcF93Lt0>FLjwZ?gDlA9VC5wYObiTA+u?5Z zLw9pGOV_~{EX@ZPyG!qMegp+KXx&^V*aZlmb+dGNbo+i_cC6`itUTyUO434#d@XyQVS zm0BT(9x)N%h(R|#0xe=dsvr?l(dk%$2;~*%ptZ z3rg!CneNg%&BvJe*B^r@Dd}`9LAan5l>fV3KXiMP9DK;w=}`g=&*s`YGW_i!XwJ#N z>YNNT=YUj!?P>(2^iD8|uyGM+`710ELF2KYLb2PU095&(eWx$`#c90oe_a-3OPQ0?u_{jR#+_f=o9&>CqW_ zq4ShScj<-iWgwe;x?O)bc6-zue8>o4R@8uL=8BR|*E5|b3{Q5R`u@G!_YC7fuy|+3 z43Med0brEz-{v<8pz-d`(l?zSn%^;Zy1wWveZt?O4H6m-s6>A;$LsHZa2Y59vZ}lG!waqGzyA+D-~g%Mhlz=S#9n~Jx?TTx zG#^ojJKykItta&2Iv_T?If+f(^lU`rdKrbiLuz>3gBm^-Q2@uuod%z=Eq{D;p(Emq{J0Pu59jq4ASHP{d2%@$d z93TO)htnp2mPvqCqkz)kx&QzFgD@<7K!MX)`UW&T0S%jO-!}&zGJ?umX0Mt~ubSho zS3qgE+x3dI>l;`daRpogKfqE)FoCni6;L{Kz4G$d|NsAwxn34-u6<+A-zEXdT(xgr z$iMpYzvCci-0^^Lr|XHA!l1zJWu4Itnp}SaUE}6^qTBUE$H5NYOZ?khFLfUB=q~*L zUVzIo4P-dWG>{XaS;Yxf!W@5b_$A2f4&RGl=_XKO04WDE__w)Ugd3F64KwJN>jjnO z+8gyS2i|y52Q~!kK$T9{BQOV=gB^GS=D;J}u17!)JkP()^*r3%0Jyne7dRDwtamB^ zaljpzZct_f3m$(V_7axCLCFS`JE0yr{^Iuwm=s3de*l-lmiG_AWwGS_{~n#8J34=P zbeHZx&ifTL2cLuUzH7G!IPbeMdz5s#Zt47C_`ma)$Y#Q@HbhL(tqoXop94=njwW(hg9g^g^d= z3rG&u`)u&&uHDgH+Rz=k;zdR3-~Zs=tw(1osHN}G?RvnYy8$xA0qzEPbVDWff;t7= zBJG`1b0F0dNFk&L3+^_7w02JbC6eY>jGa?K9k*^U+1YypG>dYvdn$;&{0J-^0W#w_ zxIY96PSC(2v=0a}$D`Xp0MwxXDe87T&>bMrITh4Z0rf6GJxnmC^STG)4`@foquaHk z8$}efK)HJ=$QtX=7XBts?*TMF1RAq^2`ZesU0bwULDHSAAhB*R37)6i1u8{5TS3c9 zJ-SN|bi2vG`k7n7-9y(FWw6GB4;VWSbRPFO_?!upKsvXAJ7(Q@6*V7Y@#x$N8rSP~ zZD~Hh(b>uaYHff=Ne1=?0>teO(C7?(0%Cb|J8*b(y50b>y8~1@L$82Z zy`WiG56u&xa>4aSCrJ1LIL((nc`=_2G)wAw0$iqE;BU4Dm5Z)dKsvfZuXJAXVEou! z`oyF2d}r&PfB*l#$OozK_I=Sk6~yfnY3vTY(tL=qGxW;Kosd{O0qWm@2MHjDlVKU8Jyj?PxlVBd>Q&>HAo5EE&HV24NZ0cMZxUXU)2=7Y>0-Ju&iy0?Nx zEPT3KLFv{5JVF2(AcTw%fLJf?27yNim_bu^|Nj36SKy#&I8Ybd8tha4wl;8JfL-p< z2^Q=2ZD~HptPOT`H+TpeG?`aAqm$9yq56tjr+zSc{1_1^J z{x(n};H4%DXuW~!4i9FCRo1Q>paTs2EYSWQ*m+>08&vxALJF6upfaSBrOWU@$H8vb zHJyi=5B={9UDN!7p`&*_D3V%4LFud&RKi$;2Y2|VAB0GCwSp9^V1!!K1DeI}1{XZt zy&&bvy`antYA&DbJmqol5t9ew1&_{F&{)0)blk!gSi!~ zz7tHnTm?1;Jlu)RAqev!yX!odr-F5Nmu~Rr?!`!j;0=eJ*F3tXf>;RFp23L3J z7J=%im!Odvk8ZHB9-XZqAtCSpzZ6qO&&zT-$>z zwdw2ywU<1)T~~NmgU9aqnGiu)FT?=mbxozT}4R4?y{_dDTux5#9}^x_d#|VI>Y|q#W!PP%~NcLg&Fw za4`ibteOupYJ;8L*$W!j?go?AzNi+1+KI>(gZLhhh9kTg3LXoGXop!0s?i~~!2K9{ zqm!qp+qb9rC8IXjs^*`}4*ZjkIW#-vSyx z_2}LUGWEqyFYtuK3XkUFjL?SIGSGE=y`V-IsL27jUIJ{GYY)8U1IK7*E2voktNB0` zvj;@TqZ=#(Eoi_&2C9ibg-mxZ$Xdv>1|$V^gIm2m-L4JbN@*)(wi7h10b;$-@cR4T z@Fa9%13V=HN(~_6I$c+Ibl08$8{Zwez(W%>3kIK_X@1A-(YY0}rr?+lq$+-~+UxKC zm)cAW4Bf4uh_!Z|!{4L{P6vBIk^5q`CnPjF!Am(_%mH0t*9)50wmit+0?POh%X}Aj zXrAym_=Cd((#Rw_TY%5wwfrLD|Tfr2hScD`uAC<+Lkp$$dT^2gwOPPs`W}GxBte}VkLH6+kirVw*U16(b)3UM>CE>= z_f!zKQ=}1WTJwQ_pbECm(8f#K^zxRQFbph;`{4{^<~rPl_MGrH~`ooqIug zUl_ZA!lQOYCwS$_3l%reGB(g66i^8y0qWYdf=$6JfxxR3KsB{TH)OB@y37c&puwZF z72HPx1qY<`0a=HdKR^l)`2(`z0aW^cST9z&{soQ2xOAR_mqDPOj7vATVsh#1y#tDq zZg9cx0;#QBx*>wFiVRdjg)V@0l{}hXFne|$c%cOLAt+uvx?y!$CwSS%3-u6iSloav zmEmcJX+=wbFWF#yIB+ZY<+UIG|9c!i_#d1%Ksf{gCK)N^(jt8vT?Jiy5q4~+9 z6WVYGwF;^vJV2t|V4)Y2GeOlXxP#)+9eTop*-OHs6NDj)b{da>8X@4>MfCaE#y1;4 zV+$m7P*#F2DDi-_ zV?Z59kQ=+bEIPqW2MEUo(jxchcAep24Q{0HHyiVU0=RZYcj*yOJH`;yH%DoEfQl_} zI|j50qI)Wcw4MrbExakS0<`oLln9_rnGH}rsJHs>|NmVapqdT3)&w%|-rWl_2IR>P z@b(7C30Gch3LDkqTrAU0uXKY+EOtAf`5nXu)gO>T36i;5 zK@`LUnBAZO9Ee38FrR{3Qy^zUBKQQTZ3Xr#xOoL~1V-};BxqgRgHjhlnpdolf(>NN zizr8MW}X3QUfF=kes@Pu;Ra%Y3bz~JZXDRKR!HPRdIR9rE+~#beuS29;PeJrPX=2` z1~1=cfa^y{83(c%TE>C3KnA8jbsETf;E^HF(r-vJ0K|H6*a4}L1uh~%Nfp$%>2#d| z8tjF})dWZ*YXWFFIVhAN)h1|3wnrzZkp*JCaC7|o|K(dYP$LVJbgW%__}hd*MMC#p zP_(}&3xJUYP@qf0k<5W+b#0D>xNF(g$c4fd{l%1!BKY zvH$!3EaUUWkIO%j*U01OVljOcn+PNYMsO zqur%fKugo!`+!R@@L(5gjV!b$2%0s3%pG7UK|#A0kV`NSA5`UnR*pjkVLiIRW3kg`Ux;qd03A51Hy_~i=w825?(%HM_-~a!3@&eca*5GkNEO}u+w!8q+f|3_Ns=8W1B)+@=(gy8xfSK6y z!pjNZ_yH$ctnmZ7&j1#}(D(rffjY#H5C--3Ahj@-vI(@60j3)oKOi9w$lU_)t{=E; z0(JI4nFY0=2N{nNKj7V1;6;KU){9$K;P?Uc_dppG8YeE=;C2Dw_yAjIJr%Ty7)yN2 z#}*$TEhzB;Qq|Q8BJsrsNE}k?*%d4cPWhd#6TmdI&DU@Lt73FJ`)`JCGTuJywv2b!iWJkF^leumfp-(P$0|)!G@5orf=q zKo{xtf;J(7swmJw(YZ7Xs4kY9O*}de^2_Afa1}P|?gKR|2=b+IElza}_ zj0kCifLJd+nPK!>L1P6j-QcMKm(Jc4NWT?SwYxxiCobI(L0HG77S!9C0CE$!r3dM^ zW`X?)iX)G1P{#q3UOU0tyk6wH!c$7^3F!Vfp7!ougyJimy`Y6MhAi)R3F-}l z!+_9wt174lV%A%M);xk1a}l%N3N(e&>G}sUB>`F#gLl0Z-t}liuaEHpbzhNoXhRpl zy!ZyX{SmU{1XC7siOlyq|3NG65Kc#(pD{cDT22QFV95N6V_2|9;~NH0R`0BR;L%xn z1GJLRqto|vcr?}?0Ih#^_ILpa-O4IpAUKI)T4k4y>m3257A#c&w(|_rQxr&@@S>>x-Ay!PDHX z7r;d|sHtjs0=^Rd#b?l>&8-j>54t-*c7Vr9Ko){lK(X*QgUecw`#_7kryWpXh^*N*1e9wz=)&<5_IPxcuJB+6%_Y}f@X)T^;L{y?!lyg-fJb-i1CQ?64onOT zKHa4ce7m4vHbQIY6$QBintcitb+c4p8Z&db@L*rlHPzy%c8eGt}E(8MXkeV~DLT<(LY zg1N7D0o;ACd7w_$8O^ml4E!yi`Oo973qb2kpe;z(2ge&g5ef=(kLKDL4E$3du?O0@ z0$P^jpOAPt{1UVr1vI_n(FtBUz~2JOSm5O30U5ON0IysHFKgj%NkN(< z2kUBn!3v$c;BP4hN$v)XsJJcwCo`n|1_vK8F3Cf(k zyuZsB7+(Ag2Gv-u8=&?E?<}cF-C)&<+}KxdAc^#DSy{ z*B`Nm!S*BVcj*j$1DaC@t%iKDfR%xvJM;}`9Z2mDkM4Q_k8YFi9-XBRJi1GNcyyO> zfQs7g&?k+D7#bKD4!#oT4877Fdgb6F0qxKyotL2cI?W-<%|J`YE_z)4=aF3d0JL_l zyY>eEc9C*~GT7!9jQu#htW$GA=@+sk2kgYs45}*xl z9^Hm7;y~G^8??->^oIwtpM*!JA5_j0H1z(%!`e@QzYRJ~{llZ%K>@6@(@_HCh;9## z=7;Q^9vt9hcLyJU7JdEj=r;1`Jm_)xokyn;bfE%r2ocAdrn7-438A_~6m)DFEu?b^Cs3uH;~VH~c-4&w;vC zovxs5SD@2iK&vrO+YjKqU7(mg_@2$9`8bnD=MPZg04>RerH=s6`g7Rge9-wgpj{Lm zoxT@5nrm;!^G`pZ!sx=k?SLa_ytT3R#(#bW2AJRpSH=@AouK`jzMws5XIwfDc`#n| zu=ahy--ePIKY+`iGo1&UUov*Op6K*F!r!6`Dh@%pv)lCq^jMu1BNQ=a&~AVhJCIm7 zq};#Z0ZLlU2N*gIf|^nt94}+R615jRx?Mq%ETF0Qw9bS4+dsajk^KArr7fmBPv^nT zb1%+${{0Ut(zJ@fMOsT91H-OakRnZ^h=JjSnm+>rbkYiBU3cgiP-hsl?isX0?gnUF zj}xS%8^!ybzCWPNCs3ROU<+^femjp&-v^L<^*{vd%R+F#YUeUA z?Aihe*z*Mp3@^_5g6oPGT^yj25>)Zs=-^?4E~y0_$I^TR)Qkr$7e`Cakn@}%;Rnj# zpmUu-XD)$@>e?4C#OAPywSp7BUN;c@UGlSg;x1rNqk zkT?t!=mzb52JIl0fF90-KK}?Sk2_00ypaC#AF{y)vV;&++PHoIhoPbS-~V7+LFG2c zkuT~&BNed4UWonxIDdooV}pu+P@z)$qdS}fRNy@WYvnfg=rjk{4W&Pr!#Fy_z;y$X zen|QI0ha$eeeZZQ*S=ujZvi!X(dt!@&tEQQU|?vjy~D`g3R>L{F2O+I-yQnk1*lc( z0Xpjnaz0P(ix)F*{{L@lzQo`Se+%fuEKo3G(U%O$qab~tGqRd%?=bPV!b*?V|NsAg z2|8NEqu2IYJ~%Pm%VJ>IRmTHj^ETx%FuZ8+1|=rMPK6i2pp=Fv6%9Z+5V}6jqw&oK zux+)_-T&ZO*cl$3;4K9m9?YQ4#i5`TVhg&RK<$kMolY{~dE0Iw$OLp52YAl`NURg0 z0=!WcG&v1vv2;MxnSuKEAW`Np4v)?-4v+@8kqRJQbCm%De+$T`poROOrq@f*;ZGi* zwMN~(3p~0#Ilu#8o#1)ZZt#e%N4M{SPDdS&ZeOq)JvzBOnrkN@+CJcovM-e&16K>W z!Mnu%pX@x*{E{6sa06P130^nJ-ws}W-pk4kR@w{hm%#Q}d31yJk(V}rc6)bE0Bvx2 zp}@(&@NzY{WsD(r1SHoFk^?)XiJK9A6a&Z?p$*Wc+Jf$VAhSHcr_!pkVZE@Mu2x!=oFN;7S*`bhm-`8FhkJdb@NU zybNl6f`q}r*1Z+f_3J$N!sXC^c+K@N2b2#?Po*<3?E1?MV)N!@Gcdf!@c`us*9K5} zfhQzTe0ByXcpP^HClm0hp2j1f`~b=!44nV||5yF@|Gy&=2JLczVQ~Ao@y!EJ?&@ZJ zR|K*Fe72oO=iUdPC9<7cL5GWgmTiD|-K`K_E9l;M&?F8>kQsdLgGXm8=um*;t$Y6b z|1Sf|kKGVepc&#$2(!BtWM=aY@MdpBHn9L5V*&2Eckcxm{KD)i=may6aCa+++z0Bl zdvy1Lbay%ifEJj8ECwAb)!7Tu0hROU-U;fUgI27AmGWvNA9P7859A1xN0eKx{ zFDItgZ9(&|VB31ZZt4cR^MwMqci-9oF&{*|*n1i3Kj@MMkRWJ97AS#zXT}nMTOodg zg`ojB3_+gj?gcsKMU6f<3@adZfT$OdFgwsw%rsCY>fKrbQrkPV08H%#9cczyItk*T zg&{~G=wwJ(a_+{Gd}QW<{>P6)x)G!1I!oqMf6Sgpf_z@O{7T_=hd9J$`J1FSG%A#+G_I$db-VC5Q~|=xfImh z0}F%pD1$?%xu(m4fxi{x5)Y)3st;3Tw@a5r=RptaUQkG2MqCW2I|%Ur$TrZp<&8AhpKv1_dSt(7+RDrv@l)AY+Z7p(bcN!y=@6D#(@>55Izr6$BUP9^Fuimj_h0 zc7slx{o!GG5j2bk3SEd7x?2@MLDYBzR2#+~ej(xr8J`F3cSmiL|M2ME25I0x29vs{ zfg3oXcOXMW;E^Rz2MDw?@ke*K0%TmHv-AY0+3(SO4Aj**;Bnj)y7%4ngom~30seMS zuh^s86*M|`0o+OiFUTteZNz=k8M?)#^U#YLE$}$rgYM8Ro#1&@&|Q0F7#b`XWev18v>?0QK7jP;&${FznIo zx&yr5s67{)6I^$I#w%fd+tFQm#--c!O=sv5klz+)!l(0mFLakKfz0~CbsPgN|8W7G zNm050G|yY82@8q^kf4Ct^spb$_!;Po0C4-N`AvXF=MRrwUjfG*8Vn2!uAmw`bO&fCvJ<>>8#L+jp*!>i z=pe3cLl4ky>Fz?%*w_J&?m_{N&e9XzffAjdGq-#{cy#(+=??wT8G6U3^O{fRDbTRQ zi;Z@mhMVgK4;=9iIxiWN=stkX1A>lEeDDAtP6O(zfCr{PhxmX8Si4;xcyxlMZ(J{c zx8F9`-eBNw0TngyaT8Ft7JMl3jTd)pK|`CMMC*D3(m$Sn1i=1pd;@Omg4XAP`{5#mOprOSAdQ#y5Z67VBrBivj}vK4|r=l1AhxN4Sn$Fwg9`h zdjn|P9K!Vd-~+i?&I5W`RHyHO?g^l+`kmkfz6ZK}PrR6S2UHV*yjFYRMWQOS3;&j=!hXua~_mdz+(qsE!~!#$2t$aSU3f2 zIOrrYgbNEGUhEFg06Vk!KmkNcCulaxr`tinv-6>Er=x;Tw}XUlr=x^Vw}XIhr=x&R zHv?$>)Wg$F1AUd%iTwqV9f z*bztI=*6PY0OFDv;AIM+QSDAg2aqYp9U@@d5N1v+cL zbk}}qKF-ux`=q<}2`G)f=wV`D0QH&~zusqDOND2b}lO@Kf_I=-2>U+c!|R z+JTXQq4T&$H>g7_=+P;n;?W(*;Q=~q!J|7+0K^b@A)*2fq!*BZ9C%R#N{BBvf(!wL zKqu&69?)@Q7d*PHJUVxRwlR2gS8;fB?o7;lBY#qwkC3ji3`FK!$?EJ&rdDfY@+JP(2JP-@9Ew+ZLbi{{0`UtdkK`KD%@t z@W}iD>W)Hk6^BPBkBUdP2Zu+ej|zuJw}$|TA@IUM8I(+XUwCvna&*^z!LOJZ>;bT1 z0fLIBAryo1-wRNzIPy8}05zxRLhcX>^W z@qGsmczQngoDrO)KXliAXnxPw`M>k0$H9jz9?b_0JV5PO9H9>?U0>`|#GR@jhbMdh z#cL-xaU6F99lQfNMiZ389)Kr;3OPKwi#R+w3k5v7iv&D63qf-mFTkTgh_mdm%nu>C zAK_+ZkLH6K1l`Pq$IZ~a>@UEh?%jeOodFz>yc8e+$w~nd-Jngkt}i+bKyiHBfd!PT zJ-Qh|=bLzRJ92n*x`2|XqX38@@ZxU=Jc%lRGx@E*eZbh2R2CJ#Rs@SI}8{Z7+!4X z21$Zu4{JYozzux>jv>&%9*(jK)Mj|`ME=izc-luK1;~6c=sc@l)}H|k43Jdr(T(mt z&`ByTDgrNTK{te8Ie!Xt9@dZJu3tc5)(tMpepokx$}ax)&ycYB;n7_o(CzyLbPRat z50A_P;PQysiNmA2z`&zBz~aS6e$cT<*c6DC1%TQ#0R|r3qQwv1HXy(NV>@4sm2=rv;39UXFW&ix|hKIp2P~#hv(7JtpfESR1 zl0L3%?I!!@KV*&=+&+cOm!PL7M*e11aP9a7+zjq{u|re1;U8&&a@R zPUDw{&0j$5|KNe8JZOTXW>Dklg}~O||20Y8gF!19z`8uT8$fy9qq7z~11j1H%KEi8KxTrQ z?cj?^4B-8#AK=a(J7~}za+(l}NAqz|q<`=@?)m_fE04Q^oWalyI#by9hczSYgtIr$ zz(kI0FAfi8&@`#3gQT6m&}S1&_|-KAksTI3E1-zq|BI z=hiErEmF=C|NP$v%Ho|L!3PAn8lH6No(ht3v^?a<@B6W{bWXSH0&Ukhod-G(d2~XK zeCfOh8ZHB^hPeC)bY25^O%oIJRNfPC@eWX1&+uF4Cy(!?9i7LTk1=+Zwse=afLim` z8$fMR{$@~*tJ}4uvlX;84f1ok52GK1D&5dy1_>pd3^uq0XsDeaz>cy|No2(9=4#-F8*dvG=f%bgQA`lBm~`d z16|+-(Sd&A0C=7a)b{U$Y@q|KT7c{c_W*UJyK6yb`hZUs;A;S7EAW-dpuIOu5I;6_ zg0FRW2_61<;L#0TT-XZQ9P{F4Jy;Wn?a>L{yZ-}pcBDu5R?y}PkMG|==R$xkJm>}+ z)CoR=+ym=DWyf9jfFiZIwtvkQ;Lhw>Na084Pd`brVoCk;mWRpJRyb9}H(A*Y(GvwA2 z$YIj3GYi0{GeHl8fu0cq@_mJXNB36HKGYXyL6bgE)zEt(KxI&Og~W?bkl6;%=FaB5 zpndlY{4LN_2|6+Xw16L+N)17Ggmi+p34q6kJRr;Rp+{SJK#oIW;BNs3K=WSEK1>Gw zR?y05NFYK>ip-PX%9`1W!{fN4!&mSYYmluN(#IXayou)^_y0e5 zz^U_)N3ZV!!vmeieIR?YeVD;}S3I=AcR+YD{(xu)$@_lr$ovno+mpGH!>7|-!lN6k z2%^HV^MFsc?+0Hz z9)UWChg`bnf^2Zy%L~GRB$WF04wR{#Hr#u=mH*4`966KsB-~c|LVcq3t1uzp`hobF!zF%0(*2q zD99mO9^K&e%O0AipeN`+>}fv0*aow6?!1tG(5kem!ZYzlO^gGV#?cs&OGR`85lH#ihLm?7s;S-U>q zZv(HyISvje(8|{4y`X~t8TecFfQn?eZf6h=a=-$#@B{4x?w$%#3|@Eo!6Wk9-85*dRRE`Np?##Ypt(G7d|||mN4KcI zGsp^1W)t;+FfE|RY{3rMgYJw5n*dHFAeX=V1=mfLXJZNuXB9i0bcvffi_eI>jzI20JQ;XAAnN< zUX`=?LG>kQCZKi$1Ai;Dp$Kl0dw>_fgQj4>1N;ZTlQR(a9DoinNA7w0R% zHot(KV2$vP0qDLH@Ug+5Q4%COp~KjqQ~?TWke#5(AP^gLKHVwM@t^_Fq*D6AqxqPE z$3ak10j=Qv;L*tja;QhA8^=p6aPk7pa)5>zLF@W)#qCZ$SeKAr9@c&b#TRIZ4_nI} zbF>6Hpz6}?A>q>Lqaxwa>AK^^q#lsxkH4tm0T)w_{8JA(HhlUI!H`-GwA>MVFb-(I z(WBRv*9)}R$W+OTfngVD7T=?n_qQ_x!;8<#&;v5T`=&ul2|IkZ!&ejDVBnv6pu=^0 zm+N+u|0e&z+jTsWFM5DSyP+m;_5_=J!jpkvmnFDy!g~p1@=Tb?ph#`r0czVb^0z`4 z27^YlBs{t+6wq6upoHtw?V;e=`N+4^OTnkxL&CSyOTwqyL%_GwOTed_!>2of!?!bv z!?%;`#mf_*fN=fs5>z)p>;Vm5Fz~lRw%gWzc(DxJX0843V(wees)5=M$J;?lKrLlh z`w2AP(0nihbi%Vo=lK^7%Ai3K(1?8~s8`1U8gK>$41WvcEJ#o;hm4|SRr~>+CI;%D zcY|h&LqB*l9)b+eg|6s4<q`Ltm;?e2a@KP7H_6}}-^8rwI6VxFE?GXc;4LJ-D zY&>ZBW5Z4c1_1`}LH!>*I^7@>bJhVG{B59WX9rmFphqWo`Q%H`sml;c9WSeuglO+}@Blm7+ED{?SR_=B2dJ+4|NlR_IpCfUw3CEtj)up<7ffKYIvqh{)SyfN zF`F4=m+uD;W{@~2{2f7qlAry#d}`5DMBO`l8$S0q79zPS6NLDRj4J=?PHl&t)&DQmH+Gl*_@( zu0i!1ICu7fj;sRRpIo}7+x0^y=uR$BL!|VCPq!%44>}U z1<=wJG;Ms)qqB5^Pv<$%f@>d8iRL@Oqtmy+r}N|sU64l*4K&EIR4nsBi2W+vr5~F2 zLF)m?Hd9bh3L2k))B`WL4nT`@&`dAbE1>cTwC$|tHa(H$c8iEFkITvXm67R*b&-Z383SYp~GsR zE2xkZf)?r^%5(<{aG|H|m;h?MfJzJ4d?Pm<&Ti29n%OJCqZ8Vl?)FUZu=WM-iRN!ggp?m2 zy20fM$n)J3pk>Sl<_VwyZOe=N?Vy=a!%LvnFi3?*cY(%>sUVHuo>+V9Zx3fi{TS^5Hex_<487meBAj0D>31MUlgR(-rU13K^k z{Baj0tBwl>~46RDQ1F)c?7UXeQ(YnW@x%L2Ja|x)(@deMD>;V_JACL-M=vWmf zrn?(K=79QyUi#-7iBS34Ty`WQ@Trmv;Cx-5#0FO@4EObzWM`s`` z^#ydh?g3p8Q2GHj=>;C?@dD3IhdFqF4jP?& z+@az7e}{(Wj1K(Uj-xlKe}a}~^_rfwV_?{|6w>r%vS(m;F;4>8sD`#MLDn7wXH{4Q z<;UUCe6Rp4)O;)hR7akY0H^8?%{xJX$H3nLDj}g7Ls0^{5mc>0>)j8{J0Qwi5visD z)^-E;mcYemr|%4C`gEPq?YjrsKtdiVYeFoyY5=u(ai{{1<@xS`b_O9Mg&yFU*U%Fl zouFCO&V%q$K`1W?P(0i1}e9X(#4hP&&)+47hVs1E`ew(?{y;PB`!lK=^MG8Z^_ zFqgjY&<@?;!FUalyMq#5G>L-KJm`?{4<4Of4&Zh_Xiq*QOn6TjfCiz!bJWKnOh~&H zXWJE2`GIEg~s|bATFK?$beI zsjkp!1KfB7WjIjP3Ni`QLWS4bpw%Gg?FmqOwz>AlbMO{i{%r@qg*VcCC;V^_a1{?O zze_)KPlMDfI!q(G3jzC*q!U4PMqTBTWM)3@34}%tZLJ}{y#(}MF zImgVv0KRt$bYCI^Xhs2i2sp?b(8^qtk;u;TFXTl)TMDNz@VA0y5k0#1g1Tcb-u{N} zU4S$$YCpWt{sIc#+7B<3?tvI!WuO+-%Q$eK92D*q5}@msL4BHYZ~y-XwRs`ys9`0G z8;1vEXcrQr;0WYzISs0kK}n4jR1|`4q6ancnIWAZ$R#`A)4$KXcySdJeZC)>cY=}z z1LPJR{+3p#0g!G6=p2)7*ALL1Ml0w529R|g5Ys!4zgYC<_kYkloC(MpXxEMzJYm?q z6)e_y{DmKAdI@r5320BV2V^D^>LXCHg050==kVyRZ~*OA@aU{?fTTo_SGvJ7RiNWL zJi5W>dA!g;gb1htfa-VR@Hh?~&h~wPr5qNWstd|Apw&^LlORk1s4GxRIqnbx$thre zcxZ#CT3>+oo`EVsZ7%_E^A57Z5upZh3M*{m;|s{bB;Oa{<{?B5dO|C-1o7yEOyhwY z?~ubHn4JV*lMT$Eopj*oIdFV|FCqdr^T2nszlae8l@E|J-a(FnH})Vx%)Skv&IovO zr8W2v8R!Y-{H+zx2nMYbg5A9gxg+4k)z|QZh7uqi-J(%C;Gi?`=oSr!FfFhL9k_-8 zr4e`n>V)(uK^Y9RArzzyOU>fp0Zk#GDH8C_6EFc#_=6gM;AsJHTMSYJz>_8XzFAPL zy@cBHp}PZ=u0by8>;QGg(aI&zSPZB)0qRvi#vdW$vEaf6RC43*kL`l>20)`Hux4@T zix=Sie28uo_zFSzc?_WS8({OHg_%cpSpujD=%HQi-~l=S39{xKJPZsjLc#oY(4u>A z(bSR;jl1R@Aipv2x5hyQ@HgEpz^cIQ?;G8rPhQ*wl|JAOM(u?c1;3zMx54EectYRx z$4ii%;2gLM6sTZFc9*_*kqm09BD{yPADN`^(}IN`Xu~+F9p^!_O$a*_5bK#i>-oXq z2kN|n_IHEt01Yu(hjH*XTOxc4UatA#(noMh0kkFM!i(0Q*t|Lqwul9?ZovW6 zoPOY8?a0C33^`2+w3?iu+x1TCflk*m{4J3%_1&&#K#MP2PoOM>IRieZ;0S*UI0%s) z^kOP#1_+dtwUM0pVn5WsFTri2McQ5!q@)jF8%TXbnV?s(6AGzBMw?F*jf9c^Y{zUvUkwJsm^0BKnuMfvjkY`XZ~%h zr5X$jpgaP}ADy)zyFeZO?%E%n=IG-?kVTCLpG!18XJBCX-|hP6-~$d-28RC(oX0_& z7aXb#{~2H$302VMR?g#~Lp&xx)<;6ddycz;GAje9anM=%p}F=46Mrx0;53h3)AvT8 zGLO~W97I`cV0h8a1HR-B6keUK9WPVCTOAo3Aj`@@y&4XWPS8cwpcW)({haTE7Zu>$ z_NE6xMu9I`t9|jp3M2-}4liC9f+}E8R}j>LJ@`Tla!N0#SL%Aee3%~-2%?Z$DtdQn`=+}=Wm4^BDDqN@RzEf-VtOF9lUs> zx%L4Ae+wvEc!0A%?D&EU4E(L&>6KnnHjwMPS?$bV0a3>d3J9=yo#4&&FV90-;5$IA zoze%L=U;e2{I$ZPxpoBustXo_)b`ebR!SW6=oNhr+8o*KyW&L|H?&6&wf2A^$UfHN zrXUIw{_M!sf?7H+L5ngxdTVEZM>=~&LqJ`6kWt!bM$Lg3RRcF_4;PXTO8)=<|B@Fr zOYYHYIuoMY7p}aQi-7@rGF|7noeZEDKLt5F%lASjc=64PA}&yU8`!-;t(gq2`%8F9!!XV8c@BaTo5ag z;Z`i>WMF_^uhCq40X`v6d*MYnSO#)JR3sp|g?Uq}hrxPM}+at!~3_N;G)Ad0C52|Zw zKfHLr0pftB0&8C|@V9~|PNE)_P-DI@%9X1}*DAivcGwFff2p&@RX}$J!S! z)^k7u3~GcD#0VR>5j7}A+<55^3F;RwszDOqJyo?YUbvtc!wWIyD>y%aqTUpw8t#%S zkWv^l)`zH%3=s8Ezz)!OKH02u(X4xEi)KE4kMly25uxIJexmcl*cljH z4G*}0H^DscXg(MLI@Q+o$cyEu5{(Bz9R#r4ffp;$<-qq7cb@Nb-SZ-!oq@p-Jb48= zp$AlLbo)MV>2&09>301BI{h~LV+3fT^)n=EKk(>m1hqv#b?0#hP`L)0D|v9-0klXA z%=CaaR-keBUk?=TtPS9l4hr-GAfw=cvE?PWQPXQG2@bT{7cXXk0gL3 zmT+S#LCWC9Y=AYyp@$JcR+6GdC1@K8@_OV1k6zQ05Mv*KQU++~up8t;Igru4rbobX zw#PtnplKX%x3QpO5HFf51Q_^{3dEhDW)Z0Shsb)Ua8aYTy4i|H(&;09fEb)&gL2Q^Ya)3TsQad;64R&@k9QXF0+fo%jw3aI^f!=oF# zDd>hrw;*UGDX8=4)9rf0wcA0V)AhiM)w98)(BQaxaqcxV;*3FsRqblUcL-okJAAx`=nUIV-z6T+ zwGD_t`UwpDt>6PSAfnKkt>J|?$in8@2@L!#P_YRw%$Y#>`46~>mIaCz*n+g$2H3)k z7uP`zr|w$N&32&H&x>%do;4snpgruJt)Pa?3uTb$-L(gxEfeq(lWyM+F5Qkij?D)c zT{<0kV2$2ZP-~;x^^Zs65m4BJ+TIxLeNb!OqxrbRi@Tr=l%QQewLd(Xk4L;%59WUW z%Qqeeg~W?pU?Irakd4PdLG_{&Ec61bs(U{;Q|p0+o`8g2K$b#)=5qIgBJu@j{X@6w z7jD-N+^%o9U0-m!K0)*t(D!$N)|;X4CjoV%7JN_n4hX1M`z~m<|`2|dV z1Cu|%Pp_<@76XH0@PAcBEe3`Opm8~dm(TwH|DOSx?09(x!~&JfFE4>u zppye%o&d2xyZ*sD_A@~HXI_F1Psjit=DG?b3mW@-xd6nP2mU`D+(m`5yS#5vdsXcvzKllF@8|99<<6hLk`3; z0*P6JSfG{Y8KEGS97qgw2K-AA5UU#`#sy+60#Ku@UnJ`M&3 zO$G)A9VQ0Q;i(MB;`$5>45y&tT~PI)GzYqPub3I4elApe0Ve|kC{h}s;;&g57(lzi z?6?>hTo@P_=0U}M*&yPpxFGaqsQ5uP1_sc?hCDBX4rYOvugt-~0E*&e{0s~s3=9l2 zq2k`03=E*-xU0EUKgm5u1fC{}H5eU5=DqhCP zz)-`$z~CnUp)W!Go6OF@07^0w*cli=jdxI(!@?RwgZQ9rw4imFAU?4eqz|MP7Iz>s zKztZR7bn$xm^m%#iH(6lk&S@?bjlPgG#D6IA>je?KMTa~{7`>_!iAj$ z5)L5$gWSi;%D}+E!oa}C%D^DP3i77_14s?XUxLuEVPO>jtKo%)86&Fz0~-q@OhI8O z0}3-}*nz?j#E;9+B6;AdlC5MpCs z5M^UvkYIzvEvSb83Nujnsjx9HsIf6HfL26ku`w{{uqiMwvKTNhvIv09Wr4;IJ2-wA z7`WLWX-JTbfkA|gfkB*&fk6tIUQE~+7>wB%7>w8$7!27M7!24L81&f~81z7$K}3L1 z$O0vHWDLqzAPhR}0E9vL4rC!{JQS42K-DW~?;dDu1JvgO4IP8p@t}2s;Eo@K?x9pW zvR^=KkY7L;YTJMT zG-d@Ui$PT<=m0#>r3Vb4zy}2|NFiupBB&5&U}PZI4WQv-kUKz=7$CQRMxa6N0Znaz z+yvSf2s+pSG?Wc;AE=WGT6_Uo-wbjBXuuLQrwQ8c0UAsO4b+0_M^GaLR3CymG@x}u zps5?sO&Or$Q9;WrLC0D!GchnQvoJ6)b1*P43otM+D=;uHJ1{UXH!v_TgFTI!3P9lv z!k};lr8kgUKy_#>0|Ucf5N2dx0PVv2%g(^?m!E;*uRH_8Uwa0Izx50ZfB%CpBg5bS z%nX13vorkt&(HApzdXa=|Mm=j|JO78{r?|?8UOzO&;0lQe|FIQN~pF&+6A@dJt*P<(*mU^Kl9zw`o1D=-X7J0P5G!OpOXwNTbL-7Cq%nbkkvorkv&(HAxzdXbL|Mm?3|JQ?> z+#t;O|Nnnb*2R)LK`z9^AU|M=GB7YOGBLBTvaxe;a&hzU^6?7@2nq@b2@8veh=_=a zii(OsfjAUMpaBg1f_fs;a7LP@oP48fZWh z4QLSn=zhZKPpCpr-UY=H2!qsu;s{iXgV><5mzRNo0aSm2_~>$=xP++%)h{qMs4M`n zL1j2dAE;ae@j+z_NDd?pvKOBmNG(hsDD8mCe~>!|WgSQkWFANkq!y+RSq@|` zvK&YaNFPWKJ~@yYkU1drAU}X;P&o(E1ETTCfz*O9vObV`AblWl5XL443QJsaAaM`| z=>w?))rTPSKo}$r!r0_s?gW+3pm;>r2a*Ss*)TbnJjf1ExPj_@kQ}Jq1!0gph!2tj zRrjDe22>w|Fw72+94JGAvrWD2-y11Brvu6S5r0O)z~RIZ&9u%tV(1@nLcxbs&8pJs=F? zgD}V~AR3n(NI$Y1h!0W^(g$KA%YpojE(epxCkKinbbX*S4{|56J`fvZ9t?x@fG{Ww zL1w_jK{QAlx0?C2W3(QQI94t+MnvLjkp!PgSA1E!M z%YoYYU^!@7LbeZ7rs0wUr5R*7P5+(;?gUkf!1EoWdJ3wIy(g$LLIa3$d4fXAT~@K zM1%MsIZ#-D*dQ^OnIJw$4weS7$-&YfHaS>2LzV-%0c0jNIat~S$$``%>qAbv=yK?3 z7gUyk(jN$e(h|rYpfCZkL2@t*;)BFN7{rE&gJ=*R6xM{~Kxq)99)>}BKp3PCmj=Z*-ThjhfN zCI|8}Ob*0GHWNL4BFmwtPgwc^nE_&BRLXP~0J#AfgVHSsgZv7@pfm)+ptJ@GACS92 z{ss99l)pfE2~^I){0xeJYI*_LZ6G$tZ6FMC8wi8k21-N3x{ZZ_nSlu_WMlwAOgWHx zkPHNa7Uh93%#RRJ2no>(VdEk}dLbBM0wV*ui*V^AT^wQ$jD*++B0=E@3Oi7kf$|Hj z`imgDNW~zxMFoer#HXYt=jRp_r4|Fep~=VT`7Bc$}Jic3ZpO;JcKD#|ZX zNX}15W$?>~@={VuQj<$kQy5&qk{ao$d8tL2$smo{dHLme3TZ`&xv2^%sl~}fnFS^J zMGRr7Ma7xWq%`d7{NXsu$ z0EJabW?njjGc+_ojzpNAS&ZgloXYY`OBC|c6q52w^HLa`^YcnlD@qiKGxAGwQWTO> z6*BWOOBB*Fi;7Da9CLE=lM_oo7D2++3T#JeUVdph%xw(*1*t_4HJ}hnEG@~%FUqV+ zO;JcJQAjQ=DoV{OQ79b2tAkoaccI3Gx)kOJJ`sAc^N@7K7c) z;GCET3e4o9)Wnih1tb+|Mftg)&@9VLNlj5GPRuRHNiBw^w9LGe)C!Qzvp(r&Gl$!L5Q&Y1+smf5#SkHg~oU=eFAQ+tQoSjQ5kg_W* zA44)LjHj2(prHZEneio>1tmoaY6`}h3bwWiP}4wW#22TQ1i*3*LvnISg|1z0NpeYr zf}*X0U#O1{#Nx!1tkU8VXRylR_{_YL)S|q^9Ecn^8lh4e$pMK)nzpu38$j-LhLkM` zO^Oh&6od0Uq{Kim0-SS-^*|;i<`!#eGAL?jq+}+SXey|wDQF~vnVL|mp&2e7T6lmx zplAzrcR^8VT4seUn4?frqX6+6SUW@zW<)_HI3VDj(g-y+($Y*RE&&Pa+9hWsX6A)} zQYa|s&?GYwi!&f{+6oz0CT`bfWxOKwKx^*0z|-Rl;vlpXlYg@ z>)K_Mra43GwY60M36$p-Wd|1|CZ{rJBxfWRK~&|ZrGd&^gi0higbCUTaCST>u^^iO zPV_mcdFdq?SWQ8-L0bWZk6~I)W(gj%FzwQY6cosMFw9KND=Eq^s0>an0+lJ?AOMFn zA{5YV)mA_gftU#nb#P%7oLb0GoLU$RD!2kligfLAGD{S|x_rPPpR~# zN=BMsC#5A8mmo{p*eW0-lS)c+N{SL8_QA3}*yQ+>{L-YHRJX+95_s;&PfIIKEr~Y- zc>rAEbKwK>|_@&1+zX!mRhnEJ-a&%qfN# zotF>uBdRz|bx=+!Oakm~h%T6{Q)+r<9wG;z*b|?bSDac@f+&L_T66LfQxIa{S`^8f zsU;ZY5sIPE!U|TNR3#Vd+GXZBm8PY^3q&L_PyNE@ip)9c|6I2frDiA}KCPR!GatN#$YyiGk$G6N?qnGV@Zo7?N{R6Z1+DrXif2Uyzyy z)&^=0=NF}b8rAs<>8YURXHF`({RekL8mOJ3kd~MU3T&wTARED|6hK}o1+`f6^WZuV z_9W-z7pFojB2gzS&=rb7l^(bmo(yR)W3dK{;;j6nVg;z3P>t{~=3*#^$34QW;4pzk zB*=eU48@=_KQ#qCc9Ro}K@H*Lk_xy}67$eodT^^D{jW@<;K8XFWUB(WQc=iu*3sR;Klj5sU;bi>3Is}pf-PbQGQ-J!t+QjNGnQB1*H*Wv$FD&6jV#O z7(f^@4+@$a2aO4U4t)gmJwY^RyZ|)w$jHFRsK~&?n8?V?_yM}!4z%V@XDVoP1G4@O z)Gy{?&}BFP8U$isV0fU!z50m_8^nF$kzIF*~R+F+9*=V*H@X z#PmU*iD7{~6QhDN6T<}=X2uWim>CLWSQsySVqsWd$jX#p#>ybb4Zh(4KdrE{Qk8I*2-(p^w`3Y4A) zrB^`dO;CCdls*QfFF@&AQ2Gg!hIzh09g;3KctdEhjS&C9 z2R6uxlm~(k`hpCEKA;YvFHC^Y4}PN38-79f0sIhi8h%6g3S2B;`wmDz=md2LJz)}r z291Bf+%`&&hQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgRu^}L+q>v!(80?q; zTD--;z#!-lA0J;_l9-$wpPZ2$pI(|+l)?~LBrpLYo|0NrRKPI7A*C`WCBC?%G$}p_ zG(lLz(C`4Zj(`z#yc|Q30@$R)V$d*md{Idr!vuyR4X{{lYHo5tCCGS?m;p>IH@_?u zCTIZ@1P%EoXJmsEI)KFxP6LhFfy6xo9paP0qhugnfS^Ns8hB_9#ESs&QuEV5>;w=S zG!O>jWPmsYMVWaeV7USi7c|HSlB{SqV1(aKyS_0y30C5W-LtXL7`FWs43dJQw`IR8a9T<|J0cViR z0SuYq%yiIX<^+bK6Bu&kiJ4%_E}+POXMaJ4-9Q%3EiFl{h)+p{JK_PREX)-zFr`5A zK_D|fU`mzdK_wwck};lvm4PEZHzPi^A~m_RBsD$*GQkw-5$YDt5DOZLXSmPm7VH|& za755GC@4NXwFEpC7+eAxA7+>(9N_Qo6VGs3BqG9tp+z(|F*A?hng)0fj}sh+R~bR8 z8X!Z143n84Q&ujipy|XSh6Svdkl9;?CSjP#eu=rM3?0Jx1tpoei8sRyMQ%CFnnSJO>q}9Oc91kUXf;CGGh3{Xu@!S znSm+3JiEAnp_7TBkBOP#gD3+t!vkps24=$uhDA&!5ey%h7?!azFdH#!U@~F2&K%Eh zhlPPDo?#~w!y6_Brc%&K2Z%)s7nnc`RT!>^n86ay@DprY2Q$NNW>n+mGlPv=!pzLjpaC~-BQwJ`W@d&D-VDqP2YeZr z85;cIa%Y(tE-^DRTnGTkC4z(*9xyXZWaCN<0kkr(EG$OrR4>hhcD#L#s1{NoVAMEjvkY?D!3l8LqybRZQ8JIw`p$r`y;KE=PFAKwi!;FkD zl?V73SRm!n9*%s5^IVYP{G|kFkqvZp2E%LxkPDA-I5Vt~1-p>p9tX&E47d3pE@QaF z&%hL)m{OLQmzTNj>NWksErLm_UBZ%qvZ0*ab_H`ve)7QY%Ury7=Hr z3BUowutJf6DHt-f8c@eEJ+7+U!mnBbYivNgzX>$VHxu-JY!x~VPUucmMdZSB2XBT56RB!6d9PHioxkIwIsEOVWuD`gCO|l z;Rd5U?+%tu#<@qoPHpIu>(}z?E*<$*v-VizzZ#+{YrC_Qi~Wq2*H{Z--J+_ z6n}(3?GZoc{L;JYU93^PR_3d2DwZ=Ar(C}&H8 zTOAN_hJ})#H5K`3@zAD4s}y>JWx7;eeo<~>PG(gq#Lg{JEDRrRGc&L-G~5A^2f*Zn zyCC)kFnIw?E_e@fq@B94zCl=XIL)Dzyxh?9D=oHx}{hcCVU5}gUZ~2$xM`D zVYq-S^9?5RQ-X!z!*^!J_#%cz5eBB5REA!W#BzqoA}kCa8dw-u7#=i&&b)|cSR}%5 zR0Opa-zfsF@%M|cFkEN`sl6~AOwMLuU|<0+EJ$T|BLWW9Pa=saDGWbEAfd$YPlScx zz&eoP4`6b^dJy}SP!tA~cv-=?y zyMH3vJyRUX?(LX%zZGX;xUc~fKod5C$+IBx!DkS;;0uU+045v0g4hed_JkiG z^1xD7P$Js^ChvjB3!gybgU=xH!&eab;2Y?~4``m5ECFrK&y-+cxbO=s4< zZjtYk0OeqYBQT|>Bv=?W{01p)_yZyz`~evVTEW8bSfauiykdx9g**#G!(Wi}1qk_% zmC>2umn^6u-6Y8{Rg!@ToTBnSr{5-fHXo{if<(uK1;GNe3%6? zal$eXxnMbn+^_;f9#{z?C#(XIA69|2!PjuEkYZQ|%4MrTk_*;?$O~&hN)pQ%j!EUD zGMoX)?gfkQXJcSsiD!5&1t~CEq>UIRNHa{6hGrsYN%2n#?8Yu>7KR50K>8+}0Fej4 zFw=1_rk9)XemZl46Dv(hyTWOB+To{FF9{h-X+KW5n=AhT)S83&VqF zARPbv4g76+3cXUJ42HU!z~$TLmcX~MKWNgos%g_ zEl5o)VYnj0!mwZtJFK}5RdoWQ>H$pEGmxrfAfq3w1(6?ifyfPeKxD%{5V>JLi2QH> zL>@TA&cMLx#LzG6%y3cBH7|wXgi<`iIVFY($_&ilf_9l~PJVJW!xlwwBlW8y3&RA^ zIh^3e^buKbEp$L0+5qcQVPW`imK~|ayCw^2W`W!cYJ7izs6U|$RliJyg<--4nCv7u zuxT^opt5UJSQrjm2D$RVRhXJ>5H)*XY7WV)b5c{%Q&XUA5r%d0EDQ^7gUtW% z21HJH2O=MU$pxQ4cEvNClxNtZjMT_@B9Gk2IHb(Nu;435Wg5dbdC+*rR0RemXRx|Za4x`6VLEZk>QXMQlL#%0{4;_W-75TY&Z&10}i28N|2DcqRhgu;WWsA z2WL1KL3IQ~{G2iiL&G_c_=ag91@A!O7r6lHrUh3&Vr2AoC&N zvRD-y0mo3nV2dilE>#wW55GZ{UHA(k7yJX04V(-t3f zI>TC!@Me&3QEDpKnC0q7ebXZvEDQ~MML{+Cevl%_U;|j)RrS;o-w@}L3PjuawmJ*L zgrgwU2Tp*<2PZ*#;u$`uGpyBMV1~!WR1J76%+>%$3?$$eYOpXYcnH#Y;0=h}@D@Z) zcn=~Ed;pObK8iBJ7lK~^Den>kH5+?Cq70!FYQ z2?+skuQ>!LI0P6N7#IjRI5fyyP*VVd1&kLMH|QSVYS6nN65tSUfKeepA;3W)ApvA6 z*b)bYfCk2d13U@~7y}M49^m-E)WEoaYXTz(1|%qKU`#OBz&3&N0Mi7<1&kBe3>tV8 zCNMb&G=O-F1)y+a1eqEjkdPpt019IVg9RK4AJ{H%9N@jc`ha5rqrn2!21bX11&k9I zAFw5CV0^$0ipve)7zH^Ru2W$FQv+i`g2MwwP>5b&oWR(?sGzWb@q(bh1jc{^%ni&7 zmuglSQHEnFdkrTU`hDEnBXvhaRKK9Mu!Q^3uFThux(%jMXkaI<_(Ms7!4*c zU10ve_<*s2Dd7QQ17pGl7Lf1;W&?*0i~$Q6A+e<}fvJII0^0+|fCi=oj2plA2DFi7hJp#w|_1q&D#a5XR%JOF!j0m}kzg%2DF4;T|B zfc)rifc*pG1?COR511w}gKiyXI1|ReAi==Ga3_a>L4kpVp{9y~!GM8ge83~vM&!RD>FzzEj=p`Hn>-lU5O zY<|cICW!cN2tDT)Gnk+A8$$btv4Yj_v4+qed|1KyR){k&fG*F1OinN`{6^#RNH8#@ zF*7o3{uTwg-2=Qf6LcyrXninfEoP_$1A_`90|ShoBf-D`xv2_bB1nx20|Uba@caa1 zjj10aDj#It0bYpQB&c~XK1@I8ECrB>AU;SxG9RS>0aX7|sD2n9q#k5Hcv23u$Qg7- zCnEy`G9RSBfDdB-cBpgDS-SUZ{Q;AEqDK zy&(IM`5^rV)FAp-LG{D<@?P zhw)+hk;5CLADIu*-(U*SUjo$+&hv`SQ52PQN57IAS z4$(g!svpLO=|>JPkbY!7NPh!V|30XG7$2s80d#!>h!4__%m?XL@PgR?45}Z-2dM{z zKeBs4`jPn{{U4zEnWZ7|597o1BZn7AKQbSrAGZHM8mb@0hv`SQ52PQN57OV@1986| zR6mRl(+@i91XKiq_#pkre2{+7q0t~YAE#p?bLh<*VXNcs(c@)c#Uw`Ufhm8Zyjkb6FaLCmv*ng`>< z+ylC63^_cI`5^rc;Sl`+Q2j7|91iy&>j(J{nGZ5=0@S=xsCh6x%st5MDUg0-K1lxt zsQwnHei$F7A9k({h!4__%m?W|0M$PMsvpJ&sRzXmvU@@Lk@+C~0uhk#mVSHlsH$e5Df$E3xVfsO5I3cG8WIo9L15o`p zp!#8an11B=0=XZV57Pets{aX8Ka3C4PbhyZh=hdy2dI7+AEti-h=G(pk@+C|4Wc3X znPnmQ3&sa&1I0gbc!S)J%m?Xrfa>Rm>WA@R`a$PhA%`C_AEbW)RKGG*Ka3C4kDT5> z_9OE_`aeMRn?m)&_%Qv*;SJJ{%m?Wgh=KUu8LA(~hv`RlFGxQ!AEdtksy`5_AI692 zN45{7ADIu*KLM&g7OEe{hv}byR-PgALHZTqA?`1Q>WA@R`jOoWvLBfb(%%5p-vZSS zV^H9XQ2j7IO#cRu=aJGsG9RShARVIL4XPi;hv_GjekMTm zheGwk_%Qv1@~=P!#QtQcei$F7AGy2+`5&1Na(@C;e<4&qj1SX~+#Uz%N9Kd{8)QQ4 zZ-?rK@!|SG8{?6}?*bAZq@N)dqJIulKa3C4PbmBnp!!!q^~3lu{mA|WxgVJivVQ|q z{|=~r7$2seQ1~0jyEA{NI4Y2iuWA@R`jPVwNIxzk@j1SWfI`0#dB|&_Ueq=sK{{yK0QmB3yAEX}Se$a$F zvVLSfNdJZ^i2M7Y`eA&S{s5%(3$h=X57N(22hqO_svpLO>2E-5PayL_`WqS``gcL~ z!}u`$$m1Iz`;qw|{U4zEFF^If_%Qv*<5M90$b69ghDM0}&!GBYe3*XZ{wYX5G9RSB zpb4U%RS}XNVSJc=WA@R`jOKMNIxfY>jn1WEreK1@Gyc!BIk=7a1%0M%~@)eqyt^eccENa2Uf2kCz>5n_KZR6mRl z(grI3Hh>sN`jPn{{Szib^p`;O!}uU=ApOYpf!vSG2kGB11){$nsvpLO=|^tAg7hQv zLHZv|h3H=a)eqyt^dsjNkbY!7NWZ}}i2hwr{V+bU`WvA7Z$S0K_%Qv*?MIOP$b69f z6Q)D#e+ShMK}<@+^cz6+--GIh@rl*%0M-8)svpLO=||4*Ap4Q|Ao~+$ zL;TOL0!hCxK1@Gyc!TsK^FjJ2K=ms^^~3lu{mAhR(vQps>AwKgZwJ*6?>VSJc=beuLEz`>#Uv!}u`$$o>Vn zADIu*9{|<=9I7A2hv`T5FGxQ!AEf^PRR4FVei$F7A348(^ds{@`Y%BBbE`tqFN{yD z{s&O~;!yoCK1@GydIi~!%m>-8um%!-%253+ z^glQW(O(bM597o1BhN>H!Vj4b(%*0fqJIKZKa9UX6?=c0P<{3QYTkCJc`!cAJ;?I` zAon2iLGCFy2XW6SsD2n9rr!Z{nF3OHAoD@`H{68ie+<<+=E=7g7hQvLGCet>Mw`thw)+hk;4z9ADIu*UjWtL3Dpnd z!}Jr%4;P^NXF>JD_%Qv*;SaJOnGdpG;SR+AYoPjJe3*XZ^19v7$2seQ2Lty)h`a!591T7e*;v%HdH^157WN^ z&Hc!Hkoyy!L)`BO)eqyt^dpaFfx-`&57Peusy_^>AI6922i+tM8lnO5LHd#TApHz) zA@*lL^~3le^`QRo8<1wC@)*W%Qisf!H9+UbCPMiWp!~&9{st(2JCuI`%0CU|e}M9z zL-`ytA@ghe8W8gppnP>G-vG+Dgz_C`LDYLg`4^!4SSbI&Y>0dslwU9h!q12D1Li{b zGobtjQ2s$E|G+$m{3|HmVLpVnm`2L5hEH zdU*#)FY}@1F+lks_kz+3q5P%*Rlf$RAI2wEzXMeNPN;qupIH3~Q2nQ&`eA&Se&qTE zQ zNc{0=L(&6`57UoaKY{#@%m?XT0M#!A)eqyt^glqeADIu*-|z!sza~^aj1SXKDEt;c z^;Hh;%e;ialj8Ck7hMy4sXF>JD_%Qu; zNcj!qei*+@8&ckEAhLYu(?%^H=4oRu9~_|Wc?NY4jQ>j;d->pkHeUeaOY1N&sBtkc z2;f*BfnI*u=%BjKR|mWMCj5qk*EAhSdWG><=wNpra(sir519{&{|iv_PC?Cs@nPXl zDE)na>c0im597o1C!nQ&WIo9L0PyW<3=FTK`eA&SenR#aK=uEE>WA@R`jOjbApaxt zLH0j@>gU&mgeQy-(~ms=0Md`l2kF1S2)cibfk6eTAI692X8^5{K`MWc`C$Fr5d97? z{ZKwkKk|Gq$bMu#NdE?2i2hKhei$F7pHTQ+fa*_z>WA@R`jOXzfb2)+gX}NhgV{RvS0d!YJZd}8$SApt> z@nQOr%S({|k@=wTR}h7SzX4P~j1SXKDE~P?_1i%8!}u`$gvR3-#31&2K=s4;F#X8x z#pZqosQxghei$F7pHTcyfa*_y>WA@R`U&}e15|$zR6mRl(@)6#AE5eMp!#8an0`mJ z_5+MRO%Kw3*g|CcVVxdo{k&BVd;RPn4vCL1Q1`(2jQZH?XXN%YD1MOnp!8J$HBV9> zl0RU4nE#N~W7EF?s$T`FAI2wE{{^UiJ*a*dAEsXqEqsyr`1)(s`l#XQqK`d186+U# z*$TBE#-D*BJPDN-0#NfdLCu5lVg4bM-U6Wd4@33C_{8d;0M&m5svpLO>1P04n2X$A zK;nba!vr-*c-)5Rhw@?i1JK6bk@+C~4=f@2A3^oQ_%Qv1@+*TCME_T)ei)xv{R&Y1 zj0TYO4Ch1FlM=Gu0jggJsvpKD*8T*jei^8K7@t`E4N(31Q2j7IvHBN4^*ccI!}u`$ z$n7goen#el;?Kbv68^qW{V+aEKl1ubkbY!7NWXv$M1K@iKa3C4zX5Wb0(gBSNIx1faFIk@+C~0hJK_lc4%xe7Jsy^>F_q*MA`W3!wT}L-oV>F#Uwej|Wiw+oAel ze3*V@`#}Cj=7a1vsDilv0#rYY57SSmz3>65{|;0?j1SXKDE|pmL+t+w)eqyt^dq|$ zoBI=>`k4(O`5(rI=_h1=1602ZR6mRl(@)6$3sC)9Q2j7IOh2Lc6R3gs-yEtR#)s)A zeqtmhw+KkzX7V>9I7A2CszLjsD3Y~ei$F7pHO>~p$?Kh zVxanAe3*Vh{#StN&xh)V@nQN2<B z{0d|2`N;ukeJ`lIN9Kd-s}HRZ|IC7#2jj#1LnuB3+93K@LiNM=F#X8wBar>be31Pc zp!#<~^~3lu{e!uTL^!Nd)UjeEg#)s)wKx^+I^Fj6tOorHR z3Dpnd!}KH1--6-~nGe#x0IJ^usvpKDR=>d%i2V^z{V+bU`VT<$Cqwna_%Qv1(vQGY zi2Y?y{V+aEKcV`*0II(OsvpLO=_izbCP4Mifa-_wVfvBRr-Q;DnGXtohiMS^Z-eTG z@nQN2xqksv|8b~(7$2seP zG9RRW!aRt6MpH=sf%BpLRR@qkNcj($57K{OK19D5R6mRl(gwXfg{)7b( z{n}9dFg{E_a(M^RkIV<@XIKc)?+Voqzk@j1SXKDE=Iv z`m3P&VSJc=2DJJEnGdqxVHL#wUYLF;AEuvB`cHtWp9R$qdPmuiu zt0DHUgzAU!LCQe!N2oor0jhsHR6mRl(@&^9c>t>aFjPN`57SSm{B~FaasL&lei$F7 zA342%{Ey5B`Tqe_|5K=b7$2q|IlY4PBlAJ}3)VvH{|?m;|T(5WIjlL!#arll2H9HK1@HM_&WgAuLac)WA@R`U%xv4N(25Q2j7IvHCAS^_M{P z!}u`$gwp>5sQw11ei$F7pHThdun`h|{ZRcdKC${2K=sdq>WA@R`U$yz162PesD2n9 zrk_y!U4ZJ}57iIj!}JqMzXF>e{yz=X597o16SCg`s{aO5Ka3C4PbmF3i2cY8xLgoA zkn#q`pKp%6|3j$$OMsep5NaNb4{|T4{!l<`e<1TgF#QE+^*=Hnq+eh+#QrZ({V+aEe*l{Mk@+C~3Hu@XSu7yo3FE``A3&@B zk@+C~3dbP&rJ(v@e3*Vh=WBjA0nx7l)eqyt^dqlF0hQm#e31PCXCeB{p!#8an11B? z2BaUE57Peus^1-|AI692CzO5^&Oz*tg6fCyVfvBRQ-JM9T5kce|G))^{xYb37$2se zVEf@BM1MO}zXOyH(@&`UX@IJq3Dpnd!}KH9_aOHp^Fi+a0M)++svpLO=|>K4kbY!7 zNdJUO5cltf>WA@R`jP8vkbY!7NWa2mi2k!s{V+aEe*#+mLFR+>H(Z10e+ktO%SxOLHY~sLiDRZ z^~3lu{e;SYgL@GD)=>R0K1@II{1nLj$b69f4EG`Wy`lPHe3*V@|AO=*^FjJIK=mg; z^~3lu{e;|K@Bm_eIaEK457UqAUXcCBe31PP4H{yz-W597o16KX#NK=q%8 z>WA@R`jPVs$p6TEu>Ya@??Lr%fbxmeFYp-R{F#Uw=2VLFOX~N z5dDQv{V+aEe>z(E4deG(LE5VUM7B?tTA{X2w_0IupH6_f=P%SfFutHQ_Vy`qegK6J zG9MH^AE4%$SVPh$j1Ti4q4Z_&91=dRQ2j7IOh58@WFY&I`5^lRUO@E6L-oV>F#X8q zSAg^*^FjI#yoBg4f$E3xVfvBxuY>d>^FjI_yn^U&hU$m$VfvBFbC7;yK1lzD*AV@a zq55Hbn11B;1V}$JAEe*m4MhJ^sD2n9rk_yyJOI_d1F9d!hv_GjzaBvKpM~m&@rl*X z@D}3!`%wKbK1@Gydko}%WIo9M3!wVHLG{D1AEe*m z4@CcTsD2n9rXRUI53(Pb57NH@s(&?9Ka5YTeucjf`wu|%!}u`$g!)$jQ2iI7`eA%x z^*2EEzlQ3E@nQOr!yDv(WIo9M4F4hS{|D6%qko3<0 z<-_z7%Kr*b_1aMVFg{E_!SI9Xw}R>qfbwDb3AGOdm>}-=gX)Lz;rc-gr1XcL{x2{? z^d~~~!}uU=p!AO%Uf9C#0xLv+BTPS(57UoaUxM@_^FiTnzy{Gj1F9d!hv`S&F9On! z%m?XLV29}64b>0h!}KHD2hxws2kCEs>c0xr597o16RN)tK=r?a>WA@R`jN{!kp0Mf zko^H15cmIu>WA@R`WK*$4bHXGKLF*!^ds*V0oxCffTl-~`vdqP_IpG1!}uU&p!Bx@Y5gupKQbSr|AQ<WA@R`W?{5Cy@Cd`x_J?`jWA@R`a$=MgIaJPKFIyZe31JE zv>^Jcq55Hbka|$~A;&jJKQbSrpFta0h!}NnI zSETqy=7aU?KF#Uw;uLV&3>!JE#e3*XZ`~q@6G9Tpr38oPDUx(_4 z@nQN2)jtoQ`oBT-!}u`$$l(pLADIubzrhS*zoa81{lWM!{mAJJq#v0N(*FRe-x#VN z#)s)A6n+Zk5c}Ps`eA&SenRmV0M(xY)eqwntG@xNzXqxw#)s)ARDUgi>hFWWA@R`U%;80jhsHR6mRl(@&`Wcwhku|5H%?Fg{E_q4xU&D~SGQF#S+I zOh58^CQ$l8=7ZAD2V01KMkh%6gY%)|v&iKwNIx&hv^qU8$UwkgWNyC3u6CG zsD2n9rk~LKa)K{J{|Bgk7$2q|IlQoipMW1kzmPK|{lWOe>OTP0uL9K%;}fgDz#n42 z2~nH6CDNPLiffmn$CPN;quAEqC< zz5?k-=7aPz#6k2gfa-_wVfvBRV}kS}^FjI@;vxDsL-oV>F#X8&4M;ySAEbW)RR1BU zei$F7pJ4e5)qf4De*=^c(@(Jcg{prH)qerXhv`QSFOd6@`5^ZnNPzgC+Xa&TV0@VV z27c7|L*|3@I}||l8$i-PY591T7U!e(NKa(pY{loag>JNbG z7li7E@rl*n0M)Mm)eqwntA7Jjzdlqyj8Clo2T=XiQ2j7IOh3W)b2B9T+@bmvpnRBq zLhYXdsQOT-ei$F7pHTTX0jfU{svpLO=_fS)9MA%Be=bx%j1SXK$o~_d`YWOOVSHls zUx4awh3bd#VfqQBe}-0w`zJ#6!}u`$gu+h&s(&t2Ka3C4kKA4aWA@R`U$!J162P>sD2n9rXP8}3FLlcKFIwEZIJN00o4!V!}Jr1 z{{>L}PoVl?e3C}=7aQa=!fVxg6fCyVfvBVJ0ShYe2{*D2@w5`Q2j7IvHA<3`U9Z)VSJc=Lh(NV zsy`O0AI692Clr4dp!zeR`eA&SenR$tfa))U>WA@R`jPvKpzuTHgTn8^L`e8`K=s4; zF#Ux3KLL{<`e#A)!}u`$gvvjG$q@b9p!#8an0`Xx7Xa0N3aTH*hv`3nb{+{bALM_5 z;C9$@yD?|0e_%Qv1 z`f~;R5c^G_`eA&Se&q9TK<-E8gWSJC1ft&^svpLO=_h2rgD6CQ6jVQq57Upl{taY5 zG9P4rf*3@96;wZr57Uo)9~wwMG9RRWg9Jo>4^%&l57SR5{1YT0`WHd{lZ?5^bh01^glpbkATbv>EB=l(eDJ+597o1Bc~To{3G*0`Y%{R z^k+f!!}u`$gyO%!2BNVVSJc=LgD`b zs{b!kKa3C44{jVHjYlK%LH2(Lh1f6d14;idK1@HM@~0pSqF)!PAI2wE{|BgkJE(pb zAEv(q5`181gTf!i_w#}5cSwNlcSwNpCqVg`Q2qrdzX-|?SPXIROejAA%HIv;=Ro-n zp!^0X|0k3`VJ$>IhcCqbIZ(a=l)nJV*M#yL)i&;6zS#GF6of7Fg{E_q5Rem3$cG2R6mRl(+^Ilc=B6A97O+Fn0_dqX#MdJ{r6z{p?sKr zLgnQFsQQmk{V+aEKk|BeQ2ZhDLE)c}2(e$lACewme3*Vh?Ja>Mh<{Rg1>v!ME6d}8%Gq(JO%g6fCyVfqQRmmWa% z&w=WP@nQM}(E2OLd{F#-$c5Oy5vm`?hv`RdAAsT?nGe!$kPp#+7^)w}hv`QiuLbEx z=7aQ4fa<>t)eqyt^dpa#gY+ZwLHZdAAof3p>WA@R`U!>K0;vAKQ2j7IOh58@Vj%mG z`5^lbltS#63V@`47$2seQ2KEwgXp({>WA@R`jPz$vLBfbvVQ|qe>hY>j1SX~Jl+e^ zkIV<@pHL35zYMA$#)s)An13oD`n#d}9iV)eenRc#3sCj*q55Hbn0^JM@*d=VWIo9K zAF3esZ-eTG@nQOr=bJ(Lk@+C~6B;1;FGKai_%Qv1>W>Xj{STn}VSJeW0<``fG9P5W zLMz1nuTcFkK1@GyegU~3nGe$60M*YK2uXi1K1@Gye1r5O^FjI(+939;LG{D3@Y{0&e(NI!D> z?Q|e&dyO*)mCqN1z5TWT>R#_4NP2WA@R`jOY?gY+ZwLHZT?A^NkR`eA&Se&qEXApOXEkp2r3Ao?qy z`eA&Se&qEzApOXEkbZ+n5dGay{V+aEKcW3P2PQ-GFNErc@nQN2)mI5qAo^EB^~3lu z{io6TN638S^m8DHfq{>af#CozWNqAKC?7N)2Quq7lwTkMkyj3esK1~K;hRGF2Mi(n zSSY{29Kvsf@&iI3{Ap1BhcF0#1(fd)4&iTu@+Uy~N1^-;Q2s?I{{WPKAIcYqfare< zv zgYrKlL-=B$knj-5fbdnIe1=>I-w4W2fbt!n{0W5+d0!}hLlJ}@4dpY`LHPMl{)Yw# zzZJ@FXoT>WK=~7zA^iPNzCj;^e;vwK=!fuMLHQG){6A3sfe8?KxiE--HcWx=&7ph) z=>6E9P(JK_??@;gc3*ccln=XqyAsNW-KX6O<-_jBo(Sc`?z^4~<-_i;UJ2#H?xQ{q z<-_imehB5m?u-5e<-_iOW(tS+2X=q6D3lMok691OhuyDi1Lec+OZJBHVfP=0L;0}# zj8ma}*!{%CP(JLw;YKJQc7O0hC?9tJ??NaacAxKNC?9q|?-3{;cHizbC?9rz?mH+S zb|3C9C?9seEoTJ8|FHXNMWKAy{j*w7KI}eOM<{;+^uE_nC?9s;Yc`Y*yT28b??L$$ zb|32`s66a`)iqE)?EcH0P(JKF%F|Fj?0&|ZP(JLw#1~LL?Eb@FP(JKFL%v9e|6unM zN4uyot&V2*nLeQP(JMbr8FoXc0WuJln=YFr3T7}-Jj6`<-_i? zm;&X)?w42u<-_iK*Z}3j?r+!&<-_h{I0@y$?pL@5<-_hvcmd_Z?mzef<-_hXV2Fl< z7wr6fUML@S9=<%34?CaU5Xy(0Zw{)LK^xR6C?9sdsw$KZJ1^A? z%7>kQ>H+1$&NGdG@?qzbra<|y^F|AyeAxM+4NyMpJkTB}A9lXyY$zXgUgs((A9nuc zJ}4h{p5|#NA9gfJYeT5@omZs@<-^XO(uMM2=Sf*X`LOe$ z+@Smf==o2ln*KJ2^zO(-9>f8Grv-J`LOk+ z+o62edeI9|K5YHxdng~ao|7vD;vd-hOl2q^w%*bf%7?9=jD+%G>miGweAs%%ekdQd zK5+w-4_j||9m-~$K5RYR5-1%W|#eAs%fNGKn+KC1%Chpo42 zhw@?TrzS)Bu=P+2p?ui-rc+QpY`xM?C?B@|NHHDaKG=F9dng~aKFAx&hpqQXg7RVO zcZ#8W*m|56C?B@IW+IdiTQ9Q!%7?9g*#zao*0UUj@?q;!u0r{+^(K#@eAxPtKTtkw zJ&1G$#6Ph09okSnY`umRln+~f;SJ@()>C9d`LOj7?NB~!y~6@1AGUsB3zQFAk8lpg zhpsnx0_DTj3w(w0Ve|j2nGpBF=66M*eAs-g9+VH8e|3fOVe^?OP(EyavKq>V%{NYf z@?rCTtDt%oY<$ub#Ajh(_%IDJerXTo7tDb0!=d~JC_fF#p8(}o zK=~V>{2nO(0+hc1%Krf6?}73;WEP$xDh4Mc@`B6}Q0rY(O3MhX9ls^s1-vH&WgYseb4Sha(^2egWuu8wn^MwjM_V%7?ATv4HXg zpz||MP(ExuPBN4ao8Kvg@?rBk%}_pUerGm_58CSiU9YnZ$`^q0uS5B;^*zi53=Dio z>w7@cr2LEw3=Tgb{09t>^8*({`2|q^4j3PLf6Or`A9nxERVW{JpU(>@A9lYG6C=dEu={#M zp?ui=S)g^lAos!U^U{aP!|oSzhVo(em4!n2u=~4Gp?uhVSmjXu1gQTyq5KQH5dY1D z@fjiftuQ_}gntUkhux2L3(CI$HSZOa@9+*1et)2R1}LAC3F2Sa{Zg_}KJ30KBPbts ze->!nJIMbRp!yS_^052CilO`uZz1-zLHV%z4uy5%xj(u=B#tK>4uq z)9*m}u=ChIL;0}#0r;6A{(+qruL9-6&M!BH@?qzpdqMfI^X=21eAs#G)lfd{{(?Rz zA9kO`Vkker58~g=P`-gMgntamFA#$8??L$s1R?z2P<{a)gfGPcaleBZgs%(bH<&{B zu2B9469_*E$_L$x3YtG_gz_f@L*!>b`3^x4{uU^|!3V;>1mmki_)nqy18NZdUnu{B zDuge^3UMC;bbihf%HQA(k@tb}KX^g-DNw!wl-~&D3qa@VKG0_89G4B>Bv@*AM;I|k*$?jN}h<3sP4e+cC}K>6>WeAs>W zKcRfs{qt<>5dXsNPv?d5VfQ(!LHV%z-OZtV*nQ_-P(JMb>If(wb{}~rln=Wfxe3aL z-Phd@<-_jJo(tu}?q^>D<-_iG-V5c!?u$PM<-_hDe+cC>K<~4D59Pz|7iZvr_!o9x zvLuubyZ>7a%7@*jZ35#%@0)jr@?q=KVxWB3`q3gNAGZFd6Uv9JCs_gIH$c}b?uPPV z>qW0a`LOjXFQI(cdYZpbK5V@uA1A~=u=PqxFg|oWl|7UXTMrfkS3~)*^->d{ zeAxQERZu=`J>otnA9lX-4JaRWUhx|!AAbHY7sP$A^Mr+kwyB5laodjF_!G*9 zod?Ow4e<}`d`&GVA9miGCzKC6KdunUhn;8H59Pzo_goC+!_GTB2Ia%fqrU~^KY*Tx z_!i2Coo~v*191=Ryh$-AA9nteHk1!L57HURhn>$91?9ueTdINbVduw9gYseL(d~rs zq31nbg7RVK-9Ci!4bma;^8w0-pNG#2buaY1dubRSdj6m;ln*;U+XKpnoky4k<3rD@ ztAg@j=gm!r@(ofU_OF2Q1)%E(4ng@3rb6VeK=~6UL-_BZe1mBaJ})1{y$LfRd_^dK z0hDhCZm=d1?hUx51G7RsM69pc_lDF4A62tOan4}kh-GL&zS z2a#U~5%A_lNQspyg2_l)s?~B3};W!_Eipg7Ouh=g-fE@;5-&t89hx9cDoEpNH}< zK*Q?=ls^F~&m;tKKkUA6Q79jFf4K&f54+FX7RuiMT`v<34uq zIj2JTu=@^{L-`EQ{IDC!hn;747Rra6&-Vh#hn+vjEDUi!?EE}gC?9qnpcRx4J6|vX z%7>lbR|DlI%!atX56Yha<*$bF9iZ`Z9Lk5?mvINmhn-*g3Cf3^N694uao>a#i23qR zegm|;Fop6jK=Yd~lwSai|9B`LcD{H8ln=Y#z75Ks04+bKK>4uy(-%Vdu=^P{Lirb< z<;zhhe*={N7|Ms82mTYvhn+9UBMR})1!(xlL;0}tyA7ax*nJf4P<{Y3e^_azP`&^(|F4DeLGxIk_VEEI{{Yndt5AMGEu???63S12^8bSQkkzpu z{eof;|G?IRii?5c2_ywxQK*W>H%8+-q45LI_(^E|QZ#-88ovXLKLL$D4UIn+jlT?y zzZQ+Z8I8XSjeii0e-e#<5siNnjsFmh{~e9bC5{@t@@RY`G`JPZsDJPZtuJPZs@JPZt=wvr1E1A{9M1A`k6 z1A{x{%tZ%%9tH*j9tH+_+O5D4?-~>opPpLcT2z!@6kJl2nU~HG?-uMD@8_IfnpYBD zlv$GMlwX>c0ufCr&CE%04oOVPNyQL|FG?)OB8W|8d3JFDl2R9R10e!v2Eqhk1}5j{ z78Iox7efq!@r+QpNN$Izh%YV9%u7f11Y8E;u4It!VMdkamAEAqm!NUu(NxD5XXX|l z99NoGf~=w_HL)ZW#Zs^c#8VKHQ&M3LLFYr9j9o2~C22*eC{}>@$eLjB?3b7ejlRrc zaKuCG%gZmyP0Y!xN(DzfL@d9cBr`WL2OL2@`Ovh1CK#WYSCU$kmzV?5Qk0sQ0!cj( zesN}1YJOTgvfs;6i;6Sz^ZZJ4lTwQys>(qr4W`W_)GZz*si6r#{R|RC1Q{~VFoGc- zl^cUeq(=$qn zA@(7s43HuO-!KB|Bov`|7w3`+bghOFMqqIy`%yK4g94Ub(5*4SG|mKU94>Jj)|fzo z2A4HBj06kgFcF`DCYZrv0u2Uqcf-W7+5(cq6Fepn7{Ox_fjxL&;yA3q8$2cv;Bdfn zd3;7`ntxtSC2koU)?tPnY8e4a1@JNg#6ipY5DP&$9yzUp_>c&O1wXO~l2ed`p#g|2 zf~GLIBoUS$ic@n^lS^Dulk-zRXdIeTpmzE<&7LGLuW-ZUhH0x^2a&CB7lfB^3dQMTxnoC7?78H4Tzv z;}JmsX2$2_C#Ham&CJhBEUJVU4v{HJO+&H}E*4OfnwD9Cst}w6LH0mY6{nV<*^dy< zNli&lO$jba4$iDfg_!~gGS56rG5^vMBtaJ}3ZY`C3KLVZN{dUJ!LBHV+JYj6)baqk zJ2^2YCm)(z;*&E{le1AZ!xAXeEEv}*H9a#An;2SD!xK1I1ElOk4qO!Tkfez)1{8Vd zF38DGhGrOq#UQcx^rF<%l*;(jiV|$vAgy&sctD%TNXZ4}M(6yzlFYnPSmeQkT|q5< z6upqhLUS7~AED-0xU*rx&?d5DQ93L-VfG@07P?i4OoP)_uo9H8D5ym7b3rAfMFjB~ zyvTrA1sA}u0V0EJG(rTA;`pS>pwzU~qSU-(cnCu@q6R0j7b4D2TCWIRK#?6v7yGpu`(OEk0*KMS zi$R^Ml2lMqMJgZBh0#MCE)R-bZ2Dm0I5k2%gH07QKRxPXieXNrA|w6s5v4 zCa9GLiUFAUpymK51d;g=PauhaLIIhJrUB|Vs0wg1Cb1;dufnOaBo&qq(o;*oxdq8p z>8WV7U3x0Gm5U`9Kr)%hC7v$eumgua+%|Bv0Fyx^KU6K?Fhpnpj{`X8r@%r5wI2Y^ zr!XPdFcC};Y!WCprRE`5^XaK2U>Qi*BWyyc&eNfdjAC%J7%3FfQ%izV3xiAYi;!HD zj2>V}jVq`HNFwMd7+DInnHG;MRgBWAL6!kmGsqP_c1e8pf{j4(CddTT#DXLOEkLj< zhe{%%7o-u?+sI5tO3g@uXvRR5qNHquYNYgyB#6}rbmteR7C{m=JVweAb233)nxNDI zPzr-(0C>j|oRBh$!KnqRDHpjVmz!AO?2}rSngf;0ORXsJ%qu7@38^fAr4Eo7v^^S< zk0?4|I`Uzq1vmvkMKRmnkRA`JkBUP)j&I zKCu`Sjqyb#c?|LK@wutF$pw}1$r;&DUT%I_DiXIiwFJg1E=f$z265uk5;Jo^$`K;z zrHMr;5TTURqM`zZcvyoyEjcH@IF%tjEj2%lAwDg?AT^I6KCPfAGp_{12aO6afcvcs z@oB}Wso4ziX(g#SISlb>;C?^I-H?_)#H9=%7lXSq1tl3E&GE_kc`5NJsl_El`IQXu zD59YBjV4r_nGPP#L{V3sm|23P4%~D@@N-K`QY$cRfC!^n0TF|=NnkcW#7gtPqA8U* zDe=W6rAhHgnR%cQ4*k&L)S_bjg2d9ClG+Vq{tzUzBZFkX%rdm7J4YoKuu;WLcyiqK|5Vu6a_TahkbaK4M4+HUtD2 z$HD3hR5SFFA(}xI6?nwiCAAnT1d2qMc@RYi&wvItA>}Gq69H+YJ|k${2-O%+MuRvP zJba8PiN$bqA(IFrXxWU_IcU;2odZ&b(>X9nEOtYLkiyFZ$wO(GIXTc^#4_Lp(TeP8 zyu*=(5h&6)eF9R4(@woQo*BTuzD6` zteFYKdRT#wk{Vo@o0Oje8omZ;$;r>pcFHU%hRgzjM2fRB3qT?WC-}G;njv@~Hpn#O z3L((}gSkkpu;hG{ zK_+-}78Ez2r0C-cN|@lF!QLfB?=K-Vf)WOT2TI3aV_+IEyI$mUpFs-&U@dY)d_l8) zDrk8?d_hrWSz<{lC=bINfM{u>bi>nAOOX3&2*(?lKtmH0cO(rPf#-vRQwyP$D%b%a zLm&wnxmgbx_=SvbW|qK4!_m|tRS#gJGV?$UUSwmi_u3K0Kr4Af2Mi(QTbfgniQJt* zNFk*lgdj9b2=rmF^m-6RfD!@HcrHjHB^B%hl>Rp;l?El2Bm1*BwInDf6x+mJzzHhqzvj+XeI@TV9ljq3AAjAqz8*Bm?N&BRyQP-IQ5~3VG9tbKTtG*OCS^x zXg!QF1p^gBoml||4O9Z^K!kshXj13Y;E`&iv zFiZ`I4UR)ZoCl{CmV!q9i+%Dz%c*e5LK;v=feTp)0~G*G0b`WIX~p2u7t{hlYsSDW zNlArS53wEOPLTV-O)_u;4@cVzVrXItq*(*v!J0h~ha#FhV7m~eV7L<8GJs`XP;CKP zP*#wdnhj~YK#T*q31k(dO$AZ`nP3K`^i+rvnEenXU|T`s6>$G07MGYqqZ?GNSwPw4 znK>ySK9mb$qSiVv1E31PEe%kL29=?ydC^=7O~cdxY6%5{pX05_8~_aVe>3iKRIu&c3P8neq6P%#`@F%+wr|f$-#F z3^8aq0&ZdyUko)qKJk!d zwJ(eT4hB#dq9#(<^q?L{M${%i9{*%cZ*L#C`3s;`FZJ}7O}BW31m4V*iLLJ z%uJ9~pgJF~llAa9QxCV(@H-163v&{c28N)mJcd+AdIinf=H};-Gbn^=f^L$5QIdHw zs80aO;HYDg7{vyvUeI6=NE5703SOxS=YV8j^AV8cjG*=_stBn61=j%Lph|;`02M;8 zMTelo58^`_>LB$X9;i4fh7?DL5)bSam}#J)VMz6Xnsp#zATwaq2y&HzsurBhP#f7~ zjL4xFplfDgWD4nzAqNd~@E0@iQCj%P*cJxs<>f=x34?u{R-B63dn0)O5!D1;^HhTr zBSTnPftFT~);@-7QMJO73b;W8(E(n=4DK?(N-R)?h7^OK*hi9&FUo-~iA52Ft!M^i zdPs{H+5keb1xXIFU=$SG@N@^V2cZIzkf1d@WaSe`0aO4~<%4Tzw7MOn1Y`zmPykXN zqKhKZ8>FfSrDyt%s}R$607U~RPavv(kO*o`4{|w{{85mfpF>WW1k$H#l453&oC-=^ z$V&n-5*A1;C^>-@fzlInsUTu8A!J;Gp`rq`YK|P|Ky>OF8>A&CS%MrC5#a%wnt`r} z#&8xy56EGVm4%=-4X9*-trCn!6#x}D5fL7srYTfCc(wweBOW%t29XAN6iE`qg|9nA z(}}Tu(AWqvX$I2(?hk-D#zx3%291r-mLWo#dtjBQOB9WbaIAKWhZUe;zu{Aa@Hr@! zz;nweOD@5KluqCg8HibUl_5+kf-J5A#TY0N!^1fqAz@|`pPO2go*IOz6lw-&Vk#Nb z1xb$wjnIKx%8=TZoTP-LT{qPJUAs~Zs@Nx{LPy;K6 z)ql9EUlc{)Dg#TkiJ}O_W>^XZtANcDf;r&iNxha2coT^}1E@-YO`DMu7s&c`jZM-_ zlEIMyDy6|y3S@0EWcVE=e?#m?)((mhP^pNmx`G%3=_x{5F6eu7z+=e}DTp#80a!~L zB9RQ*{3ULfuS*P;+r1&Kw8IXS60=n^2sNP5wwAySYs6J-ksq<%;C4JcZX z`Djbckp}EQVVawn2d;y0ZAyVyjBQH`T+A1`R|q+J@b6)Qr4L9W0NJtFWY7Z}(tv>O z^#J(+t_erG0VWT1Cya}z=t1QVD4~Gb5AZd_Ad{g2i6yB}y{UPiweraQZ}3uR)LIxW z1PMGa*BM$bfUE&e(&MlP;V95R2}pHf3Unv|RGovwu|;)d?WQUFd^7XDa=Mtj6hQ;qzw#G zOlk@R8G@Q1L8id0giXJ~tc0fy$ebIpQo{&np@l4nRN5d58)Dz^g}&Vk>n1N8o5i5} zw_py3q(G#SALLJHR)Qp7B(DUAK$0nPGanq+kUlJuN>D}saZwXAL^oIm(b)-P3Rnjy zpCgA2B&g8>4z~0QA_++;uu)yuk}TNF5y)gz_klAUtf>Od0OaI26a#e4lTFhMlHlzm zShz!yEtZNIMK>tRf!iR^>I*cv327)H1aJ@EgI0|}2jjq%9YQyl3mUb9O2aBlgbJ7l z7DXuI-k9qqaV%E@IR><54Slg%F4|(dWbm|gNIvRL3b<~pJ3*XFDzI-!!C2r54K|Rk z(8CRE1zFoyia`UaeyQcK1eTc(-`#<^Cj{??5~O?xiT29moK$d=1I$Iy3yr$uoW$G$ z)WCu#cH;gh%+OC#aA3ABz4|2`GaU^~);B}^ww7~MTsR!hUQAWvPw#lRDnh#g@l zRU+7fs48Kz74T4jHrBw#Ko%V#X2{@+ma(Zu6GLpM0SBIIL1s>VI((!b<_Pq7g^S~G z7o-6S$~9=#p~Ni24^S2G)CAInH!Z;wKsADEYLFOulpy6@s8S>maQ9Fw(9xaL)(D9`vm{ z;58rDsWc@Ww80nbEJ!SY>PWQQ zomyC$S(ccSng`i<30o%*THOmODIs|qsT>Biz(8EoMiWw_4x|8VrBjJpW?qU@YH^8g zVo7pFJY*%JuTL=ACPI&7RCUlTh6wc#!$I8#P=gU{He`Ijs5mt}H#HBmLmMnw4BEL7 zoLUHtJah*@4Tj1>h5|#1Qd7~~16f|;RN|PIg5of!KA5y2ln)xk3n+n4c!GvEkYr2{ zQXqwN5LkrLE;t&lRzzd zuzJ5FXYk5P=*}Tfbi-CFg4iH?;IWKl;u}6E4mw@~JZFuz90*cJKz2zvBerP6lQy_= zMJg~rmV#?CoLvmCEGTt=l!E*YV}m6?fdckeQEEzQa;j%uN-As{GDHO04+pD(BvY^x zU~M`0bT&8_p=}X@Wq8;GKOzS~ya_fbJ`H@93)ljf28cvTerZxpDx{Z%MH;g078-Y` zI&%`MVC&2&{f?^<48dP=|qZUxch@u3NQNi}1MmTbcz)?k_=L=Y1qbWyoHNu}T^-zbR ztAp19prQgch=Jl#*uG-Oun1Cf0Ww4h6@ZL$B8`+ll!L2r&=4m?G3p2tXqXhN0QU$d z*d@7{dG6p%QD7dTTZC{KSOONbK{@Dqp+OcQ+zE*na8U>u4n=Y`mXZh80!z?ZB2YUD zvg#7lX+kVq0?9=cmtYI?)8Vhy{tcfXh8}8E_K^WCv_60qoD*#Oze?kO3^& zBQ2`~D}XK(f-GM0@rj4b{P@BcU^TG7LT{SurJ)!NQ-;+3MpFR|SI{UTZbdK`fz1YO z%PYoMb_Gg*sI4gQ&{I5U0Z%5VO#@n)1YUW9SUY1lZsiNGqhT1Zo4L^%l@ZT;OV*O4HI(ivsfVp_@SA z5};NwiZHlK1@A7RsDp%?Z%BMmYEEimajIiZj#qwCP-=00X;Ct4W43Pyc&8y23t=Xs zSO*)JL$(nrmW1hMs0@aqK?CgAPke!80+1HC`$3~2V8f9`P*gyDh)oRQGq_H0l7w`2 z;E5703EK4NmR|%3sA5nG%t^7 znh$3vGa{#iC^OPcNl8vL0dM+&>>LK|Ny*Gf1&uE;#DluPkkc&~;`2*OKpVEfyKx!d z( zcmlCO4N*v=55WU1jd*M%VK~dbFVBlCb4`AU3E{2e11?UeF0$XJ9uh>{XiV}eYBE?|L3fCCIthJqZA+;o9Tf&2oJf^tDI z58`6WuV5uG^`KS?j1886=>$oD*hp@HcoiimL9Lf!9BZCHI-x!UjW$46vXHo{4rB=G znkA68z%IpHmxR;?1?fXdbs#P@L?OOFG6gho0OHa!R6+KEJpqbtP$0sRFJ#pZl1D(2 zuyqL#NoYXAPQrtBs9;Niz||x66)3nDc_4)pL_g}H4J->Zuq=Xr`w3~e1KeAXz2&J$ z9z#h-5L=;MfsAn>8kb;6aA@IL3xK8;oa(SOz;I2FBloM3oCvlU<^@P?g(y-WlAzTw z$bGZXNzKto&6LqeO;_J|aE66dEuh*2#s*7(supk#f=pqKPHIBeM8b+d(CBr1QVD2M z6T1v}CJ)-LgcVWXF>vr?E!r#+s)EsJ%is`~_|a)gP$&>e8EEw#`g9~@?gevI47$3} zX-mlbDoPs^T>il#0M}p=sACUm$_&0~OSDEKXmB4qAPpIIN15?K3~QqZLuXtdQzcZH zQzBvhh?)~Zgywvp{)ZOskg<63=3S6lr%3H>Sk+2&caP4K4usELD@rXGJ=6m_8;19U z4A2oZutFK{X&ERAai5$4QU*F7V@MzB0X_v0HSvR|_;89t_7a0PYr;p8NZ!|m({ymb z4qfvAnUEY^)d3oTA!StuO7IaipNn?>EGSV!3tUK13md3}?3qSAngi0thO}nDt!UW1 OX?_vuD@i~XTmk^TZ&K|5 literal 0 HcmV?d00001 diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 4a171bd09..5bbb8b9bf 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -47,6 +47,10 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + if (ctx == NULL) { + DISPLAY("Error: could not allocate space for context\n"); + return NULL; + } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); @@ -76,6 +80,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) { unsigned u; for (u=0; unumJobs; u++) { + DISPLAY("freeing compression job %u\n", u); + DISPLAY("%u\n", ctx->numJobs); jobDescription job = ctx->jobs[u]; if (job.dst.start) free(job.dst.start); if (job.src.start) free(job.src.start); @@ -84,6 +90,7 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { + /* TODO: wait until jobs finish */ int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); @@ -130,6 +137,8 @@ static void* outputThread(void* arg) { DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; + DISPLAY("casted ctx\n"); + unsigned currJob = 0; for ( ; ; ) { jobDescription* job = &ctx->jobs[currJob]; @@ -225,7 +234,7 @@ int main(int argCount, const char* argv[]) BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize / FILE_CHUNK_SIZE) + 1; + size_t const numJobsPrelim = (fileSize >> 22) + 1; /* TODO: figure out why can't divide here */ size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; adaptCCtx* ctx = NULL; @@ -236,7 +245,7 @@ int main(int argCount, const char* argv[]) ret = 1; goto cleanup; } - if (!srcFilename || !dstFilename || !src) { + if (!srcFilename || !dstFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; @@ -270,13 +279,14 @@ int main(int argCount, const char* argv[]) /* creating jobs */ for ( ; ; ) { + DISPLAY("in job creation loop\n"); size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ret = 1; goto cleanup; } - + DISPLAY("reading was fine\n"); /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); @@ -285,19 +295,13 @@ int main(int argCount, const char* argv[]) goto cleanup; } } + if (feof(srcFile)) break; } - - /* file compression completed */ - { - int const fileCloseError = fclose(srcFile); - int const cctxReleaseError = freeCCtx(ctx); - if (fileCloseError | cctxReleaseError) { - ret = 1; - goto cleanup; - } - } + DISPLAY("cleanup\n"); cleanup: + /* file compression completed */ + ret |= (srcFile != NULL) ? fclose(srcFile) : 0; + ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; if (src != NULL) free(src); - if (ctx != NULL) freeCCtx(ctx); return ret; } From 95ea54b4cf3fd49d4ada2809ebfa41d73303761b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:24:22 -0700 Subject: [PATCH 007/145] added code for waiitng for all jobs to finish --- contrib/adaptive-compression/v2 | Bin 467087 -> 466752 bytes contrib/adaptive-compression/v2.c | 37 ++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 index 0c0e194ebae05845c80f8f24d2534a53a56c414d..38034cdab55dde6c55382233c34d86b0b58d2083 100755 GIT binary patch delta 40567 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 gtqk!jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); + pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); + pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); ctx->numJobs = numJobs; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; + ctx->allJobsCompleted = 0; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); return NULL; @@ -90,15 +96,21 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { - /* TODO: wait until jobs finish */ - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const fileError = fclose(ctx->dstFile); - freeCompressionJobs(ctx); - free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + while (ctx->allJobsCompleted == 0) { + pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); + } + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + { + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const fileError = fclose(ctx->dstFile); + freeCompressionJobs(ctx); + free(ctx->jobs); + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + } } static void* compressionThread(void* arg) @@ -164,6 +176,10 @@ static void* outputThread(void* arg) currJob++; if (currJob >= ctx->numJobs || ctx->threadError) { /* finished with all jobs */ + pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + ctx->allJobsCompleted = 1; + pthread_cond_signal(&ctx->allJobsCompleted_cond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); break; } } @@ -267,6 +283,7 @@ int main(int argCount, const char* argv[]) goto cleanup; } } + /* create compression thread */ { pthread_t compression; @@ -297,7 +314,7 @@ int main(int argCount, const char* argv[]) } if (feof(srcFile)) break; } - DISPLAY("cleanup\n"); + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From cd50382c0374dbe5109027ed126a6f22b0a75763 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 19:28:48 -0700 Subject: [PATCH 008/145] fixed some issues with segfaults --- contrib/adaptive-compression/v2 | Bin 466752 -> 0 bytes contrib/adaptive-compression/v2.c | 11 ++++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) delete mode 100755 contrib/adaptive-compression/v2 diff --git a/contrib/adaptive-compression/v2 b/contrib/adaptive-compression/v2 deleted file mode 100755 index 38034cdab55dde6c55382233c34d86b0b58d2083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466752 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%0DRf-mO)ukw2U-kII)`=U#Pu_N?8 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≪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 z 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, NULL); ctx->numJobs = numJobs; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + { + unsigned u; + for (u=0; ujobs[u].jobCompleted_mutex = &ctx->jobCompleted_mutex; + ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; + ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; + ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; + } + } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -314,7 +323,7 @@ int main(int argCount, const char* argv[]) } if (feof(srcFile)) break; } - + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From 0b70152a9b262e6e2f4106c1ca261d7546663865 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 3 Jul 2017 20:05:42 -0700 Subject: [PATCH 009/145] working I believe --- contrib/adaptive-compression/v2.c | 68 ++++++++++++++++++------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index edb2d0f3f..2f58e7503 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -33,6 +33,7 @@ typedef struct { unsigned compressionLevel; unsigned numActiveThreads; unsigned numJobs; + unsigned lastJobID; unsigned nextJobID; unsigned threadError; unsigned allJobsCompleted; @@ -63,7 +64,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); ctx->numJobs = numJobs; + ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + DISPLAY("jobs %p\n", ctx->jobs); { unsigned u; for (u=0; ujobCompleted_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); int const fileError = fclose(ctx->dstFile); freeCompressionJobs(ctx); free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError; + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError | allJobsMutexError | allJobsCondError; } } @@ -131,10 +136,10 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJob]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { + DISPLAY("waiting\n"); pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); - /* compress the data */ { size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); @@ -145,8 +150,12 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } + pthread_mutex_lock(job->jobCompleted_mutex); + job->jobCompleted = 1; + pthread_cond_signal(job->jobCompleted_cond); + pthread_mutex_unlock(job->jobCompleted_mutex); currJob++; - if (currJob >= ctx->numJobs || ctx->threadError) { + if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ break; } @@ -158,7 +167,6 @@ static void* outputThread(void* arg) { DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; - DISPLAY("casted ctx\n"); unsigned currJob = 0; for ( ; ; ) { @@ -183,7 +191,7 @@ static void* outputThread(void* arg) } } currJob++; - if (currJob >= ctx->numJobs || ctx->threadError) { + if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; @@ -220,29 +228,30 @@ static size_t getFileSize(const char* const filename) static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; - jobDescription job = ctx->jobs[nextJob]; - job.compressionLevel = ctx->compressionLevel; - job.src.start = malloc(srcSize); - job.src.size = srcSize; - job.dst.size = ZSTD_compressBound(srcSize); - job.dst.start = malloc(job.dst.size); - job.jobCompleted = 0; - job.jobCompleted_cond = &ctx->jobCompleted_cond; - job.jobCompleted_mutex = &ctx->jobCompleted_mutex; - job.jobReady_cond = &ctx->jobReady_cond; - job.jobReady_mutex = &ctx->jobReady_mutex; - job.jobID = nextJob; - if (!job.src.start || !job.dst.start) { + jobDescription* job = &ctx->jobs[nextJob]; + job->compressionLevel = ctx->compressionLevel; + job->src.start = malloc(srcSize); + job->src.size = srcSize; + job->dst.size = ZSTD_compressBound(srcSize); + job->dst.start = malloc(job->dst.size); + job->jobCompleted = 0; + job->jobCompleted_cond = &ctx->jobCompleted_cond; + job->jobCompleted_mutex = &ctx->jobCompleted_mutex; + job->jobReady_cond = &ctx->jobReady_cond; + job->jobReady_mutex = &ctx->jobReady_mutex; + job->jobID = nextJob; + if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ - if (job.src.start) free(job.src.start); - if (job.dst.start) free(job.dst.start); + DISPLAY("Error: problem occurred during job creation\n"); + if (job->src.start) free(job->src.start); + if (job->dst.start) free(job->dst.start); return 1; } - memcpy(job.src.start, data, srcSize); - pthread_mutex_lock(job.jobReady_mutex); - job.jobReady = 1; - pthread_cond_signal(job.jobReady_cond); - pthread_mutex_unlock(job.jobReady_mutex); + memcpy(job->src.start, data, srcSize); + pthread_mutex_lock(job->jobReady_mutex); + job->jobReady = 1; + pthread_cond_signal(job->jobReady_cond); + pthread_mutex_unlock(job->jobReady_mutex); ctx->nextJobID++; return 0; } @@ -305,14 +314,12 @@ int main(int argCount, const char* argv[]) /* creating jobs */ for ( ; ; ) { - DISPLAY("in job creation loop\n"); size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ret = 1; goto cleanup; } - DISPLAY("reading was fine\n"); /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); @@ -321,9 +328,12 @@ int main(int argCount, const char* argv[]) goto cleanup; } } - if (feof(srcFile)) break; + if (feof(srcFile)) { + ctx->lastJobID = ctx->nextJobID; + break; + } } - + cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; From a47ebb16070af45f31407f614b1aee8f031ffc3d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:23:46 -0700 Subject: [PATCH 010/145] removed print statements --- contrib/adaptive-compression/v2.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 2f58e7503..9a050bf7d 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -66,7 +66,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->numJobs = numJobs; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - DISPLAY("jobs %p\n", ctx->jobs); { unsigned u; for (u=0; unumJobs; u++) { - DISPLAY("freeing compression job %u\n", u); - DISPLAY("%u\n", ctx->numJobs); jobDescription job = ctx->jobs[u]; if (job.dst.start) free(job.dst.start); if (job.src.start) free(job.src.start); @@ -129,14 +126,12 @@ static int freeCCtx(adaptCCtx* ctx) static void* compressionThread(void* arg) { - DISPLAY("started compression thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { jobDescription* job = &ctx->jobs[currJob]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { - DISPLAY("waiting\n"); pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); @@ -165,7 +160,6 @@ static void* compressionThread(void* arg) static void* outputThread(void* arg) { - DISPLAY("started output thread\n"); adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; From 9a147d86715da50610c905d347c88ae10756c9f5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:37:52 -0700 Subject: [PATCH 011/145] removed unnecessary checks for null pointer on free --- contrib/adaptive-compression/v2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index 9a050bf7d..df6bf5705 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -98,8 +98,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) unsigned u; for (u=0; unumJobs; u++) { jobDescription job = ctx->jobs[u]; - if (job.dst.start) free(job.dst.start); - if (job.src.start) free(job.src.start); + free(job.dst.start); + free(job.src.start); } } @@ -237,8 +237,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ DISPLAY("Error: problem occurred during job creation\n"); - if (job->src.start) free(job->src.start); - if (job->dst.start) free(job->dst.start); + free(job->src.start); + free(job->dst.start); return 1; } memcpy(job->src.start, data, srcSize); @@ -332,6 +332,6 @@ cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; - if (src != NULL) free(src); + free(src); return ret; } From c9f49198b858a89781562d07a35772712c272b8a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:49:27 -0700 Subject: [PATCH 012/145] fixed TODOs --- contrib/adaptive-compression/v2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/v2.c index df6bf5705..c25f1db82 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/v2.c @@ -174,13 +174,13 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - return arg; /* TODO: return something else if error */ + return arg; } { size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); - return arg; /* TODO: return something else if error */ + return arg; } } } @@ -262,7 +262,7 @@ int main(int argCount, const char* argv[]) BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize >> 22) + 1; /* TODO: figure out why can't divide here */ + size_t const numJobsPrelim = (fileSize / ((size_t)FILE_CHUNK_SIZE)); size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; int ret = 0; adaptCCtx* ctx = NULL; From 5df4cb053029b4b4fcb5c90f8ee831ae423623c2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 09:57:50 -0700 Subject: [PATCH 013/145] renamed files --- contrib/adaptive-compression/Makefile | 8 ++++---- contrib/adaptive-compression/{v2.c => multi.c} | 4 ++-- contrib/adaptive-compression/run.sh | 8 ++++++-- contrib/adaptive-compression/{v1.c => single.c} | 0 4 files changed, 12 insertions(+), 8 deletions(-) rename contrib/adaptive-compression/{v2.c => multi.c} (99%) rename contrib/adaptive-compression/{v1.c => single.c} (100%) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index fbab219c3..c6862198b 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -18,15 +18,15 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean v1 v2 -v1: $(ZSTD_FILES) v1.c +all: clean single multi +single: $(ZSTD_FILES) single.c $(CC) $(FLAGS) $^ -o $@ -v2: $(ZSTD_FILES) v2.c +multi: $(ZSTD_FILES) multi.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f v1 v2 + @$(RM) -f single multi @$(RM) -rf *.dSYM @$(RM) -f tmp* @echo "finished cleaning" diff --git a/contrib/adaptive-compression/v2.c b/contrib/adaptive-compression/multi.c similarity index 99% rename from contrib/adaptive-compression/v2.c rename to contrib/adaptive-compression/multi.c index c25f1db82..e1fa31774 100644 --- a/contrib/adaptive-compression/v2.c +++ b/contrib/adaptive-compression/multi.c @@ -174,7 +174,7 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - return arg; + return arg; } { size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); @@ -253,7 +253,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - if (argCount < 2) { + if (argCount < 3) { DISPLAY("Error: not enough arguments\n"); return 1; } diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index f9e276730..b9c4caf7f 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,2 +1,6 @@ -make clean v2 -./v2 tests/test2048.pdf tmp.zst +make clean multi +./multi tests/test2048.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete" +make clean diff --git a/contrib/adaptive-compression/v1.c b/contrib/adaptive-compression/single.c similarity index 100% rename from contrib/adaptive-compression/v1.c rename to contrib/adaptive-compression/single.c From 9ccd55f3a8957eca5d580b669b4c69db349fff06 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 10:20:56 -0700 Subject: [PATCH 014/145] free ctx fields when error occurs during creation --- contrib/adaptive-compression/multi.c | 57 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index e1fa31774..a461dca72 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -47,6 +47,34 @@ typedef struct { FILE* dstFile; } adaptCCtx; +static void freeCompressionJobs(adaptCCtx* ctx) +{ + unsigned u; + for (u=0; unumJobs; u++) { + jobDescription job = ctx->jobs[u]; + free(job.dst.start); + free(job.src.start); + } +} + +static int freeCCtx(adaptCCtx* ctx) +{ + { + int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); + int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); + } + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError; + } +} + static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { @@ -80,12 +108,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->allJobsCompleted = 0; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); + freeCCtx(ctx); return NULL; } { FILE* dstFile = fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); + freeCCtx(ctx); return NULL; } ctx->dstFile = dstFile; @@ -93,35 +123,15 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return ctx; } -static void freeCompressionJobs(adaptCCtx* ctx) -{ - unsigned u; - for (u=0; unumJobs; u++) { - jobDescription job = ctx->jobs[u]; - free(job.dst.start); - free(job.src.start); - } -} -static int freeCCtx(adaptCCtx* ctx) + +static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { pthread_mutex_lock(&ctx->allJobsCompleted_mutex); while (ctx->allJobsCompleted == 0) { pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); } pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); - { - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const fileError = fclose(ctx->dstFile); - freeCompressionJobs(ctx); - free(ctx->jobs); - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileError | allJobsMutexError | allJobsCondError; - } } static void* compressionThread(void* arg) @@ -140,7 +150,7 @@ static void* compressionThread(void* arg) size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); if (ZSTD_isError(compressedSize)) { ctx->threadError = 1; - DISPLAY("Error: somethign went wrong during compression\n"); + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); return arg; } job->compressedSize = compressedSize; @@ -329,6 +339,7 @@ int main(int argCount, const char* argv[]) } cleanup: + waitUntilAllJobsCompleted(ctx); /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; From dd8a591d5d74d50f49ea34088e401ed8eadd78a0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 10:48:04 -0700 Subject: [PATCH 015/145] moved main logic for job creation into a separate function --- contrib/adaptive-compression/multi.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a461dca72..b89d0c239 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -260,15 +260,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 0; } -/* return 0 if successful, else return error */ -int main(int argCount, const char* argv[]) +static int compressFilename(const char* const srcFilename, const char* const dstFilename) { - if (argCount < 3) { - DISPLAY("Error: not enough arguments\n"); - return 1; - } - const char* const srcFilename = argv[1]; - const char* const dstFilename = argv[2]; BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); @@ -346,3 +339,13 @@ cleanup: free(src); return ret; } + +/* return 0 if successful, else return error */ +int main(int argCount, const char* argv[]) +{ + if (argCount < 3) { + DISPLAY("Error: not enough arguments\n"); + return 1; + } + return compressFilename(argv[1], argv[2]); +} From a2680e5b9605941b2b9d250f3a7915fa7f48d5b8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 11:52:55 -0700 Subject: [PATCH 016/145] removed calculation of file size and replaced with limited number of available jobs --- contrib/adaptive-compression/multi.c | 39 +++++++++++++++++++++++----- contrib/adaptive-compression/run.sh | 33 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index b89d0c239..a60f48ece 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,5 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 +#define MAX_NUM_JOBS 30; typedef unsigned char BYTE; #include /* fprintf */ @@ -22,10 +23,13 @@ typedef struct { unsigned jobID; unsigned jobCompleted; unsigned jobReady; + unsigned jobWritten; pthread_mutex_t* jobCompleted_mutex; pthread_cond_t* jobCompleted_cond; pthread_mutex_t* jobReady_mutex; pthread_cond_t* jobReady_cond; + pthread_mutex_t* jobWrite_mutex; + pthread_cond_t* jobWrite_cond; size_t compressedSize; } jobDescription; @@ -43,6 +47,8 @@ typedef struct { pthread_cond_t jobReady_cond; pthread_mutex_t allJobsCompleted_mutex; pthread_cond_t allJobsCompleted_cond; + pthread_mutex_t jobWrite_mutex; + pthread_cond_t jobWrite_cond; jobDescription* jobs; FILE* dstFile; } adaptCCtx; @@ -66,12 +72,14 @@ static int freeCCtx(adaptCCtx* ctx) int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); + int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); } - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError; + return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; } } @@ -91,6 +99,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_cond_init(&ctx->jobReady_cond, NULL); pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobWrite_mutex, NULL); + pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -101,6 +111,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; + ctx->jobs[u].jobWrite_mutex = &ctx->jobWrite_mutex; + ctx->jobs[u].jobWrite_cond = &ctx->jobWrite_cond; + ctx->jobs[u].jobWritten = 1; } } ctx->nextJobID = 0; @@ -139,7 +152,8 @@ static void* compressionThread(void* arg) adaptCCtx* ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { - jobDescription* job = &ctx->jobs[currJob]; + unsigned const currJobIndex = currJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[currJobIndex]; pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); @@ -174,7 +188,8 @@ static void* outputThread(void* arg) unsigned currJob = 0; for ( ; ; ) { - jobDescription* job = &ctx->jobs[currJob]; + unsigned const currJobIndex = currJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[currJobIndex]; pthread_mutex_lock(job->jobCompleted_mutex); while (job->jobCompleted == 0) { pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); @@ -187,7 +202,7 @@ static void* outputThread(void* arg) return arg; } { - size_t const writeSize = fwrite(ctx->jobs[currJob].dst.start, 1, compressedSize, ctx->dstFile); + size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); return arg; @@ -195,6 +210,10 @@ static void* outputThread(void* arg) } } currJob++; + pthread_mutex_lock(job->jobWrite_mutex); + job->jobWritten = 1; + pthread_cond_signal(job->jobWrite_cond); + pthread_mutex_unlock(job->jobWrite_mutex); if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ pthread_mutex_lock(&ctx->allJobsCompleted_mutex); @@ -232,13 +251,20 @@ static size_t getFileSize(const char* const filename) static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; - jobDescription* job = &ctx->jobs[nextJob]; + unsigned const nextJobIndex = nextJob % ctx->numJobs; + jobDescription* job = &ctx->jobs[nextJobIndex]; + pthread_mutex_lock(job->jobWrite_mutex); + while (job->jobWritten == 0) { + pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); + } + pthread_mutex_unlock(job->jobWrite_mutex); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->dst.start = malloc(job->dst.size); job->jobCompleted = 0; + job->jobWritten = 0; job->jobCompleted_cond = &ctx->jobCompleted_cond; job->jobCompleted_mutex = &ctx->jobCompleted_mutex; job->jobReady_cond = &ctx->jobReady_cond; @@ -265,8 +291,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); size_t fileSize = getFileSize(srcFilename); - size_t const numJobsPrelim = (fileSize / ((size_t)FILE_CHUNK_SIZE)); - size_t const numJobs = (numJobsPrelim * FILE_CHUNK_SIZE) == fileSize ? numJobsPrelim : numJobsPrelim + 1; + size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index b9c4caf7f..9d2e98706 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,6 +1,39 @@ make clean multi + ./multi tests/test2048.pdf tmp.zst zstd -d tmp.zst diff tmp tests/test2048.pdf echo "diff test complete" +rm tmp* + +./multi tests/test512.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test64.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test16.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test4.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete" +rm tmp* + +./multi tests/test.pdf tmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete" +rm tmp* + make clean From 898c1a5b467047637f9ebb8cbe6b525bdee987d5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 11:54:21 -0700 Subject: [PATCH 017/145] removed references to file size computation and file size function --- contrib/adaptive-compression/multi.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a60f48ece..79904551d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -226,28 +226,6 @@ static void* outputThread(void* arg) return arg; } - -static size_t getFileSize(const char* const filename) -{ - FILE* fd = fopen(filename, "rb"); - if (fd == NULL) { - DISPLAY("Error: could not open file in order to get file size\n"); - return -1; /* intentional underflow */ - } - if (fseek(fd, 0, SEEK_END) != 0) { - DISPLAY("Error: fseek failed during file size computation\n"); - return -1; - } - { - size_t const fileSize = ftell(fd); - if (fclose(fd) != 0) { - DISPLAY("Error: could not close file during file size computation\n"); - return -1; - } - return fileSize; - } -} - static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; @@ -290,17 +268,12 @@ static int compressFilename(const char* const srcFilename, const char* const dst { BYTE* const src = malloc(FILE_CHUNK_SIZE); FILE* const srcFile = fopen(srcFilename, "rb"); - size_t fileSize = getFileSize(srcFilename); size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; /* checking for errors */ - if (fileSize == (size_t)(-1)) { - ret = 1; - goto cleanup; - } if (!srcFilename || !dstFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; From b42108386a5eaccafbc5b8d15e3ac71e4c548a9a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 12:20:16 -0700 Subject: [PATCH 018/145] added some basic parsing for args --- contrib/adaptive-compression/multi.c | 35 +++++++++++++++++++++++----- contrib/adaptive-compression/run.sh | 12 +++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 79904551d..4eb3d3b16 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,6 +1,8 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 30; +#define stdinmark "/*stdin*\\" +#define stdoutmark "/*stdout*\\" typedef unsigned char BYTE; #include /* fprintf */ @@ -125,7 +127,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return NULL; } { - FILE* dstFile = fopen(outFilename, "wb"); + unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); + FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); freeCCtx(ctx); @@ -267,7 +270,8 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) static int compressFilename(const char* const srcFilename, const char* const dstFilename) { BYTE* const src = malloc(FILE_CHUNK_SIZE); - FILE* const srcFile = fopen(srcFilename, "rb"); + unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); + FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; @@ -341,9 +345,28 @@ cleanup: /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - if (argCount < 3) { - DISPLAY("Error: not enough arguments\n"); - return 1; + const char* inFilename = stdinmark; + const char* outFilename = stdoutmark; + unsigned nextArgumentIsOutFilename = 0; + int argNum; + for (argNum=1; argNum Date: Wed, 5 Jul 2017 13:23:34 -0700 Subject: [PATCH 019/145] added tests to run.sh --- contrib/adaptive-compression/run.sh | 52 +++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index ec2df0387..e6cb2066e 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -1,39 +1,79 @@ make clean multi +echo "running file tests" + ./multi tests/test2048.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test2048.pdf -echo "diff test complete" +echo "diff test complete: test2048.pdf" rm tmp* ./multi tests/test512.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test512.pdf -echo "diff test complete" +echo "diff test complete: test512.pdf" rm tmp* ./multi tests/test64.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test64.pdf -echo "diff test complete" +echo "diff test complete: test64.pdf" rm tmp* ./multi tests/test16.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test16.pdf -echo "diff test complete" +echo "diff test complete: test16.pdf" rm tmp* ./multi tests/test4.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test4.pdf -echo "diff test complete" +echo "diff test complete: test4.pdf" rm tmp* ./multi tests/test.pdf -otmp.zst zstd -d tmp.zst diff tmp tests/test.pdf -echo "diff test complete" +echo "diff test complete: test.pdf" +rm tmp* + +echo "Running std input/output tests" + +cat tests/test2048.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete: test2048.pdf" +rm tmp* + +cat tests/test512.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete: test512.pdf" +rm tmp* + +cat tests/test64.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete: test64.pdf" +rm tmp* + +cat tests/test16.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete: test16.pdf" +rm tmp* + +cat tests/test4.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete: test4.pdf" +rm tmp* + +cat tests/test.pdf | ./multi -otmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete: test.pdf" rm tmp* make clean From faeb6e0b1b534c5f73a9282f49cf1209a1417148 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 14:19:56 -0700 Subject: [PATCH 020/145] added filenameTable for multiple files --- contrib/adaptive-compression/multi.c | 75 ++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 4eb3d3b16..a6fd64ce8 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -3,6 +3,7 @@ #define MAX_NUM_JOBS 30; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" +#define MAX_PATH 256 typedef unsigned char BYTE; #include /* fprintf */ @@ -342,31 +343,73 @@ cleanup: return ret; } +static int compressFilenames(const char** filenameTable, unsigned numFiles) +{ + int ret = 0; + unsigned fileNum; + char outFile[MAX_PATH]; + for (fileNum=0; fileNum MAX_PATH) { + DISPLAY("Error: output filename is too long\n"); + return 1; + } + ret |= compressFilename(filename, outFile); + } + return ret; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { - const char* inFilename = stdinmark; - const char* outFilename = stdoutmark; - unsigned nextArgumentIsOutFilename = 0; + const char* outFilename = NULL; + const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); + unsigned filenameIdx = 0; + filenameTable[0] = stdinmark; + int ret = 0; int argNum; + + if (filenameTable == NULL) { + DISPLAY("Error: could not allocate sapce for filename table.\n"); + return 1; + } + for (argNum=1; argNum 1 && argument[1] == 'o') { + argument += 2; + outFilename = argument; + continue; + } + else { + DISPLAY("Error: invalid argument provided\n"); + ret = 1; + goto _main_exit; } } - if (nextArgumentIsOutFilename) { - outFilename = argument; - } - else { - inFilename = argument; - } + /* regular files to be compressed */ + filenameTable[filenameIdx++] = argument; } - return compressFilename(inFilename, outFilename); + + /* error checking with number of files */ + if (filenameIdx > 1 && outFilename != NULL) { + DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); + ret = 1; + goto _main_exit; + } + + /* compress files */ + if (filenameIdx <= 1) { + ret |= compressFilename(filenameTable[0], outFilename); + } + else { + ret |= compressFilenames(filenameTable, filenameIdx); + } +_main_exit: + free(filenameTable); + return ret; } From 3f52ca94bf8bef4c3378f440e18421245ac8e675 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 14:36:09 -0700 Subject: [PATCH 021/145] added more tests, changed makefile --- contrib/adaptive-compression/Makefile | 2 ++ contrib/adaptive-compression/multi.c | 4 ++-- contrib/adaptive-compression/run.sh | 30 +++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index c6862198b..2ebec9ba9 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,4 +29,6 @@ clean: @$(RM) -f single multi @$(RM) -rf *.dSYM @$(RM) -f tmp* + @$(RM) -f tests/*.zst + @$(RM) -f tests/tmp* @echo "finished cleaning" diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index a6fd64ce8..089dfe5a3 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,6 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 30; +#define MAX_NUM_JOBS 50; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -96,7 +96,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = 6; /* default */ - pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); + pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ pthread_cond_init(&ctx->jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index e6cb2066e..b906ae7d1 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -76,4 +76,34 @@ diff tmp tests/test.pdf echo "diff test complete: test.pdf" rm tmp* +echo "Running multi-file tests" +./multi tests/* +zstd -d tests/test.pdf.zst -o tests/tmp +zstd -d tests/test2.pdf.zst -o tests/tmp2 +zstd -d tests/test4.pdf.zst -o tests/tmp4 +zstd -d tests/test8.pdf.zst -o tests/tmp8 +zstd -d tests/test16.pdf.zst -o tests/tmp16 +zstd -d tests/test32.pdf.zst -o tests/tmp32 +zstd -d tests/test64.pdf.zst -o tests/tmp64 +zstd -d tests/test128.pdf.zst -o tests/tmp128 +zstd -d tests/test256.pdf.zst -o tests/tmp256 +zstd -d tests/test512.pdf.zst -o tests/tmp512 +zstd -d tests/test1024.pdf.zst -o tests/tmp1024 +zstd -d tests/test2048.pdf.zst -o tests/tmp2048 + +diff tests/test.pdf tests/tmp +diff tests/test2.pdf tests/tmp2 +diff tests/test4.pdf tests/tmp4 +diff tests/test8.pdf tests/tmp8 +diff tests/test16.pdf tests/tmp16 +diff tests/test32.pdf tests/tmp32 +diff tests/test64.pdf tests/tmp64 +diff tests/test128.pdf tests/tmp128 +diff tests/test256.pdf tests/tmp256 +diff tests/test512.pdf tests/tmp512 +diff tests/test1024.pdf tests/tmp1024 +diff tests/test2048.pdf tests/tmp2048 + +echo "finished with tests" + make clean From cc714f3bd3346fe62530ef0ae3c8179e4ef6423c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 16:54:34 -0700 Subject: [PATCH 022/145] added print statements and debuglog --- contrib/adaptive-compression/multi.c | 23 ++++++++++++++++++++++- contrib/adaptive-compression/pipetests.sh | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100755 contrib/adaptive-compression/pipetests.sh diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 089dfe5a3..40045381f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,9 +1,11 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 50; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 +#define DEFAULT_DISPLAY_LEVEL 1 typedef unsigned char BYTE; #include /* fprintf */ @@ -12,7 +14,7 @@ typedef unsigned char BYTE; #include /* memset */ #include "zstd.h" - +static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; typedef struct { void* start; @@ -158,11 +160,13 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; + // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(job->jobReady_mutex); while(job->jobReady == 0) { pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); } pthread_mutex_unlock(job->jobReady_mutex); + // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); @@ -175,11 +179,13 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(job->jobCompleted_mutex); job->jobCompleted = 1; + DEBUGLOG(2, "signaling for job %u\n", currJob); pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ + DEBUGLOG(2, "all jobs finished compressing\n"); break; } } @@ -194,11 +200,14 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; + DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(job->jobCompleted_mutex); while (job->jobCompleted == 0) { + DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); } pthread_mutex_unlock(job->jobCompleted_mutex); + DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -214,12 +223,17 @@ static void* outputThread(void* arg) } } currJob++; + DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(job->jobWrite_mutex); job->jobWritten = 1; pthread_cond_signal(job->jobWrite_cond); pthread_mutex_unlock(job->jobWrite_mutex); + DEBUGLOG(2, "unlocking job write mutex\n"); + + DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished with all jobs */ + DEBUGLOG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -235,11 +249,13 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; + // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(job->jobWrite_mutex); while (job->jobWritten == 0) { pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); } pthread_mutex_unlock(job->jobWrite_mutex); + // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; @@ -329,6 +345,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { + DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); ctx->lastJobID = ctx->nextJobID; break; } @@ -384,6 +401,10 @@ int main(int argCount, const char* argv[]) outFilename = argument; continue; } + else if (strlen(argument) > 1 && argument[1] == 'v') { + g_displayLevel++; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh new file mode 100755 index 000000000..2924cd5ad --- /dev/null +++ b/contrib/adaptive-compression/pipetests.sh @@ -0,0 +1,2 @@ +make clean multi +pv -q -L 50m tests/test2048.pdf | ./multi -v -otmp.zst From 6f3ad1b22e034cf27f34d63838c6f7cdfe55b015 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 17:24:21 -0700 Subject: [PATCH 023/145] fixed the problem with pipeline tests by changing how jobs move through the threads --- contrib/adaptive-compression/multi.c | 81 +++++++++++----------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 40045381f..fd04a53b8 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -26,15 +26,6 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; - unsigned jobCompleted; - unsigned jobReady; - unsigned jobWritten; - pthread_mutex_t* jobCompleted_mutex; - pthread_cond_t* jobCompleted_cond; - pthread_mutex_t* jobReady_mutex; - pthread_cond_t* jobReady_cond; - pthread_mutex_t* jobWrite_mutex; - pthread_cond_t* jobWrite_cond; size_t compressedSize; } jobDescription; @@ -45,6 +36,9 @@ typedef struct { unsigned lastJobID; unsigned nextJobID; unsigned threadError; + unsigned jobReadyID; + unsigned jobCompletedID; + unsigned jobWrittenID; unsigned allJobsCompleted; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; @@ -107,20 +101,11 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_mutex_init(&ctx->jobWrite_mutex, NULL); pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; + ctx->jobReadyID = 0; + ctx->jobCompletedID = 0; + ctx->jobWrittenID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - { - unsigned u; - for (u=0; ujobs[u].jobCompleted_mutex = &ctx->jobCompleted_mutex; - ctx->jobs[u].jobCompleted_cond = &ctx->jobCompleted_cond; - ctx->jobs[u].jobReady_mutex = &ctx->jobReady_mutex; - ctx->jobs[u].jobReady_cond = &ctx->jobReady_cond; - ctx->jobs[u].jobWrite_mutex = &ctx->jobWrite_mutex; - ctx->jobs[u].jobWrite_cond = &ctx->jobWrite_cond; - ctx->jobs[u].jobWritten = 1; - } - } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -161,11 +146,11 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); - pthread_mutex_lock(job->jobReady_mutex); - while(job->jobReady == 0) { - pthread_cond_wait(job->jobReady_cond, job->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex); + while(currJob + 1 > ctx->jobReadyID) { + pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } - pthread_mutex_unlock(job->jobReady_mutex); + pthread_mutex_unlock(&ctx->jobReady_mutex); // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { @@ -177,11 +162,11 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } - pthread_mutex_lock(job->jobCompleted_mutex); - job->jobCompleted = 1; + pthread_mutex_lock(&ctx->jobCompleted_mutex); + ctx->jobCompletedID++; DEBUGLOG(2, "signaling for job %u\n", currJob); - pthread_cond_signal(job->jobCompleted_cond); - pthread_mutex_unlock(job->jobCompleted_mutex); + pthread_cond_signal(&ctx->jobCompleted_cond); + pthread_mutex_unlock(&ctx->jobCompleted_mutex); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ @@ -201,12 +186,12 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUGLOG(2, "outputThread(): waiting on job completed\n"); - pthread_mutex_lock(job->jobCompleted_mutex); - while (job->jobCompleted == 0) { + pthread_mutex_lock(&ctx->jobCompleted_mutex); + while (currJob + 1 > ctx->jobCompletedID) { DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); - pthread_cond_wait(job->jobCompleted_cond, job->jobCompleted_mutex); + pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } - pthread_mutex_unlock(job->jobCompleted_mutex); + pthread_mutex_unlock(&ctx->jobCompleted_mutex); DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; @@ -224,10 +209,10 @@ static void* outputThread(void* arg) } currJob++; DEBUGLOG(2, "locking job write mutex\n"); - pthread_mutex_lock(job->jobWrite_mutex); - job->jobWritten = 1; - pthread_cond_signal(job->jobWrite_cond); - pthread_mutex_unlock(job->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex); + ctx->jobWrittenID++; + pthread_cond_signal(&ctx->jobWrite_cond); + pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); @@ -250,23 +235,17 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(job->jobWrite_mutex); - while (job->jobWritten == 0) { - pthread_cond_wait(job->jobWrite_cond, job->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex); + while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } - pthread_mutex_unlock(job->jobWrite_mutex); + pthread_mutex_unlock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->dst.start = malloc(job->dst.size); - job->jobCompleted = 0; - job->jobWritten = 0; - job->jobCompleted_cond = &ctx->jobCompleted_cond; - job->jobCompleted_mutex = &ctx->jobCompleted_mutex; - job->jobReady_cond = &ctx->jobReady_cond; - job->jobReady_mutex = &ctx->jobReady_mutex; job->jobID = nextJob; if (!job->src.start || !job->dst.start) { /* problem occurred, free things then return */ @@ -276,10 +255,10 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 1; } memcpy(job->src.start, data, srcSize); - pthread_mutex_lock(job->jobReady_mutex); - job->jobReady = 1; - pthread_cond_signal(job->jobReady_cond); - pthread_mutex_unlock(job->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex); + ctx->jobReadyID++; + pthread_cond_signal(&ctx->jobReady_cond); + pthread_mutex_unlock(&ctx->jobReady_mutex); ctx->nextJobID++; return 0; } From 79d4657ce5232cf7900407087962bb4ca978572e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 5 Jul 2017 17:44:36 -0700 Subject: [PATCH 024/145] small changes --- contrib/adaptive-compression/multi.c | 9 ++++++--- contrib/adaptive-compression/pipetests.sh | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index fd04a53b8..208a0a790 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -148,6 +148,7 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { + DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); @@ -185,14 +186,14 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { - DEBUGLOG(2, "inside job completed wait loop waiting on %u\n", currJob); + DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); - DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + // DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -236,7 +237,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); + DISPLAY("Creating new compression job -- nextJob: %u, jobWrittenID: %u, numJObs: %u\n", nextJob, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + DEBUGLOG(2, "waiting on job writtten, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh index 2924cd5ad..743ce381c 100755 --- a/contrib/adaptive-compression/pipetests.sh +++ b/contrib/adaptive-compression/pipetests.sh @@ -1,2 +1,2 @@ make clean multi -pv -q -L 50m tests/test2048.pdf | ./multi -v -otmp.zst +pv -q -L 500m tests/test2048.pdf | ./multi -v -otmp.zst From 94fe291b831ec9d0a631b052dda2aec69b01a111 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 10:29:16 -0700 Subject: [PATCH 025/145] small changes --- contrib/adaptive-compression/multi.c | 11 +++++++---- contrib/adaptive-compression/pipetests.sh | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 208a0a790..8371a0bcb 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,7 +1,7 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 50; +#define MAX_NUM_JOBS 100; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -168,6 +168,7 @@ static void* compressionThread(void* arg) DEBUGLOG(2, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompleted_cond); pthread_mutex_unlock(&ctx->jobCompleted_mutex); + DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { /* finished compressing all jobs */ @@ -189,7 +190,7 @@ static void* outputThread(void* arg) // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { - DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); @@ -208,6 +209,7 @@ static void* outputThread(void* arg) } } } + DEBUGLOG(2, "finished job write %u\n", currJob); currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); @@ -237,9 +239,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DISPLAY("Creating new compression job -- nextJob: %u, jobWrittenID: %u, numJObs: %u\n", nextJob, ctx->jobWrittenID, ctx->numJobs); + // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { - DEBUGLOG(2, "waiting on job writtten, nextJob: %u\n", nextJob); + DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); @@ -262,6 +264,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); + DEBUGLOG(2, "finished job creation %u\n", nextJob); ctx->nextJobID++; return 0; } diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh index 743ce381c..71f123a17 100755 --- a/contrib/adaptive-compression/pipetests.sh +++ b/contrib/adaptive-compression/pipetests.sh @@ -1,2 +1,2 @@ make clean multi -pv -q -L 500m tests/test2048.pdf | ./multi -v -otmp.zst +pv -q -L 100m tests/test2048.pdf | ./multi -v -otmp.zst From 592a0d9495319e7eef455282674020b422f7254d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 10:49:26 -0700 Subject: [PATCH 026/145] changed to work with std out --- contrib/adaptive-compression/multi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 8371a0bcb..6459e245a 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -274,20 +274,21 @@ static int compressFilename(const char* const srcFilename, const char* const dst BYTE* const src = malloc(FILE_CHUNK_SIZE); unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); + const char* const outFilename = (stdinUsed && !dstFilename) ? stdoutmark : dstFilename; size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !srcFile) { + if (!srcFilename || !outFilename || !src || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; } /* creating context */ - ctx = createCCtx(numJobs, dstFilename); + ctx = createCCtx(numJobs, outFilename); if (ctx == NULL) { ret = 1; goto cleanup; From f57849b9c61da16f27347704d58c2db969b08a1e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 11:05:51 -0700 Subject: [PATCH 027/145] added ability to set initial compression level --- contrib/adaptive-compression/multi.c | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 6459e245a..da182b177 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -6,6 +6,7 @@ #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 +#define DEFAULT_COMPRESSION_LEVEL 6 typedef unsigned char BYTE; #include /* fprintf */ @@ -15,6 +16,7 @@ typedef unsigned char BYTE; #include "zstd.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; +static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; typedef struct { void* start; @@ -91,7 +93,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) return NULL; } memset(ctx, 0, sizeof(adaptCCtx)); - ctx->compressionLevel = 6; /* default */ + ctx->compressionLevel = g_compressionLevel; pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ pthread_cond_init(&ctx->jobCompleted_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); @@ -362,6 +364,26 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles) return ret; } +/*! readU32FromChar() : + @return : unsigned integer value read from input in `char` format + allows and interprets K, KB, KiB, M, MB and MiB suffix. + Will also modify `*stringPtr`, advancing it to position where it stopped reading. + Note : function result can overflow if digit string > MAX_UINT */ +static unsigned readU32FromChar(const char** stringPtr) +{ + unsigned result = 0; + while ((**stringPtr >='0') && (**stringPtr <='9')) + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + if ((**stringPtr=='K') || (**stringPtr=='M')) { + result <<= 10; + if (**stringPtr=='M') result <<= 10; + (*stringPtr)++ ; + if (**stringPtr=='i') (*stringPtr)++; + if (**stringPtr=='B') (*stringPtr)++; + } + return result; +} + /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { @@ -391,6 +413,12 @@ int main(int argCount, const char* argv[]) g_displayLevel++; continue; } + else if (strlen(argument) > 1 && argument[1] == 'i') { + argument += 2; + g_compressionLevel = readU32FromChar(&argument); + DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From a407ccc215fa826d84ea9760b8bfe426489d406e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 13:09:17 -0700 Subject: [PATCH 028/145] added ability to congregate statistics into single print statement rather than using debug --- contrib/adaptive-compression/multi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index da182b177..6aa60315d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -17,12 +17,19 @@ typedef unsigned char BYTE; static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; +static unsigned g_displayStats = 0; typedef struct { void* start; size_t size; } buffer_t; +typedef struct { + unsigned waitCompleted; + unsigned waitReady; + unsigned waitWritten; +} stat_t; + typedef struct { buffer_t src; buffer_t dst; @@ -50,6 +57,7 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + stat_t stats; jobDescription* jobs; FILE* dstFile; } adaptCCtx; @@ -150,6 +158,7 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { + ctx->stats.waitReady++; DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } @@ -192,6 +201,7 @@ static void* outputThread(void* arg) // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { + ctx->stats.waitCompleted++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } @@ -243,6 +253,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) pthread_mutex_lock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { + ctx->stats.waitWritten++; DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } @@ -271,6 +282,14 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 0; } +static void printStats(stat_t stats) +{ + DISPLAY("========STATISTICS========\n"); + DISPLAY("# times waited on job ready: %u\n", stats.waitReady); + DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); + DISPLAY("# times waited on job written: %u\n\n", stats.waitWritten); +} + static int compressFilename(const char* const srcFilename, const char* const dstFilename) { BYTE* const src = malloc(FILE_CHUNK_SIZE); @@ -341,6 +360,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst cleanup: waitUntilAllJobsCompleted(ctx); + if (g_displayStats) printStats(ctx->stats); /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; @@ -419,6 +439,10 @@ int main(int argCount, const char* argv[]) DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); continue; } + else if (strlen(argument) > 1 && argument[1] == 's') { + g_displayStats = 1; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From ff9f2cd057041543851489c1ce8cc179efc2dacc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 16:06:53 -0700 Subject: [PATCH 029/145] added some basic logic for altering compression level --- contrib/adaptive-compression/multi.c | 71 +++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 6aa60315d..2b2aacabf 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,12 +1,13 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 100; +#define MAX_NUM_JOBS 2; #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 +#define DEFAULT_ADAPT_PARAM 2 typedef unsigned char BYTE; #include /* fprintf */ @@ -27,7 +28,10 @@ typedef struct { typedef struct { unsigned waitCompleted; unsigned waitReady; - unsigned waitWritten; + unsigned waitWrite; + unsigned readyCounter; + unsigned completedCounter; + unsigned writeCounter; } stat_t; typedef struct { @@ -47,8 +51,9 @@ typedef struct { unsigned threadError; unsigned jobReadyID; unsigned jobCompletedID; - unsigned jobWrittenID; + unsigned jobWriteID; unsigned allJobsCompleted; + unsigned adaptParam; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; pthread_mutex_t jobReady_mutex; @@ -113,12 +118,13 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->numJobs = numJobs; ctx->jobReadyID = 0; ctx->jobCompletedID = 0; - ctx->jobWrittenID = 0; + ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; + ctx->adaptParam = DEFAULT_ADAPT_PARAM; if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); freeCCtx(ctx); @@ -148,6 +154,41 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); } +static unsigned adaptCompressionLevel(adaptCCtx* ctx) +{ + unsigned reset = 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.completedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.completedCounter ? 1 : 0; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; + // unsigned const writeSlow = ((compressWaiting && createWaiting)) ? 1 : 0; + // unsigned const compressSlow = ((writeWaiting && createWaiting)) ? 1 : 0; + // unsigned const createSlow = ((compressWaiting && writeWaiting)) ? 1 : 0; + DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUGLOG(2, "increasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel++; + reset = 1; + } + else if (compressSlow && ctx->compressionLevel > 1) { + DEBUGLOG(2, "decreasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel--; + reset = 1; + } + if (reset) { + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.completedCounter = 0; + } + return ctx->compressionLevel; +} + static void* compressionThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -159,6 +200,7 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; + ctx->stats.readyCounter++; DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } @@ -166,7 +208,10 @@ static void* compressionThread(void* arg) // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { - size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, job->compressionLevel); + unsigned const cLevel = adaptCompressionLevel(ctx); + // unsigned const cLevel = job->compressionLevel; + DEBUGLOG(2, "cLevel used: %u\n", cLevel); + size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); if (ZSTD_isError(compressedSize)) { ctx->threadError = 1; DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); @@ -202,6 +247,7 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { ctx->stats.waitCompleted++; + ctx->stats.completedCounter++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } @@ -225,7 +271,7 @@ static void* outputThread(void* arg) currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - ctx->jobWrittenID++; + ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); @@ -251,14 +297,17 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWrittenID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWrittenID, ctx->numJobs); - while (nextJob - ctx->jobWrittenID >= ctx->numJobs) { - ctx->stats.waitWritten++; - DEBUGLOG(2, "waiting on job written, nextJob: %u\n", nextJob); + // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + while (nextJob - ctx->jobWriteID >= ctx->numJobs) { + ctx->stats.waitWrite++; + ctx->stats.writeCounter++; + DEBUGLOG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + + job->compressionLevel = ctx->compressionLevel; job->src.start = malloc(srcSize); job->src.size = srcSize; @@ -287,7 +336,7 @@ static void printStats(stat_t stats) DISPLAY("========STATISTICS========\n"); DISPLAY("# times waited on job ready: %u\n", stats.waitReady); DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); - DISPLAY("# times waited on job written: %u\n\n", stats.waitWritten); + DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } static int compressFilename(const char* const srcFilename, const char* const dstFilename) From b6cc0847162ff6eb7e7398f92736ecf4941b614c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 17:48:18 -0700 Subject: [PATCH 030/145] added really simple progress update in the corner --- contrib/adaptive-compression/multi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 2b2aacabf..324a570ba 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -14,11 +14,14 @@ typedef unsigned char BYTE; #include /* malloc, free */ #include /* pthread functions */ #include /* memset */ +#include /* clock(), CLOCKS_PER_SEC */ #include "zstd.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; +static clock_t g_time = 0; +static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ typedef struct { void* start; @@ -235,6 +238,16 @@ static void* compressionThread(void* arg) return arg; } +static void displayProgress(unsigned jobDoneID) +{ + clock_t currTime = clock(); + unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; + if (refresh) { + fprintf(stdout, "%u jobs completed\r", jobDoneID+1); + fflush(stdout); + } +} + static void* outputThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -268,6 +281,7 @@ static void* outputThread(void* arg) } } DEBUGLOG(2, "finished job write %u\n", currJob); + displayProgress(currJob); currJob++; DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); @@ -348,6 +362,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; + g_time = clock(); /* checking for errors */ From 57ec0232a832ad93c1b9b57145e6a21c6c41da84 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 18:09:10 -0700 Subject: [PATCH 031/145] added help menu --- contrib/adaptive-compression/multi.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 324a570ba..4e33a0514 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -1,4 +1,5 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 2; @@ -243,7 +244,7 @@ static void displayProgress(unsigned jobDoneID) clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; if (refresh) { - fprintf(stdout, "%u jobs completed\r", jobDoneID+1); + fprintf(stdout, "\r%u jobs completed", jobDoneID+1); fflush(stdout); } } @@ -468,6 +469,18 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } +static void help() +{ + PRINT("Usage:\n"); + PRINT(" ./multi [options] [file(s)]\n"); + PRINT("\n"); + PRINT("Options:\n"); + PRINT(" -oFILE : specify the output file name\n"); + PRINT(" -v : display debug information\n"); + PRINT(" -i# : provide initial compression level\n"); + PRINT(" -s : display information stats\n"); + PRINT(" -h : display help/information\n"); +} /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) { @@ -507,6 +520,10 @@ int main(int argCount, const char* argv[]) g_displayStats = 1; continue; } + else if (strlen(argument) > 1 && argument[1] == 'h') { + help(); + return 0; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From 2939301023cdda52bd6e7883206a58722f861463 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 20:30:20 -0700 Subject: [PATCH 032/145] fixed problem with progress bar not persisting, added time elapsed --- contrib/adaptive-compression/multi.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 4e33a0514..814018d68 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -22,6 +22,7 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; static clock_t g_time = 0; +static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ typedef struct { @@ -239,13 +240,19 @@ static void* compressionThread(void* arg) return arg; } -static void displayProgress(unsigned jobDoneID) +static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; + double timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); if (refresh) { - fprintf(stdout, "\r%u jobs completed", jobDoneID+1); - fflush(stdout); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0fms |", jobDoneID, cLevel, timeElapsed); + if (last) { + fprintf(stdout, "\n"); + } + else { + fflush(stdout); + } } } @@ -282,8 +289,8 @@ static void* outputThread(void* arg) } } DEBUGLOG(2, "finished job write %u\n", currJob); - displayProgress(currJob); currJob++; + displayProgress(currJob, ctx->compressionLevel, currJob >= ctx->lastJobID); DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; @@ -364,6 +371,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst int ret = 0; adaptCCtx* ctx = NULL; g_time = clock(); + g_startTime = clock(); /* checking for errors */ From f351848b76d6c3f780f1679556eb269dec81d4d8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 6 Jul 2017 20:40:00 -0700 Subject: [PATCH 033/145] added data amount --- contrib/adaptive-compression/multi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 814018d68..7c769e499 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -24,6 +24,7 @@ static unsigned g_displayStats = 0; static clock_t g_time = 0; static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ +static size_t g_streamedSize = 0; typedef struct { void* start; @@ -244,9 +245,10 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; - double timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); + double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); + double const sizeMB = (double)g_streamedSize / (1 << 20); if (refresh) { - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0fms |", jobDoneID, cLevel, timeElapsed); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB |", jobDoneID, cLevel, timeElapsed, sizeMB); if (last) { fprintf(stdout, "\n"); } @@ -372,6 +374,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst adaptCCtx* ctx = NULL; g_time = clock(); g_startTime = clock(); + g_streamedSize = 0; /* checking for errors */ @@ -416,6 +419,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst ret = 1; goto cleanup; } + g_streamedSize += readSize; /* reading was fine, now create the compression job */ { int const error = createCompressionJob(ctx, src, readSize); From 00bc5df4e0de9811a3579052b0fbeaa949aecb93 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 09:35:39 -0700 Subject: [PATCH 034/145] added compression rate to status bar --- contrib/adaptive-compression/multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 7c769e499..f8568c42f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -247,8 +247,9 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); double const sizeMB = (double)g_streamedSize / (1 << 20); + double const avgCompRate = sizeMB / timeElapsed; if (refresh) { - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB |", jobDoneID, cLevel, timeElapsed, sizeMB); + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { fprintf(stdout, "\n"); } From 4679132f59f02aa2c8fc3a3a3bf7a561a22e58f4 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:25:38 -0700 Subject: [PATCH 035/145] updated avg compression rate, also hiding progress bar behind a flag now --- contrib/adaptive-compression/multi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index f8568c42f..5223d7068 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -25,6 +25,7 @@ static clock_t g_time = 0; static clock_t g_startTime = 0; static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ static size_t g_streamedSize = 0; +static unsigned g_useProgressBar = 0; typedef struct { void* start; @@ -243,11 +244,12 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { + if (!g_useProgressBar) return; clock_t currTime = clock(); unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); double const sizeMB = (double)g_streamedSize / (1 << 20); - double const avgCompRate = sizeMB / timeElapsed; + double const avgCompRate = sizeMB * 1000 / timeElapsed; if (refresh) { fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { @@ -537,6 +539,10 @@ int main(int argCount, const char* argv[]) help(); return 0; } + else if (strlen(argument) > 1 && argument[1] == 'p') { + g_useProgressBar = 1; + continue; + } else { DISPLAY("Error: invalid argument provided\n"); ret = 1; From f7e6b358d04d8b1da2599ed039c1c12d301db6f2 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:29:06 -0700 Subject: [PATCH 036/145] added tests that check to ensure stdout is working --- contrib/adaptive-compression/run.sh | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index b906ae7d1..f030be555 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -76,6 +76,42 @@ diff tmp tests/test.pdf echo "diff test complete: test.pdf" rm tmp* +cat tests/test2048.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test2048.pdf +echo "diff test complete: test2048.pdf" +rm tmp* + +cat tests/test512.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test512.pdf +echo "diff test complete: test512.pdf" +rm tmp* + +cat tests/test64.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test64.pdf +echo "diff test complete: test64.pdf" +rm tmp* + +cat tests/test16.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test16.pdf +echo "diff test complete: test16.pdf" +rm tmp* + +cat tests/test4.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test4.pdf +echo "diff test complete: test4.pdf" +rm tmp* + +cat tests/test.pdf | ./multi > tmp.zst +zstd -d tmp.zst +diff tmp tests/test.pdf +echo "diff test complete: test.pdf" +rm tmp* + echo "Running multi-file tests" ./multi tests/* zstd -d tests/test.pdf.zst -o tests/tmp From 532f439961747c60d13d8dde0dee4aafb1342426 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 10:58:43 -0700 Subject: [PATCH 037/145] cleaned up code for arguments a bit --- contrib/adaptive-compression/multi.c | 60 +++++++++++++--------------- contrib/adaptive-compression/run.sh | 3 ++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 5223d7068..d88d37878 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -515,39 +515,35 @@ int main(int argCount, const char* argv[]) const char* argument = argv[argNum]; /* output filename designated with "-o" */ - if (argument[0]=='-') { - if (strlen(argument) > 1 && argument[1] == 'o') { - argument += 2; - outFilename = argument; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'v') { - g_displayLevel++; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'i') { - argument += 2; - g_compressionLevel = readU32FromChar(&argument); - DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); - continue; - } - else if (strlen(argument) > 1 && argument[1] == 's') { - g_displayStats = 1; - continue; - } - else if (strlen(argument) > 1 && argument[1] == 'h') { - help(); - return 0; - } - else if (strlen(argument) > 1 && argument[1] == 'p') { - g_useProgressBar = 1; - continue; - } - else { - DISPLAY("Error: invalid argument provided\n"); - ret = 1; - goto _main_exit; + if (argument[0]=='-' && strlen(argument) > 1) { + switch (argument[1]) { + case 'o': + argument += 2; + outFilename = argument; + break; + case 'v': + g_displayLevel++; + break; + case 'i': + argument += 2; + g_compressionLevel = readU32FromChar(&argument); + DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + break; + case 's': + g_displayStats = 1; + break; + case 'h': + help(); + goto _main_exit; + case 'p': + g_useProgressBar = 1; + break; + default: + DISPLAY("Error: invalid argument provided\n"); + ret = 1; + goto _main_exit; } + continue; } /* regular files to be compressed */ diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index f030be555..bcd8c7188 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -140,6 +140,9 @@ diff tests/test512.pdf tests/tmp512 diff tests/test1024.pdf tests/tmp1024 diff tests/test2048.pdf tests/tmp2048 +echo "Running Args Tests" +./multi -h +./multi -i22 -p -s -otmp.zst tests/test2048.pdf echo "finished with tests" make clean From 70a4153bd38ddf2f44067193c5fcdf7b1273073b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 11:32:14 -0700 Subject: [PATCH 038/145] added ability to force output to stdout, wrote an additional test for this functionality --- contrib/adaptive-compression/multi.c | 20 +++++++++++++++----- contrib/adaptive-compression/run.sh | 8 ++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index d88d37878..7802c429d 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -95,7 +95,7 @@ static int freeCCtx(adaptCCtx* ctx) int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = ctx->dstFile != NULL ? fclose(ctx->dstFile) : 0; + int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); @@ -448,7 +448,7 @@ cleanup: return ret; } -static int compressFilenames(const char** filenameTable, unsigned numFiles) +static int compressFilenames(const char** filenameTable, unsigned numFiles, unsigned forceStdout) { int ret = 0; unsigned fileNum; @@ -459,7 +459,13 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles) DISPLAY("Error: output filename is too long\n"); return 1; } - ret |= compressFilename(filename, outFile); + if (!forceStdout) { + ret |= compressFilename(filename, outFile); + } + else { + ret |= compressFilename(filename, stdoutmark); + } + } return ret; } @@ -503,6 +509,7 @@ int main(int argCount, const char* argv[]) const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; filenameTable[0] = stdinmark; + unsigned forceStdout = 0; int ret = 0; int argNum; @@ -538,6 +545,9 @@ int main(int argCount, const char* argv[]) case 'p': g_useProgressBar = 1; break; + case 'c': + forceStdout = 1; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; @@ -551,7 +561,7 @@ int main(int argCount, const char* argv[]) } /* error checking with number of files */ - if (filenameIdx > 1 && outFilename != NULL) { + if (filenameIdx > 1 && (outFilename != NULL && strcmp(outFilename, stdoutmark))) { DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); ret = 1; goto _main_exit; @@ -562,7 +572,7 @@ int main(int argCount, const char* argv[]) ret |= compressFilename(filenameTable[0], outFilename); } else { - ret |= compressFilenames(filenameTable, filenameIdx); + ret |= compressFilenames(filenameTable, filenameIdx, forceStdout); } _main_exit: free(filenameTable); diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index bcd8c7188..67814ec1b 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -140,9 +140,17 @@ diff tests/test512.pdf tests/tmp512 diff tests/test1024.pdf tests/tmp1024 diff tests/test2048.pdf tests/tmp2048 +rm -f tests/*.zst tests/tmp* echo "Running Args Tests" ./multi -h ./multi -i22 -p -s -otmp.zst tests/test2048.pdf +rm tmp* + +echo "Running Tests With Multiple Files > stdout" +./multi tests/* -c > tmp.zst +zstd -d tmp.zst +rm tmp* + echo "finished with tests" make clean From 8c0eb62920f0badddabedd4804b79806c1bf2a73 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 11:47:16 -0700 Subject: [PATCH 039/145] removed unnecessary comments, uncommented DEBUGLOG for later use --- contrib/adaptive-compression/multi.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 7802c429d..acb06f074 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -171,9 +171,6 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - // unsigned const writeSlow = ((compressWaiting && createWaiting)) ? 1 : 0; - // unsigned const compressSlow = ((writeWaiting && createWaiting)) ? 1 : 0; - // unsigned const createSlow = ((compressWaiting && writeWaiting)) ? 1 : 0; DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; @@ -203,7 +200,7 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - // DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); + DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; @@ -212,11 +209,10 @@ static void* compressionThread(void* arg) pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - // DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); + DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - // unsigned const cLevel = job->compressionLevel; DEBUGLOG(2, "cLevel used: %u\n", cLevel); size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); if (ZSTD_isError(compressedSize)) { @@ -269,7 +265,7 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - // DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + DEBUGLOG(2, "outputThread(): waiting on job completed\n"); pthread_mutex_lock(&ctx->jobCompleted_mutex); while (currJob + 1 > ctx->jobCompletedID) { ctx->stats.waitCompleted++; @@ -278,7 +274,7 @@ static void* outputThread(void* arg) pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); } pthread_mutex_unlock(&ctx->jobCompleted_mutex); - // DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -322,9 +318,9 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - // DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); + DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; @@ -332,7 +328,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - // DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; From 09d7c6a994d5f7a67a7442ad92d4dfc2e0a06bda Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 13:18:55 -0700 Subject: [PATCH 040/145] changed completed variables to compressed for clarity --- contrib/adaptive-compression/multi.c | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index acb06f074..03667d48f 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -33,11 +33,11 @@ typedef struct { } buffer_t; typedef struct { - unsigned waitCompleted; + unsigned waitCompressed; unsigned waitReady; unsigned waitWrite; unsigned readyCounter; - unsigned completedCounter; + unsigned compressedCounter; unsigned writeCounter; } stat_t; @@ -57,12 +57,12 @@ typedef struct { unsigned nextJobID; unsigned threadError; unsigned jobReadyID; - unsigned jobCompletedID; + unsigned jobCompressedID; unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - pthread_mutex_t jobCompleted_mutex; - pthread_cond_t jobCompleted_cond; + pthread_mutex_t jobCompressed_mutex; + pthread_cond_t jobCompressed_cond; pthread_mutex_t jobReady_mutex; pthread_cond_t jobReady_cond; pthread_mutex_t allJobsCompleted_mutex; @@ -87,8 +87,8 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { { - int const completedMutexError = pthread_mutex_destroy(&ctx->jobCompleted_mutex); - int const completedCondError = pthread_cond_destroy(&ctx->jobCompleted_cond); + int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); + int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); @@ -100,7 +100,7 @@ static int freeCCtx(adaptCCtx* ctx) freeCompressionJobs(ctx); free(ctx->jobs); } - return completedMutexError | completedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; + return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; } } @@ -114,8 +114,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompleted_mutex, NULL); /* TODO: add checks for errors on each mutex */ - pthread_cond_init(&ctx->jobCompleted_cond, NULL); + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); /* TODO: add checks for errors on each mutex */ + pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); @@ -124,7 +124,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) pthread_cond_init(&ctx->jobWrite_cond, NULL); ctx->numJobs = numJobs; ctx->jobReadyID = 0; - ctx->jobCompletedID = 0; + ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -164,14 +164,14 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) static unsigned adaptCompressionLevel(adaptCCtx* ctx) { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.completedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.completedCounter ? 1 : 0; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter ? 1 : 0; unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUGLOG(2, "ready: %u completed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.completedCounter, ctx->stats.writeCounter); + DEBUGLOG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } @@ -188,7 +188,7 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; - ctx->stats.completedCounter = 0; + ctx->stats.compressedCounter = 0; } return ctx->compressionLevel; } @@ -222,11 +222,11 @@ static void* compressionThread(void* arg) } job->compressedSize = compressedSize; } - pthread_mutex_lock(&ctx->jobCompleted_mutex); - ctx->jobCompletedID++; + pthread_mutex_lock(&ctx->jobCompressed_mutex); + ctx->jobCompressedID++; DEBUGLOG(2, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompleted_cond); - pthread_mutex_unlock(&ctx->jobCompleted_mutex); + pthread_cond_signal(&ctx->jobCompressed_cond); + pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; if (currJob >= ctx->lastJobID || ctx->threadError) { @@ -266,14 +266,14 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUGLOG(2, "outputThread(): waiting on job completed\n"); - pthread_mutex_lock(&ctx->jobCompleted_mutex); - while (currJob + 1 > ctx->jobCompletedID) { - ctx->stats.waitCompleted++; - ctx->stats.completedCounter++; + pthread_mutex_lock(&ctx->jobCompressed_mutex); + while (currJob + 1 > ctx->jobCompressedID) { + ctx->stats.waitCompressed++; + ctx->stats.compressedCounter++; DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobCompleted_cond, &ctx->jobCompleted_mutex); + pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } - pthread_mutex_unlock(&ctx->jobCompleted_mutex); + pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "outputThread(): continuing after job completed\n"); { size_t const compressedSize = job->compressedSize; @@ -320,7 +320,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompletedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompletedID, ctx->jobWriteID, ctx->numJobs); + DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; @@ -358,7 +358,7 @@ static void printStats(stat_t stats) { DISPLAY("========STATISTICS========\n"); DISPLAY("# times waited on job ready: %u\n", stats.waitReady); - DISPLAY("# times waited on job completed: %u\n", stats.waitCompleted); + DISPLAY("# times waited on job compressed: %u\n", stats.waitCompressed); DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } From 11fc0f41197f885def5744e50dfe5418364ca724 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 13:55:38 -0700 Subject: [PATCH 041/145] changed completed -> compressed --- contrib/adaptive-compression/multi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 03667d48f..647d54ba4 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -265,16 +265,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job completed\n"); + DEBUGLOG(2, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUGLOG(2, "waiting on job completed, nextJob: %u\n", currJob); + DEBUGLOG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "outputThread(): continuing after job completed\n"); + DEBUGLOG(2, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { From c0c236a28b13ccbc71465eba4c7a52bae96a8e8a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 15:13:40 -0700 Subject: [PATCH 042/145] changed to using compressCCtx --- contrib/adaptive-compression/multi.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 647d54ba4..943bd8564 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -72,6 +72,7 @@ typedef struct { stat_t stats; jobDescription* jobs; FILE* dstFile; + ZSTD_CCtx* cctx; } adaptCCtx; static void freeCompressionJobs(adaptCCtx* ctx) @@ -96,11 +97,12 @@ static int freeCCtx(adaptCCtx* ctx) int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); } - return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError; + return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; } } @@ -132,6 +134,12 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->threadError = 0; ctx->allJobsCompleted = 0; ctx->adaptParam = DEFAULT_ADAPT_PARAM; + ctx->cctx = ZSTD_createCCtx(); + if (!ctx->cctx) { + DISPLAY("Error: could not allocate ZSTD_CCtx\n"); + freeCCtx(ctx); + return NULL; + } if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); freeCCtx(ctx); @@ -214,7 +222,7 @@ static void* compressionThread(void* arg) { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUGLOG(2, "cLevel used: %u\n", cLevel); - size_t const compressedSize = ZSTD_compress(job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); + size_t const compressedSize = ZSTD_compressCCtx(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); if (ZSTD_isError(compressedSize)) { ctx->threadError = 1; DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); From 1c9d6b2c6b26f0c916218d5ecfad8a155273c403 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 15:42:20 -0700 Subject: [PATCH 043/145] rewrote time elapsed with UTIL --- contrib/adaptive-compression/Makefile | 1 + contrib/adaptive-compression/multi.c | 38 +++++++++++++-------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 2ebec9ba9..2d3de33ea 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -1,5 +1,6 @@ ZSTDDIR = ../../lib +PRGDIR = ../../programs ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 943bd8564..9f981f812 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -15,17 +15,16 @@ typedef unsigned char BYTE; #include /* malloc, free */ #include /* pthread functions */ #include /* memset */ -#include /* clock(), CLOCKS_PER_SEC */ #include "zstd.h" +#include "util.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; -static clock_t g_time = 0; -static clock_t g_startTime = 0; -static clock_t const refreshRate = CLOCKS_PER_SEC / 60; /* 60 Hz */ +static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 0; +static UTIL_freq_t g_ticksPerSecond; typedef struct { void* start; @@ -39,7 +38,7 @@ typedef struct { unsigned readyCounter; unsigned compressedCounter; unsigned writeCounter; -} stat_t; +} cStat_t; typedef struct { buffer_t src; @@ -69,7 +68,7 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; - stat_t stats; + cStat_t stats; jobDescription* jobs; FILE* dstFile; ZSTD_CCtx* cctx; @@ -249,19 +248,17 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) { if (!g_useProgressBar) return; - clock_t currTime = clock(); - unsigned const refresh = currTime - g_time > refreshRate ? 1 : 0; - double const timeElapsed = (double)((currTime - g_startTime) * 1000 / CLOCKS_PER_SEC); + UTIL_time_t currTime; + UTIL_getTime(&currTime); + double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - if (refresh) { - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); - if (last) { - fprintf(stdout, "\n"); - } - else { - fflush(stdout); - } + fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); + if (last) { + fprintf(stdout, "\n"); + } + else { + fflush(stdout); } } @@ -362,7 +359,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) return 0; } -static void printStats(stat_t stats) +static void printStats(cStat_t stats) { DISPLAY("========STATISTICS========\n"); DISPLAY("# times waited on job ready: %u\n", stats.waitReady); @@ -379,8 +376,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; - g_time = clock(); - g_startTime = clock(); + UTIL_getTime(&g_startTime); g_streamedSize = 0; @@ -517,6 +513,8 @@ int main(int argCount, const char* argv[]) int ret = 0; int argNum; + UTIL_initTimer(&g_ticksPerSecond); + if (filenameTable == NULL) { DISPLAY("Error: could not allocate sapce for filename table.\n"); return 1; From 7163ffafde9fdc2eac50ae12056b2cf66daa2945 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 15:56:00 -0700 Subject: [PATCH 044/145] playing around with adapt param --- contrib/adaptive-compression/multi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 9f981f812..41fd9af49 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -8,7 +8,7 @@ #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 -#define DEFAULT_ADAPT_PARAM 2 +#define DEFAULT_ADAPT_PARAM 1 typedef unsigned char BYTE; #include /* fprintf */ From c3ae23d459611438ede914036a6ace1b37849ad6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 7 Jul 2017 17:07:05 -0700 Subject: [PATCH 045/145] added ability to compress without specifying out filename --- contrib/adaptive-compression/multi.c | 21 +++++++++++++-------- contrib/adaptive-compression/run.sh | 6 ++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 41fd9af49..78d9ca154 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -367,18 +367,28 @@ static void printStats(cStat_t stats) DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } -static int compressFilename(const char* const srcFilename, const char* const dstFilename) +static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) { BYTE* const src = malloc(FILE_CHUNK_SIZE); unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); - const char* const outFilename = (stdinUsed && !dstFilename) ? stdoutmark : dstFilename; + const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; + const char* outFilename = outFilenameIntermediate; + char fileAndSuffix[MAX_PATH]; size_t const numJobs = MAX_NUM_JOBS; int ret = 0; adaptCCtx* ctx = NULL; UTIL_getTime(&g_startTime); g_streamedSize = 0; + if (!outFilenameIntermediate) { + if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) { + DISPLAY("Error: output filename is too long\n"); + ret = 1; + goto cleanup; + } + outFilename = fileAndSuffix; + } /* checking for errors */ if (!srcFilename || !outFilename || !src || !srcFile) { @@ -452,15 +462,10 @@ static int compressFilenames(const char** filenameTable, unsigned numFiles, unsi { int ret = 0; unsigned fileNum; - char outFile[MAX_PATH]; for (fileNum=0; fileNum MAX_PATH) { - DISPLAY("Error: output filename is too long\n"); - return 1; - } if (!forceStdout) { - ret |= compressFilename(filename, outFile); + ret |= compressFilename(filename, NULL); } else { ret |= compressFilename(filename, stdoutmark); diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh index 67814ec1b..3c40969e6 100755 --- a/contrib/adaptive-compression/run.sh +++ b/contrib/adaptive-compression/run.sh @@ -151,6 +151,12 @@ echo "Running Tests With Multiple Files > stdout" zstd -d tmp.zst rm tmp* +echo "Running single test without output filename" +./multi tests/test2048.pdf -p +zstd -d tests/test2048.pdf.zst -o tmp +diff tmp tests/test2048.pdf +rm tmp* + echo "finished with tests" make clean From 62ebbabd32dfccb5363cb245c06f7affb7535727 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 09:36:22 -0700 Subject: [PATCH 046/145] updated error checking in each thread --- contrib/adaptive-compression/multi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/multi.c index 78d9ca154..4c3f584e1 100644 --- a/contrib/adaptive-compression/multi.c +++ b/contrib/adaptive-compression/multi.c @@ -284,12 +284,14 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); + ctx->threadError = 1; return arg; } { size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); + ctx->threadError = 1; return arg; } } @@ -429,6 +431,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); + ctx->threadError = 1; ret = 1; goto cleanup; } @@ -438,6 +441,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst int const error = createCompressionJob(ctx, src, readSize); if (error != 0) { ret = error; + ctx->threadError = 1; goto cleanup; } } From 82f0d64beefb4d6c7041ab131602b22c23e320ae Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:51:50 -0700 Subject: [PATCH 047/145] removed single.c --- contrib/adaptive-compression/single.c | 73 --------------------------- 1 file changed, 73 deletions(-) delete mode 100644 contrib/adaptive-compression/single.c diff --git a/contrib/adaptive-compression/single.c b/contrib/adaptive-compression/single.c deleted file mode 100644 index ef8062d8f..000000000 --- a/contrib/adaptive-compression/single.c +++ /dev/null @@ -1,73 +0,0 @@ -#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) -#define FILE_CHUNK_SIZE 4 << 20 -typedef unsigned char BYTE; - -#include -#include -#include "zstd.h" - - - -/* return 0 if successful, else return error */ -int main(int argCount, const char* argv[]) -{ - const char* const srcFilename = argv[1]; - const char* const dstFilename = argv[2]; - FILE* const srcFile = fopen(srcFilename, "rb"); - FILE* const dstFile = fopen(dstFilename, "wb"); - BYTE* const src = malloc(FILE_CHUNK_SIZE); - size_t const dstSize = ZSTD_compressBound(FILE_CHUNK_SIZE); - BYTE* const dst = malloc(dstSize); - int ret = 0; - - /* checking for errors */ - if (!srcFilename || !dstFilename || !src || !dst) { - DISPLAY("Error: initial variables could not be allocated\n"); - ret = 1; - goto cleanup; - } - - /* compressing in blocks */ - for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { - DISPLAY("Error: could not read %d bytes\n", FILE_CHUNK_SIZE); - ret = 1; - goto cleanup; - } - { - size_t const compressedSize = ZSTD_compress(dst, dstSize, src, readSize, 6); - if (ZSTD_isError(compressedSize)) { - DISPLAY("Error: something went wrong during compression\n"); - ret = 1; - goto cleanup; - } - { - size_t const writeSize = fwrite(dst, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { - DISPLAY("Error: could not write compressed data to file\n"); - ret = 1; - goto cleanup; - } - } - } - if (feof(srcFile)) { - /* reached end of file */ - break; - } - } - - /* file compression completed */ - { - int const error = fclose(srcFile); - if (ret != 0) { - DISPLAY("Error: could not close the file\n"); - ret = error; - goto cleanup; - } - } -cleanup: - if (src != NULL) free(src); - if (dst != NULL) free(dst); - return ret; -} From ced3ec5714073fd17a4711d132a6da876617833a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:53:02 -0700 Subject: [PATCH 048/145] removed scripts --- contrib/adaptive-compression/pipetests.sh | 2 - contrib/adaptive-compression/run.sh | 162 ---------------------- 2 files changed, 164 deletions(-) delete mode 100755 contrib/adaptive-compression/pipetests.sh delete mode 100755 contrib/adaptive-compression/run.sh diff --git a/contrib/adaptive-compression/pipetests.sh b/contrib/adaptive-compression/pipetests.sh deleted file mode 100755 index 71f123a17..000000000 --- a/contrib/adaptive-compression/pipetests.sh +++ /dev/null @@ -1,2 +0,0 @@ -make clean multi -pv -q -L 100m tests/test2048.pdf | ./multi -v -otmp.zst diff --git a/contrib/adaptive-compression/run.sh b/contrib/adaptive-compression/run.sh deleted file mode 100755 index 3c40969e6..000000000 --- a/contrib/adaptive-compression/run.sh +++ /dev/null @@ -1,162 +0,0 @@ -make clean multi - -echo "running file tests" - -./multi tests/test2048.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -./multi tests/test512.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -./multi tests/test64.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -./multi tests/test16.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -./multi tests/test4.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -./multi tests/test.pdf -otmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -echo "Running std input/output tests" - -cat tests/test2048.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -cat tests/test512.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -cat tests/test64.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -cat tests/test16.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -cat tests/test4.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -cat tests/test.pdf | ./multi -otmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -cat tests/test2048.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test2048.pdf -echo "diff test complete: test2048.pdf" -rm tmp* - -cat tests/test512.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test512.pdf -echo "diff test complete: test512.pdf" -rm tmp* - -cat tests/test64.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test64.pdf -echo "diff test complete: test64.pdf" -rm tmp* - -cat tests/test16.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test16.pdf -echo "diff test complete: test16.pdf" -rm tmp* - -cat tests/test4.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test4.pdf -echo "diff test complete: test4.pdf" -rm tmp* - -cat tests/test.pdf | ./multi > tmp.zst -zstd -d tmp.zst -diff tmp tests/test.pdf -echo "diff test complete: test.pdf" -rm tmp* - -echo "Running multi-file tests" -./multi tests/* -zstd -d tests/test.pdf.zst -o tests/tmp -zstd -d tests/test2.pdf.zst -o tests/tmp2 -zstd -d tests/test4.pdf.zst -o tests/tmp4 -zstd -d tests/test8.pdf.zst -o tests/tmp8 -zstd -d tests/test16.pdf.zst -o tests/tmp16 -zstd -d tests/test32.pdf.zst -o tests/tmp32 -zstd -d tests/test64.pdf.zst -o tests/tmp64 -zstd -d tests/test128.pdf.zst -o tests/tmp128 -zstd -d tests/test256.pdf.zst -o tests/tmp256 -zstd -d tests/test512.pdf.zst -o tests/tmp512 -zstd -d tests/test1024.pdf.zst -o tests/tmp1024 -zstd -d tests/test2048.pdf.zst -o tests/tmp2048 - -diff tests/test.pdf tests/tmp -diff tests/test2.pdf tests/tmp2 -diff tests/test4.pdf tests/tmp4 -diff tests/test8.pdf tests/tmp8 -diff tests/test16.pdf tests/tmp16 -diff tests/test32.pdf tests/tmp32 -diff tests/test64.pdf tests/tmp64 -diff tests/test128.pdf tests/tmp128 -diff tests/test256.pdf tests/tmp256 -diff tests/test512.pdf tests/tmp512 -diff tests/test1024.pdf tests/tmp1024 -diff tests/test2048.pdf tests/tmp2048 - -rm -f tests/*.zst tests/tmp* -echo "Running Args Tests" -./multi -h -./multi -i22 -p -s -otmp.zst tests/test2048.pdf -rm tmp* - -echo "Running Tests With Multiple Files > stdout" -./multi tests/* -c > tmp.zst -zstd -d tmp.zst -rm tmp* - -echo "Running single test without output filename" -./multi tests/test2048.pdf -p -zstd -d tests/test2048.pdf.zst -o tmp -diff tmp tests/test2048.pdf -rm tmp* - -echo "finished with tests" - -make clean From ed72ea54380dc732fe9c648f9cf5ee683fad17d0 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 10:58:03 -0700 Subject: [PATCH 049/145] removed single from Makefile --- contrib/adaptive-compression/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 2d3de33ea..3027b88c3 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,15 +19,13 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean single multi -single: $(ZSTD_FILES) single.c - $(CC) $(FLAGS) $^ -o $@ +all: clean multi multi: $(ZSTD_FILES) multi.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f single multi + @$(RM) -f multi @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst From 7e09b508ff5e58fb1c4272b1616d808d10110392 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 11:05:37 -0700 Subject: [PATCH 050/145] changed name --- contrib/adaptive-compression/Makefile | 6 +++--- contrib/adaptive-compression/{multi.c => adapt.c} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename contrib/adaptive-compression/{multi.c => adapt.c} (100%) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 3027b88c3..38e3a7787 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,13 +19,13 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean multi +all: clean adapt -multi: $(ZSTD_FILES) multi.c +adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ clean: - @$(RM) -f multi + @$(RM) -f adapt @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst diff --git a/contrib/adaptive-compression/multi.c b/contrib/adaptive-compression/adapt.c similarity index 100% rename from contrib/adaptive-compression/multi.c rename to contrib/adaptive-compression/adapt.c From cc7f8e4d71bb2bb355f0c0a89de42ba176d83d64 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 11:10:11 -0700 Subject: [PATCH 051/145] small changes --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 4c3f584e1..61fb31421 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -115,7 +115,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); /* TODO: add checks for errors on each mutex */ + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); From e410d63d458142c3930341637f05bcc3c9397e78 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 15:37:14 -0700 Subject: [PATCH 052/145] made input buffer an internal part of the compression context --- contrib/adaptive-compression/adapt.c | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 61fb31421..f87bdf8c8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -31,6 +31,11 @@ typedef struct { size_t size; } buffer_t; +typedef struct { + size_t filled; + buffer_t buffer; +} inBuff_t; + typedef struct { unsigned waitCompressed; unsigned waitReady; @@ -68,6 +73,7 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + inBuff_t input; cStat_t stats; jobDescription* jobs; FILE* dstFile; @@ -97,6 +103,7 @@ static int freeCCtx(adaptCCtx* ctx) int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; + free(ctx->input.buffer.start); if (ctx->jobs){ freeCompressionJobs(ctx); free(ctx->jobs); @@ -115,7 +122,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) } memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); + pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); pthread_mutex_init(&ctx->jobReady_mutex, NULL); pthread_cond_init(&ctx->jobReady_cond, NULL); @@ -134,6 +141,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->allJobsCompleted = 0; ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); + ctx->input.filled = 0; + ctx->input.buffer.size = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.size); + if (!ctx->input.buffer.start) { + DISPLAY("Error: could not allocate input buffer\n"); + freeCCtx(ctx); + return NULL; + } if (!ctx->cctx) { DISPLAY("Error: could not allocate ZSTD_CCtx\n"); freeCCtx(ctx); @@ -320,7 +335,7 @@ static void* outputThread(void* arg) return arg; } -static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) +static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; @@ -351,7 +366,7 @@ static int createCompressionJob(adaptCCtx* ctx, BYTE* data, size_t srcSize) free(job->dst.start); return 1; } - memcpy(job->src.start, data, srcSize); + memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); @@ -371,7 +386,6 @@ static void printStats(cStat_t stats) static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) { - BYTE* const src = malloc(FILE_CHUNK_SIZE); unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; @@ -393,7 +407,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } /* checking for errors */ - if (!srcFilename || !outFilename || !src || !srcFile) { + if (!srcFilename || !outFilename || !srcFile) { DISPLAY("Error: initial variables could not be allocated\n"); ret = 1; goto cleanup; @@ -428,7 +442,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(src, 1, FILE_CHUNK_SIZE, srcFile); + size_t const readSize = fread(ctx->input.buffer.start, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; @@ -438,7 +452,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst g_streamedSize += readSize; /* reading was fine, now create the compression job */ { - int const error = createCompressionJob(ctx, src, readSize); + int const error = createCompressionJob(ctx, readSize); if (error != 0) { ret = error; ctx->threadError = 1; @@ -458,7 +472,6 @@ cleanup: /* file compression completed */ ret |= (srcFile != NULL) ? fclose(srcFile) : 0; ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; - free(src); return ret; } From 7aa36df6df018bf7611b5c469d6cbfb8f3f05a6e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:03:09 -0700 Subject: [PATCH 053/145] fixed memory leak that was happening when creating jobs --- contrib/adaptive-compression/adapt.c | 57 +++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f87bdf8c8..1299264d8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,24 +92,24 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { - { - int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); - int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); - int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; - int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; - free(ctx->input.buffer.start); - if (ctx->jobs){ - freeCompressionJobs(ctx); - free(ctx->jobs); - } - return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; + if (!ctx) return 0; + int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); + int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); + int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); + int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); + int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); + int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); + int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); + int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; + free(ctx->input.buffer.start); + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); } + free(ctx); + return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; } static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) @@ -136,6 +136,20 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobWriteID = 0; ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + /* allocating buffers for jobs */ + { + unsigned jobNum; + for (jobNum=0; jobNumjobs[jobNum]; + job->src.start = malloc(FILE_CHUNK_SIZE); + job->dst.start = malloc(FILE_CHUNK_SIZE); + if (!job->src.start || !job->dst.start) { + DISPLAY("Could not allocate buffers for jobs\n"); + freeCCtx(ctx); + return NULL; + } + } + } ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; @@ -354,18 +368,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) job->compressionLevel = ctx->compressionLevel; - job->src.start = malloc(srcSize); job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); - job->dst.start = malloc(job->dst.size); job->jobID = nextJob; - if (!job->src.start || !job->dst.start) { - /* problem occurred, free things then return */ - DISPLAY("Error: problem occurred during job creation\n"); - free(job->src.start); - free(job->dst.start); - return 1; - } memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; From c36552ef8a8aef2c097cba517346e8a7d5f0582c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:10:19 -0700 Subject: [PATCH 054/145] dst buffer should use ZSTD_compressBound to determine how much space it needs --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1299264d8..e1ac0aaf3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -142,7 +142,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) for (jobNum=0; jobNumjobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); - job->dst.start = malloc(FILE_CHUNK_SIZE); + job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); From 01fc7c42441ef75ead51f4289bdf13a799993ad9 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 16:27:58 -0700 Subject: [PATCH 055/145] changed how the detection of the last job works --- contrib/adaptive-compression/adapt.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e1ac0aaf3..8f3acd5c9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -50,6 +50,7 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; + unsigned lastJob; size_t compressedSize; } jobDescription; @@ -57,7 +58,6 @@ typedef struct { unsigned compressionLevel; unsigned numActiveThreads; unsigned numJobs; - unsigned lastJobID; unsigned nextJobID; unsigned threadError; unsigned jobReadyID; @@ -134,15 +134,15 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->lastJobID = -1; /* intentional underflow */ ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); - /* allocating buffers for jobs */ + /* initializing jobs */ { unsigned jobNum; for (jobNum=0; jobNumjobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); + job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); @@ -265,7 +265,7 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobCompressed_mutex); DEBUGLOG(2, "finished job compression %u\n", currJob); currJob++; - if (currJob >= ctx->lastJobID || ctx->threadError) { + if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ DEBUGLOG(2, "all jobs finished compressing\n"); break; @@ -327,7 +327,7 @@ static void* outputThread(void* arg) } DEBUGLOG(2, "finished job write %u\n", currJob); currJob++; - displayProgress(currJob, ctx->compressionLevel, currJob >= ctx->lastJobID); + displayProgress(currJob, ctx->compressionLevel, job->lastJob); DEBUGLOG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; @@ -335,8 +335,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUGLOG(2, "unlocking job write mutex\n"); - DEBUGLOG(2, "checking if done: %u/%u\n", currJob, ctx->lastJobID); - if (currJob >= ctx->lastJobID || ctx->threadError) { + if (job->lastJob || ctx->threadError) { /* finished with all jobs */ DEBUGLOG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); @@ -349,7 +348,7 @@ static void* outputThread(void* arg) return arg; } -static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) +static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; @@ -371,6 +370,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize) job->src.size = srcSize; job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; + job->lastJob = last; memcpy(job->src.start, ctx->input.buffer.start, srcSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; @@ -457,7 +457,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst g_streamedSize += readSize; /* reading was fine, now create the compression job */ { - int const error = createCompressionJob(ctx, readSize); + int const last = feof(srcFile); + int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { ret = error; ctx->threadError = 1; @@ -466,7 +467,6 @@ static int compressFilename(const char* const srcFilename, const char* const dst } if (feof(srcFile)) { DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); - ctx->lastJobID = ctx->nextJobID; break; } } From f91854549111eb90ba5f7f15d142b8687e4ab74f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 10 Jul 2017 18:16:42 -0700 Subject: [PATCH 056/145] made some progress on improving compression ratio, but problems exist with speed limits, and for some reason higher compression levels are really slow --- contrib/adaptive-compression/adapt.c | 102 ++++++++++++++++++--------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 8f3acd5c9..2bf8bb81e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1,6 +1,6 @@ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define PRINT(...) fprintf(stdout, __VA_ARGS__) -#define DEBUGLOG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } +#define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 #define MAX_NUM_JOBS 2; #define stdinmark "/*stdin*\\" @@ -15,7 +15,7 @@ typedef unsigned char BYTE; #include /* malloc, free */ #include /* pthread functions */ #include /* memset */ -#include "zstd.h" +#include "zstd_internal.h" #include "util.h" static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; @@ -48,6 +48,7 @@ typedef struct { typedef struct { buffer_t src; buffer_t dst; + buffer_t dict; unsigned compressionLevel; unsigned jobID; unsigned lastJob; @@ -87,6 +88,7 @@ static void freeCompressionJobs(adaptCCtx* ctx) jobDescription job = ctx->jobs[u]; free(job.dst.start); free(job.src.start); + free(job.dict.start); } } @@ -142,8 +144,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) jobDescription* job = &ctx->jobs[jobNum]; job->src.start = malloc(FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); + job->dict.start = malloc(FILE_CHUNK_SIZE); job->lastJob = 0; - if (!job->src.start || !job->dst.start) { + if (!job->src.start || !job->dst.start || !job->dict.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); return NULL; @@ -207,17 +210,17 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUGLOG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUGLOG(2, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel++; reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - DEBUGLOG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel--; reset = 1; } @@ -236,38 +239,63 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "compressionThread(): waiting on job ready\n"); + DEBUG(2, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; - DEBUGLOG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUGLOG(2, "compressionThread(): continuing after job ready\n"); + DEBUG(2, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - DEBUGLOG(2, "cLevel used: %u\n", cLevel); - size_t const compressedSize = ZSTD_compressCCtx(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size, cLevel); - if (ZSTD_isError(compressedSize)) { + ZSTD_parameters params = ZSTD_getParams(cLevel, job->src.size, 0); + DEBUG(2, "cLevel used: %u\n", cLevel); + + /* begin compression */ + { + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->dict.start, job->dict.size, params, 0); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + ctx->threadError = 1; + return arg; + } + } + + /* continue compression */ + if (currJob != 0) { /* not first job */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, 0); + if (ZSTD_isError(hSize)) { + job->compressedSize = hSize; + ctx->threadError = 1; + return arg; + } + ZSTD_invalidateRepCodes(ctx->cctx); + } + job->compressedSize = (job->lastJob) ? + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + if (ZSTD_isError(job->compressedSize)) { + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; - DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(compressedSize)); return arg; } - job->compressedSize = compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; - DEBUGLOG(2, "signaling for job %u\n", currJob); + DEBUG(2, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompressed_cond); pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "finished job compression %u\n", currJob); + DEBUG(2, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ - DEBUGLOG(2, "all jobs finished compressing\n"); + DEBUG(2, "all jobs finished compressing\n"); break; } } @@ -299,16 +327,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUGLOG(2, "outputThread(): waiting on job compressed\n"); + DEBUG(2, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUGLOG(2, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUGLOG(2, "outputThread(): continuing after job compressed\n"); + DEBUG(2, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -325,19 +353,19 @@ static void* outputThread(void* arg) } } } - DEBUGLOG(2, "finished job write %u\n", currJob); + DEBUG(2, "finished job write %u\n", currJob); currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); - DEBUGLOG(2, "locking job write mutex\n"); + DEBUG(2, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "unlocking job write mutex\n"); + DEBUG(2, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ - DEBUGLOG(2, "all jobs finished writing\n"); + DEBUG(2, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -353,17 +381,17 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUGLOG(2, "createCompressionJob(): wait for job write\n"); + DEBUG(2, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); + DEBUG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; - DEBUGLOG(2, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUGLOG(2, "createCompressionJob(): continuing after job write\n"); + DEBUG(2, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; @@ -371,13 +399,21 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start, srcSize); + memcpy(job->src.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + job->dict.size = ctx->input.filled; + memcpy(job->dict.start, ctx->input.buffer.start, ctx->input.filled); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUGLOG(2, "finished job creation %u\n", nextJob); + DEBUG(2, "finished job creation %u\n", nextJob); ctx->nextJobID++; + + /* if not on the last job, reuse data as dictionary in next job */ + if (!last) { + ctx->input.filled = srcSize; + memmove(ctx->input.buffer.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + } return 0; } @@ -447,7 +483,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(ctx->input.buffer.start, 1, FILE_CHUNK_SIZE, srcFile); + size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; @@ -466,7 +502,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { - DEBUGLOG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + DEBUG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -563,7 +599,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUGLOG(2, "g_compressionLevel: %u\n", g_compressionLevel); + DEBUG(2, "g_compressionLevel: %u\n", g_compressionLevel); break; case 's': g_displayStats = 1; From 7ec5928626b74f9f91b3de09026584ab41b7d4ad Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 10:23:25 -0700 Subject: [PATCH 057/145] fixed an error where -c argument wasn't working for single files --- contrib/adaptive-compression/adapt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2bf8bb81e..e03bcc8e1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -2,7 +2,7 @@ #define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } #define FILE_CHUNK_SIZE 4 << 20 -#define MAX_NUM_JOBS 2; +#define MAX_NUM_JOBS 2 #define stdinmark "/*stdin*\\" #define stdoutmark "/*stdout*\\" #define MAX_PATH 256 @@ -612,6 +612,7 @@ int main(int argCount, const char* argv[]) break; case 'c': forceStdout = 1; + outFilename = stdoutmark; break; default: DISPLAY("Error: invalid argument provided\n"); From 34afb9b23e0df74e8a9261d8e7e05b8f762d5738 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 11:50:00 -0700 Subject: [PATCH 058/145] changed to using ZSTD_compressBegin_usingDict() and fixed strange issue with ZSTD_compressContinue() --- contrib/adaptive-compression/adapt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e03bcc8e1..9a527ff5e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -252,13 +252,12 @@ static void* compressionThread(void* arg) /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - ZSTD_parameters params = ZSTD_getParams(cLevel, job->src.size, 0); DEBUG(2, "cLevel used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->dict.start, job->dict.size, params, 0); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -269,7 +268,7 @@ static void* compressionThread(void* arg) /* continue compression */ if (currJob != 0) { /* not first job */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, 0); + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { job->compressedSize = hSize; ctx->threadError = 1; From a3c077b8c6a88e5c1f3bcfe61cc010c3f1721760 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:00:52 -0700 Subject: [PATCH 059/145] added error message, updated copying dictionary into the input buffer --- contrib/adaptive-compression/adapt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9a527ff5e..2bd1c4f95 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -270,6 +270,7 @@ static void* compressionThread(void* arg) if (currJob != 0) { /* not first job */ size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { + DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; ctx->threadError = 1; return arg; @@ -410,8 +411,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - ctx->input.filled = srcSize; - memmove(ctx->input.buffer.start, ctx->input.buffer.start + ctx->input.filled, srcSize); + size_t const newDictSize = srcSize; + size_t const oldDictSize = ctx->input.filled; + memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); + ctx->input.filled = newDictSize; } return 0; } From 7c54e09347e47807b5c037cb5d90441560dc80ff Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:15:41 -0700 Subject: [PATCH 060/145] updated DEBUG statements --- contrib/adaptive-compression/adapt.c | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2bd1c4f95..89a20a50a 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -210,17 +210,17 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; - DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel++; reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); ctx->compressionLevel--; reset = 1; } @@ -239,20 +239,20 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(2, "compressionThread(): waiting on job ready\n"); + DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; - DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); } pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUG(2, "compressionThread(): continuing after job ready\n"); + DEBUG(3, "compressionThread(): continuing after job ready\n"); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); - DEBUG(2, "cLevel used: %u\n", cLevel); + DEBUG(3, "cLevel used: %u\n", cLevel); /* begin compression */ { @@ -288,14 +288,14 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; - DEBUG(2, "signaling for job %u\n", currJob); + DEBUG(3, "signaling for job %u\n", currJob); pthread_cond_signal(&ctx->jobCompressed_cond); pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUG(2, "finished job compression %u\n", currJob); + DEBUG(3, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { /* finished compressing all jobs */ - DEBUG(2, "all jobs finished compressing\n"); + DEBUG(3, "all jobs finished compressing\n"); break; } } @@ -327,16 +327,16 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(2, "outputThread(): waiting on job compressed\n"); + DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; - DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex); - DEBUG(2, "outputThread(): continuing after job compressed\n"); + DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { @@ -353,19 +353,19 @@ static void* outputThread(void* arg) } } } - DEBUG(2, "finished job write %u\n", currJob); + DEBUG(3, "finished job write %u\n", currJob); currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); - DEBUG(2, "locking job write mutex\n"); + DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond); pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUG(2, "unlocking job write mutex\n"); + DEBUG(3, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ - DEBUG(2, "all jobs finished writing\n"); + DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond); @@ -381,17 +381,17 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUG(2, "createCompressionJob(): wait for job write\n"); + DEBUG(3, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex); - DEBUG(2, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); + DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; - DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex); - DEBUG(2, "createCompressionJob(): continuing after job write\n"); + DEBUG(3, "createCompressionJob(): continuing after job write\n"); job->compressionLevel = ctx->compressionLevel; @@ -406,7 +406,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); pthread_mutex_unlock(&ctx->jobReady_mutex); - DEBUG(2, "finished job creation %u\n", nextJob); + DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; /* if not on the last job, reuse data as dictionary in next job */ @@ -504,7 +504,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } if (feof(srcFile)) { - DEBUG(2, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -601,7 +601,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUG(2, "g_compressionLevel: %u\n", g_compressionLevel); + DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; case 's': g_displayStats = 1; From 72a183efad16ace8a3fb48e9fc40ff6df56135c9 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 15:49:52 -0700 Subject: [PATCH 061/145] changed dictionary size, added debugging statements --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 89a20a50a..ad3d7f7a6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -253,7 +253,7 @@ static void* compressionThread(void* arg) { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - + DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); @@ -408,10 +408,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; - + DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = srcSize; + size_t const newDictSize = srcSize/16; size_t const oldDictSize = ctx->input.filled; memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); ctx->input.filled = newDictSize; From 0a401852c46b3ab238f20e3e775e0e1966afc05f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 11 Jul 2017 16:50:50 -0700 Subject: [PATCH 062/145] added debug statement --- contrib/adaptive-compression/adapt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ad3d7f7a6..b0bcb74f2 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -254,6 +254,7 @@ static void* compressionThread(void* arg) unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); + DEBUG(2, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); From 356ddb649f6c199cb087bf4a74d028c5e80c0f38 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 12:21:21 -0700 Subject: [PATCH 063/145] working with flush job->src.size and fixed cLevel --- contrib/adaptive-compression/adapt.c | 46 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b0bcb74f2..b841cefaf 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,6 +29,7 @@ static UTIL_freq_t g_ticksPerSecond; typedef struct { void* start; size_t size; + size_t capacity; } buffer_t; typedef struct { @@ -74,6 +75,8 @@ typedef struct { pthread_cond_t allJobsCompleted_cond; pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; + size_t lastDictSize; + size_t targetDictSize; inBuff_t input; cStat_t stats; jobDescription* jobs; @@ -136,6 +139,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; + ctx->targetDictSize = FILE_CHUNK_SIZE >> 1; + ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ { @@ -151,6 +156,9 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) freeCCtx(ctx); return NULL; } + job->src.capacity = FILE_CHUNK_SIZE; + job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); + job->dict.capacity = FILE_CHUNK_SIZE; } } ctx->nextJobID = 0; @@ -159,8 +167,8 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); ctx->input.filled = 0; - ctx->input.buffer.size = 2 * FILE_CHUNK_SIZE; - ctx->input.buffer.start = malloc(ctx->input.buffer.size); + ctx->input.buffer.capacity = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); freeCCtx(ctx); @@ -249,16 +257,19 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); + DEBUG(3, "%.*s\n", (int)job->dict.size, (char*)job->dict.start); + DEBUG(3, "DICTIONARY ENDED\n"); + DEBUG(2, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(2, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); - DEBUG(2, "compression level used: %u\n", cLevel); + DEBUG(3, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); + DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, cLevel); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, 6); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -268,8 +279,8 @@ static void* compressionThread(void* arg) } /* continue compression */ - if (currJob != 0) { /* not first job */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + if (currJob != 0) { /* not first job flush/overwrite the frame header */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; @@ -279,13 +290,14 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.size, job->src.start, job->src.size); + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; return arg; } + job->dst.size = job->compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex); ctx->jobCompressedID++; @@ -394,15 +406,15 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->jobWrite_mutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); - + DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; - job->dst.size = ZSTD_compressBound(srcSize); job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start + ctx->input.filled, srcSize); - job->dict.size = ctx->input.filled; - memcpy(job->dict.start, ctx->input.buffer.start, ctx->input.filled); + memcpy(job->src.start, ctx->input.buffer.start + ctx->lastDictSize, srcSize); + job->dict.size = ctx->lastDictSize; + DEBUG(3, "copied %zu bytes\n", ctx->lastDictSize); + memcpy(job->dict.start, ctx->input.buffer.start, ctx->lastDictSize); pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); @@ -412,9 +424,11 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = srcSize/16; - size_t const oldDictSize = ctx->input.filled; + size_t const newDictSize = ctx->targetDictSize; + size_t const oldDictSize = ctx->lastDictSize; + DEBUG(3, "newDictSize %zu oldDictSize %zu\n", newDictSize, oldDictSize); memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); + ctx->lastDictSize = newDictSize; ctx->input.filled = newDictSize; } return 0; From 5353d350aea592e87825129c86d57edeaf0a082e Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:02:20 -0700 Subject: [PATCH 064/145] working with fixed compression level and fixed dictionary size --- contrib/adaptive-compression/adapt.c | 29 +++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b841cefaf..eb1e24a79 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -49,11 +49,11 @@ typedef struct { typedef struct { buffer_t src; buffer_t dst; - buffer_t dict; unsigned compressionLevel; unsigned jobID; unsigned lastJob; size_t compressedSize; + size_t dictSize; } jobDescription; typedef struct { @@ -91,7 +91,6 @@ static void freeCompressionJobs(adaptCCtx* ctx) jobDescription job = ctx->jobs[u]; free(job.dst.start); free(job.src.start); - free(job.dict.start); } } @@ -139,7 +138,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = FILE_CHUNK_SIZE >> 1; + ctx->targetDictSize = FILE_CHUNK_SIZE >> 15; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ @@ -147,18 +146,16 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) unsigned jobNum; for (jobNum=0; jobNumjobs[jobNum]; - job->src.start = malloc(FILE_CHUNK_SIZE); + job->src.start = malloc(2 * FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); - job->dict.start = malloc(FILE_CHUNK_SIZE); job->lastJob = 0; - if (!job->src.start || !job->dst.start || !job->dict.start) { + if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); freeCCtx(ctx); return NULL; } job->src.capacity = FILE_CHUNK_SIZE; job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); - job->dict.capacity = FILE_CHUNK_SIZE; } } ctx->nextJobID = 0; @@ -257,19 +254,17 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); - DEBUG(3, "%.*s\n", (int)job->dict.size, (char*)job->dict.start); DEBUG(3, "DICTIONARY ENDED\n"); - DEBUG(2, "%.*s", (int)job->src.size, (char*)job->src.start); + DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { unsigned const cLevel = adaptCompressionLevel(ctx); DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(3, "dictSize: %zu, srcSize: %zu\n", job->dict.size, job->src.size); DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->dict.start, job->dict.size, 6); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, 6); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -280,7 +275,7 @@ static void* compressionThread(void* arg) /* continue compression */ if (currJob != 0) { /* not first job flush/overwrite the frame header */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, 0); if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; @@ -290,8 +285,8 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start, job->src.size); + ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size) : + ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); ctx->threadError = 1; @@ -411,10 +406,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->src.size = srcSize; job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start + ctx->lastDictSize, srcSize); - job->dict.size = ctx->lastDictSize; - DEBUG(3, "copied %zu bytes\n", ctx->lastDictSize); - memcpy(job->dict.start, ctx->input.buffer.start, ctx->lastDictSize); + memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); + job->dictSize = ctx->lastDictSize; pthread_mutex_lock(&ctx->jobReady_mutex); ctx->jobReadyID++; pthread_cond_signal(&ctx->jobReady_cond); From 74d3a6f5aefaf7d35a7af87e66b3046c1ead2593 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:18:41 -0700 Subject: [PATCH 065/145] passes tests with adaptive compression level --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index eb1e24a79..e9230ddb1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -264,7 +264,7 @@ static void* compressionThread(void* arg) /* begin compression */ { size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, 6); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); From 3c16edd26ac531d358cad5031ac3f12bed12f45b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:40:24 -0700 Subject: [PATCH 066/145] added copyright header, removed clean from makefile --- contrib/adaptive-compression/Makefile | 2 +- contrib/adaptive-compression/adapt.c | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 38e3a7787..ed1a55ad4 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,7 +19,7 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: clean adapt +all: adapt adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e9230ddb1..67fb75646 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -1,3 +1,19 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include /* fprintf */ +#include /* malloc, free */ +#include /* pthread functions */ +#include /* memset */ +#include "zstd_internal.h" +#include "util.h" + #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define PRINT(...) fprintf(stdout, __VA_ARGS__) #define DEBUG(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } @@ -11,13 +27,6 @@ #define DEFAULT_ADAPT_PARAM 1 typedef unsigned char BYTE; -#include /* fprintf */ -#include /* malloc, free */ -#include /* pthread functions */ -#include /* memset */ -#include "zstd_internal.h" -#include "util.h" - static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static unsigned g_displayStats = 0; @@ -138,7 +147,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = FILE_CHUNK_SIZE >> 15; + ctx->targetDictSize = 1 << 12; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ From 954d999abfbf7dc22cb4afe3e7a1cc19c8edd865 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 16:50:43 -0700 Subject: [PATCH 067/145] fixed up freeCCtx() removed BYTE since it wasn't being used --- contrib/adaptive-compression/adapt.c | 36 +++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 67fb75646..3cafbca30 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,6 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 -typedef unsigned char BYTE; static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -106,23 +105,26 @@ static void freeCompressionJobs(adaptCCtx* ctx) static int freeCCtx(adaptCCtx* ctx) { if (!ctx) return 0; - int const compressedMutexError = pthread_mutex_destroy(&ctx->jobCompressed_mutex); - int const compressedCondError = pthread_cond_destroy(&ctx->jobCompressed_cond); - int const readyMutexError = pthread_mutex_destroy(&ctx->jobReady_mutex); - int const readyCondError = pthread_cond_destroy(&ctx->jobReady_cond); - int const allJobsMutexError = pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - int const allJobsCondError = pthread_cond_destroy(&ctx->allJobsCompleted_cond); - int const jobWriteMutexError = pthread_mutex_destroy(&ctx->jobWrite_mutex); - int const jobWriteCondError = pthread_cond_destroy(&ctx->jobWrite_cond); - int const fileCloseError = (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; - int const cctxError = ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)) ? 1 : 0; - free(ctx->input.buffer.start); - if (ctx->jobs){ - freeCompressionJobs(ctx); - free(ctx->jobs); + { + int error = 0; + error |= pthread_mutex_destroy(&ctx->jobCompressed_mutex); + error |= pthread_cond_destroy(&ctx->jobCompressed_cond); + error |= pthread_mutex_destroy(&ctx->jobReady_mutex); + error |= pthread_cond_destroy(&ctx->jobReady_cond); + error |= pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); + error |= pthread_cond_destroy(&ctx->allJobsCompleted_cond); + error |= pthread_mutex_destroy(&ctx->jobWrite_mutex); + error |= pthread_cond_destroy(&ctx->jobWrite_cond); + error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); + free(ctx->input.buffer.start); + if (ctx->jobs){ + freeCompressionJobs(ctx); + free(ctx->jobs); + } + free(ctx); + return error; } - free(ctx); - return compressedMutexError | compressedCondError | readyMutexError | readyCondError | fileCloseError | allJobsMutexError | allJobsCondError | jobWriteMutexError | jobWriteCondError | cctxError; } static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) From b5b18cf66477fecca9ba42982baa0bd9779c9957 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 17:10:58 -0700 Subject: [PATCH 068/145] changed to malloc, added comment about adaptive compression level, and changed ternary operators --- contrib/adaptive-compression/adapt.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3cafbca30..bcac46de1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -130,12 +130,11 @@ static int freeCCtx(adaptCCtx* ctx) static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { - adaptCCtx* ctx = malloc(sizeof(adaptCCtx)); + adaptCCtx* ctx = calloc(1, sizeof(adaptCCtx)); if (ctx == NULL) { DISPLAY("Error: could not allocate space for context\n"); return NULL; } - memset(ctx, 0, sizeof(adaptCCtx)); ctx->compressionLevel = g_compressionLevel; pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); pthread_cond_init(&ctx->jobCompressed_cond, NULL); @@ -216,16 +215,24 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); } +/* + * Compression level is changed depending on which part of the compression process is lagging + * Currently, three theads exist for job creation, compression, and file writing respectively. + * adaptCompressionLevel() increments or decrements compression level based on which of the threads is lagging + * job creation or file writing lag => increased compression level + * compression thread lag => decreased compression level + * detecting which thread is lagging is done by keeping track of how many calls each thread makes to pthread_cond_wait + */ static unsigned adaptCompressionLevel(adaptCCtx* ctx) { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter ? 1 : 0; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter ? 1 : 0; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter ? 1 : 0; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)) ? 1 : 0; - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)) ? 1 : 0; - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)) ? 1 : 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; From 7c886db0a8ff8ef8941c2e4a24ea75a3e2756849 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 12 Jul 2017 17:28:53 -0700 Subject: [PATCH 069/145] changed to stderr --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index bcac46de1..9c6986446 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -336,12 +336,12 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stdout, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); + fprintf(stderr, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); if (last) { - fprintf(stdout, "\n"); + fprintf(stderr, "\n"); } else { - fflush(stdout); + fflush(stderr); } } From 766663f1f12bb8d7db9740114c7c33ce52f85d9c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 10:15:27 -0700 Subject: [PATCH 070/145] added altering dictionary size depending on compression level --- contrib/adaptive-compression/adapt.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9c6986446..81027666b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -84,7 +84,6 @@ typedef struct { pthread_mutex_t jobWrite_mutex; pthread_cond_t jobWrite_cond; size_t lastDictSize; - size_t targetDictSize; inBuff_t input; cStat_t stats; jobDescription* jobs; @@ -148,7 +147,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->jobReadyID = 0; ctx->jobCompressedID = 0; ctx->jobWriteID = 0; - ctx->targetDictSize = 1 << 12; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); /* initializing jobs */ @@ -255,6 +253,14 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) return ctx->compressionLevel; } +static size_t getUseableDictSize(unsigned compressionLevel) +{ + ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + unsigned overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; + size_t overlapSize = 1 << (params.cParams.windowLog - overlapLog); + return overlapSize; +} + static void* compressionThread(void* arg) { adaptCCtx* ctx = (adaptCCtx*)arg; @@ -281,8 +287,10 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { + size_t useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start, job->dictSize, cLevel); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); @@ -435,12 +443,11 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { - size_t const newDictSize = ctx->targetDictSize; size_t const oldDictSize = ctx->lastDictSize; - DEBUG(3, "newDictSize %zu oldDictSize %zu\n", newDictSize, oldDictSize); - memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize + srcSize - newDictSize, newDictSize); - ctx->lastDictSize = newDictSize; - ctx->input.filled = newDictSize; + DEBUG(3, "oldDictSize %zu\n", oldDictSize); + memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize, srcSize); + ctx->lastDictSize = srcSize; + ctx->input.filled = srcSize; } return 0; } From 9165e97fc69d6a6f0476575b696d5f93f5070ea6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 13:50:23 -0700 Subject: [PATCH 071/145] added some tests for correctness, time, and compression ratio --- contrib/adaptive-compression/Makefile | 15 +- contrib/adaptive-compression/datagencli.c | 129 +++++++++++ .../adaptive-compression/test-correctness.sh | 205 ++++++++++++++++++ .../adaptive-compression/test-performance.sh | 34 +++ 4 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 contrib/adaptive-compression/datagencli.c create mode 100755 contrib/adaptive-compression/test-correctness.sh create mode 100755 contrib/adaptive-compression/test-performance.sh diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index ed1a55ad4..f2059a193 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -19,13 +19,24 @@ CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -all: adapt +all: adapt datagen adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ +datagen : $(PRGDIR)/datagen.c datagencli.c + $(CC) $(FLAGS) $^ -o $@$(EXT) + +test-adapt-correctness: datagen adapt + @./test-correctness.sh + @echo "test correctness complete" + +test-adapt-performance: datagen adapt + @./test-performance.sh + @echo "test performance complete" + clean: - @$(RM) -f adapt + @$(RM) -f adapt datagen @$(RM) -rf *.dSYM @$(RM) -f tmp* @$(RM) -f tests/*.zst diff --git a/contrib/adaptive-compression/datagencli.c b/contrib/adaptive-compression/datagencli.c new file mode 100644 index 000000000..8a81939d1 --- /dev/null +++ b/contrib/adaptive-compression/datagencli.c @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + + +/*-************************************ +* Dependencies +**************************************/ +#include "util.h" /* Compiler options */ +#include /* fprintf, stderr */ +#include "datagen.h" /* RDG_generate */ + + +/*-************************************ +* Constants +**************************************/ +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define SIZE_DEFAULT ((64 KB) + 1) +#define SEED_DEFAULT 0 +#define COMPRESSIBILITY_DEFAULT 50 + + +/*-************************************ +* Macros +**************************************/ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } +static unsigned displayLevel = 2; + + +/*-******************************************************* +* Command line +*********************************************************/ +static int usage(const char* programName) +{ + DISPLAY( "Compressible data generator\n"); + DISPLAY( "Usage :\n"); + DISPLAY( " %s [args]\n", programName); + DISPLAY( "\n"); + DISPLAY( "Arguments :\n"); + DISPLAY( " -g# : generate # data (default:%i)\n", SIZE_DEFAULT); + DISPLAY( " -s# : Select seed (default:%i)\n", SEED_DEFAULT); + DISPLAY( " -P# : Select compressibility in %% (default:%i%%)\n", + COMPRESSIBILITY_DEFAULT); + DISPLAY( " -h : display help and exit\n"); + return 0; +} + + +int main(int argc, const char** argv) +{ + unsigned probaU32 = COMPRESSIBILITY_DEFAULT; + double litProba = 0.0; + U64 size = SIZE_DEFAULT; + U32 seed = SEED_DEFAULT; + const char* const programName = argv[0]; + + int argNb; + for(argNb=1; argNb='0') && (*argument<='9')) + size *= 10, size += *argument++ - '0'; + if (*argument=='K') { size <<= 10; argument++; } + if (*argument=='M') { size <<= 20; argument++; } + if (*argument=='G') { size <<= 30; argument++; } + if (*argument=='B') { argument++; } + break; + case 's': + argument++; + seed=0; + while ((*argument>='0') && (*argument<='9')) + seed *= 10, seed += *argument++ - '0'; + break; + case 'P': + argument++; + probaU32 = 0; + while ((*argument>='0') && (*argument<='9')) + probaU32 *= 10, probaU32 += *argument++ - '0'; + if (probaU32>100) probaU32 = 100; + break; + case 'L': /* hidden argument : Literal distribution probability */ + argument++; + litProba=0.; + while ((*argument>='0') && (*argument<='9')) + litProba *= 10, litProba += *argument++ - '0'; + if (litProba>100.) litProba=100.; + litProba /= 100.; + break; + case 'v': + displayLevel = 4; + argument++; + break; + default: + return usage(programName); + } + } } } /* for(argNb=1; argNb tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g500MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g250MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g125MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g50MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g25MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g5MB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g500KB > tmp +./adapt -otmp.zst tmp +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- streaming" +./datagen -g1GB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +cat tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- read limit" +./datagen -g1GB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -L 50m -q tmp | ./adapt > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- write limit" +./datagen -g1GB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +echo -e "\ncorrectness tests -- read and write limits" +./datagen -g1GB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst +zstd -d tmp.zst -o tmp2 +diff -q tmp tmp2 +rm tmp* + + +make clean diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh new file mode 100755 index 000000000..6a88325d5 --- /dev/null +++ b/contrib/adaptive-compression/test-performance.sh @@ -0,0 +1,34 @@ +echo "testing time" +./datagen -g1GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +./datagen -g2GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +./datagen -g4GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +rm tmp* + +echo -e "\ntesting compression ratio" +./datagen -g1GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g2GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g4GB > tmp +time ./adapt -otmp1.zst tmp +time zstd -1 -o tmp2.zst tmp +ls -l tmp1.zst tmp2.zst +rm tmp* From 0d9665cef5fb39a55acfe2069ae443501d7079cf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 14:46:54 -0700 Subject: [PATCH 072/145] added additional tests for performance, allowed force compression level for testing purposes --- contrib/adaptive-compression/adapt.c | 61 +++++++++++-------- .../adaptive-compression/test-performance.sh | 29 ++++++++- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 81027666b..c2160714d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -33,6 +33,7 @@ static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 0; static UTIL_freq_t g_ticksPerSecond; +static unsigned g_forceCompressionLevel = 0; typedef struct { void* start; @@ -223,34 +224,39 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static unsigned adaptCompressionLevel(adaptCCtx* ctx) { - unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); - DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); - if (allSlow) { - reset = 1; + if (g_forceCompressionLevel) { + return g_compressionLevel; } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel++; - reset = 1; + else { + unsigned reset = 0; + unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); + unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); + unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); + DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel++; + reset = 1; + } + else if (compressSlow && ctx->compressionLevel > 1) { + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + ctx->compressionLevel--; + reset = 1; + } + if (reset) { + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.compressedCounter = 0; + } + return ctx->compressionLevel; } - else if (compressSlow && ctx->compressionLevel > 1) { - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel--; - reset = 1; - } - if (reset) { - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; - } - return ctx->compressionLevel; } static size_t getUseableDictSize(unsigned compressionLevel) @@ -649,6 +655,9 @@ int main(int argCount, const char* argv[]) forceStdout = 1; outFilename = stdoutmark; break; + case 'f': + g_forceCompressionLevel = 1; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh index 6a88325d5..6c4991c48 100755 --- a/contrib/adaptive-compression/test-performance.sh +++ b/contrib/adaptive-compression/test-performance.sh @@ -1,4 +1,4 @@ -echo "testing time" +echo "testing time -- no limits set" ./datagen -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp @@ -14,7 +14,7 @@ time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -echo -e "\ntesting compression ratio" +echo -e "\ntesting compression ratio -- no limits set" ./datagen -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp @@ -32,3 +32,28 @@ time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* + +echo e "\ntesting performance at various compression levels -- no limits set" +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i5 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -5 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i10 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -10 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* + +./datagen -g1GB > tmp +echo "adapt" +time ./adapt -i15 -f tmp -otmp1.zst +echo "zstdcli" +time zstd -15 tmp -o tmp2.zst +ls -l tmp1.zst tmp2.zst +rm tmp* From 65a4ce2635f519f1db3e9f8027f1596429f789cf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 14:57:24 -0700 Subject: [PATCH 073/145] added tests for forced compression level --- .../adaptive-compression/test-correctness.sh | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 6277fafba..e6ac6dfbb 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -201,5 +201,40 @@ zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* +echo -e "\ncorrectness tests -- forced compression level" +./datagen -g1GB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* +./datagen -g100MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g10MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g1MB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g100KB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* + +./datagen -g10KB > tmp +./adapt tmp -otmp.zst -i11 -f +zstd -d tmp.zst -o tmp2 +diff tmp tmp2 +rm tmp* make clean From 0c8b9436b78e5192650ba9d50e1c52e2f0110aec Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 13 Jul 2017 16:38:20 -0700 Subject: [PATCH 074/145] removed goto statements for the most part --- contrib/adaptive-compression/adapt.c | 116 ++++++++++++++++----------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c2160714d..addd0c59d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,6 +92,11 @@ typedef struct { ZSTD_CCtx* cctx; } adaptCCtx; +typedef struct { + FILE* srcFile; + adaptCCtx* ctx; +} fcResources; + static void freeCompressionJobs(adaptCCtx* ctx) { unsigned u; @@ -207,6 +212,7 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { + if (!ctx) return; pthread_mutex_lock(&ctx->allJobsCompleted_mutex); while (ctx->allJobsCompleted == 0) { pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); @@ -466,40 +472,10 @@ static void printStats(cStat_t stats) DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } -static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) +static int performCompression(adaptCCtx* ctx, FILE* const srcFile) { - unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); - FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); - const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; - const char* outFilename = outFilenameIntermediate; - char fileAndSuffix[MAX_PATH]; - size_t const numJobs = MAX_NUM_JOBS; - int ret = 0; - adaptCCtx* ctx = NULL; - UTIL_getTime(&g_startTime); - g_streamedSize = 0; - - if (!outFilenameIntermediate) { - if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) { - DISPLAY("Error: output filename is too long\n"); - ret = 1; - goto cleanup; - } - outFilename = fileAndSuffix; - } - - /* checking for errors */ - if (!srcFilename || !outFilename || !srcFile) { - DISPLAY("Error: initial variables could not be allocated\n"); - ret = 1; - goto cleanup; - } - - /* creating context */ - ctx = createCCtx(numJobs, outFilename); - if (ctx == NULL) { - ret = 1; - goto cleanup; + if (!ctx || !srcFile) { + return 1; } /* create output thread */ @@ -507,8 +483,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst pthread_t out; if (pthread_create(&out, NULL, &outputThread, ctx)) { DISPLAY("Error: could not create output thread\n"); - ret = 1; - goto cleanup; + ctx->threadError = 1; + return 1; } } @@ -517,8 +493,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst pthread_t compression; if (pthread_create(&compression, NULL, &compressionThread, ctx)) { DISPLAY("Error: could not create compression thread\n"); - ret = 1; - goto cleanup; + ctx->threadError = 1; + return 1; } } @@ -528,8 +504,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); ctx->threadError = 1; - ret = 1; - goto cleanup; + return 1; } g_streamedSize += readSize; /* reading was fine, now create the compression job */ @@ -537,9 +512,8 @@ static int compressFilename(const char* const srcFilename, const char* const dst int const last = feof(srcFile); int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { - ret = error; ctx->threadError = 1; - goto cleanup; + return error; } } if (feof(srcFile)) { @@ -548,12 +522,60 @@ static int compressFilename(const char* const srcFilename, const char* const dst } } -cleanup: - waitUntilAllJobsCompleted(ctx); - if (g_displayStats) printStats(ctx->stats); - /* file compression completed */ - ret |= (srcFile != NULL) ? fclose(srcFile) : 0; - ret |= (ctx != NULL) ? freeCCtx(ctx) : 0; + /* success -- created all jobs */ + return 0; +} + +static fcResources createFileCompressionResources(const char* const srcFilename, const char* const dstFilenameOrNull) +{ + fcResources fcr; + unsigned const stdinUsed = !strcmp(srcFilename, stdinmark); + FILE* const srcFile = stdinUsed ? stdin : fopen(srcFilename, "rb"); + const char* const outFilenameIntermediate = (stdinUsed && !dstFilenameOrNull) ? stdoutmark : dstFilenameOrNull; + const char* outFilename = outFilenameIntermediate; + char fileAndSuffix[MAX_PATH]; + size_t const numJobs = MAX_NUM_JOBS; + + memset(&fcr, 0, sizeof(fcr)); + + if (!outFilenameIntermediate) { + if (snprintf(fileAndSuffix, MAX_PATH, "%s.zst", srcFilename) + 1 > MAX_PATH) { + DISPLAY("Error: output filename is too long\n"); + return fcr; + } + outFilename = fileAndSuffix; + } + + /* checking for errors */ + if (!outFilename || !srcFile) { + DISPLAY("Error: initial variables could not be allocated\n"); + return fcr; + } + + /* creating context */ + fcr.ctx = createCCtx(numJobs, outFilename); + fcr.srcFile = srcFile; + return fcr; +} + +static int freeFileCompressionResources(fcResources* fcr) +{ + int ret = 0; + waitUntilAllJobsCompleted(fcr->ctx); + if (g_displayStats) printStats(fcr->ctx->stats); + ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; + ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; + return ret; +} + +static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) +{ + int ret = 0; + UTIL_getTime(&g_startTime); + g_streamedSize = 0; + fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); + ret |= performCompression(fcr.ctx, fcr.srcFile); + ret |= freeFileCompressionResources(&fcr); return ret; } From 1ab3f06f0041b5a29eadaa2da81112424999f554 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 14 Jul 2017 16:29:29 -0700 Subject: [PATCH 075/145] updated tests to use different seeds when executing different tests --- .../adaptive-compression/test-correctness.sh | 78 +++++++++---------- .../adaptive-compression/test-performance.sh | 18 ++--- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index e6ac6dfbb..b057cbd64 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -1,238 +1,238 @@ echo "correctness tests -- general" -./datagen -g1GB > tmp +./datagen -s1 -g1GB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g500MB > tmp +./datagen -s2 -g500MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g250MB > tmp +./datagen -s3 -g250MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g125MB > tmp +./datagen -s4 -g125MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g50MB > tmp +./datagen -s5 -g50MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g25MB > tmp +./datagen -s6 -g25MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s7 -g10MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g5MB > tmp +./datagen -s8 -g5MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g500KB > tmp +./datagen -s9 -g500KB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- streaming" -./datagen -g1GB > tmp +./datagen -s10 -g1GB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s11 -g100MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s12 -g10MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s13 -g1MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s14 -g100KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s15 -g10KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read limit" -./datagen -g1GB > tmp +./datagen -s16 -g1GB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s17 -g100MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s18 -g10MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s19 -g1MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s20 -g100KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s21 -g10KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- write limit" -./datagen -g1GB > tmp +./datagen -s22 -g1GB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s23 -g100MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s24 -g10MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s25 -g1MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s26 -g100KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s27 -g10KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read and write limits" -./datagen -g1GB > tmp +./datagen -s28 -g1GB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s29 -g100MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s30 -g10MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s31 -g1MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s32 -g100KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s33 -g10KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 diff -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- forced compression level" -./datagen -g1GB > tmp +./datagen -s34 -g1GB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g100MB > tmp +./datagen -s35 -g100MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g10MB > tmp +./datagen -s36 -g10MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g1MB > tmp +./datagen -s37 -g1MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g100KB > tmp +./datagen -s38 -g100KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 rm tmp* -./datagen -g10KB > tmp +./datagen -s39 -g10KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 diff tmp tmp2 diff --git a/contrib/adaptive-compression/test-performance.sh b/contrib/adaptive-compression/test-performance.sh index 6c4991c48..958cb3cc8 100755 --- a/contrib/adaptive-compression/test-performance.sh +++ b/contrib/adaptive-compression/test-performance.sh @@ -1,40 +1,40 @@ echo "testing time -- no limits set" -./datagen -g1GB > tmp +./datagen -s1 -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -./datagen -g2GB > tmp +./datagen -s2 -g2GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* -./datagen -g4GB > tmp +./datagen -s3 -g4GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp rm tmp* echo -e "\ntesting compression ratio -- no limits set" -./datagen -g1GB > tmp +./datagen -s4 -g1GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g2GB > tmp +./datagen -s5 -g2GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g4GB > tmp +./datagen -s6 -g4GB > tmp time ./adapt -otmp1.zst tmp time zstd -1 -o tmp2.zst tmp ls -l tmp1.zst tmp2.zst rm tmp* echo e "\ntesting performance at various compression levels -- no limits set" -./datagen -g1GB > tmp +./datagen -s7 -g1GB > tmp echo "adapt" time ./adapt -i5 -f tmp -otmp1.zst echo "zstdcli" @@ -42,7 +42,7 @@ time zstd -5 tmp -o tmp2.zst ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g1GB > tmp +./datagen -s8 -g1GB > tmp echo "adapt" time ./adapt -i10 -f tmp -otmp1.zst echo "zstdcli" @@ -50,7 +50,7 @@ time zstd -10 tmp -o tmp2.zst ls -l tmp1.zst tmp2.zst rm tmp* -./datagen -g1GB > tmp +./datagen -s9 -g1GB > tmp echo "adapt" time ./adapt -i15 -f tmp -otmp1.zst echo "zstdcli" From 50ce4eaeb65fcdd521c5be710cfb0dff7c02e1bf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 10:12:44 -0700 Subject: [PATCH 076/145] added error detection for pthread initialization, added compression completion measurement, fixed const values --- contrib/adaptive-compression/adapt.c | 169 ++++++++++++++++++--------- lib/compress/zstd_compress.c | 9 ++ lib/zstd.h | 6 +- 3 files changed, 131 insertions(+), 53 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index addd0c59d..d45bdf85b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,6 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 +#define MAX_COMPRESSION_LEVEL_CHANGE 10 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -65,6 +66,16 @@ typedef struct { size_t dictSize; } jobDescription; +typedef struct { + pthread_mutex_t pMutex; + int noError; +} mutex_t; + +typedef struct { + pthread_cond_t pCond; + int noError; +} cond_t; + typedef struct { unsigned compressionLevel; unsigned numActiveThreads; @@ -76,14 +87,16 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - pthread_mutex_t jobCompressed_mutex; - pthread_cond_t jobCompressed_cond; - pthread_mutex_t jobReady_mutex; - pthread_cond_t jobReady_cond; - pthread_mutex_t allJobsCompleted_mutex; - pthread_cond_t allJobsCompleted_cond; - pthread_mutex_t jobWrite_mutex; - pthread_cond_t jobWrite_cond; + unsigned completionMeasured; + double completion; + mutex_t jobCompressed_mutex; + cond_t jobCompressed_cond; + mutex_t jobReady_mutex; + cond_t jobReady_cond; + mutex_t allJobsCompleted_mutex; + cond_t allJobsCompleted_cond; + mutex_t jobWrite_mutex; + cond_t jobWrite_cond; size_t lastDictSize; inBuff_t input; cStat_t stats; @@ -107,20 +120,38 @@ static void freeCompressionJobs(adaptCCtx* ctx) } } +static int destroyMutex(mutex_t* mutex) +{ + if (mutex->noError) { + int const ret = pthread_mutex_destroy(&mutex->pMutex); + return ret; + } + return 0; +} + +static int destroyCond(cond_t* cond) +{ + if (cond->noError) { + int const ret = pthread_cond_destroy(&cond->pCond); + return ret; + } + return 0; +} + static int freeCCtx(adaptCCtx* ctx) { if (!ctx) return 0; { int error = 0; - error |= pthread_mutex_destroy(&ctx->jobCompressed_mutex); - error |= pthread_cond_destroy(&ctx->jobCompressed_cond); - error |= pthread_mutex_destroy(&ctx->jobReady_mutex); - error |= pthread_cond_destroy(&ctx->jobReady_cond); - error |= pthread_mutex_destroy(&ctx->allJobsCompleted_mutex); - error |= pthread_cond_destroy(&ctx->allJobsCompleted_cond); - error |= pthread_mutex_destroy(&ctx->jobWrite_mutex); - error |= pthread_cond_destroy(&ctx->jobWrite_cond); - error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; + error |= destroyMutex(&ctx->jobCompressed_mutex); + error |= destroyCond(&ctx->jobCompressed_cond); + error |= destroyMutex(&ctx->jobReady_mutex); + error |= destroyCond(&ctx->jobReady_cond); + error |= destroyMutex(&ctx->allJobsCompleted_mutex); + error |= destroyCond(&ctx->allJobsCompleted_cond); + error |= destroyMutex(&ctx->jobWrite_mutex); + error |= destroyCond(&ctx->jobWrite_cond); + error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -132,23 +163,41 @@ static int freeCCtx(adaptCCtx* ctx) } } +static int initMutex(mutex_t* mutex) +{ + int const ret = pthread_mutex_init(&mutex->pMutex, NULL); + mutex->noError = !ret; + return ret; +} + +static int initCond(cond_t* cond) +{ + int const ret = pthread_cond_init(&cond->pCond, NULL); + cond->noError = !ret; + return ret; +} + static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) { - adaptCCtx* ctx = calloc(1, sizeof(adaptCCtx)); + adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); if (ctx == NULL) { DISPLAY("Error: could not allocate space for context\n"); return NULL; } ctx->compressionLevel = g_compressionLevel; - pthread_mutex_init(&ctx->jobCompressed_mutex, NULL); - pthread_cond_init(&ctx->jobCompressed_cond, NULL); - pthread_mutex_init(&ctx->jobReady_mutex, NULL); - pthread_cond_init(&ctx->jobReady_cond, NULL); - pthread_mutex_init(&ctx->allJobsCompleted_mutex, NULL); - pthread_cond_init(&ctx->allJobsCompleted_cond, NULL); - pthread_mutex_init(&ctx->jobWrite_mutex, NULL); - pthread_cond_init(&ctx->jobWrite_cond, NULL); + { + int pthreadError = 0; + pthreadError |= initMutex(&ctx->jobCompressed_mutex); + pthreadError |= initCond(&ctx->jobCompressed_cond); + pthreadError |= initMutex(&ctx->jobReady_mutex); + pthreadError |= initCond(&ctx->jobReady_cond); + pthreadError |= initMutex(&ctx->allJobsCompleted_mutex); + pthreadError |= initCond(&ctx->allJobsCompleted_cond); + pthreadError |= initMutex(&ctx->jobWrite_mutex); + pthreadError |= initCond(&ctx->jobWrite_cond); + if (pthreadError) return NULL; + } ctx->numJobs = numJobs; ctx->jobReadyID = 0; ctx->jobCompressedID = 0; @@ -213,11 +262,11 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { if (!ctx) return; - pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); while (ctx->allJobsCompleted == 0) { - pthread_cond_wait(&ctx->allJobsCompleted_cond, &ctx->allJobsCompleted_mutex); + pthread_cond_wait(&ctx->allJobsCompleted_cond.pCond, &ctx->allJobsCompleted_mutex.pMutex); } - pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } /* @@ -252,14 +301,20 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { + double const completion = ctx->completion; + unsigned const maxChange = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel--; + DEBUG(2, "completion: %f\n", completion); + ctx->compressionLevel -= change; reset = 1; } if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; + ctx->completion = 1; + ctx->completionMeasured = 0; } return ctx->compressionLevel; } @@ -281,14 +336,14 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - pthread_mutex_lock(&ctx->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID) { ctx->stats.waitReady++; ctx->stats.readyCounter++; DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobReady_cond, &ctx->jobReady_mutex); + pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobReady_mutex); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); @@ -299,7 +354,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { - size_t useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); @@ -332,11 +387,11 @@ static void* compressionThread(void* arg) } job->dst.size = job->compressedSize; } - pthread_mutex_lock(&ctx->jobCompressed_mutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; DEBUG(3, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompressed_cond); - pthread_mutex_unlock(&ctx->jobCompressed_mutex); + pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "finished job compression %u\n", currJob); currJob++; if (job->lastJob || ctx->threadError) { @@ -374,14 +429,19 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - pthread_mutex_lock(&ctx->jobCompressed_mutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + if (!ctx->completionMeasured) { + ctx->completion = ZSTD_getCompletion(ctx->cctx); + ctx->completionMeasured = 1; + } + DEBUG(2, "output detected completion: %f\n", ctx->completion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); - pthread_cond_wait(&ctx->jobCompressed_cond, &ctx->jobCompressed_mutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobCompressed_mutex); + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; @@ -403,19 +463,19 @@ static void* outputThread(void* arg) currJob++; displayProgress(currJob, ctx->compressionLevel, job->lastJob); DEBUG(3, "locking job write mutex\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; - pthread_cond_signal(&ctx->jobWrite_cond); - pthread_mutex_unlock(&ctx->jobWrite_mutex); + pthread_cond_signal(&ctx->jobWrite_cond.pCond); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "unlocking job write mutex\n"); if (job->lastJob || ctx->threadError) { /* finished with all jobs */ DEBUG(3, "all jobs finished writing\n"); - pthread_mutex_lock(&ctx->allJobsCompleted_mutex); + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); ctx->allJobsCompleted = 1; - pthread_cond_signal(&ctx->allJobsCompleted_cond); - pthread_mutex_unlock(&ctx->allJobsCompleted_mutex); + pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } } @@ -428,15 +488,20 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUG(3, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex); + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; + if (!ctx->completionMeasured) { + ctx->completion = ZSTD_getCompletion(ctx->cctx); + ctx->completionMeasured = 1; + } + DEBUG(2, "job creation detected completion %f\n", ctx->completion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); - pthread_cond_wait(&ctx->jobWrite_cond, &ctx->jobWrite_mutex); + pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobWrite_mutex); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -446,10 +511,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->lastJob = last; memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); job->dictSize = ctx->lastDictSize; - pthread_mutex_lock(&ctx->jobReady_mutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); ctx->jobReadyID++; - pthread_cond_signal(&ctx->jobReady_cond); - pthread_mutex_unlock(&ctx->jobReady_mutex); + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f492d92bd..0c8edecec 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,6 +140,9 @@ struct ZSTD_CCtx_s { /* Multi-threading */ U32 nbThreads; ZSTDMT_CCtx* mtctx; + + /* adaptive compression */ + double completion; }; @@ -2845,6 +2848,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, BYTE* op = ostart; U32 const maxDist = 1 << cctx->appliedParams.cParams.windowLog; + cctx->completion = 0; if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); @@ -2895,6 +2899,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, } remaining -= blockSize; + cctx->completion = 1 - (double)remaining/srcSize; dstCapacity -= cSize; ip += blockSize; op += cSize; @@ -2997,6 +3002,10 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, return fhSize; } +ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx) +{ + return cctx->completion; +} size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, diff --git a/lib/zstd.h b/lib/zstd.h index 58e9a5606..e835ad3a7 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -808,7 +808,11 @@ ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); - +/*! ZSTD_getCompletion: get a double representing how much of a file/buffer has been compressed + * using ZSTD_compressContinue() + * return: a double value in the range of 0 to 1 representing how much a compression job has finished + */ +ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx); /*- Buffer-less streaming decompression (synchronous mode) From 044e40db5a97eabfc7d67e44faa751114767ac20 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 11:19:23 -0700 Subject: [PATCH 077/145] removed freeCCtx() calls from createCCtx() so that it is not called twice during errors --- contrib/adaptive-compression/adapt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d45bdf85b..a6ba47242 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -214,7 +214,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); - freeCCtx(ctx); return NULL; } job->src.capacity = FILE_CHUNK_SIZE; @@ -231,17 +230,14 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); - freeCCtx(ctx); return NULL; } if (!ctx->cctx) { DISPLAY("Error: could not allocate ZSTD_CCtx\n"); - freeCCtx(ctx); return NULL; } if (!ctx->jobs) { DISPLAY("Error: could not allocate space for jobs during context creation\n"); - freeCCtx(ctx); return NULL; } { @@ -249,7 +245,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); if (dstFile == NULL) { DISPLAY("Error: could not open output file\n"); - freeCCtx(ctx); return NULL; } ctx->dstFile = dstFile; From 708238e07e28569ff946c05b9eef3f98b32af124 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 14:01:13 -0700 Subject: [PATCH 078/145] open file outside of adaptCCtx, pass to the output thread --- contrib/adaptive-compression/adapt.c | 55 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a6ba47242..67d7fcc92 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -101,13 +101,18 @@ typedef struct { inBuff_t input; cStat_t stats; jobDescription* jobs; - FILE* dstFile; ZSTD_CCtx* cctx; } adaptCCtx; +typedef struct { + adaptCCtx* ctx; + FILE* dstFile; +} outputThreadArg; + typedef struct { FILE* srcFile; adaptCCtx* ctx; + outputThreadArg* otArg; } fcResources; static void freeCompressionJobs(adaptCCtx* ctx) @@ -151,7 +156,6 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); - error |= (ctx->dstFile != NULL && ctx->dstFile != stdout) ? fclose(ctx->dstFile) : 0; error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -177,7 +181,7 @@ static int initCond(cond_t* cond) return ret; } -static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) +static adaptCCtx* createCCtx(unsigned numJobs) { adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); @@ -240,15 +244,6 @@ static adaptCCtx* createCCtx(unsigned numJobs, const char* const outFilename) DISPLAY("Error: could not allocate space for jobs during context creation\n"); return NULL; } - { - unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); - FILE* dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); - if (dstFile == NULL) { - DISPLAY("Error: could not open output file\n"); - return NULL; - } - ctx->dstFile = dstFile; - } return ctx; } @@ -417,7 +412,9 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) static void* outputThread(void* arg) { - adaptCCtx* ctx = (adaptCCtx*)arg; + outputThreadArg* const otArg = (outputThreadArg*)arg; + adaptCCtx* const ctx = otArg->ctx; + FILE* const dstFile = otArg->dstFile; unsigned currJob = 0; for ( ; ; ) { @@ -446,7 +443,7 @@ static void* outputThread(void* arg) return arg; } { - size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, ctx->dstFile); + size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); ctx->threadError = 1; @@ -532,16 +529,16 @@ static void printStats(cStat_t stats) DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); } -static int performCompression(adaptCCtx* ctx, FILE* const srcFile) +static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { - if (!ctx || !srcFile) { + if (!ctx || !srcFile || !otArg) { return 1; } /* create output thread */ { pthread_t out; - if (pthread_create(&out, NULL, &outputThread, ctx)) { + if (pthread_create(&out, NULL, &outputThread, otArg)) { DISPLAY("Error: could not create output thread\n"); ctx->threadError = 1; return 1; @@ -606,14 +603,25 @@ static fcResources createFileCompressionResources(const char* const srcFilename, outFilename = fileAndSuffix; } + { + unsigned const stdoutUsed = !strcmp(outFilename, stdoutmark); + FILE* const dstFile = stdoutUsed ? stdout : fopen(outFilename, "wb"); + fcr.otArg = malloc(sizeof(outputThreadArg)); + if (!fcr.otArg) { + DISPLAY("Error: could not allocate space for output thread argument\n"); + return fcr; + } + fcr.otArg->dstFile = dstFile; + } /* checking for errors */ - if (!outFilename || !srcFile) { - DISPLAY("Error: initial variables could not be allocated\n"); + if (!fcr.otArg->dstFile || !srcFile) { + DISPLAY("Error: some file(s) could not be opened\n"); return fcr; } /* creating context */ - fcr.ctx = createCCtx(numJobs, outFilename); + fcr.ctx = createCCtx(numJobs); + fcr.otArg->ctx = fcr.ctx; fcr.srcFile = srcFile; return fcr; } @@ -625,6 +633,11 @@ static int freeFileCompressionResources(fcResources* fcr) if (g_displayStats) printStats(fcr->ctx->stats); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; + if (fcr->otArg) { + ret |= (fcr->otArg->dstFile != stdout) ? fclose(fcr->otArg->dstFile) : 0; + free(fcr->otArg); + /* no need to freeCCtx() on otArg->ctx because it should be the same context */ + } return ret; } @@ -634,7 +647,7 @@ static int compressFilename(const char* const srcFilename, const char* const dst UTIL_getTime(&g_startTime); g_streamedSize = 0; fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); - ret |= performCompression(fcr.ctx, fcr.srcFile); + ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg); ret |= freeFileCompressionResources(&fcr); return ret; } From 6be22f1f842edc65ec1ddcf9c961e9bb854e1dc1 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 14:39:10 -0700 Subject: [PATCH 079/145] swap buffers instead of copying memory over --- contrib/adaptive-compression/adapt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 67d7fcc92..3aec50c07 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -501,7 +501,12 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->src.size = srcSize; job->jobID = nextJob; job->lastJob = last; - memcpy(job->src.start, ctx->input.buffer.start, ctx->lastDictSize + srcSize); + { + /* swap buffer */ + void* const copy = job->src.start; + job->src.start = ctx->input.buffer.start; + ctx->input.buffer.start = copy; + } job->dictSize = ctx->lastDictSize; pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); ctx->jobReadyID++; @@ -514,7 +519,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) if (!last) { size_t const oldDictSize = ctx->lastDictSize; DEBUG(3, "oldDictSize %zu\n", oldDictSize); - memmove(ctx->input.buffer.start, ctx->input.buffer.start + oldDictSize, srcSize); + memcpy(ctx->input.buffer.start, job->src.start + oldDictSize, srcSize); ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; } From b3c9e02bb6ab8807990ddc85a99f9953b9ee3578 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 15:34:58 -0700 Subject: [PATCH 080/145] added signal to other threads whenever error occurs --- contrib/adaptive-compression/adapt.c | 44 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3aec50c07..01a73a669 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -247,13 +247,31 @@ static adaptCCtx* createCCtx(unsigned numJobs) return ctx; } +static void signalErrorToThreads(adaptCCtx* ctx) +{ + ctx->threadError = 1; + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + pthread_cond_signal(&ctx->jobWrite_cond.pCond); + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); + pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); + pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); +} static void waitUntilAllJobsCompleted(adaptCCtx* ctx) { if (!ctx) return; pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); - while (ctx->allJobsCompleted == 0) { + while (ctx->allJobsCompleted == 0 && !ctx->threadError) { pthread_cond_wait(&ctx->allJobsCompleted_cond.pCond, &ctx->allJobsCompleted_mutex.pMutex); } pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); @@ -327,7 +345,7 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - while(currJob + 1 > ctx->jobReadyID) { + while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { ctx->stats.waitReady++; ctx->stats.readyCounter++; DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -351,7 +369,7 @@ static void* compressionThread(void* arg) size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { DISPLAY("Error: something went wrong while starting compression\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } } @@ -362,7 +380,7 @@ static void* compressionThread(void* arg) if (ZSTD_isError(hSize)) { DISPLAY("Error: something went wrong while continuing compression\n"); job->compressedSize = hSize; - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } ZSTD_invalidateRepCodes(ctx->cctx); @@ -372,7 +390,7 @@ static void* compressionThread(void* arg) ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); if (ZSTD_isError(job->compressedSize)) { DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } job->dst.size = job->compressedSize; @@ -422,7 +440,7 @@ static void* outputThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (currJob + 1 > ctx->jobCompressedID) { + while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; if (!ctx->completionMeasured) { @@ -439,14 +457,14 @@ static void* outputThread(void* arg) size_t const compressedSize = job->compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } { size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); if (writeSize != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return arg; } } @@ -482,7 +500,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "createCompressionJob(): wait for job write\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); - while (nextJob - ctx->jobWriteID >= ctx->numJobs) { + while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; if (!ctx->completionMeasured) { @@ -545,7 +563,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pthread_t out; if (pthread_create(&out, NULL, &outputThread, otArg)) { DISPLAY("Error: could not create output thread\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } } @@ -555,7 +573,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pthread_t compression; if (pthread_create(&compression, NULL, &compressionThread, ctx)) { DISPLAY("Error: could not create compression thread\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } } @@ -565,7 +583,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); - ctx->threadError = 1; + signalErrorToThreads(ctx); return 1; } g_streamedSize += readSize; @@ -574,7 +592,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA int const last = feof(srcFile); int const error = createCompressionJob(ctx, readSize, last); if (error != 0) { - ctx->threadError = 1; + signalErrorToThreads(ctx); return error; } } From 5af04c57b058fcf7be1e0b8545d918c901c4bbb6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 17 Jul 2017 17:59:50 -0700 Subject: [PATCH 081/145] change parameters for compression level adapt --- contrib/adaptive-compression/adapt.c | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 01a73a669..62f5ec912 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 1 -#define MAX_COMPRESSION_LEVEL_CHANGE 10 +#define MAX_COMPRESSION_LEVEL_CHANGE 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -277,6 +277,15 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +/* this function normalizes counters when compression level is changing */ +static void reduceCounters(adaptCCtx* ctx) +{ + unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); + ctx->stats.writeCounter -= min; + ctx->stats.compressedCounter -= min; + ctx->stats.readyCounter -= min; +} + /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -285,10 +294,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) * compression thread lag => decreased compression level * detecting which thread is lagging is done by keeping track of how many calls each thread makes to pthread_cond_wait */ -static unsigned adaptCompressionLevel(adaptCCtx* ctx) +static void adaptCompressionLevel(adaptCCtx* ctx) { if (g_forceCompressionLevel) { - return g_compressionLevel; + ctx->compressionLevel = g_compressionLevel; } else { unsigned reset = 0; @@ -296,10 +305,11 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = ((compressWaiting && createWaiting) || (createWaiting && !writeWaiting)); - unsigned const compressSlow = ((writeWaiting && createWaiting) || (writeWaiting && !compressWaiting)); - unsigned const createSlow = ((compressWaiting && writeWaiting) || (compressWaiting && !createWaiting)); - DEBUG(3, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); + unsigned const writeSlow = (compressWaiting && createWaiting); + unsigned const compressSlow = (writeWaiting && createWaiting); + unsigned const createSlow = (compressWaiting && writeWaiting); + DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); if (allSlow) { reset = 1; } @@ -310,10 +320,10 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) } else if (compressSlow && ctx->compressionLevel > 1) { double const completion = ctx->completion; - unsigned const maxChange = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); + DEBUG(3, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -324,7 +334,6 @@ static unsigned adaptCompressionLevel(adaptCCtx* ctx) ctx->completion = 1; ctx->completionMeasured = 0; } - return ctx->compressionLevel; } } @@ -348,6 +357,8 @@ static void* compressionThread(void* arg) while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { ctx->stats.waitReady++; ctx->stats.readyCounter++; + reduceCounters(ctx); + adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } @@ -357,13 +368,13 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { - unsigned const cLevel = adaptCompressionLevel(ctx); + unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(2, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); @@ -443,11 +454,13 @@ static void* outputThread(void* arg) while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + reduceCounters(ctx); if (!ctx->completionMeasured) { ctx->completion = ZSTD_getCompletion(ctx->cctx); ctx->completionMeasured = 1; } - DEBUG(2, "output detected completion: %f\n", ctx->completion); + adaptCompressionLevel(ctx); + DEBUG(3, "output detected completion: %f\n", ctx->completion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -503,11 +516,13 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { ctx->stats.waitWrite++; ctx->stats.writeCounter++; + reduceCounters(ctx); if (!ctx->completionMeasured) { ctx->completion = ZSTD_getCompletion(ctx->cctx); ctx->completionMeasured = 1; } - DEBUG(2, "job creation detected completion %f\n", ctx->completion); + adaptCompressionLevel(ctx); + DEBUG(3, "job creation detected completion %f\n", ctx->completion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From ae47eab2fdb48cd55f20756f282dfdff3a7f4ee7 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 12:58:50 -0700 Subject: [PATCH 082/145] changed test cases to use -s setting on the diffs --- .../adaptive-compression/test-correctness.sh | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index b057cbd64..86d39ee4f 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -2,239 +2,239 @@ echo "correctness tests -- general" ./datagen -s1 -g1GB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s2 -g500MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s3 -g250MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s4 -g125MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s5 -g50MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s6 -g25MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s7 -g10MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s8 -g5MB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s9 -g500KB > tmp ./adapt -otmp.zst tmp zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- streaming" ./datagen -s10 -g1GB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s11 -g100MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s12 -g10MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s13 -g1MB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s14 -g100KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s15 -g10KB > tmp cat tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read limit" ./datagen -s16 -g1GB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s17 -g100MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s18 -g10MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s19 -g1MB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s20 -g100KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s21 -g10KB > tmp pv -L 50m -q tmp | ./adapt > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- write limit" ./datagen -s22 -g1GB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s23 -g100MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s24 -g10MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s25 -g1MB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s26 -g100KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s27 -g10KB > tmp pv -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- read and write limits" ./datagen -s28 -g1GB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s29 -g100MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s30 -g10MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s31 -g1MB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s32 -g100KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s33 -g10KB > tmp pv -L 50m -q tmp | ./adapt | pv -L 5m -q > tmp.zst zstd -d tmp.zst -o tmp2 -diff -q tmp tmp2 +diff -s -q tmp tmp2 rm tmp* echo -e "\ncorrectness tests -- forced compression level" ./datagen -s34 -g1GB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s35 -g100MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s36 -g10MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s37 -g1MB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s38 -g100KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* ./datagen -s39 -g10KB > tmp ./adapt tmp -otmp.zst -i11 -f zstd -d tmp.zst -o tmp2 -diff tmp tmp2 +diff -s -q tmp tmp2 rm tmp* make clean From 29c36cf051545119fddfa898e8a03ce06e0380ef Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 13:30:29 -0700 Subject: [PATCH 083/145] rename completion variable, split up fwrite operations in order to track progress --- contrib/adaptive-compression/adapt.c | 41 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 62f5ec912..404db6d94 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -87,8 +87,8 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - unsigned completionMeasured; - double completion; + unsigned compressionCompletionMeasured; + double compressionCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -319,7 +319,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if (compressSlow && ctx->compressionLevel > 1) { - double const completion = ctx->completion; + double const completion = ctx->compressionCompletion; unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); @@ -331,8 +331,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; - ctx->completion = 1; - ctx->completionMeasured = 0; + ctx->compressionCompletion = 1; + ctx->compressionCompletionMeasured = 0; } } } @@ -455,12 +455,12 @@ static void* outputThread(void* arg) ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; reduceCounters(ctx); - if (!ctx->completionMeasured) { - ctx->completion = ZSTD_getCompletion(ctx->cctx); - ctx->completionMeasured = 1; + if (!ctx->compressionCompletionMeasured) { + ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); + ctx->compressionCompletionMeasured = 1; } adaptCompressionLevel(ctx); - DEBUG(3, "output detected completion: %f\n", ctx->completion); + DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -468,14 +468,25 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; + size_t remaining = compressedSize; if (ZSTD_isError(compressedSize)) { DISPLAY("Error: an error occurred during compression\n"); signalErrorToThreads(ctx); return arg; } { - size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - if (writeSize != compressedSize) { + // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); + size_t const blockSize = 4 << 20; + size_t pos = 0; + for ( ; ; ) { + size_t const writeSize = MIN(remaining, blockSize); + size_t const ret = fwrite(job->dst.start + pos, 1, writeSize, dstFile); + if (ret != writeSize) break; + pos += ret; + remaining -= ret; + if (remaining == 0) break; + } + if (pos != compressedSize) { DISPLAY("Error: an error occurred during file write operation\n"); signalErrorToThreads(ctx); return arg; @@ -517,12 +528,12 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->stats.waitWrite++; ctx->stats.writeCounter++; reduceCounters(ctx); - if (!ctx->completionMeasured) { - ctx->completion = ZSTD_getCompletion(ctx->cctx); - ctx->completionMeasured = 1; + if (!ctx->compressionCompletion) { + ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); + ctx->compressionCompletionMeasured = 1; } adaptCompressionLevel(ctx); - DEBUG(3, "job creation detected completion %f\n", ctx->completion); + DEBUG(3, "job creation detected completion %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From a34bc30237b2e311753198912f3768ff1e7d0edc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 13:31:02 -0700 Subject: [PATCH 084/145] setting up basic readme --- contrib/adaptive-compression/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 contrib/adaptive-compression/README.md diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md new file mode 100644 index 000000000..1d2613377 --- /dev/null +++ b/contrib/adaptive-compression/README.md @@ -0,0 +1,2 @@ +ZSTD_adapt is a compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. +In many scenarios, using ZSTD without a properly adjusted compression level results in a pipe bottleneck, or a compressed file that could have been reduced in size. From ad66faf16a7093c0cc02b86da2d4c583a5bb7dbf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 15:23:11 -0700 Subject: [PATCH 085/145] added progress check for filewriting, put important shared data behind mutex when being read from/written to --- contrib/adaptive-compression/adapt.c | 78 +++++++++++++++++++++------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 404db6d94..b7f4dccf2 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -88,7 +88,9 @@ typedef struct { unsigned allJobsCompleted; unsigned adaptParam; unsigned compressionCompletionMeasured; + unsigned writeCompletionMeasured; double compressionCompletion; + double writeCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -97,6 +99,8 @@ typedef struct { cond_t allJobsCompleted_cond; mutex_t jobWrite_mutex; cond_t jobWrite_cond; + mutex_t completion_mutex; + mutex_t stats_mutex; size_t lastDictSize; inBuff_t input; cStat_t stats; @@ -156,6 +160,8 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); + error |= destroyMutex(&ctx->completion_mutex); + error |= destroyMutex(&ctx->stats_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -200,6 +206,8 @@ static adaptCCtx* createCCtx(unsigned numJobs) pthreadError |= initCond(&ctx->allJobsCompleted_cond); pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); + pthreadError |= initMutex(&ctx->completion_mutex); + pthreadError |= initMutex(&ctx->stats_mutex); if (pthreadError) return NULL; } ctx->numJobs = numJobs; @@ -315,24 +323,44 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - ctx->compressionLevel++; - reset = 1; + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->writeCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; + unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(2, "write completion: %f\n", completion); + ctx->compressionLevel += change; + reset = 1; + } } else if (compressSlow && ctx->compressionLevel > 1) { - double const completion = ctx->compressionCompletion; - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->compressionCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "completion: %f\n", completion); + ctx->compressionLevel -= change; + reset = 1; + } } if (reset) { ctx->stats.readyCounter = 0; ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1; ctx->compressionCompletionMeasured = 0; + ctx->writeCompletion = 1; + ctx->writeCompletionMeasured = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } } @@ -455,12 +483,14 @@ static void* outputThread(void* arg) ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; reduceCounters(ctx); + pthread_mutex_lock(&ctx->completion_mutex.pMutex); if (!ctx->compressionCompletionMeasured) { ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); ctx->compressionCompletionMeasured = 1; + DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); - DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -484,6 +514,14 @@ static void* outputThread(void* arg) if (ret != writeSize) break; pos += ret; remaining -= ret; + + /* update completion variable for writing */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->writeCompletionMeasured) { + ctx->writeCompletion = 1 - (double)remaining/compressedSize; + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + if (remaining == 0) break; } if (pos != compressedSize) { @@ -528,12 +566,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->stats.waitWrite++; ctx->stats.writeCounter++; reduceCounters(ctx); - if (!ctx->compressionCompletion) { - ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); - ctx->compressionCompletionMeasured = 1; - } + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); - DEBUG(3, "job creation detected completion %f\n", ctx->compressionCompletion); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } @@ -552,10 +588,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->input.buffer.start = copy; } job->dictSize = ctx->lastDictSize; - pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - ctx->jobReadyID++; - pthread_cond_signal(&ctx->jobReady_cond.pCond); - pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -567,6 +600,13 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; } + + /* signal job ready */ + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + ctx->jobReadyID++; + pthread_cond_signal(&ctx->jobReady_cond.pCond); + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + return 0; } From 2c4e4ddc50deb1798eb89de6ee0f0a539838e54c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 15:55:58 -0700 Subject: [PATCH 086/145] added mutex for stats struct --- contrib/adaptive-compression/adapt.c | 122 ++++++++++++++++----------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b7f4dccf2..35e26abb7 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -288,10 +288,12 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* this function normalizes counters when compression level is changing */ static void reduceCounters(adaptCCtx* ctx) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); ctx->stats.writeCounter -= min; ctx->stats.compressedCounter -= min; ctx->stats.readyCounter -= min; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); } /* @@ -309,58 +311,68 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else { unsigned reset = 0; - unsigned const allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - unsigned const compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - unsigned const writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - unsigned const createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - unsigned const writeSlow = (compressWaiting && createWaiting); - unsigned const compressSlow = (writeWaiting && createWaiting); - unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); - DEBUG(2, "ready: %u compressed: %u write: %u\n", ctx->stats.readyCounter, ctx->stats.compressedCounter, ctx->stats.writeCounter); - if (allSlow) { - reset = 1; - } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->writeCompletion; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; - unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; - DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(2, "write completion: %f\n", completion); - ctx->compressionLevel += change; - reset = 1; - } - } - else if (compressSlow && ctx->compressionLevel > 1) { - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletion; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; - } - } - if (reset) { - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; + unsigned allSlow; + unsigned compressWaiting; + unsigned writeWaiting; + unsigned createWaiting; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 1; - ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 1; - ctx->writeCompletionMeasured = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->stats_mutex.pMutex); + allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; + compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; + writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; + createWaiting = ctx->adaptParam < ctx->stats.writeCounter; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + { + unsigned const writeSlow = (compressWaiting && createWaiting); + unsigned const compressSlow = (writeWaiting && createWaiting); + unsigned const createSlow = (compressWaiting && writeWaiting); + DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + if (allSlow) { + reset = 1; + } + else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { + DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->writeCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; + unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(2, "write completion: %f\n", completion); + ctx->compressionLevel += change; + reset = 1; + } + } + else if (compressSlow && ctx->compressionLevel > 1) { + double completion; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + completion = ctx->compressionCompletion; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + { + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); + DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(3, "completion: %f\n", completion); + ctx->compressionLevel -= change; + reset = 1; + } + } + if (reset) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); + ctx->stats.readyCounter = 0; + ctx->stats.writeCounter = 0; + ctx->stats.compressedCounter = 0; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 1; + ctx->compressionCompletionMeasured = 0; + ctx->writeCompletion = 1; + ctx->writeCompletionMeasured = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } } } } @@ -383,8 +395,10 @@ static void* compressionThread(void* arg) DEBUG(3, "compressionThread(): waiting on job ready\n"); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitReady++; ctx->stats.readyCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -480,8 +494,10 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitCompressed++; ctx->stats.compressedCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); if (!ctx->compressionCompletionMeasured) { @@ -563,8 +579,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->stats_mutex.pMutex); ctx->stats.waitWrite++; ctx->stats.writeCounter++; + pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; @@ -719,7 +737,9 @@ static int freeFileCompressionResources(fcResources* fcr) { int ret = 0; waitUntilAllJobsCompleted(fcr->ctx); + pthread_mutex_lock(&fcr->ctx->stats_mutex.pMutex); if (g_displayStats) printStats(fcr->ctx->stats); + pthread_mutex_unlock(&fcr->ctx->stats_mutex.pMutex); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; if (fcr->otArg) { From 3d7f1afadd508de925783df775414b6e8e24e7bf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 18 Jul 2017 17:32:36 -0700 Subject: [PATCH 087/145] changed createCCtx() to split into initialization and creation --- contrib/adaptive-compression/adapt.c | 52 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 35e26abb7..7eb4333a1 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -187,14 +187,8 @@ static int initCond(cond_t* cond) return ret; } -static adaptCCtx* createCCtx(unsigned numJobs) +static int initCCtx(adaptCCtx* ctx, unsigned numJobs) { - - adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); - if (ctx == NULL) { - DISPLAY("Error: could not allocate space for context\n"); - return NULL; - } ctx->compressionLevel = g_compressionLevel; { int pthreadError = 0; @@ -208,7 +202,7 @@ static adaptCCtx* createCCtx(unsigned numJobs) pthreadError |= initCond(&ctx->jobWrite_cond); pthreadError |= initMutex(&ctx->completion_mutex); pthreadError |= initMutex(&ctx->stats_mutex); - if (pthreadError) return NULL; + if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; ctx->jobReadyID = 0; @@ -216,6 +210,12 @@ static adaptCCtx* createCCtx(unsigned numJobs) ctx->jobWriteID = 0; ctx->lastDictSize = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); + + if (!ctx->jobs) { + DISPLAY("Error: could not allocate space for jobs during context creation\n"); + return 1; + } + /* initializing jobs */ { unsigned jobNum; @@ -226,33 +226,51 @@ static adaptCCtx* createCCtx(unsigned numJobs) job->lastJob = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); - return NULL; + return 1; } job->src.capacity = FILE_CHUNK_SIZE; job->dst.capacity = ZSTD_compressBound(FILE_CHUNK_SIZE); } } + ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; ctx->adaptParam = DEFAULT_ADAPT_PARAM; + ctx->cctx = ZSTD_createCCtx(); + if (!ctx->cctx) { + DISPLAY("Error: could not allocate ZSTD_CCtx\n"); + return 1; + } + ctx->input.filled = 0; ctx->input.buffer.capacity = 2 * FILE_CHUNK_SIZE; + ctx->input.buffer.start = malloc(ctx->input.buffer.capacity); if (!ctx->input.buffer.start) { DISPLAY("Error: could not allocate input buffer\n"); + return 1; + } + return 0; +} + +static adaptCCtx* createCCtx(unsigned numJobs) +{ + + adaptCCtx* const ctx = calloc(1, sizeof(adaptCCtx)); + if (ctx == NULL) { + DISPLAY("Error: could not allocate space for context\n"); return NULL; } - if (!ctx->cctx) { - DISPLAY("Error: could not allocate ZSTD_CCtx\n"); - return NULL; + { + int const error = initCCtx(ctx, numJobs); + if (error) { + freeCCtx(ctx); + return NULL; + } + return ctx; } - if (!ctx->jobs) { - DISPLAY("Error: could not allocate space for jobs during context creation\n"); - return NULL; - } - return ctx; } static void signalErrorToThreads(adaptCCtx* ctx) From 6119cd216458d46909d2d12d858ac9bfa1c7837f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 09:43:17 -0700 Subject: [PATCH 088/145] added additional print for help menu --- contrib/adaptive-compression/adapt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 7eb4333a1..2182ad1e8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -827,6 +827,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -s : display information stats\n"); PRINT(" -h : display help/information\n"); + PRINT(" -f : force the compression level to stay constant\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) From 559ea4ff25b3728024a55fdc9966925de2e116ba Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 09:59:17 -0700 Subject: [PATCH 089/145] split up read process into smaller chunks --- contrib/adaptive-compression/adapt.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 2182ad1e8..d3ffe6a81 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -682,17 +682,30 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA /* creating jobs */ for ( ; ; ) { - size_t const readSize = fread(ctx->input.buffer.start + ctx->input.filled, 1, FILE_CHUNK_SIZE, srcFile); - if (readSize != FILE_CHUNK_SIZE && !feof(srcFile)) { + size_t pos = 0; + size_t const readBlockSize = 1 << 15; + size_t remaining = FILE_CHUNK_SIZE; + while (remaining != 0 && !feof(srcFile)) { + size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); + if (ret != readBlockSize && !feof(srcFile)) { + /* error could not read correct number of bytes */ + DISPLAY("Error: problem occurred during read from src file\n"); + signalErrorToThreads(ctx); + return 1; + } + pos += ret; + remaining -= ret; + } + if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); signalErrorToThreads(ctx); return 1; } - g_streamedSize += readSize; + g_streamedSize += pos; /* reading was fine, now create the compression job */ { int const last = feof(srcFile); - int const error = createCompressionJob(ctx, readSize, last); + int const error = createCompressionJob(ctx, pos, last); if (error != 0) { signalErrorToThreads(ctx); return error; From e11bf55d0bf0a83327031b921480625781133033 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:10:47 -0700 Subject: [PATCH 090/145] added mechanism for measuring how much of a job has been created --- contrib/adaptive-compression/adapt.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d3ffe6a81..de2e5e139 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -89,8 +89,10 @@ typedef struct { unsigned adaptParam; unsigned compressionCompletionMeasured; unsigned writeCompletionMeasured; + unsigned createCompletionMeasured; double compressionCompletion; double writeCompletion; + double createCompletion; mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -344,7 +346,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const writeSlow = (compressWaiting && createWaiting); unsigned const compressSlow = (writeWaiting && createWaiting); unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(2, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); + DEBUG(3, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); if (allSlow) { reset = 1; } @@ -352,13 +354,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->writeCompletion; + completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; + DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; - DEBUG(2, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(2, "write completion: %f\n", completion); + DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); + DEBUG(3, "write completion: %f\n", completion); ctx->compressionLevel += change; reset = 1; } @@ -418,6 +421,9 @@ static void* compressionThread(void* arg) ctx->stats.readyCounter++; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -695,6 +701,12 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pos += ret; remaining -= ret; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->createCompletionMeasured) { + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); + } + DEBUG(3, "create completion: %f\n", ctx->createCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); From 4497ecf297d3d9d71dca2f85aec3ee5467f1a37b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:14:00 -0700 Subject: [PATCH 091/145] change compression level only right before actually performing compression. When waiting, only update waiting statistics. --- contrib/adaptive-compression/adapt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index de2e5e139..b928d0a2c 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -424,7 +424,6 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } @@ -434,6 +433,7 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* compress the data */ { + adaptCompressionLevel(ctx); unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); @@ -530,7 +530,6 @@ static void* outputThread(void* arg) DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); } pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -611,7 +610,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - adaptCompressionLevel(ctx); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } From 338951cd482f630cd12b7ca4d15d84c594a77308 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 10:23:46 -0700 Subject: [PATCH 092/145] moved compression adapt to avoid warning --- contrib/adaptive-compression/adapt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b928d0a2c..cf908ca94 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -431,9 +431,12 @@ static void* compressionThread(void* arg) DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); + + /* adapt compression level */ + adaptCompressionLevel(ctx); + /* compress the data */ { - adaptCompressionLevel(ctx); unsigned const cLevel = ctx->compressionLevel; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); From f1ac518b59f471434fd4e9c7ce7c4ae609189914 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:23:40 -0700 Subject: [PATCH 093/145] split compression into smaller blocks --- contrib/adaptive-compression/adapt.c | 84 ++++++++++++++++++---------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cf908ca94..17721f87e 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -434,44 +434,66 @@ static void* compressionThread(void* arg) /* adapt compression level */ adaptCompressionLevel(ctx); - + /* compress the data */ { + size_t const compressionBlockSize = 4 << 17; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; + unsigned blockNum = 0; + size_t remaining = job->src.size; + size_t srcPos = 0; + size_t dstPos = 0; + size_t dictPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); - /* begin compression */ - { - size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); - size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; - } - } - /* continue compression */ - if (currJob != 0) { /* not first job flush/overwrite the frame header */ - size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, 0); - if (ZSTD_isError(hSize)) { - DISPLAY("Error: something went wrong while continuing compression\n"); - job->compressedSize = hSize; - signalErrorToThreads(ctx); - return arg; + /* reset compressed size */ + job->compressedSize = 0; + + while (remaining != 0) { + size_t const actualBlockSize = MIN(remaining, compressionBlockSize); + /* begin compression */ + { + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize + dictPos, useDictSize, cLevel); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } + } + + /* continue compression */ + if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ + size_t const hSize = ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, 0); + if (ZSTD_isError(hSize)) { + DISPLAY("Error: something went wrong while continuing compression\n"); + job->compressedSize = hSize; + signalErrorToThreads(ctx); + return arg; + } + ZSTD_invalidateRepCodes(ctx->cctx); + } + { + + size_t const ret = (job->lastJob && remaining <= compressionBlockSize) ? + ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : + ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); + if (ZSTD_isError(ret)) { + DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(ret)); + signalErrorToThreads(ctx); + return arg; + } + job->compressedSize += ret; + remaining -= actualBlockSize; + srcPos += actualBlockSize; + dstPos += ret; + dictPos += actualBlockSize; + blockNum++; } - ZSTD_invalidateRepCodes(ctx->cctx); - } - job->compressedSize = (job->lastJob) ? - ZSTD_compressEnd (ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size) : - ZSTD_compressContinue(ctx->cctx, job->dst.start, job->dst.capacity, job->src.start + job->dictSize, job->src.size); - if (ZSTD_isError(job->compressedSize)) { - DISPLAY("Error: something went wrong during compression: %s\n", ZSTD_getErrorName(job->compressedSize)); - signalErrorToThreads(ctx); - return arg; } job->dst.size = job->compressedSize; } From 5a85c57e3055b9a1bace0623bdfe8600d7efb128 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:47:17 -0700 Subject: [PATCH 094/145] set up new calculations compression completion progress --- contrib/adaptive-compression/adapt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 17721f87e..ab332f495 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -355,7 +355,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; - DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); + DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; @@ -375,7 +375,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); + DEBUG(2, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -493,6 +493,13 @@ static void* compressionThread(void* arg) dstPos += ret; dictPos += actualBlockSize; blockNum++; + + /* update completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (!ctx->compressionCompletionMeasured) { + ctx->compressionCompletion = 1 - (double)remaining/job->src.size; + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } job->dst.size = job->compressedSize; @@ -549,11 +556,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->compressionCompletionMeasured) { - ctx->compressionCompletion = ZSTD_getCompletion(ctx->cctx); - ctx->compressionCompletionMeasured = 1; - DEBUG(3, "output detected completion: %f\n", ctx->compressionCompletion); - } + ctx->compressionCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); From 6945b3c43d5be449d0bd1ff06c40bca2e5230fa3 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 11:51:50 -0700 Subject: [PATCH 095/145] removed previous version of completion for compression --- lib/compress/zstd_compress.c | 10 ---------- lib/zstd.h | 5 ----- 2 files changed, 15 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0c8edecec..90002c2c4 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,9 +140,6 @@ struct ZSTD_CCtx_s { /* Multi-threading */ U32 nbThreads; ZSTDMT_CCtx* mtctx; - - /* adaptive compression */ - double completion; }; @@ -2848,7 +2845,6 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, BYTE* op = ostart; U32 const maxDist = 1 << cctx->appliedParams.cParams.windowLog; - cctx->completion = 0; if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); @@ -2899,7 +2895,6 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, } remaining -= blockSize; - cctx->completion = 1 - (double)remaining/srcSize; dstCapacity -= cSize; ip += blockSize; op += cSize; @@ -3002,11 +2997,6 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, return fhSize; } -ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx) -{ - return cctx->completion; -} - size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) diff --git a/lib/zstd.h b/lib/zstd.h index e835ad3a7..6d69d94e6 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -808,11 +808,6 @@ ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -/*! ZSTD_getCompletion: get a double representing how much of a file/buffer has been compressed - * using ZSTD_compressContinue() - * return: a double value in the range of 0 to 1 representing how much a compression job has finished - */ -ZSTDLIB_API double ZSTD_getCompletion(ZSTD_CCtx* cctx); /*- Buffer-less streaming decompression (synchronous mode) From 42382c121639c8f620c2c67acfc2d2983781dffa Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 13:30:07 -0700 Subject: [PATCH 096/145] added some debug statements, adjusted end condition --- contrib/adaptive-compression/adapt.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ab332f495..d572ee191 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -375,7 +375,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); + DEBUG(3, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -452,6 +452,8 @@ static void* compressionThread(void* arg) while (remaining != 0) { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); + DEBUG(2, "remaining: %zu\n", remaining); + DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -478,8 +480,10 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - - size_t const ret = (job->lastJob && remaining <= compressionBlockSize) ? + DEBUG(2, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); + DEBUG(2, "lastJob %u\n", job->lastJob); + DEBUG(2, "compressionBlockSize %zu\n", compressionBlockSize); + size_t const ret = (job->lastJob && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); if (ZSTD_isError(ret)) { From 6767abe65273056289e431c5454b937b13bc34bc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 14:54:15 -0700 Subject: [PATCH 097/145] fixing error when file size is multiple of job size (in which case, the srcSize of the last job is 0) --- contrib/adaptive-compression/adapt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d572ee191..dcaf3aeff 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -450,7 +450,7 @@ static void* compressionThread(void* arg) /* reset compressed size */ job->compressedSize = 0; - while (remaining != 0) { + do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); DEBUG(2, "remaining: %zu\n", remaining); DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); @@ -505,7 +505,7 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } - } + } while (remaining != 0); job->dst.size = job->compressedSize; } pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); From 2a22c7915e749879e1abc8283b86212d8947e5fb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 16:00:54 -0700 Subject: [PATCH 098/145] call ZSTD_compressBegin() once --- contrib/adaptive-compression/adapt.c | 31 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index dcaf3aeff..cc72d1558 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -437,36 +437,36 @@ static void* compressionThread(void* arg) /* compress the data */ { - size_t const compressionBlockSize = 4 << 17; /* 128 KB */ + size_t const compressionBlockSize = 1 << 17; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; unsigned blockNum = 0; size_t remaining = job->src.size; size_t srcPos = 0; size_t dstPos = 0; - size_t dictPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; + /* begin compression */ + { + size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); + DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); + size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); + size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } + } + do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); DEBUG(2, "remaining: %zu\n", remaining); DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); - /* begin compression */ - { - size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); - size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize + dictPos, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; - } - } /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -495,7 +495,6 @@ static void* compressionThread(void* arg) remaining -= actualBlockSize; srcPos += actualBlockSize; dstPos += ret; - dictPos += actualBlockSize; blockNum++; /* update completion */ From dcf609f835b0c0fb2499d13c8fa755d99808341a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 19 Jul 2017 16:36:33 -0700 Subject: [PATCH 099/145] make adaptCompressionLevel oscillate less --- contrib/adaptive-compression/adapt.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cc72d1558..ce92d9140 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -358,8 +358,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE - 1)) + 1; - unsigned const change = writeSlow ? MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel) : 1; + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); DEBUG(3, "write completion: %f\n", completion); ctx->compressionLevel += change; @@ -372,7 +372,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) completion = ctx->compressionCompletion; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE-1)) + 1; + unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); DEBUG(3, "completion: %f\n", completion); @@ -388,10 +388,12 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 1; + ctx->compressionCompletion = 0; ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 1; + ctx->writeCompletion = 0; ctx->writeCompletionMeasured = 0; + ctx->createCompletion = 0; + ctx->createCompletionMeasured = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } @@ -423,6 +425,7 @@ static void* compressionThread(void* arg) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; + DEBUG(2, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -465,8 +468,8 @@ static void* compressionThread(void* arg) do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); - DEBUG(2, "remaining: %zu\n", remaining); - DEBUG(2, "actualBlockSize: %zu\n", actualBlockSize); + DEBUG(3, "remaining: %zu\n", remaining); + DEBUG(3, "actualBlockSize: %zu\n", actualBlockSize); /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -480,9 +483,9 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(2, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); - DEBUG(2, "lastJob %u\n", job->lastJob); - DEBUG(2, "compressionBlockSize %zu\n", compressionBlockSize); + DEBUG(3, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); + DEBUG(3, "lastJob %u\n", job->lastJob); + DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); size_t const ret = (job->lastJob && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); @@ -560,6 +563,7 @@ static void* outputThread(void* arg) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletionMeasured = 1; + DEBUG(2, "compressionCompletion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -576,7 +580,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = 4 << 20; + size_t const blockSize = 64 << 10; /* 64 KB */ size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -640,6 +644,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletionMeasured = 1; + DEBUG(2, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); From 7ab758a640629665ba43f4f8ce5fa38c9260b1ba Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 10:53:51 -0700 Subject: [PATCH 100/145] changed how completion is actually sampled --- contrib/adaptive-compression/adapt.c | 65 ++++++++++++---------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ce92d9140..cf232ca7b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -24,8 +24,8 @@ #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 -#define DEFAULT_ADAPT_PARAM 1 -#define MAX_COMPRESSION_LEVEL_CHANGE 3 +#define DEFAULT_ADAPT_PARAM 0 +#define MAX_COMPRESSION_LEVEL_CHANGE 4 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -87,9 +87,9 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - unsigned compressionCompletionMeasured; - unsigned writeCompletionMeasured; - unsigned createCompletionMeasured; + double compressionCompletionMeasured; + double writeCompletionMeasured; + double createCompletionMeasured; double compressionCompletion; double writeCompletion; double createCompletion; @@ -342,6 +342,9 @@ static void adaptCompressionLevel(adaptCCtx* ctx) writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; createWaiting = ctx->adaptParam < ctx->stats.writeCounter; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + DEBUG(2, "createWaiting %u\n", createWaiting); + DEBUG(2, "compressWaiting %u\n", compressWaiting); + DEBUG(2, "writeWaiting %u\n\n", writeWaiting); { unsigned const writeSlow = (compressWaiting && createWaiting); unsigned const compressSlow = (writeWaiting && createWaiting); @@ -351,14 +354,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) reset = 1; } else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(3, "increasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = writeSlow ? ctx->writeCompletion : ctx->createCompletion; - DEBUG(3, "write completion: %f, create completion: %f\n", ctx->writeCompletion, ctx->createCompletion); + completion = writeSlow ? ctx->writeCompletionMeasured : ctx->createCompletionMeasured; + DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletionMeasured, ctx->createCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); DEBUG(3, "write completion: %f\n", completion); @@ -369,13 +372,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) else if (compressSlow && ctx->compressionLevel > 1) { double completion; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletion; + completion = ctx->compressionCompletionMeasured; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); { - unsigned const maxChange = (unsigned)((1-completion) * (MAX_COMPRESSION_LEVEL_CHANGE)); + unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(3, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(3, "completion: %f\n", completion); + DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); + DEBUG(2, "completion: %f\n", completion); ctx->compressionLevel -= change; reset = 1; } @@ -386,15 +389,6 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->stats.writeCounter = 0; ctx->stats.compressedCounter = 0; pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; - ctx->compressionCompletionMeasured = 0; - ctx->writeCompletion = 0; - ctx->writeCompletionMeasured = 0; - ctx->createCompletion = 0; - ctx->createCompletionMeasured = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } } @@ -424,8 +418,8 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - DEBUG(2, "create completion: %f\n", ctx->createCompletion); + ctx->createCompletionMeasured = ctx->createCompletion; + DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); @@ -502,9 +496,8 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->compressionCompletionMeasured) { - ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - } + ctx->compressionCompletion = 1 - (double)remaining/job->src.size; + DEBUG(2, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -562,8 +555,8 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletionMeasured = 1; - DEBUG(2, "compressionCompletion %f\n", ctx->compressionCompletion); + ctx->compressionCompletionMeasured = ctx->compressionCompletion; + DEBUG(2, "waited on job %u: compressionCompletion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -580,7 +573,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = 64 << 10; /* 64 KB */ + size_t const blockSize = compressedSize >> 7; size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -591,9 +584,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->writeCompletionMeasured) { - ctx->writeCompletion = 1 - (double)remaining/compressedSize; - } + ctx->writeCompletion = 1 - (double)remaining/compressedSize; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -643,8 +634,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_unlock(&ctx->stats_mutex.pMutex); reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletionMeasured = 1; - DEBUG(2, "writeCompletion: %f\n", ctx->writeCompletion); + ctx->writeCompletionMeasured = ctx->writeCompletion; + DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); @@ -736,9 +727,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA pos += ret; remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - if (!ctx->createCompletionMeasured) { - ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - } + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } From a19916425d5d932e0e34e22dd4496114b4af6e73 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 16:19:16 -0700 Subject: [PATCH 101/145] reworked adaptCompressionLevel to only account for completion information --- contrib/adaptive-compression/adapt.c | 267 ++++++++++++--------------- 1 file changed, 119 insertions(+), 148 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index cf232ca7b..84e689a20 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,7 +29,6 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; -static unsigned g_displayStats = 0; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 0; @@ -47,15 +46,6 @@ typedef struct { buffer_t buffer; } inBuff_t; -typedef struct { - unsigned waitCompressed; - unsigned waitReady; - unsigned waitWrite; - unsigned readyCounter; - unsigned compressedCounter; - unsigned writeCounter; -} cStat_t; - typedef struct { buffer_t src; buffer_t dst; @@ -102,10 +92,9 @@ typedef struct { mutex_t jobWrite_mutex; cond_t jobWrite_cond; mutex_t completion_mutex; - mutex_t stats_mutex; + mutex_t wait_mutex; size_t lastDictSize; inBuff_t input; - cStat_t stats; jobDescription* jobs; ZSTD_CCtx* cctx; } adaptCCtx; @@ -163,7 +152,7 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); error |= destroyMutex(&ctx->completion_mutex); - error |= destroyMutex(&ctx->stats_mutex); + error |= destroyMutex(&ctx->wait_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -203,7 +192,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); pthreadError |= initMutex(&ctx->completion_mutex); - pthreadError |= initMutex(&ctx->stats_mutex); + pthreadError |= initMutex(&ctx->wait_mutex); if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; @@ -211,6 +200,10 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastDictSize = 0; + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); if (!ctx->jobs) { @@ -305,17 +298,6 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } -/* this function normalizes counters when compression level is changing */ -static void reduceCounters(adaptCCtx* ctx) -{ - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - unsigned const min = MIN(ctx->stats.compressedCounter, MIN(ctx->stats.writeCounter, ctx->stats.readyCounter)); - ctx->stats.writeCounter -= min; - ctx->stats.compressedCounter -= min; - ctx->stats.readyCounter -= min; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); -} - /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -330,67 +312,42 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressionLevel = g_compressionLevel; } else { - unsigned reset = 0; - unsigned allSlow; - unsigned compressWaiting; - unsigned writeWaiting; - unsigned createWaiting; + DEBUG(2, "compression level %u\n", ctx->compressionLevel); + /* check if compression is too slow */ + unsigned createChange; + unsigned writeChange; + unsigned compressionChange; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); + DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - allSlow = ctx->adaptParam < ctx->stats.compressedCounter && ctx->adaptParam < ctx->stats.writeCounter && ctx->adaptParam < ctx->stats.readyCounter; - compressWaiting = ctx->adaptParam < ctx->stats.readyCounter; - writeWaiting = ctx->adaptParam < ctx->stats.compressedCounter; - createWaiting = ctx->adaptParam < ctx->stats.writeCounter; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - DEBUG(2, "createWaiting %u\n", createWaiting); - DEBUG(2, "compressWaiting %u\n", compressWaiting); - DEBUG(2, "writeWaiting %u\n\n", writeWaiting); { - unsigned const writeSlow = (compressWaiting && createWaiting); - unsigned const compressSlow = (writeWaiting && createWaiting); - unsigned const createSlow = (compressWaiting && writeWaiting); - DEBUG(3, "createWaiting: %u, compressWaiting: %u, writeWaiting: %u\n", createWaiting, compressWaiting, writeWaiting); - if (allSlow) { - reset = 1; + unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); + DEBUG(2, "compressionFastChange %u\n", compressionFastChange); + + if (compressionFastChange) { + DEBUG(2, "compression level too low\n"); + ctx->compressionLevel += compressionFastChange; } - else if ((writeSlow || createSlow) && ctx->compressionLevel < (unsigned)ZSTD_maxCLevel()) { - DEBUG(2, "increasing compression level %u\n", ctx->compressionLevel); - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = writeSlow ? ctx->writeCompletionMeasured : ctx->createCompletionMeasured; - DEBUG(2, "write completion: %f, create completion: %f\n", ctx->writeCompletionMeasured, ctx->createCompletionMeasured); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); - unsigned const change = MIN(maxChange, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(3, "writeSlow: %u, change: %u\n", writeSlow, change); - DEBUG(3, "write completion: %f\n", completion); - ctx->compressionLevel += change; - reset = 1; - } - } - else if (compressSlow && ctx->compressionLevel > 1) { - double completion; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - completion = ctx->compressionCompletionMeasured; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const maxChange = MAX_COMPRESSION_LEVEL_CHANGE - (unsigned)(completion*MAX_COMPRESSION_LEVEL_CHANGE); - unsigned const change = MIN(maxChange, ctx->compressionLevel - 1); - DEBUG(2, "decreasing compression level %u\n", ctx->compressionLevel); - DEBUG(2, "completion: %f\n", completion); - ctx->compressionLevel -= change; - reset = 1; - } - } - if (reset) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.readyCounter = 0; - ctx->stats.writeCounter = 0; - ctx->stats.compressedCounter = 0; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); + else { + unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); + DEBUG(2, "compression level too high\n"); + ctx->compressionLevel -= compressionSlowChange; } } + + /* reset */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(2, "\n"); } } @@ -410,21 +367,31 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); + + /* new job, reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitReady++; - ctx->stats.readyCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); + /* compression thread is waiting, take measurements of write completion and read completion */ ctx->createCompletionMeasured = ctx->createCompletion; + ctx->writeCompletionMeasured = ctx->writeCompletion; + DEBUG(2, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + /* reset create completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); @@ -497,7 +464,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -547,21 +514,29 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); + + /* new job, reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitCompressed++; - ctx->stats.compressedCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); + /* write thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "waited on job %u: compressionCompletion %f\n", currJob, ctx->compressionCompletion); + DEBUG(2, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + /* reset compression completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressionCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; @@ -615,6 +590,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + } return arg; } @@ -628,19 +604,21 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->stats_mutex.pMutex); - ctx->stats.waitWrite++; - ctx->stats.writeCounter++; - pthread_mutex_unlock(&ctx->stats_mutex.pMutex); - reduceCounters(ctx); pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletionMeasured = ctx->writeCompletion; + /* creation thread is waiting, take measurement of compression completion */ + ctx->compressionCompletionMeasured = ctx->compressionCompletion; + DEBUG(2, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + /* reset write completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->writeCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -677,14 +655,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) return 0; } -static void printStats(cStat_t stats) -{ - DISPLAY("========STATISTICS========\n"); - DISPLAY("# times waited on job ready: %u\n", stats.waitReady); - DISPLAY("# times waited on job compressed: %u\n", stats.waitCompressed); - DISPLAY("# times waited on job Write: %u\n\n", stats.waitWrite); -} - static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { if (!ctx || !srcFile || !otArg) { @@ -710,48 +680,56 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA return 1; } } + { + unsigned currJob = 0; + /* creating jobs */ + for ( ; ; ) { + size_t pos = 0; + size_t const readBlockSize = 1 << 15; + size_t remaining = FILE_CHUNK_SIZE; - /* creating jobs */ - for ( ; ; ) { - size_t pos = 0; - size_t const readBlockSize = 1 << 15; - size_t remaining = FILE_CHUNK_SIZE; - while (remaining != 0 && !feof(srcFile)) { - size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); - if (ret != readBlockSize && !feof(srcFile)) { - /* error could not read correct number of bytes */ + /* new job reset completion */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + + while (remaining != 0 && !feof(srcFile)) { + size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); + if (ret != readBlockSize && !feof(srcFile)) { + /* error could not read correct number of bytes */ + DISPLAY("Error: problem occurred during read from src file\n"); + signalErrorToThreads(ctx); + return 1; + } + pos += ret; + remaining -= ret; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); + DEBUG(3, "create completion: %f\n", ctx->createCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } + if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); signalErrorToThreads(ctx); return 1; } - pos += ret; - remaining -= ret; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - } - if (remaining != 0 && !feof(srcFile)) { - DISPLAY("Error: problem occurred during read from src file\n"); - signalErrorToThreads(ctx); - return 1; - } - g_streamedSize += pos; - /* reading was fine, now create the compression job */ - { - int const last = feof(srcFile); - int const error = createCompressionJob(ctx, pos, last); - if (error != 0) { - signalErrorToThreads(ctx); - return error; + g_streamedSize += pos; + /* reading was fine, now create the compression job */ + { + int const last = feof(srcFile); + int const error = createCompressionJob(ctx, pos, last); + if (error != 0) { + signalErrorToThreads(ctx); + return error; + } + } + currJob++; + if (feof(srcFile)) { + DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); + break; } } - if (feof(srcFile)) { - DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); - break; - } } - /* success -- created all jobs */ return 0; } @@ -803,9 +781,6 @@ static int freeFileCompressionResources(fcResources* fcr) { int ret = 0; waitUntilAllJobsCompleted(fcr->ctx); - pthread_mutex_lock(&fcr->ctx->stats_mutex.pMutex); - if (g_displayStats) printStats(fcr->ctx->stats); - pthread_mutex_unlock(&fcr->ctx->stats_mutex.pMutex); ret |= (fcr->srcFile != NULL) ? fclose(fcr->srcFile) : 0; ret |= (fcr->ctx != NULL) ? freeCCtx(fcr->ctx) : 0; if (fcr->otArg) { @@ -873,7 +848,6 @@ static void help() PRINT(" -oFILE : specify the output file name\n"); PRINT(" -v : display debug information\n"); PRINT(" -i# : provide initial compression level\n"); - PRINT(" -s : display information stats\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); } @@ -913,9 +887,6 @@ int main(int argCount, const char* argv[]) g_compressionLevel = readU32FromChar(&argument); DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; - case 's': - g_displayStats = 1; - break; case 'h': help(); goto _main_exit; From 82e488770c979d6bef9655bc0cd409c0df6b126d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 16:38:02 -0700 Subject: [PATCH 102/145] fixed bug where writeSize could be zero --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 84e689a20..3c6b7e904 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -548,7 +548,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = compressedSize >> 7; + size_t const blockSize = MAX(compressedSize >> 7, 64 << 10); size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); From 9259c7afa412af57e7f578bc8e1f2c5d3906949c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Thu, 20 Jul 2017 18:45:33 -0700 Subject: [PATCH 103/145] semi working version that stabilizes --- contrib/adaptive-compression/adapt.c | 104 ++++++++++++--------------- 1 file changed, 45 insertions(+), 59 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3c6b7e904..f25d22a4c 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -308,47 +308,47 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { + /* check if compression is too slow */ + unsigned createChange; + unsigned writeChange; + unsigned compressionChange; + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "compression level %u\n", ctx->compressionLevel); + DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); + DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + + { + unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); + + DEBUG(2, "compressionFastChange %u\n", compressionFastChange); + + if (compressionFastChange) { + DEBUG(2, "compression level too low\n"); + ctx->compressionLevel += compressionFastChange; + } + else { + unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); + DEBUG(2, "compression level too high\n"); + ctx->compressionLevel -= compressionSlowChange; + } + } + + /* reset */ + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->createCompletionMeasured = 1; + ctx->compressionCompletionMeasured = 1; + ctx->writeCompletionMeasured = 1; + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + DEBUG(2, "\n"); + if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; } - else { - DEBUG(2, "compression level %u\n", ctx->compressionLevel); - /* check if compression is too slow */ - unsigned createChange; - unsigned writeChange; - unsigned compressionChange; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); - DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); - DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - - { - unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "compressionFastChange %u\n", compressionFastChange); - - if (compressionFastChange) { - DEBUG(2, "compression level too low\n"); - ctx->compressionLevel += compressionFastChange; - } - else { - unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); - DEBUG(2, "compression level too high\n"); - ctx->compressionLevel -= compressionSlowChange; - } - } - - /* reset */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "\n"); - } } static size_t getUseableDictSize(unsigned compressionLevel) @@ -368,10 +368,6 @@ static void* compressionThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - /* new job, reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { @@ -387,9 +383,9 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); - /* reset create completion */ + /* reset compression completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 0; + ctx->compressionCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); @@ -397,7 +393,7 @@ static void* compressionThread(void* arg) DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); /* adapt compression level */ - adaptCompressionLevel(ctx); + if (currJob) adaptCompressionLevel(ctx); /* compress the data */ { @@ -515,11 +511,6 @@ static void* outputThread(void* arg) jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - /* new job, reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); @@ -532,9 +523,9 @@ static void* outputThread(void* arg) } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); - /* reset compression completion */ + /* reset write completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressionCompletion = 0; + ctx->writeCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); @@ -615,9 +606,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - /* reset write completion */ + /* reset create completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->writeCompletion = 0; + ctx->createCompletion = 0; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); @@ -688,11 +679,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; - /* new job reset completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From e929d3b787b4a75b14546aa5f3e2c2e601eb2299 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 09:26:35 -0700 Subject: [PATCH 104/145] added priority decision making for adapt compression level --- contrib/adaptive-compression/adapt.c | 59 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f25d22a4c..758bf5589 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -308,43 +308,42 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - /* check if compression is too slow */ - unsigned createChange; - unsigned writeChange; - unsigned compressionChange; + double createCompletion, compressionCompletion, writeCompletion; + double const threshold = 0.00001; pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->createCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - writeChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->writeCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - compressionChange = MAX_COMPRESSION_LEVEL_CHANGE - ctx->compressionCompletionMeasured * MAX_COMPRESSION_LEVEL_CHANGE; - DEBUG(2, "compression level %u\n", ctx->compressionLevel); - DEBUG(2, "createCompletionMeasured %f\n", ctx->createCompletionMeasured); - DEBUG(2, "compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); - DEBUG(2, "writeCompletionMeasured %f\n", ctx->writeCompletionMeasured); + createCompletion = ctx->createCompletionMeasured; + compressionCompletion = ctx->compressionCompletionMeasured; + writeCompletion = ctx->writeCompletionMeasured; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - { - unsigned const compressionFastChange = MIN(MIN(createChange, writeChange), ZSTD_maxCLevel() - ctx->compressionLevel); - - DEBUG(2, "compressionFastChange %u\n", compressionFastChange); - - if (compressionFastChange) { - DEBUG(2, "compression level too low\n"); - ctx->compressionLevel += compressionFastChange; - } - else { - unsigned const compressionSlowChange = MIN(compressionChange, ctx->compressionLevel-1); - DEBUG(2, "compression level too high\n"); - ctx->compressionLevel -= compressionSlowChange; - } + DEBUG(2, "create completion: %f\n", createCompletion); + DEBUG(2, "compression completion: %f\n", compressionCompletion); + DEBUG(2, "write completion: %f\n", writeCompletion); + /* adapt compression based on bottleneck */ + if (1 - createCompletion > threshold) { + /* job creation was not finished, compression thread waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel += change; + } + else if (1 - writeCompletion > threshold) { + /* write thread was not finished, compression thread waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel += change; + } + else if (1 - compressionCompletion > threshold) { + /* compression thread was not finished, one of the other two threads waited */ + unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); + ctx->compressionLevel -= change; } - /* reset */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletionMeasured = 1; ctx->compressionCompletionMeasured = 1; ctx->writeCompletionMeasured = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "\n"); if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; @@ -375,7 +374,7 @@ static void* compressionThread(void* arg) /* compression thread is waiting, take measurements of write completion and read completion */ ctx->createCompletionMeasured = ctx->createCompletion; ctx->writeCompletionMeasured = ctx->writeCompletion; - DEBUG(2, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); + DEBUG(3, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -516,7 +515,7 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* write thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + DEBUG(3, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -598,7 +597,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of compression completion */ ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(2, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); + DEBUG(3, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); From 721c6a8b973c8a2e23a4f4eec56bf77e253098ea Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 09:30:24 -0700 Subject: [PATCH 105/145] added bounding to compression level change --- contrib/adaptive-compression/adapt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 758bf5589..c542c9a3d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -323,20 +323,23 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (1 - createCompletion > threshold) { /* job creation was not finished, compression thread waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += change; + ctx->compressionLevel += boundChange; } else if (1 - writeCompletion > threshold) { /* write thread was not finished, compression thread waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += change; + ctx->compressionLevel += boundChange; } else if (1 - compressionCompletion > threshold) { /* compression thread was not finished, one of the other two threads waited */ unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel -= change; + ctx->compressionLevel -= boundChange; } /* reset */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); From db109f8fef99c51154a7ff56371ff6470cbb43eb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 13:38:24 -0700 Subject: [PATCH 106/145] measure multiple completion levels during each wait --- contrib/adaptive-compression/adapt.c | 123 ++++++++++++++++----------- 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c542c9a3d..b466dbdd8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 -#define MAX_COMPRESSION_LEVEL_CHANGE 4 +#define MAX_COMPRESSION_LEVEL_CHANGE 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -77,9 +77,12 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - double compressionCompletionMeasured; - double writeCompletionMeasured; - double createCompletionMeasured; + double createWaitWriteCompletion; + double createWaitCompressionCompletion; + double compressWaitCreateCompletion; + double compressWaitWriteCompletion; + double writeWaitCreateCompletion; + double writeWaitCompressionCompletion; double compressionCompletion; double writeCompletion; double createCompletion; @@ -200,9 +203,14 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->jobCompressedID = 0; ctx->jobWriteID = 0; ctx->lastDictSize = 0; - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; + + + ctx->createWaitWriteCompletion = 1; + ctx->createWaitCompressionCompletion = 1; + ctx->compressWaitCreateCompletion = 1; + ctx->compressWaitWriteCompletion = 1; + ctx->writeWaitCreateCompletion = 1; + ctx->writeWaitCompressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -308,45 +316,61 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - double createCompletion, compressionCompletion, writeCompletion; + double createWaitWriteCompletion; + double createWaitCompressionCompletion; + double compressWaitCreateCompletion; + double compressWaitWriteCompletion; + double writeWaitCreateCompletion; + double writeWaitCompressionCompletion; double const threshold = 0.00001; + + + /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); - createCompletion = ctx->createCompletionMeasured; - compressionCompletion = ctx->compressionCompletionMeasured; - writeCompletion = ctx->writeCompletionMeasured; + DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); + DEBUG(2, "rw %f\n", ctx->createWaitWriteCompletion); + DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + DEBUG(2, "wr %f\n", ctx->writeWaitCreateCompletion); + DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); + + createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; + createWaitWriteCompletion = ctx->createWaitWriteCompletion; + compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; + compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; + writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; + writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; + + ctx->createWaitWriteCompletion = 1; + ctx->createWaitCompressionCompletion = 1; + ctx->compressWaitCreateCompletion = 1; + ctx->compressWaitWriteCompletion = 1; + ctx->writeWaitCreateCompletion = 1; + ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "create completion: %f\n", createCompletion); - DEBUG(2, "compression completion: %f\n", compressionCompletion); - DEBUG(2, "write completion: %f\n", writeCompletion); - /* adapt compression based on bottleneck */ - if (1 - createCompletion > threshold) { - /* job creation was not finished, compression thread waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - createCompletion * MAX_COMPRESSION_LEVEL_CHANGE; - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += boundChange; - } - else if (1 - writeCompletion > threshold) { - /* write thread was not finished, compression thread waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - writeCompletion * MAX_COMPRESSION_LEVEL_CHANGE; - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - DEBUG(2, "increasing compression level %u by %u\n", ctx->compressionLevel, change); - ctx->compressionLevel += boundChange; - } - else if (1 - compressionCompletion > threshold) { - /* compression thread was not finished, one of the other two threads waited */ - unsigned const change = MAX_COMPRESSION_LEVEL_CHANGE - compressionCompletion * MAX_COMPRESSION_LEVEL_CHANGE; + /* adaptation logic */ + if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { + /* both create and write threads waiting on compression */ + /* use writeWaitCompressionCompletion */ + unsigned const change = (unsigned)((1-writeWaitCompressionCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - DEBUG(2, "decreasing compression level %u by %u\n", ctx->compressionLevel, change); ctx->compressionLevel -= boundChange; } - /* reset */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->createCompletionMeasured = 1; - ctx->compressionCompletionMeasured = 1; - ctx->writeCompletionMeasured = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { + /* both create and compression thread waiting on write */ + /* use createWaitWriteCompletion */ + unsigned const change = (unsigned)((1-createWaitWriteCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + ctx->compressionLevel += boundChange; + } + else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { + /* both compression and write waiting on create */ + /* use compressWaitCreateCompletion */ + unsigned const change = (unsigned)((1-compressWaitCreateCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + ctx->compressionLevel += boundChange; + } if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; @@ -375,9 +399,9 @@ static void* compressionThread(void* arg) while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* compression thread is waiting, take measurements of write completion and read completion */ - ctx->createCompletionMeasured = ctx->createCompletion; - ctx->writeCompletionMeasured = ctx->writeCompletion; - DEBUG(3, "compression thread waiting : createCompletionMeasured %f : writeCompletionMeasured %f\n", ctx->createCompletionMeasured, ctx->writeCompletionMeasured); + ctx->compressWaitCreateCompletion = ctx->createCompletion; + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); @@ -462,7 +486,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "update on job %u: compression completion %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "compression completion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -517,8 +541,9 @@ static void* outputThread(void* arg) while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* write thread is waiting, take measurement of compression completion */ - ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(3, "write thread waiting : compressionCompletionMeasured %f\n", ctx->compressionCompletionMeasured); + ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; + ctx->writeWaitCreateCompletion = ctx->createCompletion; + DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -541,7 +566,7 @@ static void* outputThread(void* arg) } { // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); - size_t const blockSize = MAX(compressedSize >> 7, 64 << 10); + size_t const blockSize = MAX(compressedSize >> 7, 1 << 10); size_t pos = 0; for ( ; ; ) { size_t const writeSize = MIN(remaining, blockSize); @@ -553,6 +578,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; + DEBUG(3, "write completion %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -599,8 +625,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of compression completion */ - ctx->compressionCompletionMeasured = ctx->compressionCompletion; - DEBUG(3, "creation thread waiting : compression completion measured : %f\n", ctx->compressionCompletionMeasured); + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + ctx->createWaitWriteCompletion = ctx->writeCompletion; + DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); From 05fe8dd47c77f2a826a4deb58eb8a98a40f8ffdf Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 14:06:24 -0700 Subject: [PATCH 107/145] updating debug statements --- contrib/adaptive-compression/adapt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index b466dbdd8..5a3b723d3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -324,7 +324,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double writeWaitCompressionCompletion; double const threshold = 0.00001; - + DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); @@ -341,6 +341,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; + DEBUG(2, "resetting adaptive variables\n"); ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; @@ -404,7 +405,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job ready, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -545,7 +546,7 @@ static void* outputThread(void* arg) ctx->writeWaitCreateCompletion = ctx->createCompletion; DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job compressed, nextJob: %u\n", currJob); + DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -630,7 +631,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(3, "waiting on job Write, nextJob: %u\n", nextJob); + DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); From 6455ec482cbb4a76619fed60ca9ef28a8c6a2abc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 16:05:01 -0700 Subject: [PATCH 108/145] taking the maximum of the completion level reads in order to determine which one was waiting more --- contrib/adaptive-compression/adapt.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5a3b723d3..e07333fc3 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -354,23 +354,29 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { /* both create and write threads waiting on compression */ /* use writeWaitCompressionCompletion */ - unsigned const change = (unsigned)((1-writeWaitCompressionCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; + DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); } else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { /* both create and compression thread waiting on write */ /* use createWaitWriteCompletion */ - unsigned const change = (unsigned)((1-createWaitWriteCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(createWaitWriteCompletion, compressWaitWriteCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; + DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); } else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { /* both compression and write waiting on create */ /* use compressWaitCreateCompletion */ - unsigned const change = (unsigned)((1-compressWaitCreateCompletion) * MAX_COMPRESSION_LEVEL_CHANGE); + double const completion = MAX(writeWaitCreateCompletion, compressWaitCreateCompletion); + unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; + DEBUG(2, "compression and write threads waiting, tried to increase compression level by %u\n", boundChange); } if (g_forceCompressionLevel) { @@ -404,8 +410,8 @@ static void* compressionThread(void* arg) ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "compression thread waiting for nextJob: %u, compressWaitCreateCompletion %f, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job ready, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -422,6 +428,7 @@ static void* compressionThread(void* arg) /* adapt compression level */ if (currJob) adaptCompressionLevel(ctx); + DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel); /* compress the data */ { size_t const compressionBlockSize = 1 << 17; /* 128 KB */ @@ -487,7 +494,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "compression completion %f\n", ctx->compressionCompletion); + DEBUG(2, "compression completion %f\n", ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -545,8 +552,8 @@ static void* outputThread(void* arg) ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; ctx->writeWaitCreateCompletion = ctx->createCompletion; DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); + DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f, writeWaitCreateCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion, ctx->writeWaitCreateCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job compressed, nextJob: %u\n", currJob); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -579,7 +586,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(3, "write completion %f\n", ctx->writeCompletion); + DEBUG(2, "write completion %f\n", ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -630,8 +637,8 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) ctx->createWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - DEBUG(2, "waiting on job Write, nextJob: %u\n", nextJob); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); @@ -721,7 +728,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "create completion: %f\n", ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 95bef759b3f1fc52bbcf5a82c1dddaf00906df8f Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 17:49:39 -0700 Subject: [PATCH 109/145] switched over to model where reading only waits on compression thread --- contrib/adaptive-compression/adapt.c | 65 +++++++++++++++------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index e07333fc3..9057bfcfc 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -51,7 +51,7 @@ typedef struct { buffer_t dst; unsigned compressionLevel; unsigned jobID; - unsigned lastJob; + unsigned lastJobPlusOne; size_t compressedSize; size_t dictSize; } jobDescription; @@ -226,7 +226,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) jobDescription* job = &ctx->jobs[jobNum]; job->src.start = malloc(2 * FILE_CHUNK_SIZE); job->dst.start = malloc(ZSTD_compressBound(FILE_CHUNK_SIZE)); - job->lastJob = 0; + job->lastJobPlusOne = 0; if (!job->src.start || !job->dst.start) { DISPLAY("Could not allocate buffers for jobs\n"); return 1; @@ -400,22 +400,30 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); - - + /* wait until job is ready */ pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); - while(currJob + 1 > ctx->jobReadyID && !ctx->threadError) { + while (currJob + 1 > ctx->jobReadyID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* compression thread is waiting, take measurements of write completion and read completion */ + /* compression thread is waiting on creation thread, take measurement */ ctx->compressWaitCreateCompletion = ctx->createCompletion; - ctx->compressWaitWriteCompletion = ctx->writeCompletion; - DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f : compressWaitWriteCompletion %f\n", ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); + DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); DEBUG(3, "create completion: %f\n", ctx->createCompletion); - DEBUG(2, "compression thread waiting for nextJob: %u, compressWaitCreateCompletion %f, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitCreateCompletion, ctx->compressWaitWriteCompletion); + DEBUG(2, "compression thread waiting for ready: %u, compressWaitCreateCompletion %f\n", currJob, ctx->compressWaitCreateCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + /* wait until job previously in this space is written */ + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); + } + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); /* reset compression completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 0; @@ -439,10 +447,8 @@ static void* compressionThread(void* arg) size_t dstPos = 0; DEBUG(3, "cLevel used: %u\n", cLevel); DEBUG(3, "compression level used: %u\n", cLevel); - /* reset compressed size */ job->compressedSize = 0; - /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -474,10 +480,10 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(3, "write out ending: %d\n", job->lastJob && (remaining == actualBlockSize)); - DEBUG(3, "lastJob %u\n", job->lastJob); + DEBUG(3, "write out ending: %d\n", (job->lastJobPlusOne == currJob + 1) && (remaining == actualBlockSize)); + DEBUG(3, "lastJobPlusOne %u\n", job->lastJobPlusOne); DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); - size_t const ret = (job->lastJob && remaining == actualBlockSize) ? + size_t const ret = (job->lastJobPlusOne == currJob + 1 && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); if (ZSTD_isError(ret)) { @@ -503,15 +509,16 @@ static void* compressionThread(void* arg) pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; DEBUG(3, "signaling for job %u\n", currJob); - pthread_cond_signal(&ctx->jobCompressed_cond.pCond); + pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond); pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); DEBUG(3, "finished job compression %u\n", currJob); - currJob++; - if (job->lastJob || ctx->threadError) { + if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished compressing all jobs */ DEBUG(3, "all jobs finished compressing\n"); break; } + + currJob++; } return arg; } @@ -544,7 +551,6 @@ static void* outputThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "outputThread(): waiting on job compressed\n"); - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); @@ -599,8 +605,7 @@ static void* outputThread(void* arg) } } DEBUG(3, "finished job write %u\n", currJob); - currJob++; - displayProgress(currJob, ctx->compressionLevel, job->lastJob); + displayProgress(currJob, ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; @@ -608,7 +613,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); DEBUG(3, "unlocking job write mutex\n"); - if (job->lastJob || ctx->threadError) { + if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished with all jobs */ DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); @@ -617,6 +622,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + currJob++; } return arg; @@ -628,21 +634,22 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; DEBUG(3, "createCompressionJob(): wait for job write\n"); - pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); - DEBUG(3, "Creating new compression job -- nextJob: %u, jobCompressedID: %u, jobWriteID: %u, numJObs: %u\n", nextJob,ctx->jobCompressedID, ctx->jobWriteID, ctx->numJobs); - while (nextJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + + + /* wait until the job has been compressed */ + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* creation thread is waiting, take measurement of compression completion */ + /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; ctx->createWaitWriteCompletion = ctx->writeCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); - pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } - pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 0; @@ -653,7 +660,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; - job->lastJob = last; + if (last) job->lastJobPlusOne = nextJob + 1; { /* swap buffer */ void* const copy = job->src.start; From 08d9e42ec61a51fe1a0189ac3b61b1f200918e57 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 21 Jul 2017 18:02:55 -0700 Subject: [PATCH 110/145] removed useless measurements --- contrib/adaptive-compression/adapt.c | 34 ++++++++-------------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9057bfcfc..68727cbd8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -77,11 +77,9 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; - double createWaitWriteCompletion; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; - double writeWaitCreateCompletion; double writeWaitCompressionCompletion; double compressionCompletion; double writeCompletion; @@ -205,11 +203,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->lastDictSize = 0; - ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; - ctx->writeWaitCreateCompletion = 1; ctx->writeWaitCompressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -316,11 +312,9 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) */ static void adaptCompressionLevel(adaptCCtx* ctx) { - double createWaitWriteCompletion; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; - double writeWaitCreateCompletion; double writeWaitCompressionCompletion; double const threshold = 0.00001; @@ -328,25 +322,19 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* read and reset completion measurements */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "rw %f\n", ctx->createWaitWriteCompletion); DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); - DEBUG(2, "wr %f\n", ctx->writeWaitCreateCompletion); DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; - createWaitWriteCompletion = ctx->createWaitWriteCompletion; compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; - writeWaitCreateCompletion = ctx->writeWaitCreateCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; DEBUG(2, "resetting adaptive variables\n"); - ctx->createWaitWriteCompletion = 1; ctx->createWaitCompressionCompletion = 1; ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; - ctx->writeWaitCreateCompletion = 1; ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->completion_mutex.pMutex); @@ -360,19 +348,18 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressionLevel -= boundChange; DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); } - else if (1-createWaitWriteCompletion > threshold && 1-compressWaitWriteCompletion > threshold) { + else if (1-compressWaitWriteCompletion > threshold) { /* both create and compression thread waiting on write */ - /* use createWaitWriteCompletion */ - double const completion = MAX(createWaitWriteCompletion, compressWaitWriteCompletion); + double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); } - else if (1-writeWaitCreateCompletion > threshold && 1-compressWaitCreateCompletion > threshold) { + else if (1-compressWaitCreateCompletion > threshold) { /* both compression and write waiting on create */ /* use compressWaitCreateCompletion */ - double const completion = MAX(writeWaitCreateCompletion, compressWaitCreateCompletion); + double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; @@ -417,6 +404,7 @@ static void* compressionThread(void* arg) /* wait until job previously in this space is written */ pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { + /* compression thread is waiting on writer thread, take measurement */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); @@ -554,11 +542,10 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* write thread is waiting, take measurement of compression completion */ + /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; - ctx->writeWaitCreateCompletion = ctx->createCompletion; - DEBUG(3, "write thread waiting : writeWaitCreateCompletion %f : writeWaitCompressionCompletion %f\n", ctx->writeWaitCreateCompletion, ctx->writeWaitCompressionCompletion); - DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f, writeWaitCreateCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion, ctx->writeWaitCreateCompletion); + DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } @@ -642,10 +629,9 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->completion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - ctx->createWaitWriteCompletion = ctx->writeCompletion; - DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f : createWaitWriteCompletion %f\n", ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); + DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f, createWaitWriteCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion, ctx->createWaitWriteCompletion); + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } From 880f08d1049ee320aa54673b99c9ec0e57c5e397 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Sun, 23 Jul 2017 10:18:54 -0700 Subject: [PATCH 111/145] change how completion is measured in compression thread --- contrib/adaptive-compression/adapt.c | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 68727cbd8..248421457 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -25,7 +25,7 @@ #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 -#define MAX_COMPRESSION_LEVEL_CHANGE 3 +#define MAX_COMPRESSION_LEVEL_CHANGE 2 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -207,6 +207,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->compressWaitCreateCompletion = 1; ctx->compressWaitWriteCompletion = 1; ctx->writeWaitCompressionCompletion = 1; + ctx->createCompletion = 1; + ctx->writeCompletion = 1; + ctx->compressionCompletion = 1; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -387,16 +390,34 @@ static void* compressionThread(void* arg) unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; DEBUG(3, "compressionThread(): waiting on job ready\n"); + + { + /* check if compression thread will have to wait */ + unsigned willWaitForCreate = 0; + unsigned willWaitForWrite = 0; + + pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); + if (currJob + 1 > ctx->jobReadyID) willWaitForCreate = 1; + pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); + if (currJob - ctx->jobWriteID >= ctx->numJobs) willWaitForWrite = 1; + pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + + pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (willWaitForCreate || willWaitForWrite) { + ctx->compressWaitCreateCompletion = ctx->createCompletion; + ctx->compressWaitWriteCompletion = ctx->writeCompletion; + DEBUG(2, "compression will wait for create or write\n"); + DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); + } + pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } + /* wait until job is ready */ pthread_mutex_lock(&ctx->jobReady_mutex.pMutex); while (currJob + 1 > ctx->jobReadyID && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - /* compression thread is waiting on creation thread, take measurement */ - ctx->compressWaitCreateCompletion = ctx->createCompletion; - DEBUG(3, "compression thread waiting : compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); - DEBUG(3, "create completion: %f\n", ctx->createCompletion); - DEBUG(2, "compression thread waiting for ready: %u, compressWaitCreateCompletion %f\n", currJob, ctx->compressWaitCreateCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobReady_cond.pCond, &ctx->jobReady_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobReady_mutex.pMutex); @@ -404,11 +425,6 @@ static void* compressionThread(void* arg) /* wait until job previously in this space is written */ pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); while (currJob - ctx->jobWriteID >= ctx->numJobs && !ctx->threadError) { - /* compression thread is waiting on writer thread, take measurement */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); - ctx->compressWaitWriteCompletion = ctx->writeCompletion; - DEBUG(2, "compression thread waiting for write: %u, compressWaitWriteCompletion %f\n", currJob, ctx->compressWaitWriteCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); pthread_cond_wait(&ctx->jobWrite_cond.pCond, &ctx->jobWrite_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); @@ -488,7 +504,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "compression completion %f\n", ctx->compressionCompletion); + DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } } while (remaining != 0); @@ -579,7 +595,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(2, "write completion %f\n", ctx->writeCompletion); + DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); if (remaining == 0) break; @@ -721,7 +737,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->completion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(2, "create completion: %f\n", ctx->createCompletion); + DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->completion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 483d936b8760819f8f6b71c2eb0c635165293086 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Sun, 23 Jul 2017 14:09:16 -0700 Subject: [PATCH 112/145] reduced competition for completion mutex by separating mutex use based on which values is updated --- contrib/adaptive-compression/adapt.c | 93 ++++++++++++++++------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 248421457..4e5a86c72 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -92,8 +92,9 @@ typedef struct { cond_t allJobsCompleted_cond; mutex_t jobWrite_mutex; cond_t jobWrite_cond; - mutex_t completion_mutex; - mutex_t wait_mutex; + mutex_t compressionCompletion_mutex; + mutex_t createCompletion_mutex; + mutex_t writeCompletion_mutex; size_t lastDictSize; inBuff_t input; jobDescription* jobs; @@ -152,8 +153,9 @@ static int freeCCtx(adaptCCtx* ctx) error |= destroyCond(&ctx->allJobsCompleted_cond); error |= destroyMutex(&ctx->jobWrite_mutex); error |= destroyCond(&ctx->jobWrite_cond); - error |= destroyMutex(&ctx->completion_mutex); - error |= destroyMutex(&ctx->wait_mutex); + error |= destroyMutex(&ctx->compressionCompletion_mutex); + error |= destroyMutex(&ctx->createCompletion_mutex); + error |= destroyMutex(&ctx->writeCompletion_mutex); error |= ZSTD_isError(ZSTD_freeCCtx(ctx->cctx)); free(ctx->input.buffer.start); if (ctx->jobs){ @@ -192,8 +194,9 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) pthreadError |= initCond(&ctx->allJobsCompleted_cond); pthreadError |= initMutex(&ctx->jobWrite_mutex); pthreadError |= initCond(&ctx->jobWrite_cond); - pthreadError |= initMutex(&ctx->completion_mutex); - pthreadError |= initMutex(&ctx->wait_mutex); + pthreadError |= initMutex(&ctx->compressionCompletion_mutex); + pthreadError |= initMutex(&ctx->createCompletion_mutex); + pthreadError |= initMutex(&ctx->writeCompletion_mutex); if (pthreadError) return pthreadError; } ctx->numJobs = numJobs; @@ -323,28 +326,32 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); - DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); - createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; - compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; - compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; - - DEBUG(2, "resetting adaptive variables\n"); ctx->createWaitCompressionCompletion = 1; - ctx->compressWaitCreateCompletion = 1; - ctx->compressWaitWriteCompletion = 1; ctx->writeWaitCompressionCompletion = 1; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); + DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; + ctx->compressWaitWriteCompletion = 1; + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; + ctx->compressWaitCreateCompletion = 1; + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); /* adaptation logic */ - if (1-createWaitCompressionCompletion > threshold && 1-writeWaitCompressionCompletion > threshold) { - /* both create and write threads waiting on compression */ - /* use writeWaitCompressionCompletion */ + if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { + /* compression waiting on either create or write */ + /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); @@ -404,15 +411,21 @@ static void* compressionThread(void* arg) if (currJob - ctx->jobWriteID >= ctx->numJobs) willWaitForWrite = 1; pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + if (willWaitForCreate || willWaitForWrite) { - ctx->compressWaitCreateCompletion = ctx->createCompletion; - ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "compression will wait for create or write\n"); + + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); + ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + } /* wait until job is ready */ @@ -429,9 +442,9 @@ static void* compressionThread(void* arg) } pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); /* reset compression completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(3, "compressionThread(): continuing after job ready\n"); DEBUG(3, "DICTIONARY ENDED\n"); @@ -502,10 +515,10 @@ static void* compressionThread(void* arg) blockNum++; /* update completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); job->dst.size = job->compressedSize; @@ -557,20 +570,20 @@ static void* outputThread(void* arg) DEBUG(3, "outputThread(): waiting on job compressed\n"); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset write completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); DEBUG(3, "outputThread(): continuing after job compressed\n"); { @@ -593,10 +606,10 @@ static void* outputThread(void* arg) remaining -= ret; /* update completion variable for writing */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; } @@ -642,20 +655,20 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) /* wait until the job has been compressed */ pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(3, "createCompressionJob(): continuing after job write\n"); DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); @@ -735,10 +748,10 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pos += ret; remaining -= ret; - pthread_mutex_lock(&ctx->completion_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); - pthread_mutex_unlock(&ctx->completion_mutex.pMutex); + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { DISPLAY("Error: problem occurred during read from src file\n"); From e508f632d6d4f92836b339a7f2d2e70a009bbaf3 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 11:01:36 -0700 Subject: [PATCH 113/145] updated comments and debug statements --- contrib/adaptive-compression/adapt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 4e5a86c72..a90b59902 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -350,30 +350,30 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* adaptation logic */ if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { - /* compression waiting on either create or write */ + /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; - DEBUG(2, "create and write threads waiting, tried to decrease compression level by %u\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { - /* both create and compression thread waiting on write */ + /* compress waiting on write */ double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "create and compression threads waiting, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { - /* both compression and write waiting on create */ + /* compress waiting on create*/ /* use compressWaitCreateCompletion */ double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compression and write threads waiting, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n", boundChange); } if (g_forceCompressionLevel) { From d3d759301f0b6f90b2321f4b2d29120cc4ccd9b5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 13:47:39 -0700 Subject: [PATCH 114/145] changing position of endline for debug --- contrib/adaptive-compression/adapt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a90b59902..1b546f581 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -329,7 +329,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "wc %f\n\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "wc %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; ctx->createWaitCompressionCompletion = 1; @@ -356,7 +356,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); ctx->compressionLevel -= boundChange; - DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { /* compress waiting on write */ @@ -364,7 +364,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { /* compress waiting on create*/ @@ -373,7 +373,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); ctx->compressionLevel += boundChange; - DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n", boundChange); + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); } if (g_forceCompressionLevel) { @@ -517,7 +517,7 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(2, "compression completion %u %f\n", currJob, ctx->compressionCompletion); + DEBUG(3, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); @@ -608,7 +608,7 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(2, "write completion %u %f\n", currJob, ctx->writeCompletion); + DEBUG(3, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; @@ -750,7 +750,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(2, "create completion %u %f\n", currJob, ctx->createCompletion); + DEBUG(3, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { From 8328f8192a61d4c4e140eff31f1bdaf034abb805 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 14:40:23 -0700 Subject: [PATCH 115/145] updating debug statements again --- contrib/adaptive-compression/adapt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1b546f581..09884c470 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -396,7 +396,7 @@ static void* compressionThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(3, "compressionThread(): waiting on job ready\n"); + DEBUG(2, "starting compression for job %u\n", currJob); { /* check if compression thread will have to wait */ @@ -413,7 +413,7 @@ static void* compressionThread(void* arg) if (willWaitForCreate || willWaitForWrite) { - DEBUG(2, "compression will wait for create or write\n"); + DEBUG(2, "compression will wait for create or write on job %u\n", currJob); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; @@ -534,7 +534,7 @@ static void* compressionThread(void* arg) DEBUG(3, "all jobs finished compressing\n"); break; } - + DEBUG(2, "finished compressing job %u\n", currJob); currJob++; } return arg; @@ -567,7 +567,7 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* job = &ctx->jobs[currJobIndex]; - DEBUG(3, "outputThread(): waiting on job compressed\n"); + DEBUG(2, "starting write for job %u\n", currJob); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); @@ -638,6 +638,7 @@ static void* outputThread(void* arg) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); break; } + DEBUG(2, "finished writing job %u\n", currJob); currJob++; } @@ -738,6 +739,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; + DEBUG(2, "starting creation of job %u\n", currJob); while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { @@ -768,6 +770,7 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA return error; } } + DEBUG(2, "finished creating job %u\n", currJob); currJob++; if (feof(srcFile)) { DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); From 0ee3f8c2f8b65a0261158ba9bb8335a0a6900b15 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 15:06:11 -0700 Subject: [PATCH 116/145] adding more debug --- contrib/adaptive-compression/adapt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 09884c470..ec40130a6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -466,6 +466,7 @@ static void* compressionThread(void* arg) DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; + DEBUG(2, "calling ZSTD_compressBegin()\n"); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); @@ -479,6 +480,7 @@ static void* compressionThread(void* arg) return arg; } } + DEBUG(2, "finished with ZSTD_compressBegin()\n"); do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); From 4dc83ca64c9e5f0e87d08daf5b349e59ef97d5e1 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 15:14:58 -0700 Subject: [PATCH 117/145] compression thread should take measurements independently based on whether or not the create/write thread will actually bottleneck performance --- contrib/adaptive-compression/adapt.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ec40130a6..ef0da42f8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -412,14 +412,17 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - if (willWaitForCreate || willWaitForWrite) { - DEBUG(2, "compression will wait for create or write on job %u\n", currJob); - + if (willWaitForCreate) { + DEBUG(2, "compression will wait for create on job %u\n", currJob); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + } + + if (willWaitForWrite) { + DEBUG(2, "compression will wait for write on job %u\n", currJob); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); From df3754b6ed4f0f762f4702457eecc87f89593ec6 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 16:19:07 -0700 Subject: [PATCH 118/145] add quiet option, make progress bar default --- contrib/adaptive-compression/adapt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ef0da42f8..c4c46af5d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -31,7 +31,7 @@ static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; -static unsigned g_useProgressBar = 0; +static unsigned g_useProgressBar = 1; static UTIL_freq_t g_ticksPerSecond; static unsigned g_forceCompressionLevel = 0; @@ -903,6 +903,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) @@ -953,6 +954,10 @@ int main(int argCount, const char* argv[]) case 'f': g_forceCompressionLevel = 1; break; + case 'q': + g_useProgressBar = 0; + g_displayLevel = 0; + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; From 700758d67677836cc9208d868f49b9bc522b2552 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 24 Jul 2017 16:26:20 -0700 Subject: [PATCH 119/145] added help statement for -p, switched it to hide progress bar now that progress bar is default --- contrib/adaptive-compression/adapt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index c4c46af5d..f6bcddd18 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -903,6 +903,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } /* return 0 if successful, else return error */ @@ -945,7 +946,7 @@ int main(int argCount, const char* argv[]) help(); goto _main_exit; case 'p': - g_useProgressBar = 1; + g_useProgressBar = 0; break; case 'c': forceStdout = 1; From 6f1e260eddd08001631d6f8c5d760f9da6674e3a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 10:01:10 -0700 Subject: [PATCH 120/145] added mechanism for getting rid of spikes --- contrib/adaptive-compression/adapt.c | 38 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index f6bcddd18..32f54e67b 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -26,6 +26,7 @@ #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 +#define CONVERGENCE_LOWER_BOUND 3 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -77,6 +78,7 @@ typedef struct { unsigned jobWriteID; unsigned allJobsCompleted; unsigned adaptParam; + unsigned convergenceCounter; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; @@ -213,6 +215,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->createCompletion = 1; ctx->writeCompletion = 1; ctx->compressionCompletion = 1; + ctx->convergenceCounter = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -323,6 +326,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double compressWaitWriteCompletion; double writeWaitCompressionCompletion; double const threshold = 0.00001; + unsigned const prevCompressionLevel = ctx->compressionLevel; DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ @@ -347,7 +351,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); - + DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); /* adaptation logic */ if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { /* create or write waiting on compression */ @@ -355,7 +359,15 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - ctx->compressionLevel -= boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + /* reset convergence counter, might have been a spike */ + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel -= boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } else if (1-compressWaitWriteCompletion > threshold) { @@ -363,7 +375,14 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = compressWaitWriteCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - ctx->compressionLevel += boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel += boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); } else if (1-compressWaitCreateCompletion > threshold) { @@ -372,10 +391,21 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = compressWaitCreateCompletion; unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - ctx->compressionLevel += boundChange; + if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + ctx->convergenceCounter = 0; + } + else if (boundChange != 0) { + ctx->compressionLevel += boundChange; + ctx->convergenceCounter = 1; + } + DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); } + if (ctx->compressionLevel == prevCompressionLevel) { + ctx->convergenceCounter++; + } + if (g_forceCompressionLevel) { ctx->compressionLevel = g_compressionLevel; } From 85d7c919f6b85ef4deb82bc7e4c2cdbfc49b2e10 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 10:32:14 -0700 Subject: [PATCH 121/145] created independent function for controlling how completion relates to compression level change --- contrib/adaptive-compression/adapt.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 32f54e67b..87082be46 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -311,6 +311,19 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +static unsigned convertCompletionToChange(double completion) +{ + if (completion < 0.05) { + return 2; + } + else if (completion < 0.5) { + return 1; + } + else { + return 0; + } +} + /* * Compression level is changed depending on which part of the compression process is lagging * Currently, three theads exist for job creation, compression, and file writing respectively. @@ -357,7 +370,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ @@ -373,7 +386,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) else if (1-compressWaitWriteCompletion > threshold) { /* compress waiting on write */ double const completion = compressWaitWriteCompletion; - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { ctx->convergenceCounter = 0; @@ -389,7 +402,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on create*/ /* use compressWaitCreateCompletion */ double const completion = compressWaitCreateCompletion; - unsigned const change = (unsigned)((1-completion) * MAX_COMPRESSION_LEVEL_CHANGE); + unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { ctx->convergenceCounter = 0; From e02c79f8336f4057b9f01a5e9565e9269d701e01 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 11:16:27 -0700 Subject: [PATCH 122/145] started using decrease cooldown so that compression level would not decrease several times in a row --- contrib/adaptive-compression/adapt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 87082be46..fac93d273 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -26,7 +26,8 @@ #define DEFAULT_COMPRESSION_LEVEL 6 #define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 -#define CONVERGENCE_LOWER_BOUND 3 +#define CONVERGENCE_LOWER_BOUND 5 +#define CLEVEL_DECREASE_COOLDOWN 5 static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; @@ -79,6 +80,7 @@ typedef struct { unsigned allJobsCompleted; unsigned adaptParam; unsigned convergenceCounter; + unsigned cooldown; double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; @@ -216,6 +218,7 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->writeCompletion = 1; ctx->compressionCompletion = 1; ctx->convergenceCounter = 0; + ctx->cooldown = 0; ctx->jobs = calloc(1, numJobs*sizeof(jobDescription)); @@ -365,8 +368,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); + /* adaptation logic */ - if (1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) { + if (ctx->cooldown) ctx->cooldown--; + + if ((1-createWaitCompressionCompletion > threshold || 1-writeWaitCompressionCompletion > threshold) && ctx->cooldown == 0) { /* create or write waiting on compression */ /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); @@ -378,6 +384,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if (boundChange != 0) { ctx->compressionLevel -= boundChange; + ctx->cooldown = CLEVEL_DECREASE_COOLDOWN; ctx->convergenceCounter = 1; } From 310c12d07eb5a982fdcd2c025b5e3c98ec181c1b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:08:39 -0700 Subject: [PATCH 123/145] moved debug statements to a compiler flag --- contrib/adaptive-compression/Makefile | 3 +++ contrib/adaptive-compression/adapt.c | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index f2059a193..9bc19ee15 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -24,6 +24,9 @@ all: adapt datagen adapt: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) $^ -o $@ +adapt-debug: $(ZSTD_FILES) adapt.c + $(CC) $(FLAGS) -DDEBUG_MODE=2 $^ -o adapt + datagen : $(PRGDIR)/datagen.c datagencli.c $(CC) $(FLAGS) $^ -o $@$(EXT) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index fac93d273..d1fe753c9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -29,7 +29,12 @@ #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 +#ifndef DEBUG_MODE static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; +#else +static int g_displayLevel = DEBUG_MODE; +#endif + static unsigned g_compressionLevel = DEFAULT_COMPRESSION_LEVEL; static UTIL_time_t g_startTime; static size_t g_streamedSize = 0; @@ -949,7 +954,6 @@ static void help() PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); - PRINT(" -v : display debug information\n"); PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); @@ -984,9 +988,6 @@ int main(int argCount, const char* argv[]) argument += 2; outFilename = argument; break; - case 'v': - g_displayLevel++; - break; case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); From 0882cd1981dd59dff606d379a4cf294785a5594a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:26:55 -0700 Subject: [PATCH 124/145] progress bar -- don't print num jobs, time elapsed shown in seconds --- contrib/adaptive-compression/adapt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d1fe753c9..098f6fc90 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -600,7 +600,7 @@ static void* compressionThread(void* arg) return arg; } -static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) +static void displayProgress(unsigned cLevel, unsigned last) { if (!g_useProgressBar) return; UTIL_time_t currTime; @@ -608,7 +608,7 @@ static void displayProgress(unsigned jobDoneID, unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stderr, "\r| %4u jobs completed | Current Compresion Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Compression Rate: %6.2f MB/s |", jobDoneID, cLevel, timeElapsed, sizeMB, avgCompRate); + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); if (last) { fprintf(stderr, "\n"); } @@ -681,7 +681,7 @@ static void* outputThread(void* arg) } } DEBUG(3, "finished job write %u\n", currJob); - displayProgress(currJob, ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); + displayProgress(ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; From 5cfbf609a4d6afeaf7b2f101f9bf838b1d20f9e5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:31:48 -0700 Subject: [PATCH 125/145] removed old debug statements no longer being used --- contrib/adaptive-compression/adapt.c | 35 ---------------------------- 1 file changed, 35 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 098f6fc90..722ae243d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -504,10 +504,6 @@ static void* compressionThread(void* arg) ctx->compressionCompletion = 0; pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); - DEBUG(3, "compressionThread(): continuing after job ready\n"); - DEBUG(3, "DICTIONARY ENDED\n"); - DEBUG(3, "%.*s", (int)job->src.size, (char*)job->src.start); - /* adapt compression level */ if (currJob) adaptCompressionLevel(ctx); @@ -520,15 +516,12 @@ static void* compressionThread(void* arg) size_t remaining = job->src.size; size_t srcPos = 0; size_t dstPos = 0; - DEBUG(3, "cLevel used: %u\n", cLevel); - DEBUG(3, "compression level used: %u\n", cLevel); /* reset compressed size */ job->compressedSize = 0; DEBUG(2, "calling ZSTD_compressBegin()\n"); /* begin compression */ { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); - DEBUG(3, "useDictSize: %zu, job->dictSize: %zu\n", useDictSize, job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); @@ -542,8 +535,6 @@ static void* compressionThread(void* arg) do { size_t const actualBlockSize = MIN(remaining, compressionBlockSize); - DEBUG(3, "remaining: %zu\n", remaining); - DEBUG(3, "actualBlockSize: %zu\n", actualBlockSize); /* continue compression */ if (currJob != 0 || blockNum != 0) { /* not first block of first job flush/overwrite the frame header */ @@ -557,9 +548,6 @@ static void* compressionThread(void* arg) ZSTD_invalidateRepCodes(ctx->cctx); } { - DEBUG(3, "write out ending: %d\n", (job->lastJobPlusOne == currJob + 1) && (remaining == actualBlockSize)); - DEBUG(3, "lastJobPlusOne %u\n", job->lastJobPlusOne); - DEBUG(3, "compressionBlockSize %zu\n", compressionBlockSize); size_t const ret = (job->lastJobPlusOne == currJob + 1 && remaining == actualBlockSize) ? ZSTD_compressEnd (ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize) : ZSTD_compressContinue(ctx->cctx, job->dst.start + dstPos, job->dst.capacity - dstPos, job->src.start + job->dictSize + srcPos, actualBlockSize); @@ -577,7 +565,6 @@ static void* compressionThread(void* arg) /* update completion */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); ctx->compressionCompletion = 1 - (double)remaining/job->src.size; - DEBUG(3, "compression completion %u %f\n", currJob, ctx->compressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); } } while (remaining != 0); @@ -585,13 +572,10 @@ static void* compressionThread(void* arg) } pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); ctx->jobCompressedID++; - DEBUG(3, "signaling for job %u\n", currJob); pthread_cond_broadcast(&ctx->jobCompressed_cond.pCond); pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); - DEBUG(3, "finished job compression %u\n", currJob); if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished compressing all jobs */ - DEBUG(3, "all jobs finished compressing\n"); break; } DEBUG(2, "finished compressing job %u\n", currJob); @@ -633,7 +617,6 @@ static void* outputThread(void* arg) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(3, "write thread waiting : writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -645,7 +628,6 @@ static void* outputThread(void* arg) ctx->writeCompletion = 0; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); - DEBUG(3, "outputThread(): continuing after job compressed\n"); { size_t const compressedSize = job->compressedSize; size_t remaining = compressedSize; @@ -668,7 +650,6 @@ static void* outputThread(void* arg) /* update completion variable for writing */ pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->writeCompletion = 1 - (double)remaining/compressedSize; - DEBUG(3, "write completion %u %f\n", currJob, ctx->writeCompletion); pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); if (remaining == 0) break; @@ -680,18 +661,14 @@ static void* outputThread(void* arg) } } } - DEBUG(3, "finished job write %u\n", currJob); displayProgress(ctx->compressionLevel, job->lastJobPlusOne == currJob + 1); - DEBUG(3, "locking job write mutex\n"); pthread_mutex_lock(&ctx->jobWrite_mutex.pMutex); ctx->jobWriteID++; pthread_cond_signal(&ctx->jobWrite_cond.pCond); pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); - DEBUG(3, "unlocking job write mutex\n"); if (job->lastJobPlusOne == currJob + 1 || ctx->threadError) { /* finished with all jobs */ - DEBUG(3, "all jobs finished writing\n"); pthread_mutex_lock(&ctx->allJobsCompleted_mutex.pMutex); ctx->allJobsCompleted = 1; pthread_cond_signal(&ctx->allJobsCompleted_cond.pCond); @@ -710,7 +687,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; jobDescription* job = &ctx->jobs[nextJobIndex]; - DEBUG(3, "createCompressionJob(): wait for job write\n"); /* wait until the job has been compressed */ @@ -719,8 +695,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); /* creation thread is waiting, take measurement of completion */ ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(3, "creation thread waiting : createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); - DEBUG(3, "writeCompletion: %f\n", ctx->writeCompletion); DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); @@ -730,9 +704,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); - DEBUG(3, "createCompressionJob(): continuing after job write\n"); - - DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; @@ -745,13 +716,10 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) } job->dictSize = ctx->lastDictSize; - DEBUG(3, "finished job creation %u\n", nextJob); ctx->nextJobID++; - DEBUG(3, "filled: %zu, srcSize: %zu\n", ctx->input.filled, srcSize); /* if not on the last job, reuse data as dictionary in next job */ if (!last) { size_t const oldDictSize = ctx->lastDictSize; - DEBUG(3, "oldDictSize %zu\n", oldDictSize); memcpy(ctx->input.buffer.start, job->src.start + oldDictSize, srcSize); ctx->lastDictSize = srcSize; ctx->input.filled = srcSize; @@ -812,7 +780,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA remaining -= ret; pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 1 - (double)remaining/((size_t)FILE_CHUNK_SIZE); - DEBUG(3, "create completion %u %f\n", currJob, ctx->createCompletion); pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } if (remaining != 0 && !feof(srcFile)) { @@ -833,7 +800,6 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA DEBUG(2, "finished creating job %u\n", currJob); currJob++; if (feof(srcFile)) { - DEBUG(3, "THE STREAM OF DATA ENDED %u\n", ctx->nextJobID); break; } } @@ -991,7 +957,6 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); - DEBUG(3, "g_compressionLevel: %u\n", g_compressionLevel); break; case 'h': help(); From 31a9ed9883f8f4313fb5410a9bce9734ae77328d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 14:53:40 -0700 Subject: [PATCH 126/145] updated const values, added more comments --- contrib/adaptive-compression/adapt.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 722ae243d..9ebe2f9f9 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -319,6 +319,7 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->allJobsCompleted_mutex.pMutex); } +/* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { if (completion < 0.05) { @@ -350,11 +351,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) unsigned const prevCompressionLevel = ctx->compressionLevel; DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); - /* read and reset completion measurements */ + /* read and reset completion measurements */ pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - DEBUG(2, "rc %f\n", ctx->createWaitCompressionCompletion); - DEBUG(2, "wc %f\n", ctx->writeWaitCompressionCompletion); + DEBUG(2, "createWaitCompressionCompletion %f\n", ctx->createWaitCompressionCompletion); + DEBUG(2, "writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; ctx->createWaitCompressionCompletion = 1; @@ -362,13 +363,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); - DEBUG(2, "cw %f\n", ctx->compressWaitWriteCompletion); + DEBUG(2, "compressWaitWriteCompletion %f\n", ctx->compressWaitWriteCompletion); compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; ctx->compressWaitWriteCompletion = 1; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); - DEBUG(2, "cr %f\n", ctx->compressWaitCreateCompletion); + DEBUG(2, "compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); @@ -439,8 +440,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) static size_t getUseableDictSize(unsigned compressionLevel) { ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); - unsigned overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; - size_t overlapSize = 1 << (params.cParams.windowLog - overlapLog); + unsigned const overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; + size_t const overlapSize = 1 << (params.cParams.windowLog - overlapLog); return overlapSize; } @@ -450,7 +451,7 @@ static void* compressionThread(void* arg) unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[currJobIndex]; + jobDescription* const job = &ctx->jobs[currJobIndex]; DEBUG(2, "starting compression for job %u\n", currJob); { @@ -610,7 +611,7 @@ static void* outputThread(void* arg) unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[currJobIndex]; + jobDescription* const job = &ctx->jobs[currJobIndex]; DEBUG(2, "starting write for job %u\n", currJob); pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { @@ -686,7 +687,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) { unsigned const nextJob = ctx->nextJobID; unsigned const nextJobIndex = nextJob % ctx->numJobs; - jobDescription* job = &ctx->jobs[nextJobIndex]; + jobDescription* const job = &ctx->jobs[nextJobIndex]; /* wait until the job has been compressed */ @@ -736,6 +737,7 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadArg* otArg) { + /* early error check to exit */ if (!ctx || !srcFile || !otArg) { return 1; } From 9a132707aff6ece4b77864db7078c5c123b7b421 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 15:26:26 -0700 Subject: [PATCH 127/145] changing time units to seconds --- contrib/adaptive-compression/adapt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 9ebe2f9f9..919502221 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -593,7 +593,7 @@ static void displayProgress(unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f ms | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); if (last) { fprintf(stderr, "\n"); } From 8dbb07d822ec998fe92e485ab09deb6d26b6f6d5 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 16:03:43 -0700 Subject: [PATCH 128/145] updated progress bar with better representation of time, added const --- contrib/adaptive-compression/adapt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 919502221..ead68bf4a 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -439,7 +439,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) static size_t getUseableDictSize(unsigned compressionLevel) { - ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); unsigned const overlapLog = compressionLevel >= (unsigned)ZSTD_maxCLevel() ? 0 : 3; size_t const overlapSize = 1 << (params.cParams.windowLog - overlapLog); return overlapSize; @@ -447,7 +447,7 @@ static size_t getUseableDictSize(unsigned compressionLevel) static void* compressionThread(void* arg) { - adaptCCtx* ctx = (adaptCCtx*)arg; + adaptCCtx* const ctx = (adaptCCtx*)arg; unsigned currJob = 0; for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; @@ -593,7 +593,7 @@ static void displayProgress(unsigned cLevel, unsigned last) double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %5.0f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %7.2f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); if (last) { fprintf(stderr, "\n"); } @@ -638,7 +638,6 @@ static void* outputThread(void* arg) return arg; } { - // size_t const writeSize = fwrite(job->dst.start, 1, compressedSize, dstFile); size_t const blockSize = MAX(compressedSize >> 7, 1 << 10); size_t pos = 0; for ( ; ; ) { From 7cc74e0b27fbf6db0644d14b9861231c5f7486d8 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 16:55:16 -0700 Subject: [PATCH 129/145] adding more to readme --- contrib/adaptive-compression/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md index 1d2613377..09923d190 100644 --- a/contrib/adaptive-compression/README.md +++ b/contrib/adaptive-compression/README.md @@ -1,2 +1,8 @@ -ZSTD_adapt is a compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. -In many scenarios, using ZSTD without a properly adjusted compression level results in a pipe bottleneck, or a compressed file that could have been reduced in size. +###Summary + +`adapt` is a new compression tool targeted at optimizing performance across network connections. The tool aims at sensing network speeds and adapting compression level based on network or pipe speeds. +In situations where the compression level does not appropriately match the network/pipe speed, the compression may be bottlenecking the entire pipeline or the files may not be compressed as much as they potentially could be, therefore losing efficiency. It also becomes quite impractical to manually measure and set compression level, therefore the tool does it for you. + +###Using `adapt` + +In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. From 0b18d21e03b9dd161cd27b8fd616dccb7f602e30 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Tue, 25 Jul 2017 17:47:02 -0700 Subject: [PATCH 130/145] building on readme, added another help tip in the menu --- contrib/adaptive-compression/README.md | 17 +++++++++++++++++ contrib/adaptive-compression/adapt.c | 1 + 2 files changed, 18 insertions(+) diff --git a/contrib/adaptive-compression/README.md b/contrib/adaptive-compression/README.md index 09923d190..fadb071f9 100644 --- a/contrib/adaptive-compression/README.md +++ b/contrib/adaptive-compression/README.md @@ -6,3 +6,20 @@ In situations where the compression level does not appropriately match the netwo ###Using `adapt` In order to build and use the tool, you can simply run `make adapt` in the `adaptive-compression` directory under `contrib`. This will generate an executable available for use. + +###Options +`-oFILE` : write output to `FILE` + +`-i#` : provide initial compression level + +`-h` : display help/information + +`-f` : force the compression level to stay constant + +`-c` : force write to `stdout` + +`-p` : hide progress bar + +`-q` : quiet mode -- do not show progress bar or other information + +###Benchmarking / Test results diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index ead68bf4a..16701e75d 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -924,6 +924,7 @@ static void help() PRINT(" -i# : provide initial compression level\n"); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); + PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); } From be92a38d6a76ed987c44a4a364161190a8715b36 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:05:10 -0700 Subject: [PATCH 131/145] decrease completion requirements for change, move create thread wait, merge cases where compression thread should wait --- contrib/adaptive-compression/adapt.c | 54 +++++++++++----------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 16701e75d..1633b6bf8 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -322,10 +322,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { - if (completion < 0.05) { + if (completion < 0.1) { return 2; } - else if (completion < 0.5) { + else if (completion < 0.65) { return 1; } else { @@ -396,9 +396,9 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); } - else if (1-compressWaitWriteCompletion > threshold) { + else if (1-compressWaitWriteCompletion > threshold || 1-compressWaitCreateCompletion > threshold) { /* compress waiting on write */ - double const completion = compressWaitWriteCompletion; + double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { @@ -406,26 +406,11 @@ static void adaptCompressionLevel(adaptCCtx* ctx) } else if (boundChange != 0) { ctx->compressionLevel += boundChange; + ctx->cooldown = 0; ctx->convergenceCounter = 1; } - DEBUG(2, "compress waiting on write, tried to increase compression level by %u\n\n", boundChange); - } - else if (1-compressWaitCreateCompletion > threshold) { - /* compress waiting on create*/ - /* use compressWaitCreateCompletion */ - double const completion = compressWaitCreateCompletion; - unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { - ctx->convergenceCounter = 0; - } - else if (boundChange != 0) { - ctx->compressionLevel += boundChange; - ctx->convergenceCounter = 1; - } - - DEBUG(2, "compression waiting on create, tried to increase compression level by %u\n\n", boundChange); + DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } if (ctx->compressionLevel == prevCompressionLevel) { @@ -689,17 +674,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) jobDescription* const job = &ctx->jobs[nextJobIndex]; - /* wait until the job has been compressed */ - pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - /* creation thread is waiting, take measurement of completion */ - ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); - pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); - } - pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); /* reset create completion */ pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->createCompletion = 0; @@ -767,8 +741,22 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t pos = 0; size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; - + unsigned const nextJob = ctx->nextJobID; DEBUG(2, "starting creation of job %u\n", currJob); + + + /* wait until the job has been compressed */ + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + /* creation thread is waiting, take measurement of completion */ + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); + } + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From 305d5ee70f41cb89f2d373abf703b09dfedcda61 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:20:29 -0700 Subject: [PATCH 132/145] change to >= convergence counter --- contrib/adaptive-compression/adapt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 1633b6bf8..d75660806 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -384,33 +384,37 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; + DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { ctx->compressionLevel -= boundChange; ctx->cooldown = CLEVEL_DECREASE_COOLDOWN; ctx->convergenceCounter = 1; - } - DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); + DEBUG(2, "create or write threads waiting on compression, tried to decrease compression level by %u\n\n", boundChange); + } } else if (1-compressWaitWriteCompletion > threshold || 1-compressWaitCreateCompletion > threshold) { /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); - if (ctx->convergenceCounter > CONVERGENCE_LOWER_BOUND && boundChange != 0) { + if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { + /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; + DEBUG(2, "convergence counter reset, no change applied\n"); } else if (boundChange != 0) { ctx->compressionLevel += boundChange; ctx->cooldown = 0; ctx->convergenceCounter = 1; + + DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } - DEBUG(2, "compress waiting on write or create, tried to increase compression level by %u\n\n", boundChange); } if (ctx->compressionLevel == prevCompressionLevel) { From a959cc881a65b4ef250b4f963924c04c6760be0d Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 10:34:48 -0700 Subject: [PATCH 133/145] moved reset of completion to right after wait --- contrib/adaptive-compression/adapt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index d75660806..237bb4306 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -678,10 +678,6 @@ static int createCompressionJob(adaptCCtx* ctx, size_t srcSize, int last) jobDescription* const job = &ctx->jobs[nextJobIndex]; - /* reset create completion */ - pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); - ctx->createCompletion = 0; - pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); job->compressionLevel = ctx->compressionLevel; job->src.size = srcSize; job->jobID = nextJob; @@ -761,6 +757,11 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + /* reset create completion */ + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); + ctx->createCompletion = 0; + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + while (remaining != 0 && !feof(srcFile)) { size_t const ret = fread(ctx->input.buffer.start + ctx->input.filled + pos, 1, readBlockSize, srcFile); if (ret != readBlockSize && !feof(srcFile)) { From 6c1c1242fce69018710d30c785285f0cbaa850cc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 14:29:59 -0700 Subject: [PATCH 134/145] set the window log value before performing compression --- contrib/adaptive-compression/adapt.c | 16 ++++++++++------ contrib/adaptive-compression/test-correctness.sh | 5 +++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 237bb4306..5ff2e4181 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -513,12 +513,16 @@ static void* compressionThread(void* arg) { size_t const useDictSize = MIN(getUseableDictSize(cLevel), job->dictSize); size_t const dictModeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceRawDict, 1); - size_t const initError = ZSTD_compressBegin_usingDict(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, cLevel); - size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); - if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { - DISPLAY("Error: something went wrong while starting compression\n"); - signalErrorToThreads(ctx); - return arg; + ZSTD_parameters params = ZSTD_getParams(cLevel, 0, useDictSize); + params.cParams.windowLog = 23; + { + size_t const initError = ZSTD_compressBegin_advanced(ctx->cctx, job->src.start + job->dictSize - useDictSize, useDictSize, params, 0); + size_t const windowSizeError = ZSTD_setCCtxParameter(ctx->cctx, ZSTD_p_forceWindow, 1); + if (ZSTD_isError(dictModeError) || ZSTD_isError(initError) || ZSTD_isError(windowSizeError)) { + DISPLAY("Error: something went wrong while starting compression\n"); + signalErrorToThreads(ctx); + return arg; + } } } DEBUG(2, "finished with ZSTD_compressBegin()\n"); diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 86d39ee4f..8ae6604af 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -237,4 +237,9 @@ rm tmp* zstd -d tmp.zst -o tmp2 diff -s -q tmp tmp2 rm tmp* + +echo -e "\ncorrectness tests -- window size test" +./datagen -s39 -g1GB | pv -L 25m -q | ./adapt -i1 | pv -q > tmp.zst +zstd -d tmp.zst +rm tmp* make clean From 715f36ca8165d3d89f974567c883dfb0e2bc4e36 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 15:52:15 -0700 Subject: [PATCH 135/145] added definitions for conversion constants, moved forced compression check to top of adaptCompressionLevel, used ZSTD_BLOCKSIZE_MAX --- contrib/adaptive-compression/adapt.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5ff2e4181..05da8b7f7 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -28,6 +28,8 @@ #define MAX_COMPRESSION_LEVEL_CHANGE 2 #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 +#define CHANGE_BY_TWO_THRESHOLD 0.1 +#define CHANGE_BY_ONE_THRESHOLD 0.65 #ifndef DEBUG_MODE static int g_displayLevel = DEFAULT_DISPLAY_LEVEL; @@ -322,10 +324,10 @@ static void waitUntilAllJobsCompleted(adaptCCtx* ctx) /* map completion percentages to values for changing compression level */ static unsigned convertCompletionToChange(double completion) { - if (completion < 0.1) { + if (completion < CHANGE_BY_TWO_THRESHOLD) { return 2; } - else if (completion < 0.65) { + else if (completion < CHANGE_BY_ONE_THRESHOLD) { return 1; } else { @@ -350,6 +352,13 @@ static void adaptCompressionLevel(adaptCCtx* ctx) double const threshold = 0.00001; unsigned const prevCompressionLevel = ctx->compressionLevel; + + if (g_forceCompressionLevel) { + ctx->compressionLevel = g_compressionLevel; + return; + } + + DEBUG(2, "adapting compression level %u\n", ctx->compressionLevel); /* read and reset completion measurements */ @@ -420,10 +429,6 @@ static void adaptCompressionLevel(adaptCCtx* ctx) if (ctx->compressionLevel == prevCompressionLevel) { ctx->convergenceCounter++; } - - if (g_forceCompressionLevel) { - ctx->compressionLevel = g_compressionLevel; - } } static size_t getUseableDictSize(unsigned compressionLevel) @@ -500,7 +505,7 @@ static void* compressionThread(void* arg) DEBUG(2, "job %u compressed with level %u\n", currJob, ctx->compressionLevel); /* compress the data */ { - size_t const compressionBlockSize = 1 << 17; /* 128 KB */ + size_t const compressionBlockSize = ZSTD_BLOCKSIZE_MAX; /* 128 KB */ unsigned const cLevel = ctx->compressionLevel; unsigned blockNum = 0; size_t remaining = job->src.size; From ab5a78547e62c9a80936e998f4db94fe43e6d232 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 16:40:05 -0700 Subject: [PATCH 136/145] fix leaky abstraction regarding measuring completion --- contrib/adaptive-compression/adapt.c | 57 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 05da8b7f7..a16ab40cc 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -367,20 +367,16 @@ static void adaptCompressionLevel(adaptCCtx* ctx) DEBUG(2, "writeWaitCompressionCompletion %f\n", ctx->writeWaitCompressionCompletion); createWaitCompressionCompletion = ctx->createWaitCompressionCompletion; writeWaitCompressionCompletion = ctx->writeWaitCompressionCompletion; - ctx->createWaitCompressionCompletion = 1; - ctx->writeWaitCompressionCompletion = 1; pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); DEBUG(2, "compressWaitWriteCompletion %f\n", ctx->compressWaitWriteCompletion); compressWaitWriteCompletion = ctx->compressWaitWriteCompletion; - ctx->compressWaitWriteCompletion = 1; pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "compressWaitCreateCompletion %f\n", ctx->compressWaitCreateCompletion); compressWaitCreateCompletion = ctx->compressWaitCreateCompletion; - ctx->compressWaitCreateCompletion = 1; pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); @@ -462,22 +458,28 @@ static void* compressionThread(void* arg) pthread_mutex_unlock(&ctx->jobWrite_mutex.pMutex); + pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); if (willWaitForCreate) { DEBUG(2, "compression will wait for create on job %u\n", currJob); - pthread_mutex_lock(&ctx->createCompletion_mutex.pMutex); ctx->compressWaitCreateCompletion = ctx->createCompletion; DEBUG(2, "create completion %f\n", ctx->compressWaitCreateCompletion); - pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); } + else { + ctx->compressWaitCreateCompletion = 1; + } + pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); + pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); if (willWaitForWrite) { DEBUG(2, "compression will wait for write on job %u\n", currJob); - pthread_mutex_lock(&ctx->writeCompletion_mutex.pMutex); ctx->compressWaitWriteCompletion = ctx->writeCompletion; DEBUG(2, "write completion %f\n", ctx->compressWaitWriteCompletion); - pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } + else { + ctx->compressWaitWriteCompletion = 1; + } + pthread_mutex_unlock(&ctx->writeCompletion_mutex.pMutex); } @@ -610,14 +612,27 @@ static void* outputThread(void* arg) for ( ; ; ) { unsigned const currJobIndex = currJob % ctx->numJobs; jobDescription* const job = &ctx->jobs[currJobIndex]; + unsigned willWaitForCompress = 0; DEBUG(2, "starting write for job %u\n", currJob); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); - while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (currJob + 1 > ctx->jobCompressedID) willWaitForCompress = 1; + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (willWaitForCompress) { /* write thread is waiting on compression thread */ ctx->writeWaitCompressionCompletion = ctx->compressionCompletion; DEBUG(2, "writer thread waiting for nextJob: %u, writeWaitCompressionCompletion %f\n", currJob, ctx->writeWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + } + else { + ctx->writeWaitCompressionCompletion = 1; + } + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); + + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + while (currJob + 1 > ctx->jobCompressedID && !ctx->threadError) { pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); @@ -751,17 +766,27 @@ static int performCompression(adaptCCtx* ctx, FILE* const srcFile, outputThreadA size_t const readBlockSize = 1 << 15; size_t remaining = FILE_CHUNK_SIZE; unsigned const nextJob = ctx->nextJobID; + unsigned willWaitForCompress = 0; DEBUG(2, "starting creation of job %u\n", currJob); + pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); + if (nextJob - ctx->jobCompressedID >= ctx->numJobs) willWaitForCompress = 1; + pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); + + pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); + if (willWaitForCompress) { + /* creation thread is waiting, take measurement of completion */ + ctx->createWaitCompressionCompletion = ctx->compressionCompletion; + DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); + } + else { + ctx->createWaitCompressionCompletion = 1; + } + pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); /* wait until the job has been compressed */ pthread_mutex_lock(&ctx->jobCompressed_mutex.pMutex); while (nextJob - ctx->jobCompressedID >= ctx->numJobs && !ctx->threadError) { - pthread_mutex_lock(&ctx->compressionCompletion_mutex.pMutex); - /* creation thread is waiting, take measurement of completion */ - ctx->createWaitCompressionCompletion = ctx->compressionCompletion; - DEBUG(2, "create thread waiting for nextJob: %u, createWaitCompressionCompletion %f\n", nextJob, ctx->createWaitCompressionCompletion); - pthread_mutex_unlock(&ctx->compressionCompletion_mutex.pMutex); pthread_cond_wait(&ctx->jobCompressed_cond.pCond, &ctx->jobCompressed_mutex.pMutex); } pthread_mutex_unlock(&ctx->jobCompressed_mutex.pMutex); From 2320e7378af2c62e576d4ce0fa4296580172177b Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Wed, 26 Jul 2017 17:02:47 -0700 Subject: [PATCH 137/145] remove unused variable, add documentation for context fields --- contrib/adaptive-compression/adapt.c | 38 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index a16ab40cc..5cf3b9708 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -24,7 +24,6 @@ #define MAX_PATH 256 #define DEFAULT_DISPLAY_LEVEL 1 #define DEFAULT_COMPRESSION_LEVEL 6 -#define DEFAULT_ADAPT_PARAM 0 #define MAX_COMPRESSION_LEVEL_CHANGE 2 #define CONVERGENCE_LOWER_BOUND 5 #define CLEVEL_DECREASE_COOLDOWN 5 @@ -81,20 +80,54 @@ typedef struct { unsigned numJobs; unsigned nextJobID; unsigned threadError; + + /* + * JobIDs for the next jobs to be created, compressed, and written + */ unsigned jobReadyID; unsigned jobCompressedID; unsigned jobWriteID; unsigned allJobsCompleted; - unsigned adaptParam; + + /* + * counter for how many jobs in a row the compression level has not changed + * if the counter becomes >= CONVERGENCE_LOWER_BOUND, the next time the + * compression level tries to change (by non-zero amount) resets the counter + * to 1 and does not apply the change + */ unsigned convergenceCounter; + + /* + * cooldown counter in order to prevent rapid successive decreases in compression level + * whenever compression level is decreased, cooldown is set to CLEVEL_DECREASE_COOLDOWN + * whenever adaptCompressionLevel() is called and cooldown != 0, it is decremented + * as long as cooldown != 0, the compression level cannot be decreased + */ unsigned cooldown; + + /* + * XWaitYCompletion + * Range from 0.0 to 1.0 + * if the value is not 1.0, then this implies that thread X waited on thread Y to finish + * and thread Y was XWaitYCompletion finished at the time of the wait (i.e. compressWaitWriteCompletion=0.5 + * implies that the compression thread waited on the write thread and it was only 50% finished writing a job) + */ double createWaitCompressionCompletion; double compressWaitCreateCompletion; double compressWaitWriteCompletion; double writeWaitCompressionCompletion; + + /* + * Completion values + * Range from 0.0 to 1.0 + * Jobs are divided into mini-chunks in order to measure completion + * these values are updated each time a thread finishes its operation on the + * mini-chunk (i.e. finishes writing out, compressing, etc. this mini-chunk). + */ double compressionCompletion; double writeCompletion; double createCompletion; + mutex_t jobCompressed_mutex; cond_t jobCompressed_cond; mutex_t jobReady_mutex; @@ -254,7 +287,6 @@ static int initCCtx(adaptCCtx* ctx, unsigned numJobs) ctx->nextJobID = 0; ctx->threadError = 0; ctx->allJobsCompleted = 0; - ctx->adaptParam = DEFAULT_ADAPT_PARAM; ctx->cctx = ZSTD_createCCtx(); if (!ctx->cctx) { From ff54fced641bac595a4120108b563ee523650adc Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 15:30:46 -0700 Subject: [PATCH 138/145] patched style errors, add ability to bound compression level variation --- contrib/adaptive-compression/adapt.c | 46 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5cf3b9708..5e26a0db6 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -42,6 +42,8 @@ static size_t g_streamedSize = 0; static unsigned g_useProgressBar = 1; static UTIL_freq_t g_ticksPerSecond; static unsigned g_forceCompressionLevel = 0; +static unsigned g_minCLevel = 1; +static unsigned g_maxCLevel = 22; typedef struct { void* start; @@ -420,7 +422,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ctx->compressionLevel - 1); + unsigned const boundChange = ctx->compressionLevel >= g_minCLevel ? MIN(change, ctx->compressionLevel - g_minCLevel) : 0; if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -438,7 +440,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = MIN(change, ZSTD_maxCLevel() - ctx->compressionLevel); + unsigned const boundChange = g_maxCLevel >= ctx->compressionLevel ? MIN(change, g_maxCLevel - ctx->compressionLevel) : 0; if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -620,17 +622,19 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { if (!g_useProgressBar) return; - UTIL_time_t currTime; - UTIL_getTime(&currTime); - double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); - double const sizeMB = (double)g_streamedSize / (1 << 20); - double const avgCompRate = sizeMB * 1000 / timeElapsed; - fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %7.2f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); - if (last) { - fprintf(stderr, "\n"); - } - else { - fflush(stderr); + { + UTIL_time_t currTime; + UTIL_getTime(&currTime); + double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); + double const sizeMB = (double)g_streamedSize / (1 << 20); + double const avgCompRate = sizeMB * 1000 / timeElapsed; + fprintf(stderr, "\r| Comp. Level: %2u | Time Elapsed: %7.2f s | Data Size: %7.1f MB | Avg Comp. Rate: %6.2f MB/s |", cLevel, timeElapsed/1000.0, sizeMB, avgCompRate); + if (last) { + fprintf(stderr, "\n"); + } + else { + fflush(stderr); + } } } @@ -928,9 +932,9 @@ static int freeFileCompressionResources(fcResources* fcr) static int compressFilename(const char* const srcFilename, const char* const dstFilenameOrNull) { int ret = 0; + fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); UTIL_getTime(&g_startTime); g_streamedSize = 0; - fcResources fcr = createFileCompressionResources(srcFilename, dstFilenameOrNull); ret |= performCompression(fcr.ctx, fcr.srcFile, fcr.otArg); ret |= freeFileCompressionResources(&fcr); return ret; @@ -973,7 +977,7 @@ static unsigned readU32FromChar(const char** stringPtr) return result; } -static void help() +static void help(void) { PRINT("Usage:\n"); PRINT(" ./multi [options] [file(s)]\n"); @@ -986,6 +990,8 @@ static void help() PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); + PRINT(" -l# : provide lower bound for compression level\n"); + PRINT(" -u# : provide upper bound for compression level\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) @@ -993,10 +999,10 @@ int main(int argCount, const char* argv[]) const char* outFilename = NULL; const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; - filenameTable[0] = stdinmark; unsigned forceStdout = 0; int ret = 0; int argNum; + filenameTable[0] = stdinmark; UTIL_initTimer(&g_ticksPerSecond); @@ -1036,6 +1042,14 @@ int main(int argCount, const char* argv[]) g_useProgressBar = 0; g_displayLevel = 0; break; + case 'l': + argument += 2; + g_minCLevel = readU32FromChar(&argument); + break; + case 'u': + argument += 2; + g_maxCLevel = readU32FromChar(&argument); + break; default: DISPLAY("Error: invalid argument provided\n"); ret = 1; From 0f4cb67b0050503aeb89a8c91e738c3d685cf89a Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 15:55:02 -0700 Subject: [PATCH 139/145] add tests for compression bounds, fix another warning --- contrib/adaptive-compression/adapt.c | 5 +++-- contrib/adaptive-compression/test-correctness.sh | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 5e26a0db6..55f4148fb 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -621,10 +621,11 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { + + UTIL_time_t currTime; + UTIL_getTime(&currTime); if (!g_useProgressBar) return; { - UTIL_time_t currTime; - UTIL_getTime(&currTime); double const timeElapsed = (double)(UTIL_getSpanTimeMicro(g_ticksPerSecond, g_startTime, currTime) / 1000.0); double const sizeMB = (double)g_streamedSize / (1 << 20); double const avgCompRate = sizeMB * 1000 / timeElapsed; diff --git a/contrib/adaptive-compression/test-correctness.sh b/contrib/adaptive-compression/test-correctness.sh index 8ae6604af..3bea867b9 100755 --- a/contrib/adaptive-compression/test-correctness.sh +++ b/contrib/adaptive-compression/test-correctness.sh @@ -242,4 +242,11 @@ echo -e "\ncorrectness tests -- window size test" ./datagen -s39 -g1GB | pv -L 25m -q | ./adapt -i1 | pv -q > tmp.zst zstd -d tmp.zst rm tmp* + +echo -e "\ncorrectness tests -- testing bounds" +./datagen -s40 -g1GB | pv -L 25m -q | ./adapt -i1 -u4 | pv -q > tmp.zst +rm tmp* + +./datagen -s41 -g1GB | ./adapt -i14 -l4 > tmp.zst +rm tmp* make clean From 4d904ac800920286fc6d22b28e132577572cd4de Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 16:12:58 -0700 Subject: [PATCH 140/145] add flags for multithreading --- contrib/adaptive-compression/Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 9bc19ee15..0e081a670 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -6,6 +6,15 @@ ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) +# Define *.exe as extension for Windows systems +ifneq (,$(filter Windows%,$(OS))) +EXT =.exe +MULTITHREAD_LD = +else +EXT = +MULTITHREAD_LD = -pthread +endif + DEBUGFLAGS= -g -DZSTD_DEBUG=1 CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) @@ -17,7 +26,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wredundant-decls CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LD) all: adapt datagen From 51788225db8d3a2fac58614d72e0dd918fa380fb Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:27:36 -0700 Subject: [PATCH 141/145] remove exe extension from makefile, reinclude pthread flag --- contrib/adaptive-compression/Makefile | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 0e081a670..b1c498c93 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -6,15 +6,7 @@ ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) -# Define *.exe as extension for Windows systems -ifneq (,$(filter Windows%,$(OS))) -EXT =.exe -MULTITHREAD_LD = -else -EXT = -MULTITHREAD_LD = -pthread -endif - +MULTITHREAD_LDFLAGS = -pthread DEBUGFLAGS= -g -DZSTD_DEBUG=1 CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) @@ -26,7 +18,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wredundant-decls CFLAGS += $(DEBUGFLAGS) CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LD) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MULTITHREAD_LDFLAGS) all: adapt datagen From cb9af53e773c1fd1df9765028c2ffec636838158 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:28:25 -0700 Subject: [PATCH 142/145] delete empty line --- contrib/adaptive-compression/adapt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 55f4148fb..755b7e976 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -621,7 +621,6 @@ static void* compressionThread(void* arg) static void displayProgress(unsigned cLevel, unsigned last) { - UTIL_time_t currTime; UTIL_getTime(&currTime); if (!g_useProgressBar) return; From e22b60cb76a09af2dc0365bca2ee20437003f2db Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Fri, 28 Jul 2017 17:46:51 -0700 Subject: [PATCH 143/145] removed ternary operation, added assert statement, check to make sure initial compression level is within bounds --- contrib/adaptive-compression/adapt.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 755b7e976..3a57c3723 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -414,6 +414,8 @@ static void adaptCompressionLevel(adaptCCtx* ctx) pthread_mutex_unlock(&ctx->createCompletion_mutex.pMutex); DEBUG(2, "convergence counter: %u\n", ctx->convergenceCounter); + assert(g_minCLevel <= ctx->compressionLevel && g_maxCLevel >= ctx->compressionLevel); + /* adaptation logic */ if (ctx->cooldown) ctx->cooldown--; @@ -422,7 +424,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* use whichever one waited less because it was slower */ double const completion = MAX(createWaitCompressionCompletion, writeWaitCompressionCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = ctx->compressionLevel >= g_minCLevel ? MIN(change, ctx->compressionLevel - g_minCLevel) : 0; + unsigned const boundChange = MIN(change, ctx->compressionLevel - g_minCLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -440,7 +442,7 @@ static void adaptCompressionLevel(adaptCCtx* ctx) /* compress waiting on write */ double const completion = MIN(compressWaitWriteCompletion, compressWaitCreateCompletion); unsigned const change = convertCompletionToChange(completion); - unsigned const boundChange = g_maxCLevel >= ctx->compressionLevel ? MIN(change, g_maxCLevel - ctx->compressionLevel) : 0; + unsigned const boundChange = MIN(change, g_maxCLevel - ctx->compressionLevel); if (ctx->convergenceCounter >= CONVERGENCE_LOWER_BOUND && boundChange != 0) { /* reset convergence counter, might have been a spike */ ctx->convergenceCounter = 0; @@ -1000,6 +1002,7 @@ int main(int argCount, const char* argv[]) const char** filenameTable = (const char**)malloc(argCount*sizeof(const char*)); unsigned filenameIdx = 0; unsigned forceStdout = 0; + unsigned providedInitialCLevel = 0; int ret = 0; int argNum; filenameTable[0] = stdinmark; @@ -1024,6 +1027,7 @@ int main(int argCount, const char* argv[]) case 'i': argument += 2; g_compressionLevel = readU32FromChar(&argument); + providedInitialCLevel = 1; break; case 'h': help(); @@ -1062,6 +1066,20 @@ int main(int argCount, const char* argv[]) filenameTable[filenameIdx++] = argument; } + /* check initial, max, and min compression levels */ + { + unsigned const minMaxInconsistent = g_minCLevel > g_maxCLevel; + unsigned const initialNotInRange = g_minCLevel > g_compressionLevel || g_maxCLevel < g_compressionLevel; + if (minMaxInconsistent || (initialNotInRange && providedInitialCLevel)) { + DISPLAY("Error: provided compression level parameters are invalid\n"); + ret = 1; + goto _main_exit; + } + else if (initialNotInRange) { + g_compressionLevel = g_minCLevel; + } + } + /* error checking with number of files */ if (filenameIdx > 1 && (outFilename != NULL && strcmp(outFilename, stdoutmark))) { DISPLAY("Error: multiple input files provided, cannot use specified output file\n"); From f60cd3f99bccc482d89c1afac73b329a2d9adb29 Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 31 Jul 2017 09:47:09 -0700 Subject: [PATCH 144/145] print defaults and range, remove EXT --- contrib/adaptive-compression/Makefile | 2 +- contrib/adaptive-compression/adapt.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index b1c498c93..87b61b417 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -29,7 +29,7 @@ adapt-debug: $(ZSTD_FILES) adapt.c $(CC) $(FLAGS) -DDEBUG_MODE=2 $^ -o adapt datagen : $(PRGDIR)/datagen.c datagencli.c - $(CC) $(FLAGS) $^ -o $@$(EXT) + $(CC) $(FLAGS) $^ -o $@ test-adapt-correctness: datagen adapt @./test-correctness.sh diff --git a/contrib/adaptive-compression/adapt.c b/contrib/adaptive-compression/adapt.c index 3a57c3723..5cec227ec 100644 --- a/contrib/adaptive-compression/adapt.c +++ b/contrib/adaptive-compression/adapt.c @@ -986,14 +986,14 @@ static void help(void) PRINT("\n"); PRINT("Options:\n"); PRINT(" -oFILE : specify the output file name\n"); - PRINT(" -i# : provide initial compression level\n"); + PRINT(" -i# : provide initial compression level -- default %d, must be in the range [L, U] where L and U are bound values (see below for defaults)\n", DEFAULT_COMPRESSION_LEVEL); PRINT(" -h : display help/information\n"); PRINT(" -f : force the compression level to stay constant\n"); PRINT(" -c : force write to stdout\n"); PRINT(" -p : hide progress bar\n"); PRINT(" -q : quiet mode -- do not show progress bar or other information\n"); - PRINT(" -l# : provide lower bound for compression level\n"); - PRINT(" -u# : provide upper bound for compression level\n"); + PRINT(" -l# : provide lower bound for compression level -- default 1\n"); + PRINT(" -u# : provide upper bound for compression level -- default 22\n"); } /* return 0 if successful, else return error */ int main(int argCount, const char* argv[]) From 9ea7df03de87a581b3f3be1eb6e555aece56888c Mon Sep 17 00:00:00 2001 From: Paul Cruz Date: Mon, 31 Jul 2017 11:04:17 -0700 Subject: [PATCH 145/145] add install target in makefile --- contrib/adaptive-compression/Makefile | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/contrib/adaptive-compression/Makefile b/contrib/adaptive-compression/Makefile index 87b61b417..c64fce954 100644 --- a/contrib/adaptive-compression/Makefile +++ b/contrib/adaptive-compression/Makefile @@ -46,3 +46,31 @@ clean: @$(RM) -f tests/*.zst @$(RM) -f tests/tmp* @echo "finished cleaning" + +#----------------------------------------------------------------------------- +# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets +#----------------------------------------------------------------------------- +ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) + +ifneq (,$(filter $(shell uname),SunOS)) +INSTALL ?= ginstall +else +INSTALL ?= install +endif + +PREFIX ?= /usr/local +DESTDIR ?= +BINDIR ?= $(PREFIX)/bin + +INSTALL_PROGRAM ?= $(INSTALL) -m 755 + +install: adapt + @echo Installing binaries + @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ + @$(INSTALL_PROGRAM) adapt $(DESTDIR)$(BINDIR)/zstd-adaptive + @echo zstd-adaptive installation completed + +uninstall: + @$(RM) $(DESTDIR)$(BINDIR)/zstd-adaptive + @echo zstd-adaptive programs successfully uninstalled +endif