diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..7af8b41ed --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,122 @@ +# This configuration was automatically generated from a CircleCI 1.0 config. +# It should include any build commands you had along with commands that CircleCI +# inferred from your project structure. We strongly recommend you read all the +# comments in this file to understand the structure of CircleCI 2.0, as the idiom +# for configuration has changed substantially in 2.0 to allow arbitrary jobs rather +# than the prescribed lifecycle of 1.0. In general, we recommend using this generated +# configuration as a reference rather than using it in production, though in most +# cases it should duplicate the execution of your original 1.0 config. +version: 2 +jobs: + build: + working_directory: ~/facebook/zstd + parallelism: 1 + shell: /bin/bash --login + # CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did. + # If any of these refer to each other, rewrite them so that they don't or see https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables . + environment: + CIRCLE_ARTIFACTS: /tmp/circleci-artifacts + CIRCLE_TEST_REPORTS: /tmp/circleci-test-results + # In CircleCI 1.0 we used a pre-configured image with a large number of languages and other packages. + # In CircleCI 2.0 you can now specify your own image, or use one of our pre-configured images. + # The following configuration line tells CircleCI to use the specified docker image as the runtime environment for you job. + # We have selected a pre-built image that mirrors the build environment we use on + # the 1.0 platform, but we recommend you choose an image more tailored to the needs + # of each job. For more information on choosing an image (or alternatively using a + # VM instead of a container) see https://circleci.com/docs/2.0/executor-types/ + # To see the list of pre-built images that CircleCI provides for most common languages see + # https://circleci.com/docs/2.0/circleci-images/ + docker: + - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37 + command: /sbin/init + steps: + # Machine Setup + # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each + # The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out. + - checkout + # Prepare for artifact and test results collection equivalent to how it was done on 1.0. + # In many cases you can simplify this from what is generated here. + # 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/' + - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS + # Dependencies + # This would typically go in either a build or a build-and-test job when using workflows + # Restore the dependency cache + - restore_cache: + keys: + # This branch if available + - v1-dep-{{ .Branch }}- + # Default branch if not + - v1-dep-dev- + # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly + - v1-dep- + # This is based on your 1.0 configuration file or project settings + - run: sudo dpkg --add-architecture i386 + - run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update + - run: sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + # Save dependency cache + - save_cache: + key: v1-dep-{{ .Branch }}-{{ epoch }} + paths: + # This is a broad list of cache paths to include many possible development environments + # You can probably delete some of these entries + - vendor/bundle + - ~/virtualenvs + - ~/.m2 + - ~/.ivy2 + - ~/.bundle + - ~/.go_workspace + - ~/.gradle + - ~/.cache/bower + # Test + # This would typically be a build job when using workflows, possibly combined with build + # This is based on your 1.0 configuration file or project settings + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; CFLAGS="-O0 -Werror" make all && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c99build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu99build && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppc64build && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi + - run: | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make cxxtest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi + # This is based on your 1.0 configuration file or project settings + - run: echo Circle CI tests finished + # Teardown + # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each + # Save test results + - store_test_results: + path: /tmp/circleci-test-results + # Save artifacts + - store_artifacts: + path: /tmp/circleci-artifacts + - store_artifacts: + path: /tmp/circleci-test-results + + # Longer tests + #- make -C tests test-zstd-nolegacy && make clean + #- pyenv global 3.4.4; make -C tests versionsTest && make clean + #- make zlibwrapper && make clean + #- gcc -v; make -C tests test32 MOREFLAGS="-I/usr/include/x86_64-linux-gnu" && make clean + #- make uasan && make clean + #- make asan32 && make clean + #- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu" + # Valgrind tests + #- CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make clean + #- make -C tests valgrindTest && make clean + # ARM, AArch64, PowerPC, PowerPC64 tests + #- make ppctest && make clean + #- make ppc64test && make clean + #- make armtest && make clean + #- make aarch64test && make clean diff --git a/.travis.yml b/.travis.yml index 71b270196..a61b82876 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,33 +10,38 @@ addons: matrix: include: # Ubuntu 14.04 - - env: Cmd='make gcc6install && CC=gcc-6 make -j all && make clean && CC=gcc-6 make clean uasan-test-zstd collect2: error: ld terminated with signal 11 [Segmentation fault], core dumped -# to be re-enabled in a few commit, as it's possible that a random code change circumvent the ld bug -# - env: Cmd='make arminstall && make aarch64fuzz' - + - env: Cmd='make arminstall && make aarch64fuzz' - env: Cmd='make ppcinstall && make ppcfuzz' - env: Cmd='make ppcinstall && make ppc64fuzz' - - env: Cmd='make -j uasanregressiontest && make clean && make -j msanregressiontest' - - env: Cmd='make lz4install && make -C tests test-lz4 test-pool && make clean && bash tests/libzstd_partial_builds.sh' + - env: Cmd='make lz4install && make -C tests test-lz4 + && make clean && make -C tests test-pool + && make clean && bash tests/libzstd_partial_builds.sh' # tag-specific test - if: tag =~ ^v[0-9]\.[0-9] @@ -49,6 +54,7 @@ branches: only: - dev - master + - travisTest script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') diff --git a/Makefile b/Makefile index 0f2fcc958..853ceee93 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,9 @@ default: lib-release zstd-release all: allmost examples manual contrib .PHONY: allmost -allmost: allzstd - $(MAKE) -C $(ZWRAPDIR) all +allmost: allzstd zlibwrapper -#skip zwrapper, can't build that on alternate architectures without the proper zlib installed +# skip zwrapper, can't build that on alternate architectures without the proper zlib installed .PHONY: allzstd allzstd: lib $(MAKE) -C $(PRGDIR) all @@ -44,8 +43,8 @@ all32: $(MAKE) -C $(PRGDIR) zstd32 $(MAKE) -C $(TESTDIR) all32 -.PHONY: lib lib-release -lib lib-release: +.PHONY: lib lib-release libzstd.a +lib lib-release : @$(MAKE) -C $(ZSTDDIR) $@ .PHONY: zstd zstd-release @@ -59,12 +58,13 @@ zstdmt: cp $(PRGDIR)/zstd$(EXT) ./zstdmt$(EXT) .PHONY: zlibwrapper -zlibwrapper: - $(MAKE) -C $(ZWRAPDIR) test +zlibwrapper: lib + $(MAKE) -C $(ZWRAPDIR) all .PHONY: test +test: MOREFLAGS += -g -DDEBUGLEVEL=1 -Werror test: - MOREFLAGS+="-g -DDEBUGLEVEL=1 -Werror" $(MAKE) -C $(PRGDIR) allVariants + MOREFLAGS="$(MOREFLAGS)" $(MAKE) -j -C $(PRGDIR) allVariants $(MAKE) -C $(TESTDIR) $@ .PHONY: shortest @@ -113,7 +113,7 @@ clean: ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly NetBSD MSYS_NT)) HOST_OS = POSIX -CMAKE_PARAMS = -DZSTD_BUILD_CONTRIB:BOOL=ON -DZSTD_BUILD_STATIC:BOOL=ON -DZSTD_BUILD_TESTS:BOOL=ON -DZSTD_ZLIB_SUPPORT:BOOL=ON -DZSTD_LZMA_SUPPORT:BOOL=ON +CMAKE_PARAMS = -DZSTD_BUILD_CONTRIB:BOOL=ON -DZSTD_BUILD_STATIC:BOOL=ON -DZSTD_BUILD_TESTS:BOOL=ON -DZSTD_ZLIB_SUPPORT:BOOL=ON -DZSTD_LZMA_SUPPORT:BOOL=ON -DCMAKE_BUILD_TYPE=Release .PHONY: list list: @@ -286,6 +286,9 @@ gcc6install: apt-add-repo gcc7install: apt-add-repo APT_PACKAGES="libc6-dev-i386 gcc-multilib gcc-7 gcc-7-multilib" $(MAKE) apt-install +gcc8install: apt-add-repo + APT_PACKAGES="libc6-dev-i386 gcc-multilib gcc-8 gcc-8-multilib" $(MAKE) apt-install + gpp6install: apt-add-repo APT_PACKAGES="libc6-dev-i386 g++-multilib gcc-6 g++-6 g++-6-multilib" $(MAKE) apt-install @@ -347,7 +350,10 @@ bmi32build: clean $(CC) -v CFLAGS="-O3 -mbmi -m32 -Werror" $(MAKE) -C $(TESTDIR) test -staticAnalyze: clean +# static analyzer test uses clang's scan-build +# does not analyze zlibWrapper, due to detected issues in zlib source code +staticAnalyze: SCANBUILD ?= scan-build +staticAnalyze: $(CC) -v - CPPFLAGS=-g scan-build --status-bugs -v $(MAKE) all + CC=$(CC) CPPFLAGS=-g $(SCANBUILD) --status-bugs -v $(MAKE) allzstd examples contrib endif diff --git a/README.md b/README.md index 17edecb71..dc99dc0fd 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ A `cmake` project generator is provided within `build/cmake`. It can generate Makefiles or other build scripts to create `zstd` binary, and `libzstd` dynamic and static libraries. +By default, `CMAKE_BUILD_TYPE` is set to `Release`. + #### Meson A Meson project is provided within `contrib/meson`. diff --git a/appveyor.yml b/appveyor.yml index 742f61206..2b674ce3c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -181,15 +181,15 @@ - COMPILER: "gcc" HOST: "mingw" PLATFORM: "x64" - SCRIPT: "make allzstd" + SCRIPT: "CPPFLAGS=-DDEBUGLEVEL=2 CFLAGS=-Werror make -j allzstd DEBUGLEVEL=2" - COMPILER: "gcc" HOST: "mingw" PLATFORM: "x86" - SCRIPT: "make allzstd" + SCRIPT: "CFLAGS=-Werror make -j allzstd" - COMPILER: "clang" HOST: "mingw" PLATFORM: "x64" - SCRIPT: "MOREFLAGS='--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion' make allzstd" + SCRIPT: "CFLAGS='--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion' make -j allzstd" - COMPILER: "visual" HOST: "visual" diff --git a/build/.gitignore b/build/.gitignore index b00e709ba..1ceb70ebc 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -18,3 +18,14 @@ Studio* # CMake cmake/build/ +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +build +lib diff --git a/build/VS2008/fuzzer/fuzzer.vcproj b/build/VS2008/fuzzer/fuzzer.vcproj index f6ea1f855..4d444caef 100644 --- a/build/VS2008/fuzzer/fuzzer.vcproj +++ b/build/VS2008/fuzzer/fuzzer.vcproj @@ -336,6 +336,10 @@ RelativePath="..\..\..\lib\dictBuilder\cover.c" > + + @@ -482,6 +486,10 @@ RelativePath="..\..\..\lib\dictBuilder\zdict.h" > + + diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index 5d9f68327..46d7c85dd 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -348,6 +348,10 @@ RelativePath="..\..\..\lib\dictBuilder\cover.c" > + + @@ -522,6 +526,10 @@ RelativePath="..\..\..\lib\dictBuilder\zdict.h" > + + diff --git a/build/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj index 7234b02ac..950a6f39c 100644 --- a/build/VS2008/zstdlib/zstdlib.vcproj +++ b/build/VS2008/zstdlib/zstdlib.vcproj @@ -332,6 +332,10 @@ RelativePath="..\..\..\lib\dictBuilder\cover.c" > + + @@ -502,6 +506,10 @@ RelativePath="..\..\..\lib\dictBuilder\zdict.h" > + + diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index f0d1ab066..6077cd2c1 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -176,6 +176,7 @@ + @@ -199,6 +200,7 @@ + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index 92d518d3d..6e14e020e 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -43,6 +43,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index c306fcec9..18f5cb533 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -43,6 +43,7 @@ + diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 4af281326..936960b58 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -40,6 +40,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index fd9bc2b1e..1e2921de8 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -10,6 +10,10 @@ PROJECT(zstd) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) SET(ZSTD_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") + +# Ensure Release build even if not invoked via Makefile +SET(CMAKE_BUILD_TYPE "Release") + LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") INCLUDE(GNUInstallDirs) diff --git a/build/cmake/contrib/gen_html/CMakeLists.txt b/build/cmake/contrib/gen_html/CMakeLists.txt index 958e9d173..c4062d475 100644 --- a/build/cmake/contrib/gen_html/CMakeLists.txt +++ b/build/cmake/contrib/gen_html/CMakeLists.txt @@ -27,4 +27,4 @@ ADD_CUSTOM_TARGET(zstd_manual.html ALL ${GENHTML_BINARY} "${LIBVERSION}" "${LIBRARY_DIR}/zstd.h" "${PROJECT_BINARY_DIR}/zstd_manual.html" DEPENDS gen_html COMMENT "Update zstd manual") -INSTALL(FILES "${PROJECT_BINARY_DIR}/zstd_manual.html" DESTINATION "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}") +INSTALL(FILES "${PROJECT_BINARY_DIR}/zstd_manual.html" DESTINATION "${CMAKE_INSTALL_DOCDIR}") diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index c4c2f81e6..ffc196ddc 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -14,7 +14,7 @@ OPTION(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON) OPTION(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" ON) IF(NOT ZSTD_BUILD_SHARED AND NOT ZSTD_BUILD_STATIC) - MESSAGE(SEND_ERROR "You need to build at least one flavor of libstd") + MESSAGE(SEND_ERROR "You need to build at least one flavor of libzstd") ENDIF() # Define library directory, where sources and header files are located @@ -47,6 +47,7 @@ SET(Sources ${LIBRARY_DIR}/decompress/huf_decompress.c ${LIBRARY_DIR}/decompress/zstd_decompress.c ${LIBRARY_DIR}/dictBuilder/cover.c + ${LIBRARY_DIR}/dictBuilder/fastcover.c ${LIBRARY_DIR}/dictBuilder/divsufsort.c ${LIBRARY_DIR}/dictBuilder/zdict.c ${LIBRARY_DIR}/deprecated/zbuff_common.c @@ -74,6 +75,7 @@ SET(Headers ${LIBRARY_DIR}/compress/zstd_ldm.h ${LIBRARY_DIR}/compress/zstdmt_compress.h ${LIBRARY_DIR}/dictBuilder/zdict.h + ${LIBRARY_DIR}/dictBuilder/cover.h ${LIBRARY_DIR}/deprecated/zbuff.h) IF (ZSTD_LEGACY_SUPPORT) @@ -178,6 +180,7 @@ INSTALL(FILES ${LIBRARY_DIR}/zstd.h ${LIBRARY_DIR}/deprecated/zbuff.h ${LIBRARY_DIR}/dictBuilder/zdict.h + ${LIBRARY_DIR}/dictBuilder/cover.h ${LIBRARY_DIR}/common/zstd_errors.h DESTINATION "include") diff --git a/circle.yml b/circle.yml deleted file mode 100644 index ed50d59e5..000000000 --- a/circle.yml +++ /dev/null @@ -1,63 +0,0 @@ -dependencies: - override: - - sudo dpkg --add-architecture i386 - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update - - sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - -test: - override: - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; CFLAGS="-O0 -Werror" make all && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c99build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu99build && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppc64build && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make cxxtest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi - : - parallel: true - - post: - - echo Circle CI tests finished - - # Longer tests - #- make -C tests test-zstd-nolegacy && make clean - #- pyenv global 3.4.4; make -C tests versionsTest && make clean - #- make zlibwrapper && make clean - #- gcc -v; make -C tests test32 MOREFLAGS="-I/usr/include/x86_64-linux-gnu" && make clean - #- make uasan && make clean - #- make asan32 && make clean - #- make -C tests test32 CC=clang MOREFLAGS="-g -fsanitize=address -I/usr/include/x86_64-linux-gnu" - # Valgrind tests - #- CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make clean - #- make -C tests valgrindTest && make clean - # ARM, AArch64, PowerPC, PowerPC64 tests - #- make ppctest && make clean - #- make ppc64test && make clean - #- make armtest && make clean - #- make aarch64test && make clean diff --git a/contrib/experimental_dict_builders/benchmarkDictBuilder/Makefile b/contrib/experimental_dict_builders/benchmarkDictBuilder/Makefile index 681494888..72ce04f2a 100644 --- a/contrib/experimental_dict_builders/benchmarkDictBuilder/Makefile +++ b/contrib/experimental_dict_builders/benchmarkDictBuilder/Makefile @@ -2,10 +2,9 @@ ARG := CC ?= gcc CFLAGS ?= -O3 -INCLUDES := -I ../randomDictBuilder -I ../fastCover -I ../../../programs -I ../../../lib/common -I ../../../lib -I ../../../lib/dictBuilder +INCLUDES := -I ../randomDictBuilder -I ../../../programs -I ../../../lib/common -I ../../../lib -I ../../../lib/dictBuilder RANDOM_FILE := ../randomDictBuilder/random.c -FAST_FILE := ../fastCover/fastCover.c IO_FILE := ../randomDictBuilder/io.c all: run clean @@ -22,8 +21,8 @@ test: benchmarkTest clean benchmarkTest: benchmark test.sh sh test.sh -benchmark: benchmark.o io.o random.o fastCover.o libzstd.a - $(CC) $(CFLAGS) benchmark.o io.o random.o fastCover.o libzstd.a -o benchmark +benchmark: benchmark.o io.o random.o libzstd.a + $(CC) $(CFLAGS) benchmark.o io.o random.o libzstd.a -o benchmark benchmark.o: benchmark.c $(CC) $(CFLAGS) $(INCLUDES) -c benchmark.c @@ -31,9 +30,6 @@ benchmark.o: benchmark.c random.o: $(RANDOM_FILE) $(CC) $(CFLAGS) $(INCLUDES) -c $(RANDOM_FILE) -fastCover.o: $(FAST_FILE) - $(CC) $(CFLAGS) $(INCLUDES) -c $(FAST_FILE) - io.o: $(IO_FILE) $(CC) $(CFLAGS) $(INCLUDES) -c $(IO_FILE) diff --git a/contrib/experimental_dict_builders/benchmarkDictBuilder/README.md b/contrib/experimental_dict_builders/benchmarkDictBuilder/README.md index 559776e2b..6a6c7f1d2 100644 --- a/contrib/experimental_dict_builders/benchmarkDictBuilder/README.md +++ b/contrib/experimental_dict_builders/benchmarkDictBuilder/README.md @@ -14,113 +14,836 @@ make ARG="in=../../../lib/dictBuilder in=../../../lib/compress" ###Benchmarking Result: - First Cover is optimize cover, second Cover uses optimized d and k from first one. -- For every f value of fastCover, the first one is optimize fastCover and the second one uses optimized d and k from first one. +- For every f value of fastCover, the first one is optimize fastCover and the second one uses optimized d and k from first one. This is run for accel values from 1 to 10. - Fourth column is chosen d and fifth column is chosen k github: -NODICT 0.000025 2.999642 -RANDOM 0.030101 8.791189 -LEGACY 0.913108 8.173529 -COVER 59.234160 10.652243 8 1298 -COVER 6.258459 10.652243 8 1298 -FAST15 9.959246 10.555630 8 1874 -FAST15 0.077719 10.555630 8 1874 -FAST16 10.028343 10.701698 8 1106 -FAST16 0.078117 10.701698 8 1106 -FAST17 10.567355 10.650652 8 1106 -FAST17 0.124833 10.650652 8 1106 -FAST18 11.795287 10.499142 8 1826 -FAST18 0.086992 10.499142 8 1826 -FAST19 13.132451 10.527140 8 1826 -FAST19 0.134716 10.527140 8 1826 -FAST20 14.366314 10.494710 8 1826 -FAST20 0.128844 10.494710 8 1826 -FAST21 14.941238 10.503488 8 1778 -FAST21 0.134975 10.503488 8 1778 -FAST22 15.146226 10.509284 8 1826 -FAST22 0.146918 10.509284 8 1826 -FAST23 16.260552 10.509284 8 1826 -FAST23 0.158494 10.509284 8 1826 -FAST24 16.806037 10.512369 8 1826 -FAST24 0.190464 10.512369 8 1826 +NODICT 0.000004 2.999642 +RANDOM 0.024560 8.791189 +LEGACY 0.727109 8.173529 +COVER 40.565676 10.652243 8 1298 +COVER 3.608284 10.652243 8 1298 +FAST f=15 a=1 4.181024 10.570882 8 1154 +FAST f=15 a=1 0.040788 10.570882 8 1154 +FAST f=15 a=2 3.548352 10.574287 6 1970 +FAST f=15 a=2 0.035535 10.574287 6 1970 +FAST f=15 a=3 3.287364 10.613950 6 1010 +FAST f=15 a=3 0.032182 10.613950 6 1010 +FAST f=15 a=4 3.184976 10.573883 6 1058 +FAST f=15 a=4 0.029878 10.573883 6 1058 +FAST f=15 a=5 3.045513 10.580640 8 1154 +FAST f=15 a=5 0.022162 10.580640 8 1154 +FAST f=15 a=6 3.003296 10.583677 6 1010 +FAST f=15 a=6 0.028091 10.583677 6 1010 +FAST f=15 a=7 2.952655 10.622551 6 1106 +FAST f=15 a=7 0.02724 10.622551 6 1106 +FAST f=15 a=8 2.945674 10.614657 6 1010 +FAST f=15 a=8 0.027264 10.614657 6 1010 +FAST f=15 a=9 3.153439 10.564018 8 1154 +FAST f=15 a=9 0.020635 10.564018 8 1154 +FAST f=15 a=10 2.950416 10.511454 6 1010 +FAST f=15 a=10 0.026606 10.511454 6 1010 +FAST f=16 a=1 3.970029 10.681035 8 1154 +FAST f=16 a=1 0.038188 10.681035 8 1154 +FAST f=16 a=2 3.422892 10.484978 6 1874 +FAST f=16 a=2 0.034702 10.484978 6 1874 +FAST f=16 a=3 3.215836 10.632631 8 1154 +FAST f=16 a=3 0.026084 10.632631 8 1154 +FAST f=16 a=4 3.081353 10.626533 6 1106 +FAST f=16 a=4 0.030032 10.626533 6 1106 +FAST f=16 a=5 3.041241 10.545027 8 1922 +FAST f=16 a=5 0.022882 10.545027 8 1922 +FAST f=16 a=6 2.989390 10.638284 6 1874 +FAST f=16 a=6 0.028308 10.638284 6 1874 +FAST f=16 a=7 3.001581 10.797136 6 1106 +FAST f=16 a=7 0.027479 10.797136 6 1106 +FAST f=16 a=8 2.984107 10.658356 8 1058 +FAST f=16 a=8 0.021099 10.658356 8 1058 +FAST f=16 a=9 2.925788 10.523869 6 1010 +FAST f=16 a=9 0.026905 10.523869 6 1010 +FAST f=16 a=10 2.889605 10.745841 6 1874 +FAST f=16 a=10 0.026846 10.745841 6 1874 +FAST f=17 a=1 4.031953 10.672080 8 1202 +FAST f=17 a=1 0.040658 10.672080 8 1202 +FAST f=17 a=2 3.458107 10.589352 8 1106 +FAST f=17 a=2 0.02926 10.589352 8 1106 +FAST f=17 a=3 3.291189 10.662714 8 1154 +FAST f=17 a=3 0.026531 10.662714 8 1154 +FAST f=17 a=4 3.154950 10.549456 8 1346 +FAST f=17 a=4 0.024991 10.549456 8 1346 +FAST f=17 a=5 3.092271 10.541670 6 1202 +FAST f=17 a=5 0.038285 10.541670 6 1202 +FAST f=17 a=6 3.166146 10.729112 6 1874 +FAST f=17 a=6 0.038217 10.729112 6 1874 +FAST f=17 a=7 3.035467 10.810485 6 1106 +FAST f=17 a=7 0.036655 10.810485 6 1106 +FAST f=17 a=8 3.035668 10.530532 6 1058 +FAST f=17 a=8 0.037715 10.530532 6 1058 +FAST f=17 a=9 2.987917 10.589802 8 1922 +FAST f=17 a=9 0.02217 10.589802 8 1922 +FAST f=17 a=10 2.981647 10.722579 8 1106 +FAST f=17 a=10 0.021948 10.722579 8 1106 +FAST f=18 a=1 4.067144 10.634943 8 1154 +FAST f=18 a=1 0.041386 10.634943 8 1154 +FAST f=18 a=2 3.507377 10.546230 6 1970 +FAST f=18 a=2 0.037572 10.546230 6 1970 +FAST f=18 a=3 3.323015 10.648061 8 1154 +FAST f=18 a=3 0.028306 10.648061 8 1154 +FAST f=18 a=4 3.216735 10.705402 6 1010 +FAST f=18 a=4 0.030755 10.705402 6 1010 +FAST f=18 a=5 3.175794 10.588154 8 1874 +FAST f=18 a=5 0.025315 10.588154 8 1874 +FAST f=18 a=6 3.127459 10.751104 8 1106 +FAST f=18 a=6 0.023897 10.751104 8 1106 +FAST f=18 a=7 3.083017 10.780402 6 1106 +FAST f=18 a=7 0.029158 10.780402 6 1106 +FAST f=18 a=8 3.069700 10.547226 8 1346 +FAST f=18 a=8 0.024046 10.547226 8 1346 +FAST f=18 a=9 3.056591 10.674759 6 1010 +FAST f=18 a=9 0.028496 10.674759 6 1010 +FAST f=18 a=10 3.063588 10.737578 8 1106 +FAST f=18 a=10 0.023033 10.737578 8 1106 +FAST f=19 a=1 4.164041 10.650333 8 1154 +FAST f=19 a=1 0.042906 10.650333 8 1154 +FAST f=19 a=2 3.585409 10.577066 6 1058 +FAST f=19 a=2 0.038994 10.577066 6 1058 +FAST f=19 a=3 3.439643 10.639403 8 1154 +FAST f=19 a=3 0.028427 10.639403 8 1154 +FAST f=19 a=4 3.268869 10.554410 8 1298 +FAST f=19 a=4 0.026866 10.554410 8 1298 +FAST f=19 a=5 3.238225 10.615109 6 1010 +FAST f=19 a=5 0.03078 10.615109 6 1010 +FAST f=19 a=6 3.199558 10.609782 6 1874 +FAST f=19 a=6 0.030099 10.609782 6 1874 +FAST f=19 a=7 3.132395 10.794753 6 1106 +FAST f=19 a=7 0.028964 10.794753 6 1106 +FAST f=19 a=8 3.148446 10.554842 8 1298 +FAST f=19 a=8 0.024277 10.554842 8 1298 +FAST f=19 a=9 3.108324 10.668763 6 1010 +FAST f=19 a=9 0.02896 10.668763 6 1010 +FAST f=19 a=10 3.159863 10.757347 8 1106 +FAST f=19 a=10 0.023351 10.757347 8 1106 +FAST f=20 a=1 4.462698 10.661788 8 1154 +FAST f=20 a=1 0.047174 10.661788 8 1154 +FAST f=20 a=2 3.820269 10.678612 6 1106 +FAST f=20 a=2 0.040807 10.678612 6 1106 +FAST f=20 a=3 3.644955 10.648424 8 1154 +FAST f=20 a=3 0.031398 10.648424 8 1154 +FAST f=20 a=4 3.546257 10.559756 8 1298 +FAST f=20 a=4 0.029856 10.559756 8 1298 +FAST f=20 a=5 3.485248 10.646637 6 1010 +FAST f=20 a=5 0.033756 10.646637 6 1010 +FAST f=20 a=6 3.490438 10.775824 8 1106 +FAST f=20 a=6 0.028338 10.775824 8 1106 +FAST f=20 a=7 3.631289 10.801795 6 1106 +FAST f=20 a=7 0.035228 10.801795 6 1106 +FAST f=20 a=8 3.758936 10.545116 8 1346 +FAST f=20 a=8 0.027495 10.545116 8 1346 +FAST f=20 a=9 3.707024 10.677454 6 1010 +FAST f=20 a=9 0.031326 10.677454 6 1010 +FAST f=20 a=10 3.586593 10.756017 8 1106 +FAST f=20 a=10 0.027122 10.756017 8 1106 +FAST f=21 a=1 5.701396 10.655398 8 1154 +FAST f=21 a=1 0.067744 10.655398 8 1154 +FAST f=21 a=2 5.270542 10.650743 6 1106 +FAST f=21 a=2 0.052999 10.650743 6 1106 +FAST f=21 a=3 4.945294 10.652380 8 1154 +FAST f=21 a=3 0.052678 10.652380 8 1154 +FAST f=21 a=4 4.894079 10.543185 8 1298 +FAST f=21 a=4 0.04997 10.543185 8 1298 +FAST f=21 a=5 4.785417 10.630321 6 1010 +FAST f=21 a=5 0.045294 10.630321 6 1010 +FAST f=21 a=6 4.789381 10.664477 6 1874 +FAST f=21 a=6 0.046578 10.664477 6 1874 +FAST f=21 a=7 4.302955 10.805179 6 1106 +FAST f=21 a=7 0.041205 10.805179 6 1106 +FAST f=21 a=8 4.034630 10.551211 8 1298 +FAST f=21 a=8 0.040121 10.551211 8 1298 +FAST f=21 a=9 4.523868 10.799114 6 1010 +FAST f=21 a=9 0.043592 10.799114 6 1010 +FAST f=21 a=10 4.760736 10.750255 8 1106 +FAST f=21 a=10 0.043483 10.750255 8 1106 +FAST f=22 a=1 6.743064 10.640537 8 1154 +FAST f=22 a=1 0.086967 10.640537 8 1154 +FAST f=22 a=2 6.121739 10.626638 6 1970 +FAST f=22 a=2 0.066337 10.626638 6 1970 +FAST f=22 a=3 5.248851 10.640688 8 1154 +FAST f=22 a=3 0.054935 10.640688 8 1154 +FAST f=22 a=4 5.436579 10.588333 8 1298 +FAST f=22 a=4 0.064113 10.588333 8 1298 +FAST f=22 a=5 5.812815 10.652653 6 1010 +FAST f=22 a=5 0.058189 10.652653 6 1010 +FAST f=22 a=6 5.745472 10.666437 6 1874 +FAST f=22 a=6 0.057188 10.666437 6 1874 +FAST f=22 a=7 5.716393 10.806911 6 1106 +FAST f=22 a=7 0.056 10.806911 6 1106 +FAST f=22 a=8 5.698799 10.530784 8 1298 +FAST f=22 a=8 0.0583 10.530784 8 1298 +FAST f=22 a=9 5.710533 10.777391 6 1010 +FAST f=22 a=9 0.054945 10.777391 6 1010 +FAST f=22 a=10 5.685395 10.745023 8 1106 +FAST f=22 a=10 0.056526 10.745023 8 1106 +FAST f=23 a=1 7.836923 10.638828 8 1154 +FAST f=23 a=1 0.099522 10.638828 8 1154 +FAST f=23 a=2 6.627834 10.631061 6 1970 +FAST f=23 a=2 0.066769 10.631061 6 1970 +FAST f=23 a=3 5.602533 10.647288 8 1154 +FAST f=23 a=3 0.064513 10.647288 8 1154 +FAST f=23 a=4 6.005580 10.568747 8 1298 +FAST f=23 a=4 0.062022 10.568747 8 1298 +FAST f=23 a=5 5.481816 10.676921 6 1010 +FAST f=23 a=5 0.058959 10.676921 6 1010 +FAST f=23 a=6 5.460444 10.666194 6 1874 +FAST f=23 a=6 0.057687 10.666194 6 1874 +FAST f=23 a=7 5.659822 10.800377 6 1106 +FAST f=23 a=7 0.06783 10.800377 6 1106 +FAST f=23 a=8 6.826940 10.522167 8 1298 +FAST f=23 a=8 0.070533 10.522167 8 1298 +FAST f=23 a=9 6.804757 10.577799 8 1682 +FAST f=23 a=9 0.069949 10.577799 8 1682 +FAST f=23 a=10 6.774933 10.742093 8 1106 +FAST f=23 a=10 0.068395 10.742093 8 1106 +FAST f=24 a=1 8.444110 10.632783 8 1154 +FAST f=24 a=1 0.094357 10.632783 8 1154 +FAST f=24 a=2 7.289578 10.631061 6 1970 +FAST f=24 a=2 0.098515 10.631061 6 1970 +FAST f=24 a=3 8.619780 10.646289 8 1154 +FAST f=24 a=3 0.098041 10.646289 8 1154 +FAST f=24 a=4 8.508455 10.555199 8 1298 +FAST f=24 a=4 0.093885 10.555199 8 1298 +FAST f=24 a=5 8.471145 10.674363 6 1010 +FAST f=24 a=5 0.088676 10.674363 6 1010 +FAST f=24 a=6 8.426727 10.667228 6 1874 +FAST f=24 a=6 0.087247 10.667228 6 1874 +FAST f=24 a=7 8.356826 10.803027 6 1106 +FAST f=24 a=7 0.085835 10.803027 6 1106 +FAST f=24 a=8 6.756811 10.522049 8 1298 +FAST f=24 a=8 0.07107 10.522049 8 1298 +FAST f=24 a=9 6.548169 10.571882 8 1682 +FAST f=24 a=9 0.0713 10.571882 8 1682 +FAST f=24 a=10 8.238079 10.736453 8 1106 +FAST f=24 a=10 0.07004 10.736453 8 1106 + hg-commands: -NODICT 0.000026 2.425291 -RANDOM 0.046270 3.490331 -LEGACY 0.847904 3.911682 -COVER 71.691804 4.132653 8 386 -COVER 3.187085 4.132653 8 386 -FAST15 11.593687 3.920720 6 1106 -FAST15 0.082431 3.920720 6 1106 -FAST16 11.775958 4.033306 8 674 -FAST16 0.092587 4.033306 8 674 -FAST17 11.965064 4.064132 8 1490 -FAST17 0.106382 4.064132 8 1490 -FAST18 11.438197 4.086714 8 290 -FAST18 0.097293 4.086714 8 290 -FAST19 12.292512 4.097947 8 578 -FAST19 0.104406 4.097947 8 578 -FAST20 13.857857 4.102851 8 434 -FAST20 0.139467 4.102851 8 434 -FAST21 14.599613 4.105350 8 530 -FAST21 0.189416 4.105350 8 530 -FAST22 15.966109 4.104100 8 530 -FAST22 0.183817 4.104100 8 530 -FAST23 18.033645 4.098110 8 914 -FAST23 0.246641 4.098110 8 914 -FAST24 22.992891 4.117367 8 722 -FAST24 0.285994 4.117367 8 722 +NODICT 0.000005 2.425276 +RANDOM 0.046332 3.490331 +LEGACY 0.720351 3.911682 +COVER 45.507731 4.132653 8 386 +COVER 1.868810 4.132653 8 386 +FAST f=15 a=1 4.561427 3.866894 8 1202 +FAST f=15 a=1 0.048946 3.866894 8 1202 +FAST f=15 a=2 3.574462 3.892119 8 1538 +FAST f=15 a=2 0.033677 3.892119 8 1538 +FAST f=15 a=3 3.230227 3.888791 6 1346 +FAST f=15 a=3 0.034312 3.888791 6 1346 +FAST f=15 a=4 3.042388 3.899739 8 1010 +FAST f=15 a=4 0.024307 3.899739 8 1010 +FAST f=15 a=5 2.800148 3.896220 8 818 +FAST f=15 a=5 0.022331 3.896220 8 818 +FAST f=15 a=6 2.706518 3.882039 8 578 +FAST f=15 a=6 0.020955 3.882039 8 578 +FAST f=15 a=7 2.701820 3.885430 6 866 +FAST f=15 a=7 0.026074 3.885430 6 866 +FAST f=15 a=8 2.604445 3.906932 8 1826 +FAST f=15 a=8 0.021789 3.906932 8 1826 +FAST f=15 a=9 2.598568 3.870324 6 1682 +FAST f=15 a=9 0.026004 3.870324 6 1682 +FAST f=15 a=10 2.575920 3.920783 8 1442 +FAST f=15 a=10 0.020228 3.920783 8 1442 +FAST f=16 a=1 4.630623 4.001430 8 770 +FAST f=16 a=1 0.047497 4.001430 8 770 +FAST f=16 a=2 3.674721 3.974431 8 1874 +FAST f=16 a=2 0.035761 3.974431 8 1874 +FAST f=16 a=3 3.338384 3.978703 8 1010 +FAST f=16 a=3 0.029436 3.978703 8 1010 +FAST f=16 a=4 3.004412 3.983035 8 1010 +FAST f=16 a=4 0.025744 3.983035 8 1010 +FAST f=16 a=5 2.881892 3.987710 8 770 +FAST f=16 a=5 0.023211 3.987710 8 770 +FAST f=16 a=6 2.807410 3.952717 8 1298 +FAST f=16 a=6 0.023199 3.952717 8 1298 +FAST f=16 a=7 2.819623 3.994627 8 770 +FAST f=16 a=7 0.021806 3.994627 8 770 +FAST f=16 a=8 2.740092 3.954032 8 1826 +FAST f=16 a=8 0.0226 3.954032 8 1826 +FAST f=16 a=9 2.682564 3.969879 6 1442 +FAST f=16 a=9 0.026324 3.969879 6 1442 +FAST f=16 a=10 2.657959 3.969755 8 674 +FAST f=16 a=10 0.020413 3.969755 8 674 +FAST f=17 a=1 4.729228 4.046000 8 530 +FAST f=17 a=1 0.049703 4.046000 8 530 +FAST f=17 a=2 3.764510 3.991519 8 1970 +FAST f=17 a=2 0.038195 3.991519 8 1970 +FAST f=17 a=3 3.416992 4.006296 6 914 +FAST f=17 a=3 0.036244 4.006296 6 914 +FAST f=17 a=4 3.145626 3.979182 8 1970 +FAST f=17 a=4 0.028676 3.979182 8 1970 +FAST f=17 a=5 2.995070 4.050070 8 770 +FAST f=17 a=5 0.025707 4.050070 8 770 +FAST f=17 a=6 2.911833 4.040024 8 770 +FAST f=17 a=6 0.02453 4.040024 8 770 +FAST f=17 a=7 2.894796 4.015884 8 818 +FAST f=17 a=7 0.023956 4.015884 8 818 +FAST f=17 a=8 2.789962 4.039303 8 530 +FAST f=17 a=8 0.023219 4.039303 8 530 +FAST f=17 a=9 2.787625 3.996762 8 1634 +FAST f=17 a=9 0.023651 3.996762 8 1634 +FAST f=17 a=10 2.754796 4.005059 8 1058 +FAST f=17 a=10 0.022537 4.005059 8 1058 +FAST f=18 a=1 4.779117 4.038214 8 242 +FAST f=18 a=1 0.048814 4.038214 8 242 +FAST f=18 a=2 3.829753 4.045768 8 722 +FAST f=18 a=2 0.036541 4.045768 8 722 +FAST f=18 a=3 3.495053 4.021497 8 770 +FAST f=18 a=3 0.032648 4.021497 8 770 +FAST f=18 a=4 3.221395 4.039623 8 770 +FAST f=18 a=4 0.027818 4.039623 8 770 +FAST f=18 a=5 3.059369 4.050414 8 530 +FAST f=18 a=5 0.026296 4.050414 8 530 +FAST f=18 a=6 3.019292 4.010714 6 962 +FAST f=18 a=6 0.031104 4.010714 6 962 +FAST f=18 a=7 2.949322 4.031439 6 770 +FAST f=18 a=7 0.030745 4.031439 6 770 +FAST f=18 a=8 2.876425 4.032088 6 386 +FAST f=18 a=8 0.027407 4.032088 6 386 +FAST f=18 a=9 2.850958 4.053372 8 674 +FAST f=18 a=9 0.023799 4.053372 8 674 +FAST f=18 a=10 2.884352 4.020148 8 1730 +FAST f=18 a=10 0.024401 4.020148 8 1730 +FAST f=19 a=1 4.815669 4.061203 8 674 +FAST f=19 a=1 0.051425 4.061203 8 674 +FAST f=19 a=2 3.951356 4.013822 8 1442 +FAST f=19 a=2 0.039968 4.013822 8 1442 +FAST f=19 a=3 3.554682 4.050425 8 722 +FAST f=19 a=3 0.032725 4.050425 8 722 +FAST f=19 a=4 3.242585 4.054677 8 722 +FAST f=19 a=4 0.028194 4.054677 8 722 +FAST f=19 a=5 3.105909 4.064524 8 818 +FAST f=19 a=5 0.02675 4.064524 8 818 +FAST f=19 a=6 3.059901 4.036857 8 1250 +FAST f=19 a=6 0.026396 4.036857 8 1250 +FAST f=19 a=7 3.016151 4.068234 6 770 +FAST f=19 a=7 0.031501 4.068234 6 770 +FAST f=19 a=8 2.962902 4.077509 8 530 +FAST f=19 a=8 0.023333 4.077509 8 530 +FAST f=19 a=9 2.899607 4.067328 8 530 +FAST f=19 a=9 0.024553 4.067328 8 530 +FAST f=19 a=10 2.950978 4.059901 8 434 +FAST f=19 a=10 0.023852 4.059901 8 434 +FAST f=20 a=1 5.259834 4.027579 8 1634 +FAST f=20 a=1 0.061123 4.027579 8 1634 +FAST f=20 a=2 4.382150 4.025093 8 1634 +FAST f=20 a=2 0.048009 4.025093 8 1634 +FAST f=20 a=3 4.104323 4.060842 8 530 +FAST f=20 a=3 0.040965 4.060842 8 530 +FAST f=20 a=4 3.853340 4.023504 6 914 +FAST f=20 a=4 0.041072 4.023504 6 914 +FAST f=20 a=5 3.728841 4.018089 6 1634 +FAST f=20 a=5 0.037469 4.018089 6 1634 +FAST f=20 a=6 3.683045 4.069138 8 578 +FAST f=20 a=6 0.028011 4.069138 8 578 +FAST f=20 a=7 3.726973 4.063160 8 722 +FAST f=20 a=7 0.028437 4.063160 8 722 +FAST f=20 a=8 3.555073 4.057690 8 386 +FAST f=20 a=8 0.027588 4.057690 8 386 +FAST f=20 a=9 3.551095 4.067253 8 482 +FAST f=20 a=9 0.025976 4.067253 8 482 +FAST f=20 a=10 3.490127 4.068518 8 530 +FAST f=20 a=10 0.025971 4.068518 8 530 +FAST f=21 a=1 7.343816 4.064945 8 770 +FAST f=21 a=1 0.085035 4.064945 8 770 +FAST f=21 a=2 5.930894 4.048206 8 386 +FAST f=21 a=2 0.067349 4.048206 8 386 +FAST f=21 a=3 6.770775 4.063417 8 578 +FAST f=21 a=3 0.077104 4.063417 8 578 +FAST f=21 a=4 6.889409 4.066761 8 626 +FAST f=21 a=4 0.0717 4.066761 8 626 +FAST f=21 a=5 6.714896 4.051813 8 914 +FAST f=21 a=5 0.071026 4.051813 8 914 +FAST f=21 a=6 6.539890 4.047263 8 1922 +FAST f=21 a=6 0.07127 4.047263 8 1922 +FAST f=21 a=7 6.511052 4.068373 8 482 +FAST f=21 a=7 0.065467 4.068373 8 482 +FAST f=21 a=8 6.458788 4.071597 8 482 +FAST f=21 a=8 0.063817 4.071597 8 482 +FAST f=21 a=9 6.377591 4.052905 8 434 +FAST f=21 a=9 0.063112 4.052905 8 434 +FAST f=21 a=10 6.360752 4.047773 8 530 +FAST f=21 a=10 0.063606 4.047773 8 530 +FAST f=22 a=1 10.523471 4.040812 8 962 +FAST f=22 a=1 0.14214 4.040812 8 962 +FAST f=22 a=2 9.454758 4.059396 8 914 +FAST f=22 a=2 0.118343 4.059396 8 914 +FAST f=22 a=3 9.043197 4.043019 8 1922 +FAST f=22 a=3 0.109798 4.043019 8 1922 +FAST f=22 a=4 8.716261 4.044819 8 770 +FAST f=22 a=4 0.099687 4.044819 8 770 +FAST f=22 a=5 8.529472 4.070576 8 530 +FAST f=22 a=5 0.093127 4.070576 8 530 +FAST f=22 a=6 8.424241 4.070565 8 722 +FAST f=22 a=6 0.093703 4.070565 8 722 +FAST f=22 a=7 8.403391 4.070591 8 578 +FAST f=22 a=7 0.089763 4.070591 8 578 +FAST f=22 a=8 8.285221 4.089171 8 530 +FAST f=22 a=8 0.087716 4.089171 8 530 +FAST f=22 a=9 8.282506 4.047470 8 722 +FAST f=22 a=9 0.089773 4.047470 8 722 +FAST f=22 a=10 8.241809 4.064151 8 818 +FAST f=22 a=10 0.090413 4.064151 8 818 +FAST f=23 a=1 12.389208 4.051635 6 530 +FAST f=23 a=1 0.147796 4.051635 6 530 +FAST f=23 a=2 11.300910 4.042835 6 914 +FAST f=23 a=2 0.133178 4.042835 6 914 +FAST f=23 a=3 10.879455 4.047415 8 626 +FAST f=23 a=3 0.129571 4.047415 8 626 +FAST f=23 a=4 10.522718 4.038269 6 914 +FAST f=23 a=4 0.118121 4.038269 6 914 +FAST f=23 a=5 10.348043 4.066884 8 434 +FAST f=23 a=5 0.112098 4.066884 8 434 +FAST f=23 a=6 10.238630 4.048635 8 1010 +FAST f=23 a=6 0.120281 4.048635 8 1010 +FAST f=23 a=7 10.213255 4.061809 8 530 +FAST f=23 a=7 0.1121 4.061809 8 530 +FAST f=23 a=8 10.107879 4.074104 8 818 +FAST f=23 a=8 0.116544 4.074104 8 818 +FAST f=23 a=9 10.063424 4.064811 8 674 +FAST f=23 a=9 0.109045 4.064811 8 674 +FAST f=23 a=10 10.035801 4.054918 8 530 +FAST f=23 a=10 0.108735 4.054918 8 530 +FAST f=24 a=1 14.963878 4.073490 8 722 +FAST f=24 a=1 0.206344 4.073490 8 722 +FAST f=24 a=2 13.833472 4.036100 8 962 +FAST f=24 a=2 0.17486 4.036100 8 962 +FAST f=24 a=3 13.404631 4.026281 6 1106 +FAST f=24 a=3 0.153961 4.026281 6 1106 +FAST f=24 a=4 13.041164 4.065448 8 674 +FAST f=24 a=4 0.155509 4.065448 8 674 +FAST f=24 a=5 12.879412 4.054636 8 674 +FAST f=24 a=5 0.148282 4.054636 8 674 +FAST f=24 a=6 12.773736 4.081376 8 530 +FAST f=24 a=6 0.142563 4.081376 8 530 +FAST f=24 a=7 12.711310 4.059834 8 770 +FAST f=24 a=7 0.149321 4.059834 8 770 +FAST f=24 a=8 12.635459 4.052050 8 1298 +FAST f=24 a=8 0.15095 4.052050 8 1298 +FAST f=24 a=9 12.558104 4.076516 8 722 +FAST f=24 a=9 0.144361 4.076516 8 722 +FAST f=24 a=10 10.661348 4.062137 8 818 +FAST f=24 a=10 0.108232 4.062137 8 818 + hg-changelog: -NODICT 0.000007 1.377613 -RANDOM 0.297345 2.097487 -LEGACY 2.633992 2.058907 -COVER 219.179786 2.189685 8 98 -COVER 6.620852 2.189685 8 98 -FAST15 47.635082 2.130794 6 386 -FAST15 0.321297 2.130794 6 386 -FAST16 43.837676 2.144845 8 194 -FAST16 0.312640 2.144845 8 194 -FAST17 49.349017 2.156099 8 242 -FAST17 0.348459 2.156099 8 242 -FAST18 51.153784 2.172439 6 98 -FAST18 0.353106 2.172439 6 98 -FAST19 52.627045 2.180321 6 98 -FAST19 0.390612 2.180321 6 98 -FAST20 63.748782 2.187431 6 98 -FAST20 0.489544 2.187431 6 98 -FAST21 68.709198 2.184185 6 146 -FAST21 0.530852 2.184185 6 146 -FAST22 68.491639 2.182830 6 98 -FAST22 0.645699 2.182830 6 98 -FAST23 72.558688 2.186399 8 98 -FAST23 0.593539 2.186399 8 98 -FAST24 76.137195 2.185608 6 98 -FAST24 0.680132 2.185608 6 98 +NODICT 0.000017 1.377590 +RANDOM 0.186171 2.097487 +LEGACY 1.670867 2.058907 +COVER 173.561948 2.189685 8 98 +COVER 4.811180 2.189685 8 98 +FAST f=15 a=1 18.685906 2.129682 8 434 +FAST f=15 a=1 0.173376 2.129682 8 434 +FAST f=15 a=2 12.928259 2.131890 8 482 +FAST f=15 a=2 0.102582 2.131890 8 482 +FAST f=15 a=3 11.132343 2.128027 8 386 +FAST f=15 a=3 0.077122 2.128027 8 386 +FAST f=15 a=4 10.120683 2.125797 8 434 +FAST f=15 a=4 0.065175 2.125797 8 434 +FAST f=15 a=5 9.479092 2.127697 8 386 +FAST f=15 a=5 0.057905 2.127697 8 386 +FAST f=15 a=6 9.159523 2.127132 8 1682 +FAST f=15 a=6 0.058604 2.127132 8 1682 +FAST f=15 a=7 8.724003 2.129914 8 434 +FAST f=15 a=7 0.0493 2.129914 8 434 +FAST f=15 a=8 8.595001 2.127137 8 338 +FAST f=15 a=8 0.0474 2.127137 8 338 +FAST f=15 a=9 8.356405 2.125512 8 482 +FAST f=15 a=9 0.046126 2.125512 8 482 +FAST f=15 a=10 8.207111 2.126066 8 338 +FAST f=15 a=10 0.043292 2.126066 8 338 +FAST f=16 a=1 18.464436 2.144040 8 242 +FAST f=16 a=1 0.172156 2.144040 8 242 +FAST f=16 a=2 12.844825 2.148171 8 194 +FAST f=16 a=2 0.099619 2.148171 8 194 +FAST f=16 a=3 11.082568 2.140837 8 290 +FAST f=16 a=3 0.079165 2.140837 8 290 +FAST f=16 a=4 10.066749 2.144405 8 386 +FAST f=16 a=4 0.068411 2.144405 8 386 +FAST f=16 a=5 9.501121 2.140720 8 386 +FAST f=16 a=5 0.061316 2.140720 8 386 +FAST f=16 a=6 9.179332 2.139478 8 386 +FAST f=16 a=6 0.056322 2.139478 8 386 +FAST f=16 a=7 8.849438 2.142412 8 194 +FAST f=16 a=7 0.050493 2.142412 8 194 +FAST f=16 a=8 8.810919 2.143454 8 434 +FAST f=16 a=8 0.051304 2.143454 8 434 +FAST f=16 a=9 8.553900 2.140339 8 194 +FAST f=16 a=9 0.047285 2.140339 8 194 +FAST f=16 a=10 8.398027 2.143130 8 386 +FAST f=16 a=10 0.046386 2.143130 8 386 +FAST f=17 a=1 18.644657 2.157192 8 98 +FAST f=17 a=1 0.173884 2.157192 8 98 +FAST f=17 a=2 13.071242 2.159830 8 146 +FAST f=17 a=2 0.10388 2.159830 8 146 +FAST f=17 a=3 11.332366 2.153654 6 194 +FAST f=17 a=3 0.08983 2.153654 6 194 +FAST f=17 a=4 10.362413 2.156813 8 242 +FAST f=17 a=4 0.070389 2.156813 8 242 +FAST f=17 a=5 9.808159 2.155098 6 338 +FAST f=17 a=5 0.072661 2.155098 6 338 +FAST f=17 a=6 9.451165 2.153845 6 146 +FAST f=17 a=6 0.064959 2.153845 6 146 +FAST f=17 a=7 9.163097 2.155424 6 242 +FAST f=17 a=7 0.064323 2.155424 6 242 +FAST f=17 a=8 9.047276 2.156640 8 242 +FAST f=17 a=8 0.053382 2.156640 8 242 +FAST f=17 a=9 8.807671 2.152396 8 146 +FAST f=17 a=9 0.049617 2.152396 8 146 +FAST f=17 a=10 8.649827 2.152370 8 146 +FAST f=17 a=10 0.047849 2.152370 8 146 +FAST f=18 a=1 18.809502 2.168116 8 98 +FAST f=18 a=1 0.175226 2.168116 8 98 +FAST f=18 a=2 13.756502 2.170870 6 242 +FAST f=18 a=2 0.119507 2.170870 6 242 +FAST f=18 a=3 12.059748 2.163094 6 98 +FAST f=18 a=3 0.093912 2.163094 6 98 +FAST f=18 a=4 11.410294 2.172372 8 98 +FAST f=18 a=4 0.073048 2.172372 8 98 +FAST f=18 a=5 10.560297 2.166388 8 98 +FAST f=18 a=5 0.065136 2.166388 8 98 +FAST f=18 a=6 10.071390 2.162672 8 98 +FAST f=18 a=6 0.059402 2.162672 8 98 +FAST f=18 a=7 10.084214 2.166624 6 194 +FAST f=18 a=7 0.073276 2.166624 6 194 +FAST f=18 a=8 9.953226 2.167454 8 98 +FAST f=18 a=8 0.053659 2.167454 8 98 +FAST f=18 a=9 8.982461 2.161593 6 146 +FAST f=18 a=9 0.05955 2.161593 6 146 +FAST f=18 a=10 8.986092 2.164373 6 242 +FAST f=18 a=10 0.059135 2.164373 6 242 +FAST f=19 a=1 18.908277 2.176021 8 98 +FAST f=19 a=1 0.177316 2.176021 8 98 +FAST f=19 a=2 13.471313 2.176103 8 98 +FAST f=19 a=2 0.106344 2.176103 8 98 +FAST f=19 a=3 11.571406 2.172812 8 98 +FAST f=19 a=3 0.083293 2.172812 8 98 +FAST f=19 a=4 10.632775 2.177770 6 146 +FAST f=19 a=4 0.079864 2.177770 6 146 +FAST f=19 a=5 10.030190 2.175574 6 146 +FAST f=19 a=5 0.07223 2.175574 6 146 +FAST f=19 a=6 9.717818 2.169997 8 98 +FAST f=19 a=6 0.060049 2.169997 8 98 +FAST f=19 a=7 9.397531 2.172770 8 146 +FAST f=19 a=7 0.057188 2.172770 8 146 +FAST f=19 a=8 9.281061 2.175822 8 98 +FAST f=19 a=8 0.053711 2.175822 8 98 +FAST f=19 a=9 9.165242 2.169849 6 146 +FAST f=19 a=9 0.059898 2.169849 6 146 +FAST f=19 a=10 9.048763 2.173394 8 98 +FAST f=19 a=10 0.049757 2.173394 8 98 +FAST f=20 a=1 21.166917 2.183923 6 98 +FAST f=20 a=1 0.205425 2.183923 6 98 +FAST f=20 a=2 15.642753 2.182349 6 98 +FAST f=20 a=2 0.135957 2.182349 6 98 +FAST f=20 a=3 14.053730 2.173544 6 98 +FAST f=20 a=3 0.11266 2.173544 6 98 +FAST f=20 a=4 15.270019 2.183656 8 98 +FAST f=20 a=4 0.107892 2.183656 8 98 +FAST f=20 a=5 15.497927 2.174661 6 98 +FAST f=20 a=5 0.100305 2.174661 6 98 +FAST f=20 a=6 13.973505 2.172391 8 98 +FAST f=20 a=6 0.087565 2.172391 8 98 +FAST f=20 a=7 14.083296 2.172443 8 98 +FAST f=20 a=7 0.078062 2.172443 8 98 +FAST f=20 a=8 12.560048 2.175581 8 98 +FAST f=20 a=8 0.070282 2.175581 8 98 +FAST f=20 a=9 13.078645 2.173975 6 146 +FAST f=20 a=9 0.081041 2.173975 6 146 +FAST f=20 a=10 12.823328 2.177778 8 98 +FAST f=20 a=10 0.074522 2.177778 8 98 +FAST f=21 a=1 29.825370 2.183057 6 98 +FAST f=21 a=1 0.334453 2.183057 6 98 +FAST f=21 a=2 29.476474 2.182752 8 98 +FAST f=21 a=2 0.286602 2.182752 8 98 +FAST f=21 a=3 25.937186 2.175867 8 98 +FAST f=21 a=3 0.17626 2.175867 8 98 +FAST f=21 a=4 20.413865 2.179780 8 98 +FAST f=21 a=4 0.206085 2.179780 8 98 +FAST f=21 a=5 20.541889 2.178328 6 146 +FAST f=21 a=5 0.199157 2.178328 6 146 +FAST f=21 a=6 21.090670 2.174443 6 146 +FAST f=21 a=6 0.190645 2.174443 6 146 +FAST f=21 a=7 20.221569 2.177384 6 146 +FAST f=21 a=7 0.184278 2.177384 6 146 +FAST f=21 a=8 20.322357 2.179456 6 98 +FAST f=21 a=8 0.178458 2.179456 6 98 +FAST f=21 a=9 20.683912 2.174396 6 146 +FAST f=21 a=9 0.190829 2.174396 6 146 +FAST f=21 a=10 20.840865 2.174905 8 98 +FAST f=21 a=10 0.172515 2.174905 8 98 +FAST f=22 a=1 36.822827 2.181612 6 98 +FAST f=22 a=1 0.437389 2.181612 6 98 +FAST f=22 a=2 30.616902 2.183142 8 98 +FAST f=22 a=2 0.324284 2.183142 8 98 +FAST f=22 a=3 28.472482 2.178130 8 98 +FAST f=22 a=3 0.236538 2.178130 8 98 +FAST f=22 a=4 25.847028 2.181878 8 98 +FAST f=22 a=4 0.263744 2.181878 8 98 +FAST f=22 a=5 27.095881 2.180775 8 98 +FAST f=22 a=5 0.24988 2.180775 8 98 +FAST f=22 a=6 25.939172 2.170916 8 98 +FAST f=22 a=6 0.240033 2.170916 8 98 +FAST f=22 a=7 27.064194 2.177849 8 98 +FAST f=22 a=7 0.242383 2.177849 8 98 +FAST f=22 a=8 25.140221 2.178216 8 98 +FAST f=22 a=8 0.237601 2.178216 8 98 +FAST f=22 a=9 25.505283 2.177455 6 146 +FAST f=22 a=9 0.223217 2.177455 6 146 +FAST f=22 a=10 24.529362 2.176705 6 98 +FAST f=22 a=10 0.222876 2.176705 6 98 +FAST f=23 a=1 39.127310 2.183006 6 98 +FAST f=23 a=1 0.417338 2.183006 6 98 +FAST f=23 a=2 32.468161 2.183524 6 98 +FAST f=23 a=2 0.351645 2.183524 6 98 +FAST f=23 a=3 31.577620 2.172604 6 98 +FAST f=23 a=3 0.319659 2.172604 6 98 +FAST f=23 a=4 30.129247 2.183932 6 98 +FAST f=23 a=4 0.307239 2.183932 6 98 +FAST f=23 a=5 29.103376 2.183529 6 146 +FAST f=23 a=5 0.285533 2.183529 6 146 +FAST f=23 a=6 29.776045 2.174367 8 98 +FAST f=23 a=6 0.276846 2.174367 8 98 +FAST f=23 a=7 28.940407 2.178022 6 146 +FAST f=23 a=7 0.274082 2.178022 6 146 +FAST f=23 a=8 29.256009 2.179462 6 98 +FAST f=23 a=8 0.26949 2.179462 6 98 +FAST f=23 a=9 29.347312 2.170407 8 98 +FAST f=23 a=9 0.265034 2.170407 8 98 +FAST f=23 a=10 29.140081 2.171762 8 98 +FAST f=23 a=10 0.259183 2.171762 8 98 +FAST f=24 a=1 44.871179 2.182115 6 98 +FAST f=24 a=1 0.509433 2.182115 6 98 +FAST f=24 a=2 38.694867 2.180549 8 98 +FAST f=24 a=2 0.406695 2.180549 8 98 +FAST f=24 a=3 38.363769 2.172821 8 98 +FAST f=24 a=3 0.359581 2.172821 8 98 +FAST f=24 a=4 36.580797 2.184142 8 98 +FAST f=24 a=4 0.340614 2.184142 8 98 +FAST f=24 a=5 33.125701 2.183301 8 98 +FAST f=24 a=5 0.324874 2.183301 8 98 +FAST f=24 a=6 34.776068 2.173019 6 146 +FAST f=24 a=6 0.340397 2.173019 6 146 +FAST f=24 a=7 34.417625 2.176561 6 146 +FAST f=24 a=7 0.308223 2.176561 6 146 +FAST f=24 a=8 35.470291 2.182161 6 98 +FAST f=24 a=8 0.307724 2.182161 6 98 +FAST f=24 a=9 34.927252 2.172682 6 146 +FAST f=24 a=9 0.300598 2.172682 6 146 +FAST f=24 a=10 33.238355 2.173395 6 98 +FAST f=24 a=10 0.249916 2.173395 6 98 + hg-manifest: -NODICT 0.000026 1.866385 -RANDOM 0.784554 2.309436 -LEGACY 10.193714 2.506977 -COVER 988.206583 2.582528 8 434 -COVER 39.726199 2.582528 8 434 -FAST15 168.388819 2.392920 6 1826 -FAST15 1.272178 2.392920 6 1826 -FAST16 161.822607 2.480762 6 1922 -FAST16 1.164908 2.480762 6 1922 -FAST17 157.688544 2.548285 6 1682 -FAST17 1.222439 2.548285 6 1682 -FAST18 154.529585 2.567634 6 386 -FAST18 1.217596 2.567634 6 386 -FAST19 160.244979 2.581653 8 338 -FAST19 1.282450 2.581653 8 338 -FAST20 191.503297 2.586881 8 194 -FAST20 2.009748 2.586881 8 194 -FAST21 226.389709 2.590051 6 242 -FAST21 2.494543 2.590051 6 242 -FAST22 217.859055 2.591376 6 194 -FAST22 2.295693 2.591376 6 194 -FAST23 236.819791 2.591131 8 434 -FAST23 2.744711 2.591131 8 434 -FAST24 269.187800 2.591548 6 290 -FAST24 2.923671 2.591548 6 290 +NODICT 0.000004 1.866377 +RANDOM 0.696346 2.309436 +LEGACY 7.064527 2.506977 +COVER 876.312865 2.582528 8 434 +COVER 35.684533 2.582528 8 434 +FAST f=15 a=1 76.618201 2.404013 8 1202 +FAST f=15 a=1 0.700722 2.404013 8 1202 +FAST f=15 a=2 49.213058 2.409248 6 1826 +FAST f=15 a=2 0.473393 2.409248 6 1826 +FAST f=15 a=3 41.753197 2.409677 8 1490 +FAST f=15 a=3 0.336848 2.409677 8 1490 +FAST f=15 a=4 38.648295 2.407996 8 1538 +FAST f=15 a=4 0.283952 2.407996 8 1538 +FAST f=15 a=5 36.144936 2.402895 8 1874 +FAST f=15 a=5 0.270128 2.402895 8 1874 +FAST f=15 a=6 35.484675 2.394873 8 1586 +FAST f=15 a=6 0.251637 2.394873 8 1586 +FAST f=15 a=7 34.280599 2.397311 8 1778 +FAST f=15 a=7 0.23984 2.397311 8 1778 +FAST f=15 a=8 32.122572 2.396089 6 1490 +FAST f=15 a=8 0.251508 2.396089 6 1490 +FAST f=15 a=9 29.909842 2.390092 6 1970 +FAST f=15 a=9 0.251233 2.390092 6 1970 +FAST f=15 a=10 30.102938 2.400086 6 1682 +FAST f=15 a=10 0.23688 2.400086 6 1682 +FAST f=16 a=1 67.750401 2.475460 6 1346 +FAST f=16 a=1 0.796035 2.475460 6 1346 +FAST f=16 a=2 52.812027 2.480860 6 1730 +FAST f=16 a=2 0.480384 2.480860 6 1730 +FAST f=16 a=3 44.179259 2.469304 8 1970 +FAST f=16 a=3 0.332657 2.469304 8 1970 +FAST f=16 a=4 37.612728 2.478208 6 1970 +FAST f=16 a=4 0.32498 2.478208 6 1970 +FAST f=16 a=5 35.056222 2.475568 6 1298 +FAST f=16 a=5 0.302824 2.475568 6 1298 +FAST f=16 a=6 34.713012 2.486079 8 1730 +FAST f=16 a=6 0.24755 2.486079 8 1730 +FAST f=16 a=7 33.713687 2.477180 6 1682 +FAST f=16 a=7 0.280358 2.477180 6 1682 +FAST f=16 a=8 31.571412 2.475418 8 1538 +FAST f=16 a=8 0.241241 2.475418 8 1538 +FAST f=16 a=9 31.608069 2.478263 8 1922 +FAST f=16 a=9 0.241764 2.478263 8 1922 +FAST f=16 a=10 31.358002 2.472263 8 1442 +FAST f=16 a=10 0.221661 2.472263 8 1442 +FAST f=17 a=1 66.185775 2.536085 6 1346 +FAST f=17 a=1 0.713549 2.536085 6 1346 +FAST f=17 a=2 50.365000 2.546105 8 1298 +FAST f=17 a=2 0.467846 2.546105 8 1298 +FAST f=17 a=3 42.712843 2.536250 8 1298 +FAST f=17 a=3 0.34047 2.536250 8 1298 +FAST f=17 a=4 39.514227 2.535555 8 1442 +FAST f=17 a=4 0.302989 2.535555 8 1442 +FAST f=17 a=5 35.189292 2.524925 8 1202 +FAST f=17 a=5 0.273451 2.524925 8 1202 +FAST f=17 a=6 35.791683 2.523466 8 1202 +FAST f=17 a=6 0.268261 2.523466 8 1202 +FAST f=17 a=7 37.416136 2.526625 6 1010 +FAST f=17 a=7 0.277558 2.526625 6 1010 +FAST f=17 a=8 37.084707 2.533274 6 1250 +FAST f=17 a=8 0.285104 2.533274 6 1250 +FAST f=17 a=9 34.183814 2.532765 8 1298 +FAST f=17 a=9 0.235133 2.532765 8 1298 +FAST f=17 a=10 31.149235 2.528722 8 1346 +FAST f=17 a=10 0.232679 2.528722 8 1346 +FAST f=18 a=1 72.942176 2.559857 6 386 +FAST f=18 a=1 0.718618 2.559857 6 386 +FAST f=18 a=2 51.690440 2.559572 8 290 +FAST f=18 a=2 0.403978 2.559572 8 290 +FAST f=18 a=3 45.344908 2.561040 8 962 +FAST f=18 a=3 0.357205 2.561040 8 962 +FAST f=18 a=4 39.804522 2.558446 8 1010 +FAST f=18 a=4 0.310526 2.558446 8 1010 +FAST f=18 a=5 38.134888 2.561811 8 626 +FAST f=18 a=5 0.273743 2.561811 8 626 +FAST f=18 a=6 35.091890 2.555518 8 722 +FAST f=18 a=6 0.260135 2.555518 8 722 +FAST f=18 a=7 34.639523 2.562938 8 290 +FAST f=18 a=7 0.234294 2.562938 8 290 +FAST f=18 a=8 36.076431 2.563567 8 1586 +FAST f=18 a=8 0.274075 2.563567 8 1586 +FAST f=18 a=9 36.376433 2.560950 8 722 +FAST f=18 a=9 0.240106 2.560950 8 722 +FAST f=18 a=10 32.624790 2.559340 8 578 +FAST f=18 a=10 0.234704 2.559340 8 578 +FAST f=19 a=1 70.513761 2.572441 8 194 +FAST f=19 a=1 0.726112 2.572441 8 194 +FAST f=19 a=2 59.263032 2.574560 8 482 +FAST f=19 a=2 0.451554 2.574560 8 482 +FAST f=19 a=3 51.509594 2.571546 6 194 +FAST f=19 a=3 0.393014 2.571546 6 194 +FAST f=19 a=4 55.393906 2.573386 8 482 +FAST f=19 a=4 0.38819 2.573386 8 482 +FAST f=19 a=5 43.201736 2.567589 8 674 +FAST f=19 a=5 0.292155 2.567589 8 674 +FAST f=19 a=6 42.911687 2.572666 6 434 +FAST f=19 a=6 0.303988 2.572666 6 434 +FAST f=19 a=7 44.687591 2.573613 6 290 +FAST f=19 a=7 0.308721 2.573613 6 290 +FAST f=19 a=8 37.372868 2.571039 6 194 +FAST f=19 a=8 0.287137 2.571039 6 194 +FAST f=19 a=9 36.074230 2.566473 6 482 +FAST f=19 a=9 0.280721 2.566473 6 482 +FAST f=19 a=10 33.731720 2.570306 8 194 +FAST f=19 a=10 0.224073 2.570306 8 194 +FAST f=20 a=1 79.670634 2.581146 6 290 +FAST f=20 a=1 0.899986 2.581146 6 290 +FAST f=20 a=2 58.827141 2.579782 8 386 +FAST f=20 a=2 0.602288 2.579782 8 386 +FAST f=20 a=3 51.289004 2.579627 8 722 +FAST f=20 a=3 0.446091 2.579627 8 722 +FAST f=20 a=4 47.711068 2.581508 8 722 +FAST f=20 a=4 0.473007 2.581508 8 722 +FAST f=20 a=5 47.402929 2.578062 6 434 +FAST f=20 a=5 0.497131 2.578062 6 434 +FAST f=20 a=6 54.797102 2.577365 8 482 +FAST f=20 a=6 0.515061 2.577365 8 482 +FAST f=20 a=7 51.370877 2.583050 8 386 +FAST f=20 a=7 0.402878 2.583050 8 386 +FAST f=20 a=8 51.437931 2.574875 6 242 +FAST f=20 a=8 0.453094 2.574875 6 242 +FAST f=20 a=9 44.105456 2.576700 6 242 +FAST f=20 a=9 0.456633 2.576700 6 242 +FAST f=20 a=10 44.447580 2.578305 8 338 +FAST f=20 a=10 0.409121 2.578305 8 338 +FAST f=21 a=1 113.031686 2.582449 6 242 +FAST f=21 a=1 1.456971 2.582449 6 242 +FAST f=21 a=2 97.700932 2.582124 8 194 +FAST f=21 a=2 1.072078 2.582124 8 194 +FAST f=21 a=3 96.563648 2.585479 8 434 +FAST f=21 a=3 0.949528 2.585479 8 434 +FAST f=21 a=4 90.597813 2.582366 6 386 +FAST f=21 a=4 0.76944 2.582366 6 386 +FAST f=21 a=5 86.815980 2.579043 8 434 +FAST f=21 a=5 0.858167 2.579043 8 434 +FAST f=21 a=6 91.235820 2.578378 8 530 +FAST f=21 a=6 0.684274 2.578378 8 530 +FAST f=21 a=7 84.392788 2.581243 8 386 +FAST f=21 a=7 0.814386 2.581243 8 386 +FAST f=21 a=8 82.052310 2.582547 8 338 +FAST f=21 a=8 0.822633 2.582547 8 338 +FAST f=21 a=9 74.696074 2.579319 8 194 +FAST f=21 a=9 0.811028 2.579319 8 194 +FAST f=21 a=10 76.211170 2.578766 8 290 +FAST f=21 a=10 0.809715 2.578766 8 290 +FAST f=22 a=1 138.976871 2.580478 8 194 +FAST f=22 a=1 1.748932 2.580478 8 194 +FAST f=22 a=2 120.164097 2.583633 8 386 +FAST f=22 a=2 1.333239 2.583633 8 386 +FAST f=22 a=3 111.986474 2.582566 6 194 +FAST f=22 a=3 1.305734 2.582566 6 194 +FAST f=22 a=4 108.548148 2.583068 6 194 +FAST f=22 a=4 1.314026 2.583068 6 194 +FAST f=22 a=5 103.173017 2.583495 6 290 +FAST f=22 a=5 1.228664 2.583495 6 290 +FAST f=22 a=6 108.421262 2.582349 8 530 +FAST f=22 a=6 1.076773 2.582349 8 530 +FAST f=22 a=7 103.284127 2.581022 8 386 +FAST f=22 a=7 1.112117 2.581022 8 386 +FAST f=22 a=8 96.330279 2.581073 8 290 +FAST f=22 a=8 1.109303 2.581073 8 290 +FAST f=22 a=9 97.651348 2.580075 6 194 +FAST f=22 a=9 0.933032 2.580075 6 194 +FAST f=22 a=10 101.660621 2.584886 8 194 +FAST f=22 a=10 0.796823 2.584886 8 194 +FAST f=23 a=1 159.322978 2.581474 6 242 +FAST f=23 a=1 2.015878 2.581474 6 242 +FAST f=23 a=2 134.331775 2.581619 8 194 +FAST f=23 a=2 1.545845 2.581619 8 194 +FAST f=23 a=3 127.724552 2.579888 6 338 +FAST f=23 a=3 1.444496 2.579888 6 338 +FAST f=23 a=4 126.077675 2.578137 6 242 +FAST f=23 a=4 1.364394 2.578137 6 242 +FAST f=23 a=5 124.914027 2.580843 8 338 +FAST f=23 a=5 1.116059 2.580843 8 338 +FAST f=23 a=6 122.874153 2.577637 6 338 +FAST f=23 a=6 1.164584 2.577637 6 338 +FAST f=23 a=7 123.099257 2.582715 6 386 +FAST f=23 a=7 1.354042 2.582715 6 386 +FAST f=23 a=8 122.026753 2.577681 8 194 +FAST f=23 a=8 1.210966 2.577681 8 194 +FAST f=23 a=9 121.164312 2.584599 6 290 +FAST f=23 a=9 1.174859 2.584599 6 290 +FAST f=23 a=10 117.462222 2.580358 8 194 +FAST f=23 a=10 1.075258 2.580358 8 194 +FAST f=24 a=1 169.539659 2.581642 6 194 +FAST f=24 a=1 1.916804 2.581642 6 194 +FAST f=24 a=2 160.539270 2.580421 6 290 +FAST f=24 a=2 1.71087 2.580421 6 290 +FAST f=24 a=3 155.455874 2.580449 6 242 +FAST f=24 a=3 1.60307 2.580449 6 242 +FAST f=24 a=4 147.630320 2.582953 6 338 +FAST f=24 a=4 1.396364 2.582953 6 338 +FAST f=24 a=5 133.767428 2.580589 6 290 +FAST f=24 a=5 1.19933 2.580589 6 290 +FAST f=24 a=6 146.437535 2.579453 8 194 +FAST f=24 a=6 1.385405 2.579453 8 194 +FAST f=24 a=7 147.227507 2.584155 8 386 +FAST f=24 a=7 1.48942 2.584155 8 386 +FAST f=24 a=8 138.005773 2.584115 8 194 +FAST f=24 a=8 1.352 2.584115 8 194 +FAST f=24 a=9 141.442625 2.582902 8 290 +FAST f=24 a=9 1.39647 2.582902 8 290 +FAST f=24 a=10 142.157446 2.582701 8 434 +FAST f=24 a=10 1.498889 2.582701 8 434 diff --git a/contrib/experimental_dict_builders/benchmarkDictBuilder/benchmark.c b/contrib/experimental_dict_builders/benchmarkDictBuilder/benchmark.c index 7dade2da3..b19345692 100644 --- a/contrib/experimental_dict_builders/benchmarkDictBuilder/benchmark.c +++ b/contrib/experimental_dict_builders/benchmarkDictBuilder/benchmark.c @@ -5,7 +5,6 @@ #include #include #include "random.h" -#include "fastCover.h" #include "dictBuilder.h" #include "zstd_internal.h" /* includes zstd.h */ #include "io.h" @@ -149,7 +148,7 @@ double compressWithDict(sampleInfo *srcInfo, dictInfo* dInfo, int compressionLev /* Allocate dst with enough space to compress the maximum sized sample */ { size_t maxSampleSize = 0; - for (int i = 0; i < srcInfo->nbSamples; i++) { + for (i = 0; i < srcInfo->nbSamples; i++) { maxSampleSize = MAX(srcInfo->samplesSizes[i], maxSampleSize); } dstCapacity = ZSTD_compressBound(maxSampleSize); @@ -291,6 +290,9 @@ int main(int argCount, const char* argv[]) /* Initialize arguments to default values */ unsigned k = 200; unsigned d = 8; + unsigned f; + unsigned accel; + unsigned i; const unsigned cLevel = DEFAULT_CLEVEL; const unsigned dictID = 0; const unsigned maxDictSize = g_defaultMaxDictSize; @@ -305,7 +307,7 @@ int main(int argCount, const char* argv[]) const char** extendedFileList = NULL; /* Parse arguments */ - for (int i = 1; i < argCount; i++) { + for (i = 1; i < argCount; i++) { const char* argument = argv[i]; if (longCommandWArg(&argument, "in=")) { filenameTable[filenameIdx] = argument; @@ -375,6 +377,7 @@ int main(int argCount, const char* argv[]) /* for cover */ { + /* for cover (optimizing k and d) */ ZDICT_cover_params_t coverParam; memset(&coverParam, 0, sizeof(coverParam)); coverParam.zParams = zParams; @@ -388,6 +391,7 @@ int main(int argCount, const char* argv[]) goto _cleanup; } + /* for cover (with k and d provided) */ const int coverResult = benchmarkDictBuilder(srcInfo, maxDictSize, NULL, &coverParam, NULL, NULL); DISPLAYLEVEL(2, "k=%u\nd=%u\nsteps=%u\nsplit=%u\n", coverParam.k, coverParam.d, coverParam.steps, (unsigned)(coverParam.splitPoint * 100)); if(coverResult) { @@ -398,31 +402,34 @@ int main(int argCount, const char* argv[]) } /* for fastCover */ - for (unsigned f = 15; f < 25; f++){ + for (f = 15; f < 25; f++){ DISPLAYLEVEL(2, "current f is %u\n", f); - /* for fastCover (optimizing k and d) */ - ZDICT_fastCover_params_t fastParam; - memset(&fastParam, 0, sizeof(fastParam)); - fastParam.zParams = zParams; - fastParam.splitPoint = 1.0; - fastParam.f = f; - fastParam.steps = 40; - fastParam.nbThreads = 1; - const int fastOptResult = benchmarkDictBuilder(srcInfo, maxDictSize, NULL, NULL, NULL, &fastParam); - DISPLAYLEVEL(2, "k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\n", fastParam.k, fastParam.d, fastParam.f, fastParam.steps, (unsigned)(fastParam.splitPoint * 100)); - if(fastOptResult) { - result = 1; - goto _cleanup; - } - - /* for fastCover (with k and d provided) */ - for (int i = 0; i < 5; i++) { - const int fastResult = benchmarkDictBuilder(srcInfo, maxDictSize, NULL, NULL, NULL, &fastParam); - DISPLAYLEVEL(2, "k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\n", fastParam.k, fastParam.d, fastParam.f, fastParam.steps, (unsigned)(fastParam.splitPoint * 100)); - if(fastResult) { + for (accel = 1; accel < 11; accel++) { + DISPLAYLEVEL(2, "current accel is %u\n", accel); + /* for fastCover (optimizing k and d) */ + ZDICT_fastCover_params_t fastParam; + memset(&fastParam, 0, sizeof(fastParam)); + fastParam.zParams = zParams; + fastParam.f = f; + fastParam.steps = 40; + fastParam.nbThreads = 1; + fastParam.accel = accel; + const int fastOptResult = benchmarkDictBuilder(srcInfo, maxDictSize, NULL, NULL, NULL, &fastParam); + DISPLAYLEVEL(2, "k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\naccel=%u\n", fastParam.k, fastParam.d, fastParam.f, fastParam.steps, (unsigned)(fastParam.splitPoint * 100), fastParam.accel); + if(fastOptResult) { result = 1; goto _cleanup; } + + /* for fastCover (with k and d provided) */ + for (i = 0; i < 5; i++) { + const int fastResult = benchmarkDictBuilder(srcInfo, maxDictSize, NULL, NULL, NULL, &fastParam); + DISPLAYLEVEL(2, "k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\naccel=%u\n", fastParam.k, fastParam.d, fastParam.f, fastParam.steps, (unsigned)(fastParam.splitPoint * 100), fastParam.accel); + if(fastResult) { + result = 1; + goto _cleanup; + } + } } } diff --git a/contrib/seekable_format/examples/seekable_compression.c b/contrib/seekable_format/examples/seekable_compression.c index 9485bf26f..9a331a895 100644 --- a/contrib/seekable_format/examples/seekable_compression.c +++ b/contrib/seekable_format/examples/seekable_compression.c @@ -101,7 +101,7 @@ static void compressFile_orDie(const char* fname, const char* outName, int cLeve free(buffOut); } -static const char* createOutFilename_orDie(const char* filename) +static char* createOutFilename_orDie(const char* filename) { size_t const inL = strlen(filename); size_t const outL = inL + 5; @@ -109,7 +109,7 @@ static const char* createOutFilename_orDie(const char* filename) memset(outSpace, 0, outL); strcat(outSpace, filename); strcat(outSpace, ".zst"); - return (const char*)outSpace; + return (char*)outSpace; } int main(int argc, const char** argv) { @@ -124,8 +124,9 @@ int main(int argc, const char** argv) { { const char* const inFileName = argv[1]; unsigned const frameSize = (unsigned)atoi(argv[2]); - const char* const outFileName = createOutFilename_orDie(inFileName); + char* const outFileName = createOutFilename_orDie(inFileName); compressFile_orDie(inFileName, outFileName, 5, frameSize); + free(outFileName); } return 0; diff --git a/contrib/seekable_format/zstdseek_decompress.c b/contrib/seekable_format/zstdseek_decompress.c index b006ff834..2b109b9d0 100644 --- a/contrib/seekable_format/zstdseek_decompress.c +++ b/contrib/seekable_format/zstdseek_decompress.c @@ -56,6 +56,7 @@ #include /* malloc, free */ #include /* FILE* */ +#include #define XXH_STATIC_LINKING_ONLY #define XXH_NAMESPACE ZSTD_ @@ -112,7 +113,7 @@ static int ZSTD_seekable_read_buff(void* opaque, void* buffer, size_t n) static int ZSTD_seekable_seek_buff(void* opaque, long long offset, int origin) { - buffWrapper_t* buff = (buffWrapper_t*) opaque; + buffWrapper_t* const buff = (buffWrapper_t*) opaque; unsigned long long newOffset; switch (origin) { case SEEK_SET: @@ -124,6 +125,8 @@ static int ZSTD_seekable_seek_buff(void* opaque, long long offset, int origin) case SEEK_END: newOffset = (unsigned long long)buff->size - offset; break; + default: + assert(0); /* not possible */ } if (newOffset > buff->size) { return -1; diff --git a/examples/multiple_streaming_compression.c b/examples/multiple_streaming_compression.c index e395aefbc..4308a2e4d 100644 --- a/examples/multiple_streaming_compression.c +++ b/examples/multiple_streaming_compression.c @@ -158,7 +158,8 @@ int main(int argc, const char** argv) } freeResources(ress); - /* success */ + free(ofnBuffer); + printf("compressed %i files \n", argc-1); return 0; diff --git a/examples/streaming_compression.c b/examples/streaming_compression.c index f76364d84..9287ff398 100644 --- a/examples/streaming_compression.c +++ b/examples/streaming_compression.c @@ -73,7 +73,11 @@ static void compressFile_orDie(const char* fname, const char* outName, int cLeve ZSTD_CStream* const cstream = ZSTD_createCStream(); if (cstream==NULL) { fprintf(stderr, "ZSTD_createCStream() error \n"); exit(10); } size_t const initResult = ZSTD_initCStream(cstream, cLevel); - if (ZSTD_isError(initResult)) { fprintf(stderr, "ZSTD_initCStream() error : %s \n", ZSTD_getErrorName(initResult)); exit(11); } + if (ZSTD_isError(initResult)) { + fprintf(stderr, "ZSTD_initCStream() error : %s \n", + ZSTD_getErrorName(initResult)); + exit(11); + } size_t read, toRead = buffInSize; while( (read = fread_orDie(buffIn, toRead, fin)) ) { @@ -81,7 +85,11 @@ static void compressFile_orDie(const char* fname, const char* outName, int cLeve while (input.pos < input.size) { ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; toRead = ZSTD_compressStream(cstream, &output , &input); /* toRead is guaranteed to be <= ZSTD_CStreamInSize() */ - if (ZSTD_isError(toRead)) { fprintf(stderr, "ZSTD_compressStream() error : %s \n", ZSTD_getErrorName(toRead)); exit(12); } + if (ZSTD_isError(toRead)) { + fprintf(stderr, "ZSTD_compressStream() error : %s \n", + ZSTD_getErrorName(toRead)); + exit(12); + } if (toRead > buffInSize) toRead = buffInSize; /* Safely handle case when `buffInSize` is manually changed to a value < ZSTD_CStreamInSize()*/ fwrite_orDie(buffOut, output.pos, fout); } @@ -100,15 +108,15 @@ static void compressFile_orDie(const char* fname, const char* outName, int cLeve } -static const char* createOutFilename_orDie(const char* filename) +static char* createOutFilename_orDie(const char* filename) { size_t const inL = strlen(filename); size_t const outL = inL + 5; - void* outSpace = malloc_orDie(outL); + void* const outSpace = malloc_orDie(outL); memset(outSpace, 0, outL); strcat(outSpace, filename); strcat(outSpace, ".zst"); - return (const char*)outSpace; + return (char*)outSpace; } int main(int argc, const char** argv) @@ -124,8 +132,10 @@ int main(int argc, const char** argv) const char* const inFilename = argv[1]; - const char* const outFilename = createOutFilename_orDie(inFilename); + char* const outFilename = createOutFilename_orDie(inFilename); compressFile_orDie(inFilename, outFilename, 1); + free(outFilename); /* not strictly required, since program execution stops there, + * but some static analyzer main complain otherwise */ return 0; } diff --git a/lib/BUCK b/lib/BUCK index dbe8885fc..bd93b082a 100644 --- a/lib/BUCK +++ b/lib/BUCK @@ -69,6 +69,7 @@ cxx_library( ]), headers=subdir_glob([ ('dictBuilder', 'divsufsort.h'), + ('dictBuilder', 'cover.h'), ]), srcs=glob(['dictBuilder/*.c']), deps=[':common'], diff --git a/lib/Makefile b/lib/Makefile index 9cedd53b7..01689c6d5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,6 +19,9 @@ LIBVER := $(shell echo $(LIBVER_SCRIPT)) VERSION?= $(LIBVER) CPPFLAGS+= -I. -I./common -DXXH_NAMESPACE=ZSTD_ +ifeq ($(OS),Windows_NT) # MinGW assumed +CPPFLAGS += -D__USE_MINGW_ANSI_STDIO # compatibility with %zu formatting +endif CFLAGS ?= -O3 DEBUGFLAGS = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ @@ -52,11 +55,11 @@ ifeq ($(ZSTD_LIB_DECOMPRESSION), 0) endif ifneq ($(ZSTD_LIB_COMPRESSION), 0) - ZSTD_FILES += $(ZSTDCOMP_FILES) + ZSTD_FILES += $(ZSTDCOMP_FILES) endif ifneq ($(ZSTD_LIB_DECOMPRESSION), 0) - ZSTD_FILES += $(ZSTDDECOMP_FILES) + ZSTD_FILES += $(ZSTDDECOMP_FILES) endif ifneq ($(ZSTD_LIB_DEPRECATED), 0) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index b4c1af53f..b36a2fbd0 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -79,8 +79,7 @@ static const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 }; static const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 }; static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 }; -#define ZSTD_FRAMEIDSIZE 4 -static const size_t ZSTD_frameIdSize = ZSTD_FRAMEIDSIZE; /* magic number size */ +#define ZSTD_FRAMEIDSIZE 4 /* magic number size */ #define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 07b3ab89b..4075db217 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -83,7 +83,9 @@ * wkspSize should be sized to handle worst case situation, which is `1< wkspSize) return ERROR(tableLog_tooLarge); tableU16[-2] = (U16) tableLog; tableU16[-1] = (U16) maxSymbolValue; - assert(tableLog < 16); /* required for the threshold strategy to work */ + assert(tableLog < 16); /* required for threshold strategy to work */ /* For explanations on how to distribute symbol values over the table : - * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ + * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ + + #ifdef __clang_analyzer__ + memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize); /* useless initialization, just to keep scan-build happy */ + #endif /* symbol start positions */ { U32 u; @@ -124,13 +130,15 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsi U32 symbol; for (symbol=0; symbol<=maxSymbolValue; symbol++) { int nbOccurences; - for (nbOccurences=0; nbOccurences highThreshold) position = (position + step) & tableMask; /* Low proba area */ + while (position > highThreshold) + position = (position + step) & tableMask; /* Low proba area */ } } - if (position!=0) return ERROR(GENERIC); /* Must have gone through all positions */ + assert(position==0); /* Must have initialized all positions */ } /* Build table */ @@ -201,9 +209,10 @@ size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog) return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */ } -static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, - const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, - unsigned writeIsSafe) +static size_t +FSE_writeNCount_generic (void* header, size_t headerBufferSize, + const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, + unsigned writeIsSafe) { BYTE* const ostart = (BYTE*) header; BYTE* out = ostart; @@ -212,13 +221,12 @@ static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, const int tableSize = 1 << tableLog; int remaining; int threshold; - U32 bitStream; - int bitCount; - unsigned charnum = 0; - int previous0 = 0; + U32 bitStream = 0; + int bitCount = 0; + unsigned symbol = 0; + unsigned const alphabetSize = maxSymbolValue + 1; + int previousIs0 = 0; - bitStream = 0; - bitCount = 0; /* Table Size */ bitStream += (tableLog-FSE_MIN_TABLELOG) << bitCount; bitCount += 4; @@ -228,48 +236,53 @@ static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, threshold = tableSize; nbBits = tableLog+1; - while (remaining>1) { /* stops at 1 */ - if (previous0) { - unsigned start = charnum; - while (!normalizedCounter[charnum]) charnum++; - while (charnum >= start+24) { + while ((symbol < alphabetSize) && (remaining>1)) { /* stops at 1 */ + if (previousIs0) { + unsigned start = symbol; + while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++; + if (symbol == alphabetSize) break; /* incorrect distribution */ + while (symbol >= start+24) { start+=24; bitStream += 0xFFFFU << bitCount; - if ((!writeIsSafe) && (out > oend-2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ + if ((!writeIsSafe) && (out > oend-2)) + return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE) bitStream; out[1] = (BYTE)(bitStream>>8); out+=2; bitStream>>=16; } - while (charnum >= start+3) { + while (symbol >= start+3) { start+=3; bitStream += 3 << bitCount; bitCount += 2; } - bitStream += (charnum-start) << bitCount; + bitStream += (symbol-start) << bitCount; bitCount += 2; if (bitCount>16) { - if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ + if ((!writeIsSafe) && (out > oend - 2)) + return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out += 2; bitStream >>= 16; bitCount -= 16; } } - { int count = normalizedCounter[charnum++]; - int const max = (2*threshold-1)-remaining; + { int count = normalizedCounter[symbol++]; + int const max = (2*threshold-1) - remaining; remaining -= count < 0 ? -count : count; count++; /* +1 for extra accuracy */ - if (count>=threshold) count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */ + if (count>=threshold) + count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */ bitStream += count << bitCount; bitCount += nbBits; bitCount -= (count>=1; } } if (bitCount>16) { - if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ + if ((!writeIsSafe) && (out > oend - 2)) + return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out += 2; @@ -277,19 +290,23 @@ static size_t FSE_writeNCount_generic (void* header, size_t headerBufferSize, bitCount -= 16; } } + if (remaining != 1) + return ERROR(GENERIC); /* incorrect normalized distribution */ + assert(symbol <= alphabetSize); + /* flush remaining bitStream */ - if ((!writeIsSafe) && (out > oend - 2)) return ERROR(dstSize_tooSmall); /* Buffer overflow */ + if ((!writeIsSafe) && (out > oend - 2)) + return ERROR(dstSize_tooSmall); /* Buffer overflow */ out[0] = (BYTE)bitStream; out[1] = (BYTE)(bitStream>>8); out+= (bitCount+7) /8; - if (charnum > maxSymbolValue + 1) return ERROR(GENERIC); - return (out-ostart); } -size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) +size_t FSE_writeNCount (void* buffer, size_t bufferSize, + const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported */ if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported */ @@ -297,7 +314,7 @@ size_t FSE_writeNCount (void* buffer, size_t bufferSize, const short* normalized if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog)) return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0); - return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1); + return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1 /* write in buffer is safe */); } @@ -331,7 +348,7 @@ void FSE_freeCTable (FSE_CTable* ct) { free(ct); } /* provides the minimum logSize to safely represent a distribution */ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) { - U32 minBitsSrc = BIT_highbit32((U32)(srcSize - 1)) + 1; + U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1; U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; assert(srcSize > 1); /* Not supported, RLE should be used instead */ @@ -394,6 +411,9 @@ static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, } ToDistribute = (1 << tableLog) - distributed; + if (ToDistribute == 0) + return 0; + if ((total / ToDistribute) > lowOne) { /* risk of rounding to zero */ lowOne = (U32)((total * 3) / (ToDistribute * 2)); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d659baf12..29dce1250 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1032,6 +1032,9 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, ms->hashLog3 = hashLog3; memset(&ms->window, 0, sizeof(ms->window)); + ms->window.dictLimit = 1; /* start from 1, so that 1st position is valid */ + ms->window.lowLimit = 1; /* it ensures first and later CCtx usages compress the same */ + ms->window.nextSrc = ms->window.base + 1; /* see issue #1241 */ ZSTD_invalidateMatchState(ms); /* opt parser space */ @@ -1144,7 +1147,6 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, if (zc->workSpace == NULL) return ERROR(memory_allocation); zc->workSpaceSize = neededSpace; zc->workSpaceOversizedDuration = 0; - ptr = zc->workSpace; /* Statically sized space. * entropyWorkspace never moves, @@ -1281,8 +1283,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, } if (attachDict) { - const U32 cdictLen = (U32)( cdict->matchState.window.nextSrc + const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc - cdict->matchState.window.base); + const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit; if (cdictLen == 0) { /* don't even attach dictionaries with no contents */ DEBUGLOG(4, "skipping attaching empty dictionary"); @@ -1292,9 +1295,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* prep working match state so dict matches never have negative indices * when they are translated to the working context's index space. */ - if (cctx->blockState.matchState.window.dictLimit < cdictLen) { + if (cctx->blockState.matchState.window.dictLimit < cdictEnd) { cctx->blockState.matchState.window.nextSrc = - cctx->blockState.matchState.window.base + cdictLen; + cctx->blockState.matchState.window.base + cdictEnd; ZSTD_window_clear(&cctx->blockState.matchState.window); } cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 476cdc148..c4b9bb13b 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -970,7 +970,7 @@ _shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */ U32 seqPos = cur; DEBUGLOG(6, "start reverse traversal (last_pos:%u, cur:%u)", - last_pos, cur); + last_pos, cur); (void)last_pos; assert(storeEnd < ZSTD_OPT_NUM); DEBUGLOG(6, "last sequence copied into pos=%u (llen=%u,mlen=%u,ofc=%u)", storeEnd, lastSequence.litlen, lastSequence.mlen, lastSequence.off); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d5193d52a..27071ed79 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -320,7 +320,8 @@ static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq) static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem) { - ZSTDMT_seqPool* seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + if (seqPool == NULL) return NULL; ZSTDMT_setNbSeq(seqPool, 0); return seqPool; } @@ -1080,9 +1081,9 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) { ZSTD_frameProgression fps; DEBUGLOG(6, "ZSTDMT_getFrameProgression"); + fps.ingested = mtctx->consumed + mtctx->inBuff.filled; fps.consumed = mtctx->consumed; fps.produced = mtctx->produced; - fps.ingested = mtctx->consumed + mtctx->inBuff.filled; { unsigned jobNb; unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1); DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)", @@ -1092,8 +1093,8 @@ ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx) ZSTD_pthread_mutex_lock(&mtctx->jobs[wJobID].job_mutex); { size_t const cResult = mtctx->jobs[wJobID].cSize; size_t const produced = ZSTD_isError(cResult) ? 0 : cResult; - fps.consumed += mtctx->jobs[wJobID].consumed; fps.ingested += mtctx->jobs[wJobID].src.size; + fps.consumed += mtctx->jobs[wJobID].consumed; fps.produced += produced; } ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex); @@ -1545,6 +1546,8 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZS /*! ZSTDMT_flushProduced() : + * flush whatever data has been produced but not yet flushed in current job. + * move to next job if current one is fully flushed. * `output` : `pos` will be updated with amount of data flushed . * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush . * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */ @@ -1593,6 +1596,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u mtctx->jobs[wJobID].cSize += 4; /* can write this shared value, as worker is no longer active */ mtctx->jobs[wJobID].frameChecksumNeeded = 0; } + if (cSize > 0) { /* compression is ongoing or completed */ size_t const toFlush = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos); DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)", @@ -1606,7 +1610,7 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u output->pos += toFlush; mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */ - if ( (srcConsumed == srcSize) /* job completed */ + if ( (srcConsumed == srcSize) /* job is completed */ && (mtctx->jobs[wJobID].dstFlushed == cSize) ) { /* output buffer fully flushed => free this job position */ DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one", mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 8f4589d13..7c4769cd8 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -185,7 +185,7 @@ size_t ZSTD_estimateDCtxSize(void) { return sizeof(ZSTD_DCtx); } static size_t ZSTD_startingInputLength(ZSTD_format_e format) { size_t const startingInputLength = (format==ZSTD_f_zstd1_magicless) ? - ZSTD_frameHeaderSize_prefix - ZSTD_frameIdSize : + ZSTD_frameHeaderSize_prefix - ZSTD_FRAMEIDSIZE : ZSTD_frameHeaderSize_prefix; ZSTD_STATIC_ASSERT(ZSTD_FRAMEHEADERSIZE_PREFIX >= ZSTD_FRAMEIDSIZE); /* only supports formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless */ @@ -278,7 +278,7 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx) * Note 3 : Skippable Frame Identifiers are considered valid. */ unsigned ZSTD_isFrame(const void* buffer, size_t size) { - if (size < ZSTD_frameIdSize) return 0; + if (size < ZSTD_FRAMEIDSIZE) return 0; { U32 const magic = MEM_readLE32(buffer); if (magic == ZSTD_MAGICNUMBER) return 1; if ((magic & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) return 1; @@ -330,7 +330,9 @@ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, s const BYTE* ip = (const BYTE*)src; size_t const minInputSize = ZSTD_startingInputLength(format); + memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */ if (srcSize < minInputSize) return minInputSize; + if (src==NULL) return ERROR(GENERIC); /* invalid parameter */ if ( (format != ZSTD_f_zstd1_magicless) && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) { @@ -339,7 +341,7 @@ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, s if (srcSize < ZSTD_skippableHeaderSize) return ZSTD_skippableHeaderSize; /* magic number + frame length */ memset(zfhPtr, 0, sizeof(*zfhPtr)); - zfhPtr->frameContentSize = MEM_readLE32((const char *)src + ZSTD_frameIdSize); + zfhPtr->frameContentSize = MEM_readLE32((const char *)src + ZSTD_FRAMEIDSIZE); zfhPtr->frameType = ZSTD_skippableFrame; return 0; } @@ -451,7 +453,7 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) size_t skippableSize; if (srcSize < ZSTD_skippableHeaderSize) return ERROR(srcSize_wrong); - skippableSize = MEM_readLE32((const BYTE *)src + ZSTD_frameIdSize) + skippableSize = MEM_readLE32((const BYTE *)src + ZSTD_FRAMEIDSIZE) + ZSTD_skippableHeaderSize; if (srcSize < skippableSize) { return ZSTD_CONTENTSIZE_ERROR; @@ -540,6 +542,7 @@ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { + if (dst==NULL) return ERROR(dstSize_tooSmall); if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall); memcpy(dst, src, srcSize); return srcSize; @@ -1763,7 +1766,7 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) #endif if ( (srcSize >= ZSTD_skippableHeaderSize) && (MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START ) { - return ZSTD_skippableHeaderSize + MEM_readLE32((const BYTE*)src + ZSTD_frameIdSize); + return ZSTD_skippableHeaderSize + MEM_readLE32((const BYTE*)src + ZSTD_FRAMEIDSIZE); } else { const BYTE* ip = (const BYTE*)src; const BYTE* const ipstart = ip; @@ -1797,7 +1800,6 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) if (zfh.checksumFlag) { /* Final frame content checksum */ if (remainingSize < 4) return ERROR(srcSize_wrong); ip += 4; - remainingSize -= 4; } return ip - ipstart; @@ -1932,7 +1934,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, size_t skippableSize; if (srcSize < ZSTD_skippableHeaderSize) return ERROR(srcSize_wrong); - skippableSize = MEM_readLE32((const BYTE*)src + ZSTD_frameIdSize) + skippableSize = MEM_readLE32((const BYTE*)src + ZSTD_FRAMEIDSIZE) + ZSTD_skippableHeaderSize; if (srcSize < skippableSize) return ERROR(srcSize_wrong); @@ -2057,7 +2059,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c case ZSTDds_getFrameHeaderSize : assert(src != NULL); if (dctx->format == ZSTD_f_zstd1) { /* allows header */ - assert(srcSize >= ZSTD_frameIdSize); /* to read skippable magic number */ + assert(srcSize >= ZSTD_FRAMEIDSIZE); /* to read skippable magic number */ if ((MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ memcpy(dctx->headerBuffer, src, srcSize); dctx->expected = ZSTD_skippableHeaderSize - srcSize; /* remaining to load to get full skippable frame header */ @@ -2167,7 +2169,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c assert(src != NULL); assert(srcSize <= ZSTD_skippableHeaderSize); memcpy(dctx->headerBuffer + (ZSTD_skippableHeaderSize - srcSize), src, srcSize); /* complete skippable header */ - dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_frameIdSize); /* note : dctx->expected can grow seriously large, beyond local buffer size */ + dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_FRAMEIDSIZE); /* note : dctx->expected can grow seriously large, beyond local buffer size */ dctx->stage = ZSTDds_skipFrame; return 0; @@ -2268,7 +2270,7 @@ static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict if (magic != ZSTD_MAGIC_DICTIONARY) { return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */ } } - dctx->dictID = MEM_readLE32((const char*)dict + ZSTD_frameIdSize); + dctx->dictID = MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE); /* load entropy tables */ { size_t const eSize = ZSTD_loadEntropy(&dctx->entropy, dict, dictSize); @@ -2381,7 +2383,7 @@ static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict, ZSTD_dictContentType_e return 0; /* pure content mode */ } } - ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_frameIdSize); + ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_FRAMEIDSIZE); /* load entropy tables */ CHECK_E( ZSTD_loadEntropy(&ddict->entropy, ddict->dictContent, ddict->dictSize), dictionary_corrupted ); @@ -2398,6 +2400,7 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict, if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) { ddict->dictBuffer = NULL; ddict->dictContent = dict; + if (!dict) dictSize = 0; } else { void* const internalBuffer = ZSTD_malloc(dictSize, ddict->cMem); ddict->dictBuffer = internalBuffer; @@ -2510,7 +2513,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) { if (dictSize < 8) return 0; if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) return 0; - return MEM_readLE32((const char*)dict + ZSTD_frameIdSize); + return MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE); } /*! ZSTD_getDictID_fromDDict() : @@ -2855,7 +2858,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB CHECK_F(ZSTD_decompressBegin_usingDDict(zds, zds->ddict)); if ((MEM_readLE32(zds->headerBuffer) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ - zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_frameIdSize); + zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE); zds->stage = ZSTDds_skipFrame; } else { CHECK_F(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize)); diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index e32991652..74b70ef82 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -29,6 +29,7 @@ #include "mem.h" /* read */ #include "pool.h" #include "threading.h" +#include "cover.h" #include "zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY @@ -185,7 +186,7 @@ static void COVER_map_remove(COVER_map_t *map, U32 key) { } /** - * Destroyes a map that is inited with COVER_map_init(). + * Destroys a map that is inited with COVER_map_init(). */ static void COVER_map_destroy(COVER_map_t *map) { if (map->data) { @@ -223,7 +224,7 @@ static COVER_ctx_t *g_ctx = NULL; /** * Returns the sum of the sample sizes. */ -static size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) { +size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) { size_t sum = 0; unsigned i; for (i = 0; i < nbSamples; ++i) { @@ -380,14 +381,6 @@ static void COVER_group(COVER_ctx_t *ctx, const void *group, ctx->suffix[dmerId] = freq; } -/** - * A segment is a range in the source as well as the score of the segment. - */ -typedef struct { - U32 begin; - U32 end; - U32 score; -} COVER_segment_t; /** * Selects the best segment in an epoch. @@ -691,7 +684,7 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( BYTE* const dict = (BYTE*)dictBuffer; COVER_ctx_t ctx; COVER_map_t activeDmers; - parameters.splitPoint = 1.0; + parameters.splitPoint = parameters.splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters.splitPoint; /* Initialize global data */ g_displayLevel = parameters.zParams.notificationLevel; /* Checks */ @@ -737,28 +730,65 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( } } -/** - * COVER_best_t is used for two purposes: - * 1. Synchronizing threads. - * 2. Saving the best parameters and dictionary. - * - * All of the methods except COVER_best_init() are thread safe if zstd is - * compiled with multithreaded support. - */ -typedef struct COVER_best_s { - ZSTD_pthread_mutex_t mutex; - ZSTD_pthread_cond_t cond; - size_t liveJobs; - void *dict; - size_t dictSize; - ZDICT_cover_params_t parameters; - size_t compressedSize; -} COVER_best_t; + + +size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters, + const size_t *samplesSizes, const BYTE *samples, + size_t *offsets, + size_t nbTrainSamples, size_t nbSamples, + BYTE *const dict, size_t dictBufferCapacity) { + size_t totalCompressedSize = ERROR(GENERIC); + /* Pointers */ + ZSTD_CCtx *cctx; + ZSTD_CDict *cdict; + void *dst; + /* Local variables */ + size_t dstCapacity; + size_t i; + /* Allocate dst with enough space to compress the maximum sized sample */ + { + size_t maxSampleSize = 0; + i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; + for (; i < nbSamples; ++i) { + maxSampleSize = MAX(samplesSizes[i], maxSampleSize); + } + dstCapacity = ZSTD_compressBound(maxSampleSize); + dst = malloc(dstCapacity); + } + /* Create the cctx and cdict */ + cctx = ZSTD_createCCtx(); + cdict = ZSTD_createCDict(dict, dictBufferCapacity, + parameters.zParams.compressionLevel); + if (!dst || !cctx || !cdict) { + goto _compressCleanup; + } + /* Compress each sample and sum their sizes (or error) */ + totalCompressedSize = dictBufferCapacity; + i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0; + for (; i < nbSamples; ++i) { + const size_t size = ZSTD_compress_usingCDict( + cctx, dst, dstCapacity, samples + offsets[i], + samplesSizes[i], cdict); + if (ZSTD_isError(size)) { + totalCompressedSize = ERROR(GENERIC); + goto _compressCleanup; + } + totalCompressedSize += size; + } +_compressCleanup: + ZSTD_freeCCtx(cctx); + ZSTD_freeCDict(cdict); + if (dst) { + free(dst); + } + return totalCompressedSize; +} + /** * Initialize the `COVER_best_t`. */ -static void COVER_best_init(COVER_best_t *best) { +void COVER_best_init(COVER_best_t *best) { if (best==NULL) return; /* compatible with init on NULL */ (void)ZSTD_pthread_mutex_init(&best->mutex, NULL); (void)ZSTD_pthread_cond_init(&best->cond, NULL); @@ -772,7 +802,7 @@ static void COVER_best_init(COVER_best_t *best) { /** * Wait until liveJobs == 0. */ -static void COVER_best_wait(COVER_best_t *best) { +void COVER_best_wait(COVER_best_t *best) { if (!best) { return; } @@ -786,7 +816,7 @@ static void COVER_best_wait(COVER_best_t *best) { /** * Call COVER_best_wait() and then destroy the COVER_best_t. */ -static void COVER_best_destroy(COVER_best_t *best) { +void COVER_best_destroy(COVER_best_t *best) { if (!best) { return; } @@ -802,7 +832,7 @@ static void COVER_best_destroy(COVER_best_t *best) { * Called when a thread is about to be launched. * Increments liveJobs. */ -static void COVER_best_start(COVER_best_t *best) { +void COVER_best_start(COVER_best_t *best) { if (!best) { return; } @@ -816,7 +846,7 @@ static void COVER_best_start(COVER_best_t *best) { * Decrements liveJobs and signals any waiting threads if liveJobs == 0. * If this dictionary is the best so far save it and its parameters. */ -static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, +void COVER_best_finish(COVER_best_t *best, size_t compressedSize, ZDICT_cover_params_t parameters, void *dict, size_t dictSize) { if (!best) { @@ -847,10 +877,10 @@ static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, best->parameters = parameters; best->compressedSize = compressedSize; } - ZSTD_pthread_mutex_unlock(&best->mutex); if (liveJobs == 0) { ZSTD_pthread_cond_broadcast(&best->cond); } + ZSTD_pthread_mutex_unlock(&best->mutex); } } @@ -904,51 +934,10 @@ static void COVER_tryParameters(void *opaque) { } } /* Check total compressed size */ - { - /* Pointers */ - ZSTD_CCtx *cctx; - ZSTD_CDict *cdict; - void *dst; - /* Local variables */ - size_t dstCapacity; - size_t i; - /* Allocate dst with enough space to compress the maximum sized sample */ - { - size_t maxSampleSize = 0; - i = parameters.splitPoint < 1.0 ? ctx->nbTrainSamples : 0; - for (; i < ctx->nbSamples; ++i) { - maxSampleSize = MAX(ctx->samplesSizes[i], maxSampleSize); - } - dstCapacity = ZSTD_compressBound(maxSampleSize); - dst = malloc(dstCapacity); - } - /* Create the cctx and cdict */ - cctx = ZSTD_createCCtx(); - cdict = ZSTD_createCDict(dict, dictBufferCapacity, - parameters.zParams.compressionLevel); - if (!dst || !cctx || !cdict) { - goto _compressCleanup; - } - /* Compress each sample and sum their sizes (or error) */ - totalCompressedSize = dictBufferCapacity; - i = parameters.splitPoint < 1.0 ? ctx->nbTrainSamples : 0; - for (; i < ctx->nbSamples; ++i) { - const size_t size = ZSTD_compress_usingCDict( - cctx, dst, dstCapacity, ctx->samples + ctx->offsets[i], - ctx->samplesSizes[i], cdict); - if (ZSTD_isError(size)) { - totalCompressedSize = ERROR(GENERIC); - goto _compressCleanup; - } - totalCompressedSize += size; - } - _compressCleanup: - ZSTD_freeCCtx(cctx); - ZSTD_freeCDict(cdict); - if (dst) { - free(dst); - } - } + totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes, + ctx->samples, ctx->offsets, + ctx->nbTrainSamples, ctx->nbSamples, + dict, dictBufferCapacity); _cleanup: COVER_best_finish(data->best, totalCompressedSize, parameters, dict, diff --git a/lib/dictBuilder/cover.h b/lib/dictBuilder/cover.h new file mode 100644 index 000000000..82e2e1cea --- /dev/null +++ b/lib/dictBuilder/cover.h @@ -0,0 +1,83 @@ +#include /* fprintf */ +#include /* malloc, free, qsort */ +#include /* memset */ +#include /* clock */ +#include "mem.h" /* read */ +#include "pool.h" +#include "threading.h" +#include "zstd_internal.h" /* includes zstd.h */ +#ifndef ZDICT_STATIC_LINKING_ONLY +#define ZDICT_STATIC_LINKING_ONLY +#endif +#include "zdict.h" + +/** + * COVER_best_t is used for two purposes: + * 1. Synchronizing threads. + * 2. Saving the best parameters and dictionary. + * + * All of the methods except COVER_best_init() are thread safe if zstd is + * compiled with multithreaded support. + */ +typedef struct COVER_best_s { + ZSTD_pthread_mutex_t mutex; + ZSTD_pthread_cond_t cond; + size_t liveJobs; + void *dict; + size_t dictSize; + ZDICT_cover_params_t parameters; + size_t compressedSize; +} COVER_best_t; + +/** + * A segment is a range in the source as well as the score of the segment. + */ +typedef struct { + U32 begin; + U32 end; + U32 score; +} COVER_segment_t; + +/** + * Checks total compressed size of a dictionary + */ +size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters, + const size_t *samplesSizes, const BYTE *samples, + size_t *offsets, + size_t nbTrainSamples, size_t nbSamples, + BYTE *const dict, size_t dictBufferCapacity); + +/** + * Returns the sum of the sample sizes. + */ +size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) ; + +/** + * Initialize the `COVER_best_t`. + */ +void COVER_best_init(COVER_best_t *best); + +/** + * Wait until liveJobs == 0. + */ +void COVER_best_wait(COVER_best_t *best); + +/** + * Call COVER_best_wait() and then destroy the COVER_best_t. + */ +void COVER_best_destroy(COVER_best_t *best); + +/** + * Called when a thread is about to be launched. + * Increments liveJobs. + */ +void COVER_best_start(COVER_best_t *best); + +/** + * Called when a thread finishes executing, both on error or success. + * Decrements liveJobs and signals any waiting threads if liveJobs == 0. + * If this dictionary is the best so far save it and its parameters. + */ +void COVER_best_finish(COVER_best_t *best, size_t compressedSize, + ZDICT_cover_params_t parameters, void *dict, + size_t dictSize); diff --git a/lib/dictBuilder/divsufsort.c b/lib/dictBuilder/divsufsort.c index 60cceb088..ead922044 100644 --- a/lib/dictBuilder/divsufsort.c +++ b/lib/dictBuilder/divsufsort.c @@ -1637,7 +1637,7 @@ construct_SA(const unsigned char *T, int *SA, if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } - assert(k < j); + assert(k < j); assert(k != NULL); *k-- = s; } else { assert(((s == 0) && (T[s] == c1)) || (s < 0)); @@ -1701,7 +1701,7 @@ construct_BWT(const unsigned char *T, int *SA, if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } - assert(k < j); + assert(k < j); assert(k != NULL); *k-- = s; } else if(s != 0) { *j = ~s; @@ -1785,7 +1785,7 @@ construct_BWT_indexes(const unsigned char *T, int *SA, if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } - assert(k < j); + assert(k < j); assert(k != NULL); *k-- = s; } else if(s != 0) { *j = ~s; diff --git a/lib/dictBuilder/fastcover.c b/lib/dictBuilder/fastcover.c new file mode 100644 index 000000000..9a41ac2f9 --- /dev/null +++ b/lib/dictBuilder/fastcover.c @@ -0,0 +1,701 @@ +/*-************************************* +* Dependencies +***************************************/ +#include /* fprintf */ +#include /* malloc, free, qsort */ +#include /* memset */ +#include /* clock */ + +#include "mem.h" /* read */ +#include "pool.h" +#include "threading.h" +#include "cover.h" +#include "zstd_internal.h" /* includes zstd.h */ +#ifndef ZDICT_STATIC_LINKING_ONLY +#define ZDICT_STATIC_LINKING_ONLY +#endif +#include "zdict.h" + + +/*-************************************* +* Constants +***************************************/ +#define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((U32)-1) : ((U32)1 GB)) +#define FASTCOVER_MAX_F 31 +#define FASTCOVER_MAX_ACCEL 10 +#define DEFAULT_SPLITPOINT 0.75 +#define DEFAULT_F 18 +#define DEFAULT_ACCEL 1 + + +/*-************************************* +* Console display +***************************************/ +static int g_displayLevel = 2; +#define DISPLAY(...) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } +#define LOCALDISPLAYLEVEL(displayLevel, l, ...) \ + if (displayLevel >= l) { \ + DISPLAY(__VA_ARGS__); \ + } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ +#define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__) + +#define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ + if (displayLevel >= l) { \ + if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ + g_time = clock(); \ + DISPLAY(__VA_ARGS__); \ + } \ + } +#define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) +static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_time = 0; + + +/*-************************************* +* Hash Functions +***************************************/ +static const U64 prime6bytes = 227718039650203ULL; +static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } +static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } + +static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; +static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } +static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } + + +/** + * Hash the d-byte value pointed to by p and mod 2^f + */ +static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 h, unsigned d) { + if (d == 6) { + return ZSTD_hash6Ptr(p, h) & ((1 << h) - 1); + } + return ZSTD_hash8Ptr(p, h) & ((1 << h) - 1); +} + + +/*-************************************* +* Acceleration +***************************************/ +typedef struct { + unsigned finalize; /* Percentage of training samples used for ZDICT_finalizeDictionary */ + unsigned skip; /* Number of dmer skipped between each dmer counted in computeFrequency */ +} FASTCOVER_accel_t; + + +static const FASTCOVER_accel_t FASTCOVER_defaultAccelParameters[FASTCOVER_MAX_ACCEL+1] = { + { 100, 0 }, /* accel = 0, should not happen because accel = 0 defaults to accel = 1 */ + { 100, 0 }, /* accel = 1 */ + { 50, 1 }, /* accel = 2 */ + { 34, 2 }, /* accel = 3 */ + { 25, 3 }, /* accel = 4 */ + { 20, 4 }, /* accel = 5 */ + { 17, 5 }, /* accel = 6 */ + { 14, 6 }, /* accel = 7 */ + { 13, 7 }, /* accel = 8 */ + { 11, 8 }, /* accel = 9 */ + { 10, 9 }, /* accel = 10 */ +}; + + +/*-************************************* +* Context +***************************************/ +typedef struct { + const BYTE *samples; + size_t *offsets; + const size_t *samplesSizes; + size_t nbSamples; + size_t nbTrainSamples; + size_t nbTestSamples; + size_t nbDmers; + U32 *freqs; + unsigned d; + unsigned f; + FASTCOVER_accel_t accelParams; +} FASTCOVER_ctx_t; + + +/*-************************************* +* Helper functions +***************************************/ +/** + * Selects the best segment in an epoch. + * Segments of are scored according to the function: + * + * Let F(d) be the frequency of all dmers with hash value d. + * Let S_i be hash value of the dmer at position i of segment S which has length k. + * + * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) + * + * Once the dmer with hash value d is in the dictionay we set F(d) = 0. + */ +static COVER_segment_t FASTCOVER_selectSegment(const FASTCOVER_ctx_t *ctx, + U32 *freqs, U32 begin, U32 end, + ZDICT_cover_params_t parameters, + U16* segmentFreqs) { + /* Constants */ + const U32 k = parameters.k; + const U32 d = parameters.d; + const U32 f = ctx->f; + const U32 dmersInK = k - d + 1; + + /* Try each segment (activeSegment) and save the best (bestSegment) */ + COVER_segment_t bestSegment = {0, 0, 0}; + COVER_segment_t activeSegment; + + /* Reset the activeDmers in the segment */ + /* The activeSegment starts at the beginning of the epoch. */ + activeSegment.begin = begin; + activeSegment.end = begin; + activeSegment.score = 0; + + /* Slide the activeSegment through the whole epoch. + * Save the best segment in bestSegment. + */ + while (activeSegment.end < end) { + /* Get hash value of current dmer */ + const size_t index = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.end, f, d); + + /* Add frequency of this index to score if this is the first occurence of index in active segment */ + if (segmentFreqs[index] == 0) { + activeSegment.score += freqs[index]; + } + /* Increment end of segment and segmentFreqs*/ + activeSegment.end += 1; + segmentFreqs[index] += 1; + /* If the window is now too large, drop the first position */ + if (activeSegment.end - activeSegment.begin == dmersInK + 1) { + /* Get hash value of the dmer to be eliminated from active segment */ + const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); + segmentFreqs[delIndex] -= 1; + /* Subtract frequency of this index from score if this is the last occurrence of this index in active segment */ + if (segmentFreqs[delIndex] == 0) { + activeSegment.score -= freqs[delIndex]; + } + /* Increment start of segment */ + activeSegment.begin += 1; + } + + /* If this segment is the best so far save it */ + if (activeSegment.score > bestSegment.score) { + bestSegment = activeSegment; + } + } + + /* Zero out rest of segmentFreqs array */ + while (activeSegment.begin < end) { + const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); + segmentFreqs[delIndex] -= 1; + activeSegment.begin += 1; + } + + { + /* Zero the frequency of hash value of each dmer covered by the chosen segment. */ + U32 pos; + for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { + const size_t i = FASTCOVER_hashPtrToIndex(ctx->samples + pos, f, d); + freqs[i] = 0; + } + } + + return bestSegment; +} + + +static int FASTCOVER_checkParameters(ZDICT_cover_params_t parameters, + size_t maxDictSize, unsigned f, + unsigned accel) { + /* k, d, and f are required parameters */ + if (parameters.d == 0 || parameters.k == 0) { + return 0; + } + /* d has to be 6 or 8 */ + if (parameters.d != 6 && parameters.d != 8) { + return 0; + } + /* k <= maxDictSize */ + if (parameters.k > maxDictSize) { + return 0; + } + /* d <= k */ + if (parameters.d > parameters.k) { + return 0; + } + /* 0 < f <= FASTCOVER_MAX_F*/ + if (f > FASTCOVER_MAX_F || f == 0) { + return 0; + } + /* 0 < splitPoint <= 1 */ + if (parameters.splitPoint <= 0 || parameters.splitPoint > 1) { + return 0; + } + /* 0 < accel <= 10 */ + if (accel > 10 || accel == 0) { + return 0; + } + return 1; +} + + +/** + * Clean up a context initialized with `FASTCOVER_ctx_init()`. + */ +static void FASTCOVER_ctx_destroy(FASTCOVER_ctx_t *ctx) { + if (!ctx) { + return; + } + + free(ctx->freqs); + ctx->freqs = NULL; + + free(ctx->offsets); + ctx->offsets = NULL; +} + + +/** + * Calculate for frequency of hash value of each dmer in ctx->samples + */ +static void FASTCOVER_computeFrequency(U32 *freqs, FASTCOVER_ctx_t *ctx){ + const unsigned f = ctx->f; + const unsigned d = ctx->d; + const unsigned skip = ctx->accelParams.skip; + const unsigned readLength = MAX(d, 8); + size_t start; /* start of current dmer */ + size_t i; + for (i = 0; i < ctx->nbTrainSamples; i++) { + size_t currSampleStart = ctx->offsets[i]; + size_t currSampleEnd = ctx->offsets[i+1]; + start = currSampleStart; + while (start + readLength <= currSampleEnd) { + const size_t dmerIndex = FASTCOVER_hashPtrToIndex(ctx->samples + start, f, d); + freqs[dmerIndex]++; + start = start + skip + 1; + } + } +} + + +/** + * Prepare a context for dictionary building. + * The context is only dependent on the parameter `d` and can used multiple + * times. + * Returns 1 on success or zero on error. + * The context must be destroyed with `FASTCOVER_ctx_destroy()`. + */ +static int FASTCOVER_ctx_init(FASTCOVER_ctx_t *ctx, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, + unsigned d, double splitPoint, unsigned f, + FASTCOVER_accel_t accelParams) { + const BYTE *const samples = (const BYTE *)samplesBuffer; + const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); + /* Split samples into testing and training sets */ + const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples; + const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples; + const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize; + const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize; + /* Checks */ + if (totalSamplesSize < MAX(d, sizeof(U64)) || + totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) { + DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n", + (U32)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20)); + return 0; + } + /* Check if there are at least 5 training samples */ + if (nbTrainSamples < 5) { + DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples); + return 0; + } + /* Check if there's testing sample */ + if (nbTestSamples < 1) { + DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples); + return 0; + } + /* Zero the context */ + memset(ctx, 0, sizeof(*ctx)); + DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples, + (U32)trainingSamplesSize); + DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples, + (U32)testSamplesSize); + + ctx->samples = samples; + ctx->samplesSizes = samplesSizes; + ctx->nbSamples = nbSamples; + ctx->nbTrainSamples = nbTrainSamples; + ctx->nbTestSamples = nbTestSamples; + ctx->nbDmers = trainingSamplesSize - MAX(d, sizeof(U64)) + 1; + ctx->d = d; + ctx->f = f; + ctx->accelParams = accelParams; + + /* The offsets of each file */ + ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); + if (!ctx->offsets) { + DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n"); + FASTCOVER_ctx_destroy(ctx); + return 0; + } + + /* Fill offsets from the samplesSizes */ + { + U32 i; + ctx->offsets[0] = 0; + for (i = 1; i <= nbSamples; ++i) { + ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; + } + } + + /* Initialize frequency array of size 2^f */ + ctx->freqs = (U32 *)calloc(((U64)1 << f), sizeof(U32)); + + DISPLAYLEVEL(2, "Computing frequencies\n"); + FASTCOVER_computeFrequency(ctx->freqs, ctx); + + return 1; +} + + +/** + * Given the prepared context build the dictionary. + */ +static size_t FASTCOVER_buildDictionary(const FASTCOVER_ctx_t *ctx, U32 *freqs, + void *dictBuffer, size_t dictBufferCapacity, + ZDICT_cover_params_t parameters, U16* segmentFreqs){ + BYTE *const dict = (BYTE *)dictBuffer; + size_t tail = dictBufferCapacity; + /* Divide the data up into epochs of equal size. + * We will select at least one segment from each epoch. + */ + const U32 epochs = MAX(1, (U32)(dictBufferCapacity / parameters.k)); + const U32 epochSize = (U32)(ctx->nbDmers / epochs); + size_t epoch; + DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", epochs, + epochSize); + /* Loop through the epochs until there are no more segments or the dictionary + * is full. + */ + for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) { + const U32 epochBegin = (U32)(epoch * epochSize); + const U32 epochEnd = epochBegin + epochSize; + size_t segmentSize; + /* Select a segment */ + COVER_segment_t segment = FASTCOVER_selectSegment( + ctx, freqs, epochBegin, epochEnd, parameters, segmentFreqs); + + /* If the segment covers no dmers, then we are out of content */ + if (segment.score == 0) { + break; + } + + /* Trim the segment if necessary and if it is too small then we are done */ + segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); + if (segmentSize < parameters.d) { + break; + } + + /* We fill the dictionary from the back to allow the best segments to be + * referenced with the smallest offsets. + */ + tail -= segmentSize; + memcpy(dict + tail, ctx->samples + segment.begin, segmentSize); + DISPLAYUPDATE( + 2, "\r%u%% ", + (U32)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity)); + } + DISPLAYLEVEL(2, "\r%79s\r", ""); + return tail; +} + + +/** + * Parameters for FASTCOVER_tryParameters(). + */ +typedef struct FASTCOVER_tryParameters_data_s { + const FASTCOVER_ctx_t *ctx; + COVER_best_t *best; + size_t dictBufferCapacity; + ZDICT_cover_params_t parameters; +} FASTCOVER_tryParameters_data_t; + + +/** + * Tries a set of parameters and updates the COVER_best_t with the results. + * This function is thread safe if zstd is compiled with multithreaded support. + * It takes its parameters as an *OWNING* opaque pointer to support threading. + */ +static void FASTCOVER_tryParameters(void *opaque) { + /* Save parameters as local variables */ + FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t *)opaque; + const FASTCOVER_ctx_t *const ctx = data->ctx; + const ZDICT_cover_params_t parameters = data->parameters; + size_t dictBufferCapacity = data->dictBufferCapacity; + size_t totalCompressedSize = ERROR(GENERIC); + /* Initialize array to keep track of frequency of dmer within activeSegment */ + U16* segmentFreqs = (U16 *)calloc(((U64)1 << ctx->f), sizeof(U16)); + /* Allocate space for hash table, dict, and freqs */ + BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); + U32 *freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32)); + if (!segmentFreqs || !dict || !freqs) { + DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n"); + goto _cleanup; + } + /* Copy the frequencies because we need to modify them */ + memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32)); + /* Build the dictionary */ + { + const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity, + parameters, segmentFreqs); + const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100); + dictBufferCapacity = ZDICT_finalizeDictionary( + dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, + ctx->samples, ctx->samplesSizes, nbFinalizeSamples, parameters.zParams); + if (ZDICT_isError(dictBufferCapacity)) { + DISPLAYLEVEL(1, "Failed to finalize dictionary\n"); + goto _cleanup; + } + } + /* Check total compressed size */ + totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes, + ctx->samples, ctx->offsets, + ctx->nbTrainSamples, ctx->nbSamples, + dict, dictBufferCapacity); +_cleanup: + COVER_best_finish(data->best, totalCompressedSize, parameters, dict, + dictBufferCapacity); + free(data); + free(segmentFreqs); + free(dict); + free(freqs); +} + + + +static void FASTCOVER_convertToCoverParams(ZDICT_fastCover_params_t fastCoverParams, + ZDICT_cover_params_t *coverParams) { + coverParams->k = fastCoverParams.k; + coverParams->d = fastCoverParams.d; + coverParams->steps = fastCoverParams.steps; + coverParams->nbThreads = fastCoverParams.nbThreads; + coverParams->splitPoint = fastCoverParams.splitPoint; + coverParams->zParams = fastCoverParams.zParams; +} + + +static void FASTCOVER_convertToFastCoverParams(ZDICT_cover_params_t coverParams, + ZDICT_fastCover_params_t *fastCoverParams, + unsigned f, unsigned accel) { + fastCoverParams->k = coverParams.k; + fastCoverParams->d = coverParams.d; + fastCoverParams->steps = coverParams.steps; + fastCoverParams->nbThreads = coverParams.nbThreads; + fastCoverParams->splitPoint = coverParams.splitPoint; + fastCoverParams->f = f; + fastCoverParams->accel = accel; + fastCoverParams->zParams = coverParams.zParams; +} + + +ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover( + void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t parameters) { + BYTE* const dict = (BYTE*)dictBuffer; + FASTCOVER_ctx_t ctx; + ZDICT_cover_params_t coverParams; + FASTCOVER_accel_t accelParams; + /* Initialize global data */ + g_displayLevel = parameters.zParams.notificationLevel; + /* Assign splitPoint and f if not provided */ + parameters.splitPoint = parameters.splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters.splitPoint; + parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f; + parameters.accel = parameters.accel == 0 ? DEFAULT_ACCEL : parameters.accel; + /* Convert to cover parameter */ + memset(&coverParams, 0 , sizeof(coverParams)); + FASTCOVER_convertToCoverParams(parameters, &coverParams); + /* Checks */ + if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f, + parameters.accel)) { + DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n"); + return ERROR(GENERIC); + } + if (nbSamples == 0) { + DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n"); + return ERROR(GENERIC); + } + if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { + DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", + ZDICT_DICTSIZE_MIN); + return ERROR(dstSize_tooSmall); + } + /* Assign corresponding FASTCOVER_accel_t to accelParams*/ + accelParams = FASTCOVER_defaultAccelParameters[parameters.accel]; + /* Initialize context */ + if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, + coverParams.d, parameters.splitPoint, parameters.f, + accelParams)) { + DISPLAYLEVEL(1, "Failed to initialize context\n"); + return ERROR(GENERIC); + } + /* Build the dictionary */ + DISPLAYLEVEL(2, "Building dictionary\n"); + { + /* Initialize array to keep track of frequency of dmer within activeSegment */ + U16* segmentFreqs = (U16 *)calloc(((U64)1 << parameters.f), sizeof(U16)); + const size_t tail = FASTCOVER_buildDictionary(&ctx, ctx.freqs, dictBuffer, + dictBufferCapacity, coverParams, segmentFreqs); + const unsigned nbFinalizeSamples = (unsigned)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100); + const size_t dictionarySize = ZDICT_finalizeDictionary( + dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, + samplesBuffer, samplesSizes, nbFinalizeSamples, coverParams.zParams); + if (!ZSTD_isError(dictionarySize)) { + DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", + (U32)dictionarySize); + } + FASTCOVER_ctx_destroy(&ctx); + free(segmentFreqs); + return dictionarySize; + } +} + + +ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover( + void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, + ZDICT_fastCover_params_t *parameters) { + ZDICT_cover_params_t coverParams; + FASTCOVER_accel_t accelParams; + /* constants */ + const unsigned nbThreads = parameters->nbThreads; + const double splitPoint = + parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint; + const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; + const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d; + const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k; + const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k; + const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps; + const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); + const unsigned kIterations = + (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); + const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f; + const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel; + /* Local variables */ + const int displayLevel = parameters->zParams.notificationLevel; + unsigned iteration = 1; + unsigned d; + unsigned k; + COVER_best_t best; + POOL_ctx *pool = NULL; + /* Checks */ + if (splitPoint <= 0 || splitPoint > 1) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect splitPoint\n"); + return ERROR(GENERIC); + } + if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect accel\n"); + return ERROR(GENERIC); + } + if (kMinK < kMaxD || kMaxK < kMinK) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect k\n"); + return ERROR(GENERIC); + } + if (nbSamples == 0) { + LOCALDISPLAYLEVEL(displayLevel, 1, "FASTCOVER must have at least one input file\n"); + return ERROR(GENERIC); + } + if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { + LOCALDISPLAYLEVEL(displayLevel, 1, "dictBufferCapacity must be at least %u\n", + ZDICT_DICTSIZE_MIN); + return ERROR(dstSize_tooSmall); + } + if (nbThreads > 1) { + pool = POOL_create(nbThreads, 1); + if (!pool) { + return ERROR(memory_allocation); + } + } + /* Initialization */ + COVER_best_init(&best); + memset(&coverParams, 0 , sizeof(coverParams)); + FASTCOVER_convertToCoverParams(*parameters, &coverParams); + accelParams = FASTCOVER_defaultAccelParameters[accel]; + /* Turn down global display level to clean up display at level 2 and below */ + g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; + /* Loop through d first because each new value needs a new context */ + LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", + kIterations); + for (d = kMinD; d <= kMaxD; d += 2) { + /* Initialize the context for this value of d */ + FASTCOVER_ctx_t ctx; + LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); + if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams)) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); + COVER_best_destroy(&best); + POOL_free(pool); + return ERROR(GENERIC); + } + /* Loop through k reusing the same context */ + for (k = kMinK; k <= kMaxK; k += kStepSize) { + /* Prepare the arguments */ + FASTCOVER_tryParameters_data_t *data = (FASTCOVER_tryParameters_data_t *)malloc( + sizeof(FASTCOVER_tryParameters_data_t)); + LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); + if (!data) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); + COVER_best_destroy(&best); + FASTCOVER_ctx_destroy(&ctx); + POOL_free(pool); + return ERROR(GENERIC); + } + data->ctx = &ctx; + data->best = &best; + data->dictBufferCapacity = dictBufferCapacity; + data->parameters = coverParams; + data->parameters.k = k; + data->parameters.d = d; + data->parameters.splitPoint = splitPoint; + data->parameters.steps = kSteps; + data->parameters.zParams.notificationLevel = g_displayLevel; + /* Check the parameters */ + if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity, + data->ctx->f, accel)) { + DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n"); + free(data); + continue; + } + /* Call the function and pass ownership of data to it */ + COVER_best_start(&best); + if (pool) { + POOL_add(pool, &FASTCOVER_tryParameters, data); + } else { + FASTCOVER_tryParameters(data); + } + /* Print status */ + LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", + (U32)((iteration * 100) / kIterations)); + ++iteration; + } + COVER_best_wait(&best); + FASTCOVER_ctx_destroy(&ctx); + } + LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); + /* Fill the output buffer and parameters with output of the best parameters */ + { + const size_t dictSize = best.dictSize; + if (ZSTD_isError(best.compressedSize)) { + const size_t compressedSize = best.compressedSize; + COVER_best_destroy(&best); + POOL_free(pool); + return compressedSize; + } + FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel); + memcpy(dictBuffer, best.dict, dictSize); + COVER_best_destroy(&best); + POOL_free(pool); + return dictSize; + } + +} diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index b8a51789a..9acceb40b 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -698,7 +698,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, short litLengthNCount[MaxLL+1]; U32 repOffset[MAXREPOFFSET]; offsetCount_t bestRepOffset[ZSTD_REP_NUM+1]; - EStats_ress_t esr; + EStats_ress_t esr = { NULL, NULL, NULL }; ZSTD_parameters params; U32 u, huffLog = 11, Offlog = OffFSELog, mlLog = MLFSELog, llLog = LLFSELog, total; size_t pos = 0, errorCode; @@ -863,8 +863,8 @@ _cleanup: size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, const void* customDictContent, size_t dictContentSize, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, - ZDICT_params_t params) + const void* samplesBuffer, const size_t* samplesSizes, + unsigned nbSamples, ZDICT_params_t params) { size_t hSize; #define HBUFFSIZE 256 /* should prove large enough for all entropy headers */ @@ -1078,17 +1078,17 @@ size_t ZDICT_trainFromBuffer_legacy(void* dictBuffer, size_t dictBufferCapacity, size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) { - ZDICT_cover_params_t params; + ZDICT_fastCover_params_t params; DEBUGLOG(3, "ZDICT_trainFromBuffer"); memset(¶ms, 0, sizeof(params)); params.d = 8; params.steps = 4; /* Default to level 6 since no compression level information is available */ - params.zParams.compressionLevel = 6; + params.zParams.compressionLevel = 3; #if defined(DEBUGLEVEL) && (DEBUGLEVEL>=1) params.zParams.notificationLevel = DEBUGLEVEL; #endif - return ZDICT_optimizeTrainFromBuffer_cover(dictBuffer, dictBufferCapacity, + return ZDICT_optimizeTrainFromBuffer_fastCover(dictBuffer, dictBufferCapacity, samplesBuffer, samplesSizes, nbSamples, ¶ms); } diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 4094669d1..c838fd4a7 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -39,7 +39,8 @@ extern "C" { /*! ZDICT_trainFromBuffer(): * Train a dictionary from an array of samples. - * Redirect towards ZDICT_optimizeTrainFromBuffer_cover() single-threaded, with d=8 and steps=4. + * Redirect towards ZDICT_optimizeTrainFromBuffer_fastCover() single-threaded, with d=8, steps=4, + * f=18, and accel=1. * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. * The resulting dictionary will be saved into `dictBuffer`. @@ -90,6 +91,16 @@ typedef struct { ZDICT_params_t zParams; } ZDICT_cover_params_t; +typedef struct { + unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ + unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ + unsigned f; /* log of size of frequency array : constraint: 0 < f <= 31 : 1 means default(18)*/ + unsigned steps; /* Number of steps : Only used for optimization : 0 means default (32) : Higher means more parameters checked */ + unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ + double splitPoint; /* Percentage of samples used for training: the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (0.75), 1.0 when all samples are used for both training and testing */ + unsigned accel; /* Acceleration level: constraint: 0 < accel <= 10, higher means faster and less accurate, 0 means default(1) */ + ZDICT_params_t zParams; +} ZDICT_fastCover_params_t; /*! ZDICT_trainFromBuffer_cover(): * Train a dictionary from an array of samples using the COVER algorithm. @@ -116,9 +127,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( * dictionary constructed with those parameters is stored in `dictBuffer`. * * All of the parameters d, k, steps are optional. - * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8, 10, 12, 14, 16}. + * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}. * if steps is zero it defaults to its default value. - * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [16, 2048]. + * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000]. * * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) * or an error code, which can be tested with ZDICT_isError(). @@ -130,6 +141,48 @@ ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_cover_params_t* parameters); +/*! ZDICT_trainFromBuffer_fastCover(): + * Train a dictionary from an array of samples using a modified version of COVER algorithm. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * d and k are required. + * All other parameters are optional, will use default values if not provided + * The resulting dictionary will be saved into `dictBuffer`. + * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + * or an error code, which can be tested with ZDICT_isError(). + * Note: ZDICT_trainFromBuffer_fastCover() requires about 1 bytes of memory for each input byte and additionally another 6 * 2^f bytes of memory . + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. + * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. + */ +ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, + size_t dictBufferCapacity, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, + ZDICT_fastCover_params_t parameters); + +/*! ZDICT_optimizeTrainFromBuffer_fastCover(): + * The same requirements as above hold for all the parameters except `parameters`. + * This function tries many parameter combinations (specifically, k and d combinations) + * and picks the best parameters. `*parameters` is filled with the best parameters found, + * dictionary constructed with those parameters is stored in `dictBuffer`. + * All of the parameters d, k, steps, f, and accel are optional. + * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}. + * if steps is zero it defaults to its default value. + * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000]. + * If f is zero, default value of 18 is used. + * If accel is zero, default value of 1 is used. + * + * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + * or an error code, which can be tested with ZDICT_isError(). + * On success `*parameters` contains the parameters selected. + * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 1 byte of memory for each input byte and additionally another 6 * 2^f bytes of memory for each thread. + */ +ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, + size_t dictBufferCapacity, const void* samplesBuffer, + const size_t* samplesSizes, unsigned nbSamples, + ZDICT_fastCover_params_t* parameters); + /*! ZDICT_finalizeDictionary(): * Given a custom content as a basis for dictionary, and a set of samples, * finalize dictionary by adding headers and statistics. diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index a2e2cfa80..15000db6d 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -1093,6 +1093,7 @@ static size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, un if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Init, lay down lowprob symbols */ + memset(tableDecode, 0, sizeof(FSE_DECODE_TYPE) * (maxSymbolValue+1) ); /* useless init, but keep static analyzer happy, and we don't need to performance optimize legacy decoders */ DTableH.tableLog = (U16)tableLog; for (s=0; s<=maxSymbolValue; s++) { diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index a5e1b1ffc..65f07a3b5 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -1224,6 +1224,7 @@ size_t FSEv05_buildDTable(FSEv05_DTable* dt, const short* normalizedCounter, uns if (tableLog > FSEv05_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Init, lay down lowprob symbols */ + memset(tableDecode, 0, sizeof(FSEv05_FUNCTION_TYPE) * (maxSymbolValue+1) ); /* useless init, but keep static analyzer happy, and we don't need to performance optimize legacy decoders */ DTableH.tableLog = (U16)tableLog; for (s=0; s<=maxSymbolValue; s++) { if (normalizedCounter[s]==-1) { @@ -2845,6 +2846,7 @@ size_t ZSTDv05_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* static size_t ZSTDv05_copyRawBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize) { + if (dst==NULL) return ERROR(dstSize_tooSmall); if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); memcpy(dst, src, srcSize); return srcSize; diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 8b068b3e5..ca982c35e 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -3041,6 +3041,7 @@ size_t ZSTDv06_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* static size_t ZSTDv06_copyRawBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { + if (dst==NULL) return ERROR(dstSize_tooSmall); if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall); memcpy(dst, src, srcSize); return srcSize; @@ -4006,7 +4007,7 @@ size_t ZBUFFv06_decompressContinue(ZBUFFv06_DCtx* zbd, if (ZSTDv06_isError(hSize)) return hSize; if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); - zbd->lhSize += iend-ip; ip = iend; notDone = 0; + zbd->lhSize += iend-ip; *dstCapacityPtr = 0; return (hSize - zbd->lhSize) + ZSTDv06_blockHeaderSize; /* remaining header bytes + next block header */ } diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index 70b170f0f..9f95f62ad 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -3150,10 +3150,10 @@ size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, const BYTE* ip = (const BYTE*)src; if (srcSize < ZSTDv07_frameHeaderSize_min) return ZSTDv07_frameHeaderSize_min; + memset(fparamsPtr, 0, sizeof(*fparamsPtr)); if (MEM_readLE32(src) != ZSTDv07_MAGICNUMBER) { if ((MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTDv07_MAGIC_SKIPPABLE_START) { if (srcSize < ZSTDv07_skippableHeaderSize) return ZSTDv07_skippableHeaderSize; /* magic number + skippable frame length */ - memset(fparamsPtr, 0, sizeof(*fparamsPtr)); fparamsPtr->frameContentSize = MEM_readLE32((const char *)src + 4); fparamsPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */ return 0; @@ -3175,11 +3175,13 @@ size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, U32 windowSize = 0; U32 dictID = 0; U64 frameContentSize = 0; - if ((fhdByte & 0x08) != 0) return ERROR(frameParameter_unsupported); /* reserved bits, which must be zero */ + if ((fhdByte & 0x08) != 0) /* reserved bits, which must be zero */ + return ERROR(frameParameter_unsupported); if (!directMode) { BYTE const wlByte = ip[pos++]; U32 const windowLog = (wlByte >> 3) + ZSTDv07_WINDOWLOG_ABSOLUTEMIN; - if (windowLog > ZSTDv07_WINDOWLOG_MAX) return ERROR(frameParameter_unsupported); + if (windowLog > ZSTDv07_WINDOWLOG_MAX) + return ERROR(frameParameter_unsupported); windowSize = (1U << windowLog); windowSize += (windowSize >> 3) * (wlByte&7); } @@ -3201,7 +3203,8 @@ size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, case 3 : frameContentSize = MEM_readLE64(ip+pos); break; } if (!windowSize) windowSize = (U32)frameContentSize; - if (windowSize > windowSizeMax) return ERROR(frameParameter_unsupported); + if (windowSize > windowSizeMax) + return ERROR(frameParameter_unsupported); fparamsPtr->frameContentSize = frameContentSize; fparamsPtr->windowSize = windowSize; fparamsPtr->dictID = dictID; @@ -3220,11 +3223,10 @@ size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, - frame header not completely provided (`srcSize` too small) */ unsigned long long ZSTDv07_getDecompressedSize(const void* src, size_t srcSize) { - { ZSTDv07_frameParams fparams; - size_t const frResult = ZSTDv07_getFrameParams(&fparams, src, srcSize); - if (frResult!=0) return 0; - return fparams.frameContentSize; - } + ZSTDv07_frameParams fparams; + size_t const frResult = ZSTDv07_getFrameParams(&fparams, src, srcSize); + if (frResult!=0) return 0; + return fparams.frameContentSize; } diff --git a/programs/Makefile b/programs/Makefile index 4202764c2..912f9eff0 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -38,6 +38,9 @@ endif CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder \ -DXXH_NAMESPACE=ZSTD_ +ifeq ($(OS),Windows_NT) # MinGW assumed +CPPFLAGS += -D__USE_MINGW_ANSI_STDIO # compatibility with %zu formatting +endif CFLAGS ?= -O3 DEBUGFLAGS+=-Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ @@ -158,7 +161,7 @@ zstd-release: DEBUGFLAGS := zstd-release: zstd zstd32 : CPPFLAGS += $(THREAD_CPP) -zstd32 : LDFLAGS += $(THREAD_LD) +zstd32 : LDFLAGS += $(THREAD_LD) zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) zstd32 : $(ZSTDLIB_FILES) zstdcli.c fileio.c bench.c datagen.c dibio.c ifneq (,$(filter Windows%,$(OS))) diff --git a/programs/README.md b/programs/README.md index 2833875e5..0fa033ccf 100644 --- a/programs/README.md +++ b/programs/README.md @@ -151,6 +151,7 @@ Advanced arguments : Dictionary builder : --train ## : create a dictionary from a training set of files --train-cover[=k=#,d=#,steps=#,split=#] : use the cover algorithm with optional args +--train-fastcover[=k=#,d=#,f=#,steps=#,split=#,accel=#] : use the fastcover algorithm with optional args --train-legacy[=s=#] : use the legacy algorithm with selectivity (default: 9) -o file : `file` is dictionary name (default: dictionary) --maxdict=# : limit dictionary to specified size (default: 112640) @@ -185,7 +186,7 @@ version is less than `128 MiB`). Compression Speed vs Ratio | Decompression Speed ---------------------------|--------------------- -![Compression Speed vs Ratio](../doc/images/ldmCspeed.png "Compression Speed vs Ratio") | ![Decompression Speed](../doc/images/ldmDspeed.png "Decompression Speed") +![Compression Speed vs Ratio](https://raw.githubusercontent.com/facebook/zstd/v1.3.3/doc/images/ldmCspeed.png "Compression Speed vs Ratio") | ![Decompression Speed](https://raw.githubusercontent.com/facebook/zstd/v1.3.3/doc/images/ldmDspeed.png "Decompression Speed") | Method | Compression ratio | Compression speed | Decompression speed | |:-------|------------------:|-------------------------:|---------------------------:| @@ -208,10 +209,24 @@ The below table illustrates this on the [Silesia compression corpus]. [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia | Method | Compression ratio | Compression speed | Decompression speed | -|:-------|------------------:|-------------------------:|---------------------------:| -| `zstd -1` | `2.878` | `231.7 MB/s` | `594.4 MB/s` | -| `zstd -1 --long` | `2.929` | `106.5 MB/s` | `517.9 MB/s` | -| `zstd -5` | `3.274` | `77.1 MB/s` | `464.2 MB/s` | -| `zstd -5 --long` | `3.319` | `51.7 MB/s` | `371.9 MB/s` | -| `zstd -10` | `3.523` | `16.4 MB/s` | `489.2 MB/s` | -| `zstd -10 --long`| `3.566` | `16.2 MB/s` | `415.7 MB/s` | +|:-------|------------------:|------------------:|---------------------:| +| `zstd -1` | `2.878` | `231.7 MB/s` | `594.4 MB/s` | +| `zstd -1 --long` | `2.929` | `106.5 MB/s` | `517.9 MB/s` | +| `zstd -5` | `3.274` | `77.1 MB/s` | `464.2 MB/s` | +| `zstd -5 --long` | `3.319` | `51.7 MB/s` | `371.9 MB/s` | +| `zstd -10` | `3.523` | `16.4 MB/s` | `489.2 MB/s` | +| `zstd -10 --long`| `3.566` | `16.2 MB/s` | `415.7 MB/s` | + + +#### zstdgrep + +`zstdgrep` is a utility which makes it possible to `grep` directly a `.zst` compressed file. +It's used the same way as normal `grep`, for example : +`zstdgrep pattern file.zst` + +`zstdgrep` is _not_ compatible with dictionary compression. + +To search into a file compressed with a dictionary, +it's necessary to decompress it using `zstd` or `zstdcat`, +and then pipe the result to `grep`. For example : +`zstdcat -D dictionary -qc -- file.zst | grep pattern` diff --git a/programs/bench.c b/programs/bench.c index 76d1ff6dd..79ef42caa 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -171,6 +171,8 @@ struct BMK_timeState_t{ static void BMK_initCCtx(ZSTD_CCtx* ctx, const void* dictBuffer, size_t dictBufferSize, int cLevel, const ZSTD_compressionParameters* comprParams, const BMK_advancedParams_t* adv) { + ZSTD_CCtx_reset(ctx); + ZSTD_CCtx_resetParameters(ctx); if (adv->nbWorkers==1) { ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbWorkers, 0); } else { @@ -195,6 +197,7 @@ static void BMK_initCCtx(ZSTD_CCtx* ctx, static void BMK_initDCtx(ZSTD_DCtx* dctx, const void* dictBuffer, size_t dictBufferSize) { + ZSTD_DCtx_reset(dctx); ZSTD_DCtx_loadDictionary(dctx, dictBuffer, dictBufferSize); } @@ -291,34 +294,34 @@ BMK_customReturn_t BMK_benchFunction( BMK_initFn_t initFn, void* initPayload, size_t blockCount, const void* const * const srcBlockBuffers, const size_t* srcBlockSizes, - void* const * const dstBlockBuffers, const size_t* dstBlockCapacities, + void* const * const dstBlockBuffers, const size_t* dstBlockCapacities, size_t* blockResult, unsigned nbLoops) { - size_t srcSize = 0, dstSize = 0, ind = 0; + size_t dstSize = 0; U64 totalTime; BMK_customReturn_t retval; UTIL_time_t clockStart; - { - unsigned i; - for(i = 0; i < blockCount; i++) { - memset(dstBlockBuffers[i], 0xE5, dstBlockCapacities[i]); /* warm up and erase result buffer */ - } - - UTIL_sleepMilli(5); /* give processor time to other processes */ - UTIL_waitForNextTick(); - } - if(!nbLoops) { EXM_THROW_ND(1, BMK_customReturn_t, "nbLoops must be nonzero \n"); } - for(ind = 0; ind < blockCount; ind++) { - srcSize += srcBlockSizes[ind]; + { + size_t i; + for(i = 0; i < blockCount; i++) { + memset(dstBlockBuffers[i], 0xE5, dstBlockCapacities[i]); /* warm up and erase result buffer */ + } +#if 0 + /* based on testing these seem to lower accuracy of multiple calls of 1 nbLoops vs 1 call of multiple nbLoops + * (Makes former slower) + */ + UTIL_sleepMilli(5); /* give processor time to other processes */ + UTIL_waitForNextTick(); +#endif } { - unsigned i, j, firstIter = 1; + unsigned i, j; clockStart = UTIL_getTime(); if(initFn != NULL) { initFn(initPayload); } for(i = 0; i < nbLoops; i++) { @@ -327,11 +330,13 @@ BMK_customReturn_t BMK_benchFunction( if(ZSTD_isError(res)) { EXM_THROW_ND(2, BMK_customReturn_t, "Function benchmarking failed on block %u of size %u : %s \n", j, (U32)dstBlockCapacities[j], ZSTD_getErrorName(res)); - } else if(firstIter) { + } else if(i == nbLoops - 1) { dstSize += res; - } + if(blockResult != NULL) { + blockResult[j] = res; + } + } } - firstIter = 0; } totalTime = UTIL_clockSpanNano(clockStart); } @@ -353,6 +358,9 @@ void BMK_resetTimeState(BMK_timedFnState_t* r, unsigned nbSeconds) { BMK_timedFnState_t* BMK_createTimeState(unsigned nbSeconds) { BMK_timedFnState_t* r = (BMK_timedFnState_t*)malloc(sizeof(struct BMK_timeState_t)); + if(r == NULL) { + return r; + } BMK_resetTimeState(r, nbSeconds); return r; } @@ -361,13 +369,14 @@ void BMK_freeTimeState(BMK_timedFnState_t* state) { free(state); } +/* make option for dstBlocks to be */ BMK_customTimedReturn_t BMK_benchFunctionTimed( BMK_timedFnState_t* cont, BMK_benchFn_t benchFn, void* benchPayload, BMK_initFn_t initFn, void* initPayload, size_t blockCount, const void* const* const srcBlockBuffers, const size_t* srcBlockSizes, - void* const* const dstBlockBuffers, const size_t* dstBlockCapacities) + void * const * const dstBlockBuffers, const size_t * dstBlockCapacities, size_t* blockResults) { U64 fastest = cont->fastestTime; int completed = 0; @@ -382,9 +391,9 @@ BMK_customTimedReturn_t BMK_benchFunctionTimed( UTIL_sleep(COOLPERIOD_SEC); cont->coolTime = UTIL_getTime(); } - + /* reinitialize capacity */ r.result = BMK_benchFunction(benchFn, benchPayload, initFn, initPayload, - blockCount, srcBlockBuffers, srcBlockSizes, dstBlockBuffers, dstBlockCapacities, cont->nbLoops); + blockCount, srcBlockBuffers, srcBlockSizes, dstBlockBuffers, dstBlockCapacities, blockResults, cont->nbLoops); if(r.result.error) { /* completed w/ error */ r.completed = 1; return r; @@ -393,7 +402,7 @@ BMK_customTimedReturn_t BMK_benchFunctionTimed( { U64 const loopDuration = r.result.result.nanoSecPerRun * cont->nbLoops; r.completed = (cont->timeRemaining <= loopDuration); cont->timeRemaining -= loopDuration; - if (loopDuration > 0) { + if (loopDuration > (TIMELOOP_NANOSEC / 100)) { fastest = MIN(fastest, r.result.result.nanoSecPerRun); if(loopDuration >= MINUSABLETIME) { r.result.result.nanoSecPerRun = fastest; @@ -418,9 +427,9 @@ BMK_customTimedReturn_t BMK_benchFunctionTimed( /* benchMem with no allocation */ static BMK_return_t BMK_benchMemAdvancedNoAlloc( const void ** const srcPtrs, size_t* const srcSizes, - void** const cPtrs, size_t* const cSizes, + void** const cPtrs, size_t* const cCapacities, size_t* const cSizes, void** const resPtrs, size_t* const resSizes, - void* resultBuffer, void* compressedBuffer, + void** resultBufferPtr, void* compressedBuffer, const size_t maxCompressedSize, BMK_timedFnState_t* timeStateCompress, BMK_timedFnState_t* timeStateDecompress, @@ -432,7 +441,7 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( int displayLevel, const char* displayName, const BMK_advancedParams_t* adv) { size_t const blockSize = ((adv->blockSize>=32 && (adv->mode != BMK_decodeOnly)) ? adv->blockSize : srcSize) + (!srcSize); /* avoid div by 0 */ - BMK_return_t results; + BMK_return_t results = { { 0, 0, 0, 0 }, 0 } ; size_t const loadedCompressedSize = srcSize; size_t cSize = 0; double ratio = 0.; @@ -454,13 +463,13 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( srcPtr += fileSizes[fileNb]; } { size_t const decodedSize = (size_t)totalDSize64; - free(resultBuffer); - resultBuffer = malloc(decodedSize); - if (!resultBuffer) { + free(*resultBufferPtr); + *resultBufferPtr = malloc(decodedSize); + if (!(*resultBufferPtr)) { EXM_THROW(33, BMK_return_t, "not enough memory"); } if (totalDSize64 > decodedSize) { - free(resultBuffer); + free(*resultBufferPtr); EXM_THROW(32, BMK_return_t, "original size is too large"); /* size_t overflow */ } cSize = srcSize; @@ -472,7 +481,7 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( /* Init data blocks */ { const char* srcPtr = (const char*)srcBuffer; char* cPtr = (char*)compressedBuffer; - char* resPtr = (char*)resultBuffer; + char* resPtr = (char*)(*resultBufferPtr); U32 fileNb; for (nbBlocks=0, fileNb=0; fileNbmode == BMK_decodeOnly) ? thisBlockSize : ZSTD_compressBound(thisBlockSize); + cCapacities[nbBlocks] = (adv->mode == BMK_decodeOnly) ? thisBlockSize : ZSTD_compressBound(thisBlockSize); resPtrs[nbBlocks] = (void*)resPtr; resSizes[nbBlocks] = (adv->mode == BMK_decodeOnly) ? (size_t) ZSTD_findDecompressedSize(srcPtr, thisBlockSize) : thisBlockSize; srcPtr += thisBlockSize; - cPtr += cSizes[nbBlocks]; + cPtr += cCapacities[nbBlocks]; resPtr += thisBlockSize; remaining -= thisBlockSize; } @@ -538,7 +547,7 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( while(!(intermediateResultCompress.completed && intermediateResultDecompress.completed)) { if(!intermediateResultCompress.completed) { intermediateResultCompress = BMK_benchFunctionTimed(timeStateCompress, &local_defaultCompress, (void*)ctx, &local_initCCtx, (void*)&cctxprep, - nbBlocks, srcPtrs, srcSizes, cPtrs, cSizes); + nbBlocks, srcPtrs, srcSizes, cPtrs, cCapacities, cSizes); if(intermediateResultCompress.result.error) { results.error = intermediateResultCompress.result.error; return results; @@ -546,9 +555,7 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( ratio = (double)(srcSize / intermediateResultCompress.result.result.sumOfReturn); { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = ((double)srcSize / intermediateResultCompress.result.result.nanoSecPerRun) * 1000; - int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; - results.result.cSpeed = compressionSpeed * 1000000; + results.result.cSpeed = (srcSize * TIMELOOP_NANOSEC / intermediateResultCompress.result.result.nanoSecPerRun); cSize = intermediateResultCompress.result.result.sumOfReturn; results.result.cSize = cSize; ratio = (double)srcSize / results.result.cSize; @@ -556,13 +563,13 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s\r", marks[markNb], displayName, (U32)srcSize, (U32)results.result.cSize, ratioAccuracy, ratio, - cSpeedAccuracy, compressionSpeed); + results.result.cSpeed < (10 MB) ? 2 : 1, (double)results.result.cSpeed / (1 MB)); } } if(!intermediateResultDecompress.completed) { intermediateResultDecompress = BMK_benchFunctionTimed(timeStateDecompress, &local_defaultDecompress, (void*)(dctx), &local_initDCtx, (void*)&dctxprep, - nbBlocks, (const void* const*)cPtrs, cSizes, resPtrs, resSizes); + nbBlocks, (const void* const*)cPtrs, cSizes, resPtrs, resSizes, NULL); if(intermediateResultDecompress.result.error) { results.error = intermediateResultDecompress.result.error; return results; @@ -570,79 +577,79 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = results.result.cSpeed / 1000000; - int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; - double const decompressionSpeed = ((double)srcSize / intermediateResultDecompress.result.result.nanoSecPerRun) * 1000; - results.result.dSpeed = decompressionSpeed * 1000000; + results.result.dSpeed = (srcSize * TIMELOOP_NANOSEC/ intermediateResultDecompress.result.result.nanoSecPerRun); markNb = (markNb+1) % NB_MARKS; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s ,%6.1f MB/s \r", marks[markNb], displayName, (U32)srcSize, (U32)results.result.cSize, ratioAccuracy, ratio, - cSpeedAccuracy, compressionSpeed, - decompressionSpeed); + results.result.cSpeed < (10 MB) ? 2 : 1, (double)results.result.cSpeed / (1 MB), + (double)results.result.dSpeed / (1 MB)); } } } - } else { + } else { //iterMode; if(adv->mode != BMK_decodeOnly) { + BMK_customReturn_t compressionResults = BMK_benchFunction(&local_defaultCompress, (void*)ctx, &local_initCCtx, (void*)&cctxprep, - nbBlocks, srcPtrs, srcSizes, cPtrs, cSizes, adv->nbSeconds); + nbBlocks, srcPtrs, srcSizes, cPtrs, cCapacities, cSizes, adv->nbSeconds); if(compressionResults.error) { results.error = compressionResults.error; return results; } + if(compressionResults.result.nanoSecPerRun == 0) { results.result.cSpeed = 0; } else { - results.result.cSpeed = (double)srcSize / compressionResults.result.nanoSecPerRun * TIMELOOP_NANOSEC; + results.result.cSpeed = srcSize * TIMELOOP_NANOSEC / compressionResults.result.nanoSecPerRun; } + results.result.cSize = compressionResults.result.sumOfReturn; { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = results.result.cSpeed / 1000000; - int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; + cSize = compressionResults.result.sumOfReturn; + results.result.cSize = cSize; ratio = (double)srcSize / results.result.cSize; markNb = (markNb+1) % NB_MARKS; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s\r", marks[markNb], displayName, (U32)srcSize, (U32)results.result.cSize, ratioAccuracy, ratio, - cSpeedAccuracy, compressionSpeed); + results.result.cSpeed < (10 MB) ? 2 : 1, (double)results.result.cSpeed / (1 MB)); } } if(adv->mode != BMK_compressOnly) { BMK_customReturn_t decompressionResults = BMK_benchFunction( &local_defaultDecompress, (void*)(dctx), &local_initDCtx, (void*)&dctxprep, nbBlocks, - (const void* const*)cPtrs, cSizes, resPtrs, resSizes, + (const void* const*)cPtrs, cSizes, resPtrs, resSizes, NULL, adv->nbSeconds); if(decompressionResults.error) { results.error = decompressionResults.error; return results; } + if(decompressionResults.result.nanoSecPerRun == 0) { results.result.dSpeed = 0; } else { - results.result.dSpeed = (double)srcSize / decompressionResults.result.nanoSecPerRun * TIMELOOP_NANOSEC; + results.result.dSpeed = srcSize * TIMELOOP_NANOSEC / decompressionResults.result.nanoSecPerRun; } - { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - double const compressionSpeed = results.result.cSpeed / 1000000; - int const cSpeedAccuracy = (compressionSpeed < 10.) ? 2 : 1; - double const decompressionSpeed = ((double)srcSize / decompressionResults.result.nanoSecPerRun) * 1000; - results.result.dSpeed = decompressionSpeed * 1000000; + + { + int const ratioAccuracy = (ratio < 10.) ? 3 : 2; markNb = (markNb+1) % NB_MARKS; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s ,%6.1f MB/s \r", marks[markNb], displayName, (U32)srcSize, (U32)results.result.cSize, ratioAccuracy, ratio, - cSpeedAccuracy, compressionSpeed, - decompressionSpeed); + results.result.cSpeed < (10 MB) ? 2 : 1, (double)results.result.cSpeed / (1 MB), + (double)results.result.dSpeed / (1 MB)); } } } } /* CRC Checking */ - { U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); + { void* resultBuffer = *resultBufferPtr; + U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); /* adv->mode == 0 -> compress + decompress */ if ((adv->mode == BMK_both) && (crcOrig!=crcCheck)) { size_t u; @@ -682,8 +689,8 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( } /* CRC Checking */ if (displayLevel == 1) { /* hidden display mode -q, used by python speed benchmark */ - double const cSpeed = results.result.cSpeed / 1000000; - double const dSpeed = results.result.dSpeed / 1000000; + double const cSpeed = (double)results.result.cSpeed / (1 MB); + double const dSpeed = (double)results.result.dSpeed / (1 MB); if (adv->additionalParam) { DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, adv->additionalParam); } else { @@ -692,14 +699,16 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( } DISPLAYLEVEL(2, "%2i#\n", cLevel); } /* Bench */ + results.result.cMem = (1ULL << (comprParams->windowLog)) + ZSTD_sizeof_CCtx(ctx); + results.error = 0; return results; } BMK_return_t BMK_benchMemAdvanced(const void* srcBuffer, size_t srcSize, + void* dstBuffer, size_t dstCapacity, const size_t* fileSizes, unsigned nbFiles, const int cLevel, const ZSTD_compressionParameters* comprParams, const void* dictBuffer, size_t dictBufferSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx, int displayLevel, const char* displayName, const BMK_advancedParams_t* adv) { @@ -707,47 +716,72 @@ BMK_return_t BMK_benchMemAdvanced(const void* srcBuffer, size_t srcSize, U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; /* these are the blockTable parameters, just split up */ - const void ** const srcPtrs = (const void** const)malloc(maxNbBlocks * sizeof(void*)); - size_t* const srcSizes = (size_t* const)malloc(maxNbBlocks * sizeof(size_t)); + const void ** const srcPtrs = (const void**)malloc(maxNbBlocks * sizeof(void*)); + size_t* const srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - void ** const cPtrs = (void** const)malloc(maxNbBlocks * sizeof(void*)); - size_t* const cSizes = (size_t* const)malloc(maxNbBlocks * sizeof(size_t)); - void ** const resPtrs = (void** const)malloc(maxNbBlocks * sizeof(void*)); - size_t* const resSizes = (size_t* const)malloc(maxNbBlocks * sizeof(size_t)); + void ** const cPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); + size_t* const cSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + size_t* const cCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + void ** const resPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); + size_t* const resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - const size_t maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ - void* compressedBuffer = malloc(maxCompressedSize); - void* resultBuffer = malloc(srcSize); BMK_timedFnState_t* timeStateCompress = BMK_createTimeState(adv->nbSeconds); BMK_timedFnState_t* timeStateDecompress = BMK_createTimeState(adv->nbSeconds); - BMK_return_t results; - int allocationincomplete = !compressedBuffer || !resultBuffer || - !srcPtrs || !srcSizes || !cPtrs || !cSizes || !resPtrs || !resSizes; - if (!allocationincomplete) { - results = BMK_benchMemAdvancedNoAlloc(srcPtrs, srcSizes, cPtrs, cSizes, - resPtrs, resSizes, resultBuffer, compressedBuffer, maxCompressedSize, timeStateCompress, timeStateDecompress, + ZSTD_CCtx* ctx = ZSTD_createCCtx(); + ZSTD_DCtx* dctx = ZSTD_createDCtx(); + + const size_t maxCompressedSize = dstCapacity ? dstCapacity : ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); + + void* const internalDstBuffer = dstBuffer ? NULL : malloc(maxCompressedSize); + void* const compressedBuffer = dstBuffer ? dstBuffer : internalDstBuffer; + + BMK_return_t results = { { 0, 0, 0, 0 }, 0 }; + + int parametersConflict = !dstBuffer ^ !dstCapacity; + + void* resultBuffer = srcSize ? malloc(srcSize) : NULL; + + int allocationincomplete = !srcPtrs || !srcSizes || !cPtrs || + !cSizes || !cCapacities || !resPtrs || !resSizes || + !timeStateCompress || !timeStateDecompress || !compressedBuffer || !resultBuffer; + + + + if (!allocationincomplete && !parametersConflict) { + results = BMK_benchMemAdvancedNoAlloc(srcPtrs, srcSizes, cPtrs, cCapacities, cSizes, + resPtrs, resSizes, &resultBuffer, compressedBuffer, maxCompressedSize, timeStateCompress, timeStateDecompress, srcBuffer, srcSize, fileSizes, nbFiles, cLevel, comprParams, dictBuffer, dictBufferSize, ctx, dctx, displayLevel, displayName, adv); } + /* clean up */ BMK_freeTimeState(timeStateCompress); BMK_freeTimeState(timeStateDecompress); - free(compressedBuffer); + + ZSTD_freeCCtx(ctx); + ZSTD_freeDCtx(dctx); + + free(internalDstBuffer); free(resultBuffer); free((void*)srcPtrs); free(srcSizes); free(cPtrs); free(cSizes); + free(cCapacities); free(resPtrs); free(resSizes); if(allocationincomplete) { EXM_THROW(31, BMK_return_t, "allocation error : not enough memory"); } - results.error = 0; + + if(parametersConflict) { + EXM_THROW(32, BMK_return_t, "Conflicting input results"); + } return results; } @@ -755,42 +789,17 @@ BMK_return_t BMK_benchMem(const void* srcBuffer, size_t srcSize, const size_t* fileSizes, unsigned nbFiles, const int cLevel, const ZSTD_compressionParameters* comprParams, const void* dictBuffer, size_t dictBufferSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx, int displayLevel, const char* displayName) { const BMK_advancedParams_t adv = BMK_initAdvancedParams(); return BMK_benchMemAdvanced(srcBuffer, srcSize, + NULL, 0, fileSizes, nbFiles, cLevel, comprParams, dictBuffer, dictBufferSize, - ctx, dctx, displayLevel, displayName, &adv); } -static BMK_return_t BMK_benchMemCtxless(const void* srcBuffer, size_t srcSize, - const size_t* fileSizes, unsigned nbFiles, - int cLevel, const ZSTD_compressionParameters* const comprParams, - const void* dictBuffer, size_t dictBufferSize, - int displayLevel, const char* displayName, - const BMK_advancedParams_t* const adv) -{ - BMK_return_t res; - ZSTD_CCtx* ctx = ZSTD_createCCtx(); - ZSTD_DCtx* dctx = ZSTD_createDCtx(); - if(ctx == NULL || dctx == NULL) { - EXM_THROW(12, BMK_return_t, "not enough memory for contexts"); - } - res = BMK_benchMemAdvanced(srcBuffer, srcSize, - fileSizes, nbFiles, - cLevel, comprParams, - dictBuffer, dictBufferSize, - ctx, dctx, - displayLevel, displayName, adv); - ZSTD_freeCCtx(ctx); - ZSTD_freeDCtx(dctx); - return res; -} - static size_t BMK_findMaxMem(U64 requiredMem) { size_t const step = 64 MB; @@ -803,7 +812,7 @@ static size_t BMK_findMaxMem(U64 requiredMem) do { testmem = (BYTE*)malloc((size_t)requiredMem); requiredMem -= step; - } while (!testmem); + } while (!testmem && requiredMem > 0); free(testmem); return (size_t)(requiredMem); @@ -831,12 +840,12 @@ static BMK_return_t BMK_benchCLevel(const void* srcBuffer, size_t benchedSize, if (displayLevel == 1 && !adv->additionalParam) DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, adv->nbSeconds, (U32)(adv->blockSize>>10)); - res = BMK_benchMemCtxless(srcBuffer, benchedSize, - fileSizes, nbFiles, - cLevel, comprParams, - dictBuffer, dictBufferSize, - displayLevel, displayName, - adv); + res = BMK_benchMemAdvanced(srcBuffer, benchedSize, + NULL, 0, + fileSizes, nbFiles, + cLevel, comprParams, + dictBuffer, dictBufferSize, + displayLevel, displayName, adv); return res; } @@ -931,7 +940,8 @@ BMK_return_t BMK_benchFilesAdvanced(const char* const * const fileNamesTable, un if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; if (benchedSize < totalSizeToLoad) DISPLAY("Not enough memory; testing %u MB only...\n", (U32)(benchedSize >> 20)); - srcBuffer = malloc(benchedSize); + + srcBuffer = benchedSize ? malloc(benchedSize) : NULL; if (!srcBuffer) { free(dictBuffer); free(fileSizes); diff --git a/programs/bench.h b/programs/bench.h index 87cf56380..ad4ede1f0 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -32,8 +32,9 @@ extern "C" { typedef struct { size_t cSize; - double cSpeed; /* bytes / sec */ - double dSpeed; + U64 cSpeed; /* bytes / sec */ + U64 dSpeed; + size_t cMem; } BMK_result_t; ERROR_STRUCT(BMK_result_t, BMK_return_t); @@ -125,8 +126,6 @@ BMK_return_t BMK_syntheticTest(int cLevel, double compressibility, * comprParams - basic compression parameters * dictBuffer - a dictionary if used, null otherwise * dictBufferSize - size of dictBuffer, 0 otherwise - * ctx - Compression Context (must be provided) - * dctx - Decompression Context (must be provided) * diplayLevel - see BMK_benchFiles * displayName - name used by display * return @@ -138,15 +137,17 @@ BMK_return_t BMK_benchMem(const void* srcBuffer, size_t srcSize, const size_t* fileSizes, unsigned nbFiles, const int cLevel, const ZSTD_compressionParameters* comprParams, const void* dictBuffer, size_t dictBufferSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx, int displayLevel, const char* displayName); -/* See benchMem for normal parameter uses and return, see advancedParams_t for adv */ +/* See benchMem for normal parameter uses and return, see advancedParams_t for adv + * dstBuffer - destination buffer to write compressed output in, NULL if none provided. + * dstCapacity - capacity of destination buffer, give 0 if dstBuffer = NULL + */ BMK_return_t BMK_benchMemAdvanced(const void* srcBuffer, size_t srcSize, + void* dstBuffer, size_t dstCapacity, const size_t* fileSizes, unsigned nbFiles, const int cLevel, const ZSTD_compressionParameters* comprParams, const void* dictBuffer, size_t dictBufferSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx, int displayLevel, const char* displayName, const BMK_advancedParams_t* adv); @@ -170,8 +171,10 @@ typedef size_t (*BMK_initFn_t)(void*); * srcBuffers - an array of buffers to be operated on by benchFn * srcSizes - an array of the sizes of above buffers * dstBuffers - an array of buffers to be written into by benchFn - * dstCapacities - an array of the capacities of above buffers. + * dstCapacities - an array of the capacities of above buffers + * blockResults - the return value of benchFn called on each block. * nbLoops - defines number of times benchFn is run. + * assumed array of size blockCount, will have compressed size of each block written to it. * return * .error will give a nonzero value if ZSTD_isError() is nonzero for any of the return * of the calls to initFn and benchFn, or if benchFunction errors internally @@ -182,12 +185,11 @@ typedef size_t (*BMK_initFn_t)(void*); * into dstBuffer, hence this value will be the total amount of bytes written to * dstBuffer. */ -BMK_customReturn_t BMK_benchFunction( - BMK_benchFn_t benchFn, void* benchPayload, +BMK_customReturn_t BMK_benchFunction(BMK_benchFn_t benchFn, void* benchPayload, BMK_initFn_t initFn, void* initPayload, size_t blockCount, const void* const * const srcBuffers, const size_t* srcSizes, - void* const * const dstBuffers, const size_t* dstCapacities, + void * const * const dstBuffers, const size_t* dstCapacities, size_t* blockResults, unsigned nbLoops); @@ -216,7 +218,7 @@ BMK_customTimedReturn_t BMK_benchFunctionTimed(BMK_timedFnState_t* cont, BMK_initFn_t initFn, void* initPayload, size_t blockCount, const void* const * const srcBlockBuffers, const size_t* srcBlockSizes, - void* const * const dstBlockBuffers, const size_t* dstBlockCapacities); + void* const * const dstBlockBuffers, const size_t* dstBlockCapacities, size_t* blockResults); #endif /* BENCH_H_121279284357 */ diff --git a/programs/dibio.c b/programs/dibio.c index 5d1f6d6c4..4b68be6c9 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -27,6 +27,7 @@ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* errno */ +#include #include "mem.h" /* read */ #include "error_private.h" @@ -43,6 +44,7 @@ #define SAMPLESIZE_MAX (128 KB) #define MEMMULT 11 /* rough estimation : memory cost to analyze 1 byte of sample */ #define COVER_MEMMULT 9 /* rough estimation : memory cost to analyze 1 byte of sample */ +#define FASTCOVER_MEMMULT 1 /* rough estimation : memory cost to analyze 1 byte of sample */ static const size_t g_maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((size_t)(512 MB) << sizeof(size_t)); #define NOISELENGTH 32 @@ -165,6 +167,7 @@ static U32 DiB_rand(U32* src) static void DiB_shuffle(const char** fileNamesTable, unsigned nbFiles) { U32 seed = 0xFD2FB528; unsigned i; + assert(nbFiles >= 1); for (i = nbFiles - 1; i > 0; --i) { unsigned const j = DiB_rand(&seed) % (i + 1); const char* const tmp = fileNamesTable[j]; @@ -269,16 +272,19 @@ size_t ZDICT_trainFromBuffer_unsafe_legacy(void* dictBuffer, size_t dictBufferCa int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, const char** fileNamesTable, unsigned nbFiles, size_t chunkSize, - ZDICT_legacy_params_t *params, ZDICT_cover_params_t *coverParams, - int optimizeCover) + ZDICT_legacy_params_t* params, ZDICT_cover_params_t* coverParams, + ZDICT_fastCover_params_t* fastCoverParams, int optimize) { unsigned const displayLevel = params ? params->zParams.notificationLevel : coverParams ? coverParams->zParams.notificationLevel : + fastCoverParams ? fastCoverParams->zParams.notificationLevel : 0; /* should never happen */ void* const dictBuffer = malloc(maxDictSize); fileStats const fs = DiB_fileStats(fileNamesTable, nbFiles, chunkSize, displayLevel); size_t* const sampleSizes = (size_t*)malloc(fs.nbSamples * sizeof(size_t)); - size_t const memMult = params ? MEMMULT : COVER_MEMMULT; + size_t const memMult = params ? MEMMULT : + coverParams ? COVER_MEMMULT: + FASTCOVER_MEMMULT; size_t const maxMem = DiB_findMaxMem(fs.totalSizeToLoad * memMult) / memMult; size_t loadedSize = (size_t) MIN ((unsigned long long)maxMem, fs.totalSizeToLoad); void* const srcBuffer = malloc(loadedSize+NOISELENGTH); @@ -310,7 +316,8 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, /* Load input buffer */ DISPLAYLEVEL(3, "Shuffling input files\n"); DiB_shuffle(fileNamesTable, nbFiles); - nbFiles = DiB_loadFiles(srcBuffer, &loadedSize, sampleSizes, fs.nbSamples, fileNamesTable, nbFiles, chunkSize, displayLevel); + + DiB_loadFiles(srcBuffer, &loadedSize, sampleSizes, fs.nbSamples, fileNamesTable, nbFiles, chunkSize, displayLevel); { size_t dictSize; if (params) { @@ -318,17 +325,36 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, dictSize = ZDICT_trainFromBuffer_unsafe_legacy(dictBuffer, maxDictSize, srcBuffer, sampleSizes, fs.nbSamples, *params); - } else if (optimizeCover) { - dictSize = ZDICT_optimizeTrainFromBuffer_cover(dictBuffer, maxDictSize, - srcBuffer, sampleSizes, fs.nbSamples, - coverParams); - if (!ZDICT_isError(dictSize)) { - unsigned splitPercentage = (unsigned)(coverParams->splitPoint * 100); - DISPLAYLEVEL(2, "k=%u\nd=%u\nsteps=%u\nsplit=%u\n", coverParams->k, coverParams->d, coverParams->steps, splitPercentage); + } else if (coverParams) { + if (optimize) { + dictSize = ZDICT_optimizeTrainFromBuffer_cover(dictBuffer, maxDictSize, + srcBuffer, sampleSizes, fs.nbSamples, + coverParams); + if (!ZDICT_isError(dictSize)) { + unsigned splitPercentage = (unsigned)(coverParams->splitPoint * 100); + DISPLAYLEVEL(2, "k=%u\nd=%u\nsteps=%u\nsplit=%u\n", coverParams->k, coverParams->d, + coverParams->steps, splitPercentage); + } + } else { + dictSize = ZDICT_trainFromBuffer_cover(dictBuffer, maxDictSize, srcBuffer, + sampleSizes, fs.nbSamples, *coverParams); } } else { - dictSize = ZDICT_trainFromBuffer_cover(dictBuffer, maxDictSize, srcBuffer, - sampleSizes, fs.nbSamples, *coverParams); + assert(fastCoverParams != NULL); + if (optimize) { + dictSize = ZDICT_optimizeTrainFromBuffer_fastCover(dictBuffer, maxDictSize, + srcBuffer, sampleSizes, fs.nbSamples, + fastCoverParams); + if (!ZDICT_isError(dictSize)) { + unsigned splitPercentage = (unsigned)(fastCoverParams->splitPoint * 100); + DISPLAYLEVEL(2, "k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\naccel=%u\n", fastCoverParams->k, + fastCoverParams->d, fastCoverParams->f, fastCoverParams->steps, splitPercentage, + fastCoverParams->accel); + } + } else { + dictSize = ZDICT_trainFromBuffer_fastCover(dictBuffer, maxDictSize, srcBuffer, + sampleSizes, fs.nbSamples, *fastCoverParams); + } } if (ZDICT_isError(dictSize)) { DISPLAYLEVEL(1, "dictionary training failed : %s \n", ZDICT_getErrorName(dictSize)); /* should not happen */ diff --git a/programs/dibio.h b/programs/dibio.h index 499e30365..ea163fe6a 100644 --- a/programs/dibio.h +++ b/programs/dibio.h @@ -33,7 +33,7 @@ */ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, const char** fileNamesTable, unsigned nbFiles, size_t chunkSize, - ZDICT_legacy_params_t *params, ZDICT_cover_params_t *coverParams, - int optimizeCover); + ZDICT_legacy_params_t* params, ZDICT_cover_params_t* coverParams, + ZDICT_fastCover_params_t* fastCoverParams, int optimize); #endif diff --git a/programs/fileio.c b/programs/fileio.c index 85367fdfc..5f10958d7 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -727,11 +727,6 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, #endif -/*! FIO_compressFilename_internal() : - * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened. - * @return : 0 : compression completed correctly, - * 1 : missing or pb opening srcFileName - */ static unsigned long long FIO_compressZstdFrame(const cRess_t* ressPtr, const char* srcFileName, U64 fileSize, @@ -763,7 +758,8 @@ FIO_compressZstdFrame(const cRess_t* ressPtr, directive = ZSTD_e_end; result = 1; - while (inBuff.pos != inBuff.size || (directive == ZSTD_e_end && result != 0)) { + while ((inBuff.pos != inBuff.size) /* input buffer must be entirely ingested */ + || (directive == ZSTD_e_end && result != 0) ) { ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; CHECK_V(result, ZSTD_compress_generic(ress.cctx, &outBuff, &inBuff, directive)); @@ -786,7 +782,8 @@ FIO_compressZstdFrame(const cRess_t* ressPtr, (U32)(zfp.consumed >> 20), (U32)(zfp.produced >> 20), cShare ); - } else { /* g_displayLevel == 2 */ + } else { + /* g_displayLevel <= 2; only display notifications if == 2; */ DISPLAYLEVEL(2, "\rRead : %u ", (U32)(zfp.consumed >> 20)); if (fileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYLEVEL(2, "/ %u ", (U32)(fileSize >> 20)); @@ -1014,8 +1011,8 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile if (!dstFileName) { EXM_THROW(30, "zstd: %s", strerror(errno)); } } - strcpy(dstFileName, inFileNamesTable[u]); - strcat(dstFileName, suffix); + strncpy(dstFileName, inFileNamesTable[u], ifnSize+1 /* Include null */); + strncat(dstFileName, suffix, suffixSize); missed_files += FIO_compressFilename_dstFile(ress, dstFileName, inFileNamesTable[u], compressionLevel); } } diff --git a/programs/zstd.1 b/programs/zstd.1 index 58473102e..51825af74 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -194,7 +194,7 @@ All arguments after \fB\-\-\fR are treated as files Use FILEs as training set to create a dictionary\. The training set should contain a lot of small files (> 100), and weight typically 100x the target dictionary size (for example, 10 MB for a 100 KB dictionary)\. . .IP -Supports multithreading if \fBzstd\fR is compiled with threading support\. Additional parameters can be specified with \fB\-\-train\-cover\fR\. The legacy dictionary builder can be accessed with \fB\-\-train\-legacy\fR\. Equivalent to \fB\-\-train\-cover=d=8,steps=4\fR\. +Supports multithreading if \fBzstd\fR is compiled with threading support\. Additional parameters can be specified with \fB\-\-train\-cover\fR\ or \fB\-\-train\-fastcover\fR\. The legacy dictionary builder can be accessed with \fB\-\-train\-legacy\fR\. Equivalent to \fB\-\-train\-fastCover=d=8,steps=4\fR\. . .TP \fB\-o file\fR @@ -240,6 +240,25 @@ Examples: \fBzstd \-\-train\-cover=k=50 FILEs\fR . .TP +\fB\-\-train\-fastcover[=k#,d=#,f=#,steps=#,split=#,accel=#]\fR +Same as cover but with extra parameters \fIf\fR and \fIaccel\fR and different default value of split +. +.IP +If \fIsplit\fR is not specified, then it tries \fIsplit\fR = 75. If \fIf\fR is not specified, then it tries \fIf\fR = 18. Requires that 0 < \fIf\fR < 32. If \fIaccel\fR is not specified, then it tries \fIaccel\fR = 1. Requires that 0 < \fIaccel\fR <= 10. Requires that \fId\fR = 6 or \fId\fR = 8. +. +.IP +\fIf\fR is log of size of array that keeps track of frequency of subsegments of size \fId\fR. The subsegment is hashed to an index in the range [0,2^\fIf\fR - 1]. It is possible that 2 different subsegments are hashed to the same index, and they are considered as the same subsegment when computing frequency. Using a higher \fIf\fR reduces collision but takes longer. +. +.IP +Examples: +. +.IP +\fBzstd \-\-train\-fastcover FILEs\fR +. +.IP +\fBzstd \-\-train\-fastcover=d=8,f=15,accel=2 FILEs\fR +. +.TP \fB\-\-train\-legacy[=selectivity=#]\fR Use legacy dictionary builder algorithm with the given dictionary \fIselectivity\fR (default: 9)\. The smaller the \fIselectivity\fR value, the denser the dictionary, improving its efficiency but reducing its possible maximum size\. \fB\-\-train\-legacy=s=#\fR is also accepted\. . diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 055c5c244..324b765b7 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -254,6 +254,26 @@ Compression of small files similar to the sample set will be greatly improved. `zstd --train-cover=k=50,split=60 FILEs` +* `--train-fastcover[=k#,d=#,f=#,steps=#,split=#,accel=#]`: + Same as cover but with extra parameters _f_ and _accel_ and different default value of split + If _split_ is not specified, then it tries _split_ = 75. + If _f_ is not specified, then it tries _f_ = 18. + Requires that 0 < _f_ < 32. + If _accel_ is not specified, then it tries _accel_ = 1. + Requires that 0 < _accel_ <= 10. + Requires that _d_ = 6 or _d_ = 8. + + _f_ is log of size of array that keeps track of frequency of subsegments of size _d_. + The subsegment is hashed to an index in the range [0,2^_f_ - 1]. + It is possible that 2 different subsegments are hashed to the same index, and they are considered as the same subsegment when computing frequency. + Using a higher _f_ reduces collision but takes longer. + + Examples: + + `zstd --train-fastcover FILEs` + + `zstd --train-fastcover=d=8,f=15,accel=2 FILEs` + * `--train-legacy[=selectivity=#]`: Use legacy dictionary builder algorithm with the given dictionary _selectivity_ (default: 9). diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 36ba21156..f7c5934d9 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -84,7 +84,10 @@ static U32 g_ldmMinMatch = 0; static U32 g_ldmHashEveryLog = LDM_PARAM_DEFAULT; static U32 g_ldmBucketSizeLog = LDM_PARAM_DEFAULT; -#define DEFAULT_SPLITPOINT 1.0 + +#define DEFAULT_ACCEL 1 + +typedef enum { cover, fastCover, legacy } dictType; /*-************************************ * Display Macros @@ -172,6 +175,7 @@ static int usage_advanced(const char* programName) DISPLAY( "Dictionary builder : \n"); DISPLAY( "--train ## : create a dictionary from a training set of files \n"); DISPLAY( "--train-cover[=k=#,d=#,steps=#,split=#] : use the cover algorithm with optional args\n"); + DISPLAY( "--train-fastcover[=k=#,d=#,f=#,steps=#,split=#,accel=#] : use the fast cover algorithm with optional args\n"); DISPLAY( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel); DISPLAY( " -o file : `file` is dictionary name (default: %s) \n", g_defaultDictName); DISPLAY( "--maxdict=# : limit dictionary to specified size (default: %u) \n", g_defaultMaxDictSize); @@ -295,6 +299,33 @@ static unsigned parseCoverParameters(const char* stringPtr, ZDICT_cover_params_t return 1; } +/** + * parseFastCoverParameters() : + * reads fastcover parameters from *stringPtr (e.g. "--train-fastcover=k=48,d=8,f=20,steps=32,accel=2") into *params + * @return 1 means that fastcover parameters were correct + * @return 0 in case of malformed parameters + */ +static unsigned parseFastCoverParameters(const char* stringPtr, ZDICT_fastCover_params_t* params) +{ + memset(params, 0, sizeof(*params)); + for (; ;) { + if (longCommandWArg(&stringPtr, "k=")) { params->k = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "f=")) { params->f = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "steps=")) { params->steps = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "accel=")) { params->accel = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "split=")) { + unsigned splitPercentage = readU32FromChar(&stringPtr); + params->splitPoint = (double)splitPercentage / 100.0; + if (stringPtr[0]==',') { stringPtr++; continue; } else break; + } + return 0; + } + if (stringPtr[0] != 0) return 0; + DISPLAYLEVEL(4, "cover: k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\naccel=%u\n", params->k, params->d, params->f, params->steps, (unsigned)(params->splitPoint * 100), params->accel); + return 1; +} + /** * parseLegacyParameters() : * reads legacy dictioanry builter parameters from *stringPtr (e.g. "--train-legacy=selectivity=8") into *selectivity @@ -316,7 +347,19 @@ static ZDICT_cover_params_t defaultCoverParams(void) memset(¶ms, 0, sizeof(params)); params.d = 8; params.steps = 4; - params.splitPoint = DEFAULT_SPLITPOINT; + params.splitPoint = 1.0; + return params; +} + +static ZDICT_fastCover_params_t defaultFastCoverParams(void) +{ + ZDICT_fastCover_params_t params; + memset(¶ms, 0, sizeof(params)); + params.d = 8; + params.f = 18; + params.steps = 4; + params.splitPoint = 0.75; /* different from default splitPoint of cover */ + params.accel = DEFAULT_ACCEL; return params; } #endif @@ -406,7 +449,6 @@ int main(int argCount, const char* argv[]) singleThread = 0, ultra=0; double compressibility = 0.5; - BMK_advancedParams_t adv = BMK_initAdvancedParams(); unsigned bench_nbSeconds = 3; /* would be better if this value was synchronized from bench */ size_t blockSize = 0; zstd_operation_mode operation = zom_compress; @@ -432,7 +474,11 @@ int main(int argCount, const char* argv[]) #endif #ifndef ZSTD_NODICT ZDICT_cover_params_t coverParams = defaultCoverParams(); - int cover = 1; + ZDICT_fastCover_params_t fastCoverParams = defaultFastCoverParams(); + dictType dict = fastCover; +#endif +#ifndef ZSTD_NOBENCH + BMK_advancedParams_t benchParams = BMK_initAdvancedParams(); #endif @@ -528,18 +574,29 @@ int main(int argCount, const char* argv[]) operation = zom_train; if (outFileName == NULL) outFileName = g_defaultDictName; - cover = 1; + dict = cover; /* Allow optional arguments following an = */ if (*argument == 0) { memset(&coverParams, 0, sizeof(coverParams)); } else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); } else if (!parseCoverParameters(argument, &coverParams)) { CLEAN_RETURN(badusage(programName)); } continue; } + if (longCommandWArg(&argument, "--train-fastcover")) { + operation = zom_train; + if (outFileName == NULL) + outFileName = g_defaultDictName; + dict = fastCover; + /* Allow optional arguments following an = */ + if (*argument == 0) { memset(&fastCoverParams, 0, sizeof(fastCoverParams)); } + else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); } + else if (!parseFastCoverParameters(argument, &fastCoverParams)) { CLEAN_RETURN(badusage(programName)); } + continue; + } if (longCommandWArg(&argument, "--train-legacy")) { operation = zom_train; if (outFileName == NULL) outFileName = g_defaultDictName; - cover = 0; + dict = legacy; /* Allow optional arguments following an = */ if (*argument == 0) { continue; } else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); } @@ -620,7 +677,7 @@ int main(int argCount, const char* argv[]) /* Decoding */ case 'd': #ifndef ZSTD_NOBENCH - adv.mode = BMK_decodeOnly; + benchParams.mode = BMK_decodeOnly; if (operation==zom_bench) { argument++; break; } /* benchmark decode (hidden option) */ #endif operation=zom_decompress; argument++; break; @@ -713,7 +770,7 @@ int main(int argCount, const char* argv[]) case 'p': argument++; #ifndef ZSTD_NOBENCH if ((*argument>='0') && (*argument<='9')) { - adv.additionalParam = (int)readU32FromChar(&argument); + benchParams.additionalParam = (int)readU32FromChar(&argument); } else #endif main_pause=1; @@ -826,18 +883,18 @@ int main(int argCount, const char* argv[]) /* Check if benchmark is selected */ if (operation==zom_bench) { #ifndef ZSTD_NOBENCH - adv.blockSize = blockSize; - adv.nbWorkers = nbWorkers; - adv.realTime = setRealTimePrio; - adv.nbSeconds = bench_nbSeconds; - adv.ldmFlag = ldmFlag; - adv.ldmMinMatch = g_ldmMinMatch; - adv.ldmHashLog = g_ldmHashLog; + benchParams.blockSize = blockSize; + benchParams.nbWorkers = nbWorkers; + benchParams.realTime = setRealTimePrio; + benchParams.nbSeconds = bench_nbSeconds; + benchParams.ldmFlag = ldmFlag; + benchParams.ldmMinMatch = g_ldmMinMatch; + benchParams.ldmHashLog = g_ldmHashLog; if (g_ldmBucketSizeLog != LDM_PARAM_DEFAULT) { - adv.ldmBucketSizeLog = g_ldmBucketSizeLog; + benchParams.ldmBucketSizeLog = g_ldmBucketSizeLog; } if (g_ldmHashEveryLog != LDM_PARAM_DEFAULT) { - adv.ldmHashEveryLog = g_ldmHashEveryLog; + benchParams.ldmHashEveryLog = g_ldmHashEveryLog; } if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); @@ -852,22 +909,22 @@ int main(int argCount, const char* argv[]) int c; DISPLAYLEVEL(2, "Benchmarking %s \n", filenameTable[i]); for(c = cLevel; c <= cLevelLast; c++) { - BMK_benchFilesAdvanced(&filenameTable[i], 1, dictFileName, c, &compressionParams, g_displayLevel, &adv); + BMK_benchFilesAdvanced(&filenameTable[i], 1, dictFileName, c, &compressionParams, g_displayLevel, &benchParams); } } } else { for(; cLevel <= cLevelLast; cLevel++) { - BMK_benchFilesAdvanced(filenameTable, filenameIdx, dictFileName, cLevel, &compressionParams, g_displayLevel, &adv); + BMK_benchFilesAdvanced(filenameTable, filenameIdx, dictFileName, cLevel, &compressionParams, g_displayLevel, &benchParams); } } } else { for(; cLevel <= cLevelLast; cLevel++) { - BMK_syntheticTest(cLevel, compressibility, &compressionParams, g_displayLevel, &adv); + BMK_syntheticTest(cLevel, compressibility, &compressionParams, g_displayLevel, &benchParams); } } #else - (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; (void)separateFiles; + (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; (void)separateFiles; (void)compressibility; #endif goto _end; } @@ -879,17 +936,22 @@ int main(int argCount, const char* argv[]) zParams.compressionLevel = dictCLevel; zParams.notificationLevel = g_displayLevel; zParams.dictID = dictID; - if (cover) { + if (dict == cover) { int const optimize = !coverParams.k || !coverParams.d; coverParams.nbThreads = nbWorkers; coverParams.zParams = zParams; - operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, &coverParams, optimize); + operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, &coverParams, NULL, optimize); + } else if (dict == fastCover) { + int const optimize = !fastCoverParams.k || !fastCoverParams.d; + fastCoverParams.nbThreads = nbWorkers; + fastCoverParams.zParams = zParams; + operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, NULL, &fastCoverParams, optimize); } else { ZDICT_legacy_params_t dictParams; memset(&dictParams, 0, sizeof(dictParams)); dictParams.selectivityLevel = dictSelect; dictParams.zParams = zParams; - operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, &dictParams, NULL, 0); + operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, &dictParams, NULL, NULL, 0); } #endif goto _end; diff --git a/tests/.gitignore b/tests/.gitignore index 4911b2d62..da536251d 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -26,6 +26,7 @@ invalidDictionaries checkTag zcat zstdcat +tm # Tmp test directory zstdtest diff --git a/tests/Makefile b/tests/Makefile index 813380cc2..88a5d763c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -27,6 +27,9 @@ DEBUGLEVEL ?= 1 DEBUGFLAGS = -g -DDEBUGLEVEL=$(DEBUGLEVEL) CPPFLAGS += -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +ifeq ($(OS),Windows_NT) # MinGW assumed +CPPFLAGS += -D__USE_MINGW_ANSI_STDIO # compatibility with %zu formatting +endif CFLAGS ?= -O3 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ @@ -126,7 +129,7 @@ zstdmt_d_%.o : $(ZSTDDIR)/decompress/%.c fullbench32: CPPFLAGS += -m32 fullbench fullbench32 : CPPFLAGS += $(MULTITHREAD_CPP) fullbench fullbench32 : LDFLAGS += $(MULTITHREAD_LD) -fullbench fullbench32 : DEBUGFLAGS = # turn off assert() for speed measurements +fullbench fullbench32 : DEBUGFLAGS = -DNDEBUG # turn off assert() for speed measurements fullbench fullbench32 : $(ZSTD_FILES) fullbench fullbench32 : $(PRGDIR)/datagen.c $(PRGDIR)/bench.c fullbench.c $(CC) $(FLAGS) $^ -o $@$(EXT) @@ -197,7 +200,7 @@ zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c # xxh symbols not exposed from dll zstreamtest-dll : $(ZSTREAM_LOCAL_FILES) $(CC) $(CPPFLAGS) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@$(EXT) -paramgrill : DEBUGFLAGS = # turn off assert() for speed measurements +paramgrill : DEBUGFLAGS = -DNDEBUG # turn off assert() for speed measurements paramgrill : $(ZSTD_FILES) $(PRGDIR)/bench.c $(PRGDIR)/datagen.c paramgrill.c $(CC) $(FLAGS) $^ -lm -o $@$(EXT) diff --git a/tests/README.md b/tests/README.md index 24a28ab7b..f28766bd1 100644 --- a/tests/README.md +++ b/tests/README.md @@ -88,3 +88,56 @@ as well as the 10,000 original files for more detailed comparison of decompressi will choose a random seed, and for 1 minute, generate random test frames and ensure that the zstd library correctly decompresses them in both simple and streaming modes. + +#### `paramgrill` - tool for generating compression table parameters and optimizing parameters on file given constraints + +Full list of arguments +``` + -T# : set level 1 speed objective + -B# : cut input into blocks of size # (default : single block) + -S : benchmarks a single run (example command: -Sl3w10h12) + w# - windowLog + h# - hashLog + c# - chainLog + s# - searchLog + l# - searchLength + t# - targetLength + S# - strategy + L# - level + --zstd= : Single run, parameter selection syntax same as zstdcli with more parameters + (Added forceAttachDictionary / fadt) + When invoked with --optimize, this represents the sample to exceed. + --optimize= : find parameters to maximize compression ratio given parameters + Can use all --zstd= commands to constrain the type of solution found in addition to the following constraints + cSpeed= : Minimum compression speed + dSpeed= : Minimum decompression speed + cMem= : Maximum compression memory + lvl= : Searches for solutions which are strictly better than that compression lvl in ratio and cSpeed, + stc= : When invoked with lvl=, represents percentage slack in ratio/cSpeed allowed for a solution to be considered (Default 100%) + : In normal operation, represents percentage slack in choosing viable starting strategy selection in choosing the default parameters + (Lower value will begin with stronger strategies) (Default 90%) + speedRatio= (accepts decimals) + : determines value of gains in speed vs gains in ratio + when determining overall winner (default 5 (1% ratio = 5% speed)). + tries= : Maximum number of random restarts on a single strategy before switching (Default 5) + Higher values will make optimizer run longer, more chances to find better solution. + memLog : Limits the log of the size of each memotable (1 per strategy). Will use hash tables when state space is larger than max size. + Setting memLog = 0 turns off memoization + --display= : specifiy which parameters are included in the output + can use all --zstd parameter names and 'cParams' as a shorthand for all parameters used in ZSTD_compressionParameters + (Default: display all params available) + -P# : generated sample compressibility (when no file is provided) + -t# : Caps runtime of operation in seconds (default : 99999 seconds (about 27 hours )) + -v : Prints Benchmarking output + -D : Next argument dictionary file + -s : Benchmark all files separately + -q : Quiet, repeat for more quiet + -q Prints parameters + results whenever a new best is found + -qq Only prints parameters whenever a new best is found, prints final parameters + results + -qqq Only print final parameters + results + -qqqq Only prints final parameter set in the form --zstd= + -v : Verbose, cancels quiet, repeat for more volume + -v Prints all candidate parameters and results + +``` + Any inputs afterwards are treated as files to benchmark. diff --git a/tests/fullbench.c b/tests/fullbench.c index 7859745a3..12c1e1ae4 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -336,7 +336,7 @@ size_t local_ZSTD_decompressContinue(const void* src, size_t srcSize, void* dst, static size_t benchMem(const void* src, size_t srcSize, U32 benchNb, int cLevel, ZSTD_compressionParameters* cparams) { BYTE* dstBuff; - size_t const dstBuffSize = ZSTD_compressBound(srcSize); + size_t dstBuffSize = ZSTD_compressBound(srcSize); void* buff2, *buff1; const char* benchName; BMK_benchFn_t benchFunction; @@ -514,9 +514,10 @@ static size_t benchMem(const void* src, size_t srcSize, U32 benchNb, int cLevel, /* benchmark loop */ { + void* dstBuffv = (void*)dstBuff; r = BMK_benchFunction(benchFunction, buff2, NULL, NULL, 1, &src, &srcSize, - (void * const * const)&dstBuff, &dstBuffSize, g_nbIterations); + &dstBuffv, &dstBuffSize, NULL, g_nbIterations); if(r.error) { DISPLAY("ERROR %d ! ! \n", r.error); errorcode = r.error; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index f7bacd5ca..66b33c5ee 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -27,6 +27,7 @@ #include /* strcmp */ #include #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */ +#include "fse.h" #include "zstd.h" /* ZSTD_VERSION_STRING */ #include "zstd_errors.h" /* ZSTD_getErrorCode */ #include "zstdmt_compress.h" @@ -178,13 +179,9 @@ static void FUZ_displayMallocStats(mallocCounter_t count) (U32)(count.totalMalloc >> 10)); } -static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) +static int FUZ_mallocTests_internal(unsigned seed, double compressibility, unsigned part, + void* inBuffer, size_t inSize, void* outBuffer, size_t outSize) { - size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ - size_t const outSize = ZSTD_compressBound(inSize); - void* const inBuffer = malloc(inSize); - void* const outBuffer = malloc(outSize); - /* test only played in verbose mode, as they are long */ if (g_displayLevel<3) return 0; @@ -269,6 +266,28 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) return 0; } +static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) +{ + size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ + size_t const outSize = ZSTD_compressBound(inSize); + void* const inBuffer = malloc(inSize); + void* const outBuffer = malloc(outSize); + int result; + + /* Create compressible noise */ + if (!inBuffer || !outBuffer) { + DISPLAY("Not enough memory, aborting \n"); + exit(1); + } + + result = FUZ_mallocTests_internal(seed, compressibility, part, + inBuffer, inSize, outBuffer, outSize); + + free(inBuffer); + free(outBuffer); + return result; +} + #else static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) @@ -411,6 +430,26 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d : re-using a CCtx should compress the same : ", testNb++); + { int i; + for (i=0; i<20; i++) + ((char*)CNBuffer)[i] = (char)i; /* ensure no match during initial section */ + memcpy((char*)CNBuffer + 20, CNBuffer, 10); /* create one match, starting from beginning of sample, which is the difficult case (see #1241) */ + for (i=1; i<=19; i++) { + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + size_t size1, size2; + DISPLAYLEVEL(5, "l%i ", i); + size1 = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, CNBuffer, 30, i); + CHECK_Z(size1); + size2 = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, CNBuffer, 30, i); + CHECK_Z(size2); + CHECK_EQ(size1, size2); + + ZSTD_freeCCtx(cctx); + } + } + DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d : ZSTD_CCtx_getParameter() : ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); ZSTD_outBuffer out = {NULL, 0, 0}; @@ -1423,6 +1462,24 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : testing FSE_normalizeCount() PR#1255: ", testNb++); + { + short norm[32]; + unsigned count[32]; + unsigned const tableLog = 5; + size_t const nbSeq = 32; + unsigned const maxSymbolValue = 31; + size_t i; + + for (i = 0; i < 32; ++i) + count[i] = 1; + /* Calling FSE_normalizeCount() on a uniform distribution should not + * cause a division by zero. + */ + FSE_normalizeCount(norm, tableLog, count, nbSeq, maxSymbolValue); + } + DISPLAYLEVEL(3, "OK \n"); + _end: free(CNBuffer); free(compressedBuffer); @@ -1496,7 +1553,6 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD size_t const dstBufferSize = (size_t)1< /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ -#include #include #include "mem.h" @@ -27,7 +26,8 @@ #include "xxhash.h" #include "util.h" #include "bench.h" - +#include "zstd_errors.h" +#include "zstd_internal.h" /*-************************************ * Constants @@ -36,13 +36,7 @@ #define AUTHOR "Yann Collet" #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR - -#define KB *(1<<10) -#define MB *(1<<20) -#define GB *(1ULL<<30) - -#define NBLOOPS 2 -#define TIMELOOP (2 * SEC_TO_MICRO) +#define TIMELOOP_NANOSEC (1*1000000000ULL) /* 1 second */ #define NB_LEVELS_TRACKED 22 /* ensured being >= ZSTD_maxCLevel() in BMK_init_level_constraints() */ static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); @@ -56,39 +50,302 @@ static const int g_maxNbVariations = 64; * Macros **************************************/ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DISPLAYLEVEL(n, ...) if(g_displayLevel >= n) { fprintf(stderr, __VA_ARGS__); } +#define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); } + +#define TIMED 0 +#ifndef DEBUG +# define DEBUG 0 +#endif #undef MIN #undef MAX #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #define CUSTOM_LEVEL 99 +#define BASE_CLEVEL 1 + +#define FADT_MIN 0 +#define FADT_MAX ((U32)-1) + +#define ZSTD_TARGETLENGTH_MIN 0 +#define ZSTD_TARGETLENGTH_MAX 999 + +#define WLOG_RANGE (ZSTD_WINDOWLOG_MAX - ZSTD_WINDOWLOG_MIN + 1) +#define CLOG_RANGE (ZSTD_CHAINLOG_MAX - ZSTD_CHAINLOG_MIN + 1) +#define HLOG_RANGE (ZSTD_HASHLOG_MAX - ZSTD_HASHLOG_MIN + 1) +#define SLOG_RANGE (ZSTD_SEARCHLOG_MAX - ZSTD_SEARCHLOG_MIN + 1) +#define SLEN_RANGE (ZSTD_SEARCHLENGTH_MAX - ZSTD_SEARCHLENGTH_MIN + 1) +#define TLEN_RANGE 17 +#define STRT_RANGE (ZSTD_btultra - ZSTD_fast + 1) +#define FADT_RANGE 3 + +#define CHECKTIME(r) { if(BMK_timeSpan(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); return r; } } +#define CHECKTIMEGT(ret, val, _gototag) {if(BMK_timeSpan(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); ret = val; goto _gototag; } } + +#define PARAM_UNSET ((U32)-2) /* can't be -1 b/c fadt uses -1 */ + +static const char* g_stratName[ZSTD_btultra+1] = { + "(none) ", "ZSTD_fast ", "ZSTD_dfast ", + "ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ", + "ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra "}; + + +static const U32 tlen_table[TLEN_RANGE] = { 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 256, 512, 999 }; /*-************************************ -* Benchmark Parameters +* Setup for Adding new params **************************************/ -static double g_grillDuration_s = 99999; /* about 27 hours */ -static U32 g_nbIterations = NBLOOPS; -static double g_compressibility = COMPRESSIBILITY_DEFAULT; -static U32 g_blockSize = 0; -static U32 g_rand = 1; -static U32 g_singleRun = 0; -static U32 g_target = 0; -static U32 g_noSeed = 0; -static ZSTD_compressionParameters g_params = { 0, 0, 0, 0, 0, 0, ZSTD_greedy }; +/* indices for each of the variables */ +typedef enum { + wlog_ind = 0, + clog_ind = 1, + hlog_ind = 2, + slog_ind = 3, + slen_ind = 4, + tlen_ind = 5, + strt_ind = 6, + fadt_ind = 7, /* forceAttachDict */ + NUM_PARAMS = 8 +} varInds_t; -void BMK_SetNbIterations(int nbLoops) -{ - g_nbIterations = nbLoops; - DISPLAY("- %u iterations -\n", g_nbIterations); +typedef struct { + U32 vals[NUM_PARAMS]; +} paramValues_t; + +/* maximum value of parameters */ +static const U32 mintable[NUM_PARAMS] = + { ZSTD_WINDOWLOG_MIN, ZSTD_CHAINLOG_MIN, ZSTD_HASHLOG_MIN, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLENGTH_MIN, ZSTD_TARGETLENGTH_MIN, ZSTD_fast, FADT_MIN }; + +/* minimum value of parameters */ +static const U32 maxtable[NUM_PARAMS] = + { ZSTD_WINDOWLOG_MAX, ZSTD_CHAINLOG_MAX, ZSTD_HASHLOG_MAX, ZSTD_SEARCHLOG_MAX, ZSTD_SEARCHLENGTH_MAX, ZSTD_TARGETLENGTH_MAX, ZSTD_btultra, FADT_MAX }; + +/* # of values parameters can take on */ +static const U32 rangetable[NUM_PARAMS] = + { WLOG_RANGE, CLOG_RANGE, HLOG_RANGE, SLOG_RANGE, SLEN_RANGE, TLEN_RANGE, STRT_RANGE, FADT_RANGE }; + +/* ZSTD_cctxSetParameter() index to set */ +static const ZSTD_cParameter cctxSetParamTable[NUM_PARAMS] = + { ZSTD_p_windowLog, ZSTD_p_chainLog, ZSTD_p_hashLog, ZSTD_p_searchLog, ZSTD_p_minMatch, ZSTD_p_targetLength, ZSTD_p_compressionStrategy, ZSTD_p_forceAttachDict }; + +/* names of parameters */ +static const char* g_paramNames[NUM_PARAMS] = + { "windowLog", "chainLog", "hashLog","searchLog", "searchLength", "targetLength", "strategy", "forceAttachDict" }; + +/* shortened names of parameters */ +static const char* g_shortParamNames[NUM_PARAMS] = + { "wlog", "clog", "hlog","slog", "slen", "tlen", "strt", "fadt" }; + +/* maps value from { 0 to rangetable[param] - 1 } to valid paramvalues */ +static U32 rangeMap(varInds_t param, int ind) { + ind = MAX(MIN(ind, (int)rangetable[param] - 1), 0); + switch(param) { + case tlen_ind: + return tlen_table[ind]; + case fadt_ind: /* 0, 1, 2 -> -1, 0, 1 */ + return ind - 1; + case wlog_ind: /* using default: triggers -Wswitch-enum */ + case clog_ind: + case hlog_ind: + case slog_ind: + case slen_ind: + case strt_ind: + return mintable[param] + ind; + case NUM_PARAMS: + DISPLAY("Error, not a valid param\n "); + return (U32)-1; + } + return 0; /* should never happen, stop compiler warnings */ } +/* inverse of rangeMap */ +static int invRangeMap(varInds_t param, U32 value) { + value = MIN(MAX(mintable[param], value), maxtable[param]); + switch(param) { + case tlen_ind: /* bin search */ + { + int lo = 0; + int hi = TLEN_RANGE; + while(lo < hi) { + int mid = (lo + hi) / 2; + if(tlen_table[mid] < value) { + lo = mid + 1; + } if(tlen_table[mid] == value) { + return mid; + } else { + hi = mid; + } + } + return lo; + } + case fadt_ind: + return (int)value + 1; + case wlog_ind: + case clog_ind: + case hlog_ind: + case slog_ind: + case slen_ind: + case strt_ind: + return value - mintable[param]; + case NUM_PARAMS: + DISPLAY("Error, not a valid param\n "); + return -2; + } + return 0; /* should never happen, stop compiler warnings */ +} + +/* display of params */ +static void displayParamVal(FILE* f, varInds_t param, U32 value, int width) { + switch(param) { + case fadt_ind: if(width) { fprintf(f, "%*d", width, (int)value); } else { fprintf(f, "%d", (int)value); } break; + case strt_ind: if(width) { fprintf(f, "%*s", width, g_stratName[value]); } else { fprintf(f, "%s", g_stratName[value]); } break; + case wlog_ind: + case clog_ind: + case hlog_ind: + case slog_ind: + case slen_ind: + case tlen_ind: if(width) { fprintf(f, "%*u", width, value); } else { fprintf(f, "%u", value); } break; + case NUM_PARAMS: + DISPLAY("Error, not a valid param\n "); break; + } +} + +/*-************************************ +* Benchmark Parameters/Global Variables +**************************************/ + +typedef BYTE U8; + +/* General Utility */ +static U32 g_timeLimit_s = 99999; /* about 27 hours */ +static UTIL_time_t g_time; /* to be used to compare solution finding speeds to compare to original */ +static U32 g_blockSize = 0; +static U32 g_rand = 1; + +/* Display */ +static int g_displayLevel = 3; +static BYTE g_silenceParams[NUM_PARAMS]; + +/* Mode Selection */ +static U32 g_singleRun = 0; +static U32 g_optimizer = 0; +static int g_optmode = 0; + +/* For cLevel Table generation */ +static U32 g_target = 0; +static U32 g_noSeed = 0; + +/* For optimizer */ +static paramValues_t g_params; /* Initialized at the beginning of main w/ emptyParams() function */ +static double g_ratioMultiplier = 5.; +static U32 g_strictness = PARAM_UNSET; /* range 1 - 100, measure of how strict */ +static BMK_result_t g_lvltarget; + +typedef enum { + directMap, + xxhashMap, + noMemo +} memoTableType_t; + +typedef struct { + memoTableType_t tableType; + BYTE* table; + size_t tableLen; + varInds_t varArray[NUM_PARAMS]; + size_t varLen; +} memoTable_t; + +typedef struct { + BMK_result_t result; + paramValues_t params; +} winnerInfo_t; + +typedef struct { + U32 cSpeed; /* bytes / sec */ + U32 dSpeed; + U32 cMem; /* bytes */ +} constraint_t; + +typedef struct winner_ll_node winner_ll_node; +struct winner_ll_node { + winnerInfo_t res; + winner_ll_node* next; +}; + +static winner_ll_node* g_winners; /* linked list sorted ascending by cSize & cSpeed */ + +/* + * Additional Global Variables (Defined Above Use) + * g_level_constraint + * g_alreadyTested + * g_maxTries + * g_clockGranularity + */ + /*-******************************************************* -* Private functions +* General Util Functions *********************************************************/ -/* accuracy in seconds only, span can be multiple years */ -static double BMK_timeSpan(time_t tStart) { return difftime(time(NULL), tStart); } +/* nullified useless params, to ensure count stats */ +/* cleans up params for memoizing / display */ +static paramValues_t sanitizeParams(paramValues_t params) +{ + if (params.vals[strt_ind] == ZSTD_fast) + params.vals[clog_ind] = 0, params.vals[slog_ind] = 0; + if (params.vals[strt_ind] == ZSTD_dfast) + params.vals[slog_ind] = 0; + if (params.vals[strt_ind] != ZSTD_btopt && params.vals[strt_ind] != ZSTD_btultra && params.vals[strt_ind] != ZSTD_fast) + params.vals[tlen_ind] = 0; + + return params; +} + +static ZSTD_compressionParameters pvalsToCParams(paramValues_t p) { + ZSTD_compressionParameters c; + memset(&c, 0, sizeof(ZSTD_compressionParameters)); + c.windowLog = p.vals[wlog_ind]; + c.chainLog = p.vals[clog_ind]; + c.hashLog = p.vals[hlog_ind]; + c.searchLog = p.vals[slog_ind]; + c.searchLength = p.vals[slen_ind]; + c.targetLength = p.vals[tlen_ind]; + c.strategy = p.vals[strt_ind]; + /* no forceAttachDict */ + return c; +} + +static paramValues_t cParamsToPVals(ZSTD_compressionParameters c) { + paramValues_t p; + varInds_t i; + p.vals[wlog_ind] = c.windowLog; + p.vals[clog_ind] = c.chainLog; + p.vals[hlog_ind] = c.hashLog; + p.vals[slog_ind] = c.searchLog; + p.vals[slen_ind] = c.searchLength; + p.vals[tlen_ind] = c.targetLength; + p.vals[strt_ind] = c.strategy; + + /* set all other params to their minimum value */ + for(i = strt_ind + 1; i < NUM_PARAMS; i++) { + p.vals[i] = mintable[i]; + } + return p; +} + +/* equivalent of ZSTD_adjustCParams for paramValues_t */ +static paramValues_t adjustParams(paramValues_t p, const size_t maxBlockSize, const size_t dictSize) { + paramValues_t ot = p; + varInds_t i; + p = cParamsToPVals(ZSTD_adjustCParams(pvalsToCParams(p), maxBlockSize, dictSize)); + if(!dictSize) { p.vals[fadt_ind] = 0; } + /* retain value of all other parameters */ + for(i = strt_ind + 1; i < NUM_PARAMS; i++) { + p.vals[i] = ot.vals[i]; + } + return p; +} static size_t BMK_findMaxMem(U64 requiredMem) { @@ -98,16 +355,18 @@ static size_t BMK_findMaxMem(U64 requiredMem) requiredMem = (((requiredMem >> 26) + 1) << 26); if (requiredMem > maxMemory) requiredMem = maxMemory; - requiredMem += 2*step; - while (!testmem) { - requiredMem -= step; + requiredMem += 2 * step; + while (!testmem && requiredMem > 0) { testmem = malloc ((size_t)requiredMem); + requiredMem -= step; } free (testmem); - return (size_t) (requiredMem - step); + return (size_t) requiredMem; } +/* accuracy in seconds only, span can be multiple years */ +static U32 BMK_timeSpan(const UTIL_time_t tStart) { return (U32)(UTIL_clockSpanMicro(tStart) / 1000000ULL); } static U32 FUZ_rotl32(U32 x, U32 r) { @@ -126,75 +385,467 @@ U32 FUZ_rand(U32* src) return rand32 >> 5; } -/** longCommandWArg() : - * check if *stringPtr is the same as longCommand. - * If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand. - * @return 0 and doesn't modify *stringPtr otherwise. - * from zstdcli.c +/* allows zeros */ +#define CLAMPCHECK(val,min,max) { \ + if (((val)<(min)) | ((val)>(max))) { \ + DISPLAY("INVALID PARAMETER CONSTRAINTS\n"); \ + return 0; \ +} } + +static int paramValid(const paramValues_t paramTarget) { + U32 i; + for(i = 0; i < NUM_PARAMS; i++) { + CLAMPCHECK(paramTarget.vals[i], mintable[i], maxtable[i]); + } + return 1; +} + +static paramValues_t cParamUnsetMin(paramValues_t paramTarget) { + varInds_t i; + for(i = 0; i < NUM_PARAMS; i++) { + if(paramTarget.vals[i] == PARAM_UNSET) { + paramTarget.vals[i] = mintable[i]; + } + } + return paramTarget; +} + +static paramValues_t emptyParams(void) { + U32 i; + paramValues_t p; + for(i = 0; i < NUM_PARAMS; i++) { + p.vals[i] = PARAM_UNSET; + } + return p; +} + +static winnerInfo_t initWinnerInfo(const paramValues_t p) { + winnerInfo_t w1; + w1.result.cSpeed = 0.; + w1.result.dSpeed = 0.; + w1.result.cMem = (size_t)-1; + w1.result.cSize = (size_t)-1; + w1.params = p; + return w1; +} + +static paramValues_t overwriteParams(paramValues_t base, const paramValues_t mask) { + U32 i; + for(i = 0; i < NUM_PARAMS; i++) { + if(mask.vals[i] != PARAM_UNSET) { + base.vals[i] = mask.vals[i]; + } + } + return base; +} + +static void paramVaryOnce(const varInds_t paramIndex, const int amt, paramValues_t* ptr) { + ptr->vals[paramIndex] = rangeMap(paramIndex, invRangeMap(paramIndex, ptr->vals[paramIndex]) + amt); +} + +/* varies ptr by nbChanges respecting varyParams*/ +static void paramVariation(paramValues_t* ptr, memoTable_t* mtAll, const U32 nbChanges) +{ + paramValues_t p; + U32 validated = 0; + while (!validated) { + U32 i; + p = *ptr; + for (i = 0 ; i < nbChanges ; i++) { + const U32 changeID = (U32)FUZ_rand(&g_rand) % (mtAll[p.vals[strt_ind]].varLen << 1); + paramVaryOnce(mtAll[p.vals[strt_ind]].varArray[changeID >> 1], ((changeID & 1) << 1) - 1, &p); + } + validated = paramValid(p); + } + *ptr = p; +} + +/* Completely random parameter selection */ +static paramValues_t randomParams(void) +{ + varInds_t v; paramValues_t p; + for(v = 0; v <= NUM_PARAMS; v++) { + p.vals[v] = rangeMap(v, FUZ_rand(&g_rand) % rangetable[v]); + } + return p; +} + +static U64 g_clockGranularity = 100000000ULL; + +static void findClockGranularity(void) { + UTIL_time_t clockStart = UTIL_getTime(); + U64 el1 = 0, el2 = 0; + int i = 0; + do { + el1 = el2; + el2 = UTIL_clockSpanNano(clockStart); + if(el1 < el2) { + U64 iv = el2 - el1; + if(g_clockGranularity > iv) { + g_clockGranularity = iv; + i = 0; + } else { + i++; + } + } + } while(i < 10); + DEBUGOUTPUT("Granularity: %llu\n", (unsigned long long)g_clockGranularity); +} + +/*-************************************ +* Optimizer Util Functions +**************************************/ + +/* checks results are feasible */ +static int feasible(const BMK_result_t results, const constraint_t target) { + return (results.cSpeed >= target.cSpeed) && (results.dSpeed >= target.dSpeed) && (results.cMem <= target.cMem) && (!g_optmode || results.cSize <= g_lvltarget.cSize); +} + +/* hill climbing value for part 1 */ +/* Scoring here is a linear reward for all set constraints normalized between 0 to 1 + * (with 0 at 0 and 1 being fully fulfilling the constraint), summed with a logarithmic + * bonus to exceeding the constraint value. We also give linear ratio for compression ratio. + * The constant factors are experimental. */ -static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) -{ - size_t const comSize = strlen(longCommand); - int const result = !strncmp(*stringPtr, longCommand, comSize); - if (result) *stringPtr += comSize; - return result; +static double resultScore(const BMK_result_t res, const size_t srcSize, const constraint_t target) { + double cs = 0., ds = 0., rt, cm = 0.; + const double r1 = 1, r2 = 0.1, rtr = 0.5; + double ret; + if(target.cSpeed) { cs = res.cSpeed / (double)target.cSpeed; } + if(target.dSpeed) { ds = res.dSpeed / (double)target.dSpeed; } + if(target.cMem != (U32)-1) { cm = (double)target.cMem / res.cMem; } + rt = ((double)srcSize / res.cSize); + + ret = (MIN(1, cs) + MIN(1, ds) + MIN(1, cm))*r1 + rt * rtr + + (MAX(0, log(cs))+ MAX(0, log(ds))+ MAX(0, log(cm))) * r2; + + return ret; } -/*-******************************************************* -* Bench functions -*********************************************************/ - -typedef struct -{ - const char* srcPtr; - size_t srcSize; - char* cPtr; - size_t cRoom; - size_t cSize; - char* resPtr; - size_t resSize; -} blockParam_t; - - -const char* g_stratName[ZSTD_btultra+1] = { - "(none) ", "ZSTD_fast ", "ZSTD_dfast ", - "ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ", - "ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra "}; - -/* TODO: support additional parameters (more files, fileSizes) */ -static size_t -BMK_benchParam(BMK_result_t* resultPtr, - const void* srcBuffer, size_t srcSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx, - const ZSTD_compressionParameters cParams) { - - BMK_return_t res = BMK_benchMem(srcBuffer,srcSize, &srcSize, 1, 0, &cParams, NULL, 0, ctx, dctx, 0, "File"); - *resultPtr = res.result; - return res.error; +/* calculates normalized squared euclidean distance of result1 if it is in the first quadrant relative to lvlRes */ +static double resultDistLvl(const BMK_result_t result1, const BMK_result_t lvlRes) { + double normalizedCSpeedGain1 = (result1.cSpeed / lvlRes.cSpeed) - 1; + double normalizedRatioGain1 = ((double)lvlRes.cSize / result1.cSize) - 1; + if(normalizedRatioGain1 < 0 || normalizedCSpeedGain1 < 0) { + return 0.0; + } + return normalizedRatioGain1 * g_ratioMultiplier + normalizedCSpeedGain1; } -static void BMK_printWinner(FILE* f, U32 cLevel, BMK_result_t result, ZSTD_compressionParameters params, size_t srcSize) +/* return true if r2 strictly better than r1 */ +static int compareResultLT(const BMK_result_t result1, const BMK_result_t result2, const constraint_t target, size_t srcSize) { + if(feasible(result1, target) && feasible(result2, target)) { + if(g_optmode) { + return resultDistLvl(result1, g_lvltarget) < resultDistLvl(result2, g_lvltarget); + } else { + return (result1.cSize > result2.cSize) || (result1.cSize == result2.cSize && result2.cSpeed > result1.cSpeed) + || (result1.cSize == result2.cSize && result2.cSpeed == result1.cSpeed && result2.dSpeed > result1.dSpeed); + } + } + return feasible(result2, target) || (!feasible(result1, target) && (resultScore(result1, srcSize, target) < resultScore(result2, srcSize, target))); +} + +static constraint_t relaxTarget(constraint_t target) { + target.cMem = (U32)-1; + target.cSpeed *= ((double)g_strictness) / 100; + target.dSpeed *= ((double)g_strictness) / 100; + return target; +} + +static void optimizerAdjustInput(paramValues_t* pc, const size_t maxBlockSize) { + varInds_t v; + for(v = 0; v < NUM_PARAMS; v++) { + if(pc->vals[v] != PARAM_UNSET) { + U32 newval = MIN(MAX(pc->vals[v], mintable[v]), maxtable[v]); + if(newval != pc->vals[v]) { + pc->vals[v] = newval; + DISPLAY("Warning: parameter %s not in valid range, adjusting to ", g_paramNames[v]); displayParamVal(stderr, v, newval, 0); DISPLAY("\n"); + } + } + } + + if(pc->vals[wlog_ind] != PARAM_UNSET) { + + U32 sshb = maxBlockSize > 1 ? ZSTD_highbit32((U32)(maxBlockSize-1)) + 1 : 1; + /* edge case of highBit not working for 0 */ + + if(maxBlockSize < (1ULL << 31) && sshb + 1 < pc->vals[wlog_ind]) { + U32 adjust = MAX(mintable[wlog_ind], sshb); + if(adjust != pc->vals[wlog_ind]) { + pc->vals[wlog_ind] = adjust; + DISPLAY("Warning: windowLog larger than src/block size, adjusted to %u\n", pc->vals[wlog_ind]); + } + } + } + + if(pc->vals[wlog_ind] != PARAM_UNSET && pc->vals[clog_ind] != PARAM_UNSET) { + U32 maxclog; + if(pc->vals[strt_ind] == PARAM_UNSET || pc->vals[strt_ind] >= (U32)ZSTD_btlazy2) { + maxclog = pc->vals[wlog_ind] + 1; + } else { + maxclog = pc->vals[wlog_ind]; + } + + if(pc->vals[clog_ind] > maxclog) { + pc->vals[clog_ind] = maxclog; + DISPLAY("Warning: chainlog too much larger than windowLog size, adjusted to %u\n", pc->vals[clog_ind]); + } + } + + if(pc->vals[wlog_ind] != PARAM_UNSET && pc->vals[hlog_ind] != PARAM_UNSET) { + if(pc->vals[wlog_ind] + 1 < pc->vals[hlog_ind]) { + pc->vals[hlog_ind] = pc->vals[wlog_ind] + 1; + DISPLAY("Warning: hashlog too much larger than windowLog size, adjusted to %u\n", pc->vals[hlog_ind]); + } + } + + if(pc->vals[slog_ind] != PARAM_UNSET && pc->vals[clog_ind] != PARAM_UNSET) { + if(pc->vals[slog_ind] > pc->vals[clog_ind]) { + pc->vals[clog_ind] = pc->vals[slog_ind]; + DISPLAY("Warning: searchLog larger than chainLog, adjusted to %u\n", pc->vals[slog_ind]); + } + } +} + +static int redundantParams(const paramValues_t paramValues, const constraint_t target, const size_t maxBlockSize) { + return + (ZSTD_estimateCStreamSize_usingCParams(pvalsToCParams(paramValues)) > (size_t)target.cMem) /* Uses too much memory */ + || ((1ULL << (paramValues.vals[wlog_ind] - 1)) >= maxBlockSize && paramValues.vals[wlog_ind] != mintable[wlog_ind]) /* wlog too much bigger than src size */ + || (paramValues.vals[clog_ind] > (paramValues.vals[wlog_ind] + (paramValues.vals[strt_ind] > ZSTD_btlazy2))) /* chainLog larger than windowLog*/ + || (paramValues.vals[slog_ind] > paramValues.vals[clog_ind]) /* searchLog larger than chainLog */ + || (paramValues.vals[hlog_ind] > paramValues.vals[wlog_ind] + 1); /* hashLog larger than windowLog + 1 */ + +} + +/*-************************************ +* Display Functions +**************************************/ + +static void BMK_translateAdvancedParams(FILE* f, const paramValues_t params) { + varInds_t v; + int first = 1; + fprintf(f,"--zstd="); + for(v = 0; v < NUM_PARAMS; v++) { + if(g_silenceParams[v]) { continue; } + if(!first) { fprintf(f, ","); } + fprintf(f,"%s=", g_paramNames[v]); + + if(v == strt_ind) { fprintf(f,"%u", params.vals[v]); } + else { displayParamVal(f, v, params.vals[v], 0); } + first = 0; + } + fprintf(f, "\n"); +} + +static void BMK_displayOneResult(FILE* f, winnerInfo_t res, const size_t srcSize) { + varInds_t v; + int first = 1; + res.params = cParamUnsetMin(res.params); + fprintf(f," {"); + for(v = 0; v < NUM_PARAMS; v++) { + if(g_silenceParams[v]) { continue; } + if(!first) { fprintf(f, ","); } + displayParamVal(f, v, res.params.vals[v], 3); + first = 0; + } + + fprintf(f, " }, /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */\n", + (double)srcSize / res.result.cSize, (double)res.result.cSpeed / (1 MB), (double)res.result.dSpeed / (1 MB)); +} + +/* Writes to f the results of a parameter benchmark */ +/* when used with --optimize, will only print results better than previously discovered */ +static void BMK_printWinner(FILE* f, const int cLevel, const BMK_result_t result, const paramValues_t params, const size_t srcSize) { char lvlstr[15] = "Custom Level"; - DISPLAY("\r%79s\r", ""); - fprintf(f," {%3u,%3u,%3u,%3u,%3u,%3u, %s }, ", - params.windowLog, params.chainLog, params.hashLog, params.searchLog, params.searchLength, - params.targetLength, g_stratName[(U32)(params.strategy)]); + winnerInfo_t w; + w.params = params; + w.result = result; + + fprintf(f, "\r%79s\r", ""); + if(cLevel != CUSTOM_LEVEL) { - snprintf(lvlstr, 15, " Level %2u ", cLevel); + snprintf(lvlstr, 15, " Level %2d ", cLevel); } - fprintf(f, - "/* %s */ /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */\n", - lvlstr, (double)srcSize / result.cSize, result.cSpeed / 1000000., result.dSpeed / 1000000.); + + if(TIMED) { + const U64 time = UTIL_clockSpanNano(g_time); + const U64 minutes = time / (60ULL * TIMELOOP_NANOSEC); + fprintf(f, "%1lu:%2lu:%05.2f - ", (unsigned long) minutes / 60,(unsigned long) minutes % 60, (double)(time - minutes * TIMELOOP_NANOSEC * 60ULL)/TIMELOOP_NANOSEC); + } + + fprintf(f, "/* %s */ ", lvlstr); + BMK_displayOneResult(f, w, srcSize); } +/* comparison function: */ +/* strictly better, strictly worse, equal, speed-side adv, size-side adv */ +#define WORSE_RESULT 0 +#define BETTER_RESULT 1 +#define ERROR_RESULT 2 -typedef struct { - BMK_result_t result; - ZSTD_compressionParameters params; -} winnerInfo_t; +#define SPEED_RESULT 4 +#define SIZE_RESULT 5 +/* maybe have epsilon-eq to limit table size? */ +static int speedSizeCompare(const BMK_result_t r1, const BMK_result_t r2) { + if(r1.cSpeed < r2.cSpeed) { + if(r1.cSize >= r2.cSize) { + return BETTER_RESULT; + } + return SPEED_RESULT; /* r2 is smaller but not faster. */ + } else { + if(r1.cSize <= r2.cSize) { + return WORSE_RESULT; + } + return SIZE_RESULT; /* r2 is faster but not smaller */ + } +} -static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSize) +/* 0 for insertion, 1 for no insert */ +/* maintain invariant speedSizeCompare(n, n->next) = SPEED_RESULT */ +static int insertWinner(const winnerInfo_t w, const constraint_t targetConstraints) { + BMK_result_t r = w.result; + winner_ll_node* cur_node = g_winners; + /* first node to insert */ + if(!feasible(r, targetConstraints)) { + return 1; + } + + if(g_winners == NULL) { + winner_ll_node* first_node = malloc(sizeof(winner_ll_node)); + if(first_node == NULL) { + return 1; + } + first_node->next = NULL; + first_node->res = w; + g_winners = first_node; + return 0; + } + + while(cur_node->next != NULL) { + switch(speedSizeCompare(cur_node->res.result, r)) { + case WORSE_RESULT: + { + return 1; /* never insert if better */ + } + case BETTER_RESULT: + { + winner_ll_node* tmp; + cur_node->res = cur_node->next->res; + tmp = cur_node->next; + cur_node->next = cur_node->next->next; + free(tmp); + break; + } + case SIZE_RESULT: + { + cur_node = cur_node->next; + break; + } + case SPEED_RESULT: /* insert after first size result, then return */ + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = cur_node->res; + cur_node->res = w; + newnode->next = cur_node->next; + cur_node->next = newnode; + return 0; + } + } + + } + + assert(cur_node->next == NULL); + switch(speedSizeCompare(cur_node->res.result, r)) { + case WORSE_RESULT: + { + return 1; /* never insert if better */ + } + case BETTER_RESULT: + { + cur_node->res = w; + return 0; + } + case SIZE_RESULT: + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = w; + newnode->next = NULL; + cur_node->next = newnode; + return 0; + } + case SPEED_RESULT: /* insert before first size result, then return */ + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = cur_node->res; + cur_node->res = w; + newnode->next = cur_node->next; + cur_node->next = newnode; + return 0; + } + default: + return 1; + } +} + +static void BMK_printWinnerOpt(FILE* f, const U32 cLevel, const BMK_result_t result, const paramValues_t params, const constraint_t targetConstraints, const size_t srcSize) +{ + /* global winner used for constraints */ + /* cSize, cSpeed, dSpeed, cMem */ + static winnerInfo_t g_winner = { { (size_t)-1LL, 0, 0, (size_t)-1LL }, { { PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET } } }; + if(DEBUG || compareResultLT(g_winner.result, result, targetConstraints, srcSize) || g_displayLevel >= 4) { + if(DEBUG && compareResultLT(g_winner.result, result, targetConstraints, srcSize)) { + DISPLAY("New Winner: \n"); + } + + if(g_displayLevel >= 2) { BMK_printWinner(f, cLevel, result, params, srcSize); } + + if(compareResultLT(g_winner.result, result, targetConstraints, srcSize)) { + if(g_displayLevel >= 1) { BMK_translateAdvancedParams(f, params); } + g_winner.result = result; + g_winner.params = params; + } + } + + if(g_optmode && g_optimizer && (DEBUG || g_displayLevel == 3)) { + winnerInfo_t w; + winner_ll_node* n; + w.result = result; + w.params = params; + insertWinner(w, targetConstraints); + + if(!DEBUG) { fprintf(f, "\033c"); } + fprintf(f, "\n"); + + /* the table */ + fprintf(f, "================================\n"); + for(n = g_winners; n != NULL; n = n->next) { + BMK_displayOneResult(f, n->res, srcSize); + } + fprintf(f, "================================\n"); + fprintf(f, "Level Bounds: R: > %.3f AND C: < %.1f MB/s \n\n", + (double)srcSize / g_lvltarget.cSize, (double)g_lvltarget.cSpeed / (1 MB)); + + + fprintf(f, "Overall Winner: \n"); + BMK_displayOneResult(f, g_winner, srcSize); + BMK_translateAdvancedParams(f, g_winner.params); + + fprintf(f, "Latest BMK: \n");\ + BMK_displayOneResult(f, w, srcSize); + } +} + +static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, const size_t srcSize) { int cLevel; @@ -206,7 +857,7 @@ static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSi } -static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSize) +static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, const size_t srcSize) { fseek(f, 0, SEEK_SET); BMK_printWinners2(f, winners, srcSize); @@ -215,9 +866,774 @@ static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSiz } +/*-******************************************************* +* Functions to Benchmark +*********************************************************/ + typedef struct { - double cSpeed_min; - double dSpeed_min; + ZSTD_CCtx* ctx; + const void* dictBuffer; + size_t dictBufferSize; + int cLevel; + const paramValues_t* comprParams; +} BMK_initCCtxArgs; + +static size_t local_initCCtx(void* payload) { + const BMK_initCCtxArgs* ag = (const BMK_initCCtxArgs*)payload; + varInds_t i; + ZSTD_CCtx_reset(ag->ctx); + ZSTD_CCtx_resetParameters(ag->ctx); + ZSTD_CCtx_setParameter(ag->ctx, ZSTD_p_compressionLevel, ag->cLevel); + + for(i = 0; i < NUM_PARAMS; i++) { + if(ag->comprParams->vals[i] != PARAM_UNSET) + ZSTD_CCtx_setParameter(ag->ctx, cctxSetParamTable[i], ag->comprParams->vals[i]); + } + ZSTD_CCtx_loadDictionary(ag->ctx, ag->dictBuffer, ag->dictBufferSize); + + return 0; +} + +typedef struct { + ZSTD_DCtx* dctx; + const void* dictBuffer; + size_t dictBufferSize; +} BMK_initDCtxArgs; + +static size_t local_initDCtx(void* payload) { + const BMK_initDCtxArgs* ag = (const BMK_initDCtxArgs*)payload; + ZSTD_DCtx_reset(ag->dctx); + ZSTD_DCtx_loadDictionary(ag->dctx, ag->dictBuffer, ag->dictBufferSize); + return 0; +} + +/* additional argument is just the context */ +static size_t local_defaultCompress( + const void* srcBuffer, size_t srcSize, + void* dstBuffer, size_t dstSize, + void* addArgs) { + size_t moreToFlush = 1; + ZSTD_CCtx* ctx = (ZSTD_CCtx*)addArgs; + ZSTD_inBuffer in; + ZSTD_outBuffer out; + in.src = srcBuffer; + in.size = srcSize; + in.pos = 0; + out.dst = dstBuffer; + out.size = dstSize; + out.pos = 0; + assert(dstSize == ZSTD_compressBound(srcSize)); /* specific to this version, which is only used in paramgrill */ + while (moreToFlush) { + if(out.pos == out.size) { + return (size_t)-ZSTD_error_dstSize_tooSmall; + } + moreToFlush = ZSTD_compress_generic(ctx, &out, &in, ZSTD_e_end); + if (ZSTD_isError(moreToFlush)) { + return moreToFlush; + } + } + return out.pos; +} + +/* additional argument is just the context */ +static size_t local_defaultDecompress( + const void* srcBuffer, size_t srcSize, + void* dstBuffer, size_t dstSize, + void* addArgs) { + size_t moreToFlush = 1; + ZSTD_DCtx* dctx = (ZSTD_DCtx*)addArgs; + ZSTD_inBuffer in; + ZSTD_outBuffer out; + in.src = srcBuffer; + in.size = srcSize; + in.pos = 0; + out.dst = dstBuffer; + out.size = dstSize; + out.pos = 0; + while (moreToFlush) { + if(out.pos == out.size) { + return (size_t)-ZSTD_error_dstSize_tooSmall; + } + moreToFlush = ZSTD_decompress_generic(dctx, + &out, &in); + if (ZSTD_isError(moreToFlush)) { + return moreToFlush; + } + } + return out.pos; + +} + +/*-************************************ +* Data Initialization Functions +**************************************/ + +typedef struct { + void* srcBuffer; + size_t srcSize; + const void** srcPtrs; + size_t* srcSizes; + void** dstPtrs; + size_t* dstCapacities; + size_t* dstSizes; + void** resPtrs; + size_t* resSizes; + size_t nbBlocks; + size_t maxBlockSize; +} buffers_t; + +typedef struct { + size_t dictSize; + void* dictBuffer; + ZSTD_CCtx* cctx; + ZSTD_DCtx* dctx; +} contexts_t; + +static void freeNonSrcBuffers(const buffers_t b) { + free(b.srcPtrs); + free(b.srcSizes); + + if(b.dstPtrs != NULL) { + free(b.dstPtrs[0]); + } + free(b.dstPtrs); + free(b.dstCapacities); + free(b.dstSizes); + + if(b.resPtrs != NULL) { + free(b.resPtrs[0]); + } + free(b.resPtrs); + free(b.resSizes); +} + +static void freeBuffers(const buffers_t b) { + if(b.srcPtrs != NULL) { + free(b.srcBuffer); + } + freeNonSrcBuffers(b); +} + +/* srcBuffer will be freed by freeBuffers now */ +static int createBuffersFromMemory(buffers_t* buff, void * srcBuffer, const size_t nbFiles, + const size_t* fileSizes) +{ + size_t pos = 0, n, blockSize; + U32 maxNbBlocks, blockNb = 0; + buff->srcSize = 0; + for(n = 0; n < nbFiles; n++) { + buff->srcSize += fileSizes[n]; + } + + if(buff->srcSize == 0) { + DISPLAY("No data to bench\n"); + return 1; + } + + blockSize = g_blockSize ? g_blockSize : buff->srcSize; + maxNbBlocks = (U32) ((buff->srcSize + (blockSize-1)) / blockSize) + (U32)nbFiles; + + buff->srcPtrs = (const void**)calloc(maxNbBlocks, sizeof(void*)); + buff->srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + buff->dstPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); + buff->dstCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + buff->dstSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + buff->resPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); + buff->resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + if(!buff->srcPtrs || !buff->srcSizes || !buff->dstPtrs || !buff->dstCapacities || !buff->dstSizes || !buff->resPtrs || !buff->resSizes) { + DISPLAY("alloc error\n"); + freeNonSrcBuffers(*buff); + return 1; + } + + buff->srcBuffer = srcBuffer; + buff->srcPtrs[0] = (const void*)buff->srcBuffer; + buff->dstPtrs[0] = malloc(ZSTD_compressBound(buff->srcSize) + (maxNbBlocks * 1024)); + buff->resPtrs[0] = malloc(buff->srcSize); + + if(!buff->dstPtrs[0] || !buff->resPtrs[0]) { + DISPLAY("alloc error\n"); + freeNonSrcBuffers(*buff); + return 1; + } + + for(n = 0; n < nbFiles; n++) { + size_t pos_end = pos + fileSizes[n]; + for(; pos < pos_end; blockNb++) { + buff->srcPtrs[blockNb] = (const void*)((char*)srcBuffer + pos); + buff->srcSizes[blockNb] = blockSize; + pos += blockSize; + } + + if(fileSizes[n] > 0) { buff->srcSizes[blockNb - 1] = ((fileSizes[n] - 1) % blockSize) + 1; } + pos = pos_end; + } + + buff->dstCapacities[0] = ZSTD_compressBound(buff->srcSizes[0]); + buff->dstSizes[0] = buff->dstCapacities[0]; + buff->resSizes[0] = buff->srcSizes[0]; + buff->maxBlockSize = buff->srcSizes[0]; + + for(n = 1; n < blockNb; n++) { + buff->dstPtrs[n] = ((char*)buff->dstPtrs[n-1]) + buff->dstCapacities[n-1]; + buff->resPtrs[n] = ((char*)buff->resPtrs[n-1]) + buff->resSizes[n-1]; + buff->dstCapacities[n] = ZSTD_compressBound(buff->srcSizes[n]); + buff->dstSizes[n] = buff->dstCapacities[n]; + buff->resSizes[n] = buff->srcSizes[n]; + + buff->maxBlockSize = MAX(buff->maxBlockSize, buff->srcSizes[n]); + } + + buff->nbBlocks = blockNb; + + return 0; +} + +/* allocates buffer's arguments. returns success / failuere */ +static int createBuffers(buffers_t* buff, const char* const * const fileNamesTable, + size_t nbFiles) { + size_t pos = 0; + size_t n; + size_t totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, (U32)nbFiles); + size_t benchedSize = MIN(BMK_findMaxMem(totalSizeToLoad * 3) / 3, totalSizeToLoad); + size_t* fileSizes = calloc(sizeof(size_t), nbFiles); + void* srcBuffer = NULL; + int ret = 0; + + if(!totalSizeToLoad || !benchedSize) { + ret = 1; + DISPLAY("Nothing to Bench\n"); + goto _cleanUp; + } + + srcBuffer = malloc(benchedSize); + + if(!fileSizes || !srcBuffer) { + ret = 1; + goto _cleanUp; + } + + for(n = 0; n < nbFiles; n++) { + FILE* f; + U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); + if (UTIL_isDirectory(fileNamesTable[n])) { + DISPLAY("Ignoring %s directory... \n", fileNamesTable[n]); + continue; + } + if (fileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAY("Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]); + continue; + } + f = fopen(fileNamesTable[n], "rb"); + if (f==NULL) { + DISPLAY("impossible to open file %s\n", fileNamesTable[n]); + fclose(f); + ret = 10; + goto _cleanUp; + } + + DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[n]); + + if (fileSize + pos > benchedSize) fileSize = benchedSize - pos, nbFiles=n; /* buffer too small - stop after this file */ + { + char* buffer = (char*)(srcBuffer); + size_t const readSize = fread((buffer)+pos, 1, (size_t)fileSize, f); + fclose(f); + if (readSize != (size_t)fileSize) { + DISPLAY("could not read %s", fileNamesTable[n]); + ret = 1; + goto _cleanUp; + } + + fileSizes[n] = readSize; + pos += readSize; + } + } + + ret = createBuffersFromMemory(buff, srcBuffer, nbFiles, fileSizes); + +_cleanUp: + if(ret) { free(srcBuffer); } + free(fileSizes); + return ret; +} + +static void freeContexts(const contexts_t ctx) { + free(ctx.dictBuffer); + ZSTD_freeCCtx(ctx.cctx); + ZSTD_freeDCtx(ctx.dctx); +} + +static int createContexts(contexts_t* ctx, const char* dictFileName) { + FILE* f; + size_t readSize; + ctx->cctx = ZSTD_createCCtx(); + ctx->dctx = ZSTD_createDCtx(); + if(dictFileName == NULL) { + ctx->dictSize = 0; + ctx->dictBuffer = NULL; + return 0; + } + ctx->dictSize = UTIL_getFileSize(dictFileName); + ctx->dictBuffer = malloc(ctx->dictSize); + + f = fopen(dictFileName, "rb"); + + if(!f) { + DISPLAY("unable to open file\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + + if(ctx->dictSize > 64 MB || !(ctx->dictBuffer)) { + DISPLAY("dictionary too large\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + readSize = fread(ctx->dictBuffer, 1, ctx->dictSize, f); + if(readSize != ctx->dictSize) { + DISPLAY("unable to read file\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + fclose(f); + return 0; +} + +/*-************************************ +* Optimizer Memoization Functions +**************************************/ + +/* return: new length */ +/* keep old array, will need if iter over strategy. */ +/* prunes useless params */ +static size_t sanitizeVarArray(varInds_t* varNew, const size_t varLength, const varInds_t* varArray, const ZSTD_strategy strat) { + size_t i, j = 0; + for(i = 0; i < varLength; i++) { + if( !((varArray[i] == clog_ind && strat == ZSTD_fast) + || (varArray[i] == slog_ind && strat == ZSTD_fast) + || (varArray[i] == slog_ind && strat == ZSTD_dfast) + || (varArray[i] == tlen_ind && strat != ZSTD_btopt && strat != ZSTD_btultra && strat != ZSTD_fast))) { + varNew[j] = varArray[i]; + j++; + } + } + return j; +} + +/* res should be NUM_PARAMS size */ +/* constructs varArray from paramValues_t style parameter */ +/* pass in using dict. */ +static size_t variableParams(const paramValues_t paramConstraints, varInds_t* res, const int usingDictionary) { + varInds_t i; + size_t j = 0; + for(i = 0; i < NUM_PARAMS; i++) { + if(paramConstraints.vals[i] == PARAM_UNSET) { + if(i == fadt_ind && !usingDictionary) continue; /* don't use fadt if no dictionary */ + res[j] = i; j++; + } + } + return j; +} + +/* length of memo table given free variables */ +static size_t memoTableLen(const varInds_t* varyParams, const size_t varyLen) { + size_t arrayLen = 1; + size_t i; + for(i = 0; i < varyLen; i++) { + if(varyParams[i] == strt_ind) continue; /* strategy separated by table */ + arrayLen *= rangetable[varyParams[i]]; + } + return arrayLen; +} + +/* returns unique index in memotable of compression parameters */ +static unsigned memoTableIndDirect(const paramValues_t* ptr, const varInds_t* varyParams, const size_t varyLen) { + size_t i; + unsigned ind = 0; + for(i = 0; i < varyLen; i++) { + varInds_t v = varyParams[i]; + if(v == strt_ind) continue; /* exclude strategy from memotable */ + ind *= rangetable[v]; ind += (unsigned)invRangeMap(v, ptr->vals[v]); + } + return ind; +} + +static size_t memoTableGet(const memoTable_t* memoTableArray, const paramValues_t p) { + const memoTable_t mt = memoTableArray[p.vals[strt_ind]]; + switch(mt.tableType) { + case directMap: + return mt.table[memoTableIndDirect(&p, mt.varArray, mt.varLen)]; + case xxhashMap: + return mt.table[(XXH64(&p.vals, sizeof(U32) * NUM_PARAMS, 0) >> 3) % mt.tableLen]; + case noMemo: + return 0; + } + return 0; /* should never happen, stop compiler warnings */ +} + +static void memoTableSet(const memoTable_t* memoTableArray, const paramValues_t p, const BYTE value) { + const memoTable_t mt = memoTableArray[p.vals[strt_ind]]; + switch(mt.tableType) { + case directMap: + mt.table[memoTableIndDirect(&p, mt.varArray, mt.varLen)] = value; break; + case xxhashMap: + mt.table[(XXH64(&p.vals, sizeof(U32) * NUM_PARAMS, 0) >> 3) % mt.tableLen] = value; break; + case noMemo: + break; + } +} + +/* frees all allocated memotables */ +static void freeMemoTableArray(memoTable_t* const mtAll) { + int i; + if(mtAll == NULL) { return; } + for(i = 1; i <= (int)ZSTD_btultra; i++) { + free(mtAll[i].table); + } + free(mtAll); +} + +/* inits memotables for all (including mallocs), all strategies */ +/* takes unsanitized varyParams */ +static memoTable_t* createMemoTableArray(const paramValues_t p, const varInds_t* const varyParams, const size_t varyLen, const U32 memoTableLog) { + memoTable_t* mtAll = (memoTable_t*)calloc(sizeof(memoTable_t),(ZSTD_btultra + 1)); + ZSTD_strategy i, stratMin = ZSTD_fast, stratMax = ZSTD_btultra; + + if(mtAll == NULL) { + return NULL; + } + + for(i = 1; i <= (int)ZSTD_btultra; i++) { + mtAll[i].varLen = sanitizeVarArray(mtAll[i].varArray, varyLen, varyParams, i); + } + + /* no memoization */ + if(memoTableLog == 0) { + for(i = 1; i <= (int)ZSTD_btultra; i++) { + mtAll[i].tableType = noMemo; + mtAll[i].table = NULL; + mtAll[i].tableLen = 0; + } + return mtAll; + } + + + if(p.vals[strt_ind] != PARAM_UNSET) { + stratMin = p.vals[strt_ind]; + stratMax = p.vals[strt_ind]; + } + + + for(i = stratMin; i <= stratMax; i++) { + size_t mtl = memoTableLen(mtAll[i].varArray, mtAll[i].varLen); + mtAll[i].tableType = directMap; + + if(memoTableLog != PARAM_UNSET && mtl > (1ULL << memoTableLog)) { /* use hash table */ /* provide some option to only use hash tables? */ + mtAll[i].tableType = xxhashMap; + mtl = (1ULL << memoTableLog); + } + + mtAll[i].table = (BYTE*)calloc(sizeof(BYTE), mtl); + mtAll[i].tableLen = mtl; + + if(mtAll[i].table == NULL) { + freeMemoTableArray(mtAll); + return NULL; + } + } + + return mtAll; +} + +/* Sets pc to random unmeasured set of parameters */ +/* specifiy strategy */ +static void randomConstrainedParams(paramValues_t* pc, const memoTable_t* memoTableArray, const ZSTD_strategy st) +{ + size_t j; + const memoTable_t mt = memoTableArray[st]; + pc->vals[strt_ind] = st; + for(j = 0; j < mt.tableLen; j++) { + int i; + for(i = 0; i < NUM_PARAMS; i++) { + varInds_t v = mt.varArray[i]; + if(v == strt_ind) continue; + pc->vals[v] = rangeMap(v, FUZ_rand(&g_rand) % rangetable[v]); + } + + if(!(memoTableGet(memoTableArray, *pc))) break; /* only pick unpicked params. */ + } +} + +/*-************************************ +* Benchmarking Functions +**************************************/ + +/* Replicate functionality of benchMemAdvanced, but with pre-split src / dst buffers */ +/* The purpose is so that sufficient information is returned so that a decompression call to benchMemInvertible is possible */ +/* BMK_benchMemAdvanced(srcBuffer,srcSize, dstBuffer, dstSize, fileSizes, nbFiles, 0, &cParams, dictBuffer, dictSize, ctx, dctx, 0, "File", &adv); */ +/* nbSeconds used in same way as in BMK_advancedParams_t, as nbIters when in iterMode */ + +/* if in decodeOnly, then srcPtr's will be compressed blocks, and uncompressedBlocks will be written to dstPtrs */ +/* dictionary nullable, nothing else though. */ +static BMK_return_t BMK_benchMemInvertible(const buffers_t buf, const contexts_t ctx, + const int cLevel, const paramValues_t* comprParams, + const BMK_mode_t mode, const BMK_loopMode_t loopMode, const unsigned nbSeconds) { + + U32 i; + BMK_return_t results = { { 0, 0., 0., 0 }, 0 } ; + const void *const *const srcPtrs = (const void *const *const)buf.srcPtrs; + size_t const *const srcSizes = buf.srcSizes; + void** const dstPtrs = buf.dstPtrs; + size_t const *const dstCapacities = buf.dstCapacities; + size_t* const dstSizes = buf.dstSizes; + void** const resPtrs = buf.resPtrs; + size_t const *const resSizes = buf.resSizes; + const void* dictBuffer = ctx.dictBuffer; + const size_t dictBufferSize = ctx.dictSize; + const size_t nbBlocks = buf.nbBlocks; + const size_t srcSize = buf.srcSize; + ZSTD_CCtx* cctx = ctx.cctx; + ZSTD_DCtx* dctx = ctx.dctx; + + /* warmimg up memory */ + for(i = 0; i < buf.nbBlocks; i++) { + if(mode != BMK_decodeOnly) { + RDG_genBuffer(dstPtrs[i], dstCapacities[i], 0.10, 0.50, 1); + } else { + RDG_genBuffer(resPtrs[i], resSizes[i], 0.10, 0.50, 1); + } + } + + /* Bench */ + { + /* init args */ + BMK_initCCtxArgs cctxprep; + BMK_initDCtxArgs dctxprep; + cctxprep.ctx = cctx; + cctxprep.dictBuffer = dictBuffer; + cctxprep.dictBufferSize = dictBufferSize; + cctxprep.cLevel = cLevel; + cctxprep.comprParams = comprParams; + dctxprep.dctx = dctx; + dctxprep.dictBuffer = dictBuffer; + dctxprep.dictBufferSize = dictBufferSize; + + if(loopMode == BMK_timeMode) { + BMK_customTimedReturn_t intermediateResultCompress; + BMK_customTimedReturn_t intermediateResultDecompress; + BMK_timedFnState_t* timeStateCompress = BMK_createTimeState(nbSeconds); + BMK_timedFnState_t* timeStateDecompress = BMK_createTimeState(nbSeconds); + if(mode == BMK_compressOnly) { + intermediateResultCompress.completed = 0; + intermediateResultDecompress.completed = 1; + } else if (mode == BMK_decodeOnly) { + intermediateResultCompress.completed = 1; + intermediateResultDecompress.completed = 0; + } else { /* both */ + intermediateResultCompress.completed = 0; + intermediateResultDecompress.completed = 0; + } + + while(!intermediateResultCompress.completed) { + intermediateResultCompress = BMK_benchFunctionTimed(timeStateCompress, &local_defaultCompress, (void*)cctx, &local_initCCtx, (void*)&cctxprep, + nbBlocks, srcPtrs, srcSizes, dstPtrs, dstCapacities, dstSizes); + + if(intermediateResultCompress.result.error) { + results.error = intermediateResultCompress.result.error; + BMK_freeTimeState(timeStateCompress); + BMK_freeTimeState(timeStateDecompress); + return results; + } + results.result.cSpeed = (srcSize * TIMELOOP_NANOSEC) / intermediateResultCompress.result.result.nanoSecPerRun; + results.result.cSize = intermediateResultCompress.result.result.sumOfReturn; + } + + while(!intermediateResultDecompress.completed) { + intermediateResultDecompress = BMK_benchFunctionTimed(timeStateDecompress, &local_defaultDecompress, (void*)(dctx), &local_initDCtx, (void*)&dctxprep, + nbBlocks, (const void* const*)dstPtrs, dstSizes, resPtrs, resSizes, NULL); + + if(intermediateResultDecompress.result.error) { + results.error = intermediateResultDecompress.result.error; + BMK_freeTimeState(timeStateCompress); + BMK_freeTimeState(timeStateDecompress); + return results; + } + results.result.dSpeed = (srcSize * TIMELOOP_NANOSEC) / intermediateResultDecompress.result.result.nanoSecPerRun; + } + + BMK_freeTimeState(timeStateCompress); + BMK_freeTimeState(timeStateDecompress); + + } else { /* iterMode; */ + if(mode != BMK_decodeOnly) { + + BMK_customReturn_t compressionResults = BMK_benchFunction(&local_defaultCompress, (void*)cctx, &local_initCCtx, (void*)&cctxprep, + nbBlocks, srcPtrs, srcSizes, dstPtrs, dstCapacities, dstSizes, nbSeconds); + if(compressionResults.error) { + results.error = compressionResults.error; + return results; + } + if(compressionResults.result.nanoSecPerRun == 0) { + results.result.cSpeed = 0; + } else { + results.result.cSpeed = srcSize * TIMELOOP_NANOSEC / compressionResults.result.nanoSecPerRun; + } + results.result.cSize = compressionResults.result.sumOfReturn; + } + + if(mode != BMK_compressOnly) { + BMK_customReturn_t decompressionResults; + decompressionResults = BMK_benchFunction( + &local_defaultDecompress, (void*)(dctx), + &local_initDCtx, (void*)&dctxprep, nbBlocks, + (const void* const*)dstPtrs, dstSizes, resPtrs, resSizes, NULL, + nbSeconds); + + if(decompressionResults.error) { + results.error = decompressionResults.error; + return results; + } + + if(decompressionResults.result.nanoSecPerRun == 0) { + results.result.dSpeed = 0; + } else { + results.result.dSpeed = srcSize * TIMELOOP_NANOSEC / decompressionResults.result.nanoSecPerRun; + } + } + } + } + /* Bench */ + results.result.cMem = (1 << (comprParams->vals[wlog_ind])) + ZSTD_sizeof_CCtx(cctx); + return results; +} + +static int BMK_benchParam(BMK_result_t* resultPtr, + const buffers_t buf, const contexts_t ctx, + const paramValues_t cParams) { + BMK_return_t res = BMK_benchMemInvertible(buf, ctx, BASE_CLEVEL, &cParams, BMK_both, BMK_timeMode, 3); + *resultPtr = res.result; + return res.error; +} + + +#define CBENCHMARK(conditional, resultvar, tmpret, mode, loopmode, sec) { \ + if(conditional) { \ + BMK_return_t tmpret = BMK_benchMemInvertible(buf, ctx, BASE_CLEVEL, &cParams, mode, loopmode, sec); \ + if(tmpret.error) { DEBUGOUTPUT("Benchmarking failed\n"); return ERROR_RESULT; } \ + if(mode != BMK_decodeOnly) { \ + resultvar.cSpeed = tmpret.result.cSpeed; \ + resultvar.cSize = tmpret.result.cSize; \ + resultvar.cMem = tmpret.result.cMem; \ + } \ + if(mode != BMK_compressOnly) { resultvar.dSpeed = tmpret.result.dSpeed; } \ + } \ +} + +/* Benchmarking which stops when we are sufficiently sure the solution is infeasible / worse than the winner */ +#define VARIANCE 1.2 +static int allBench(BMK_result_t* resultPtr, + const buffers_t buf, const contexts_t ctx, + const paramValues_t cParams, + const constraint_t target, + BMK_result_t* winnerResult, int feas) { + BMK_result_t resultMax, benchres; + U64 loopDurationC = 0, loopDurationD = 0; + double uncertaintyConstantC = 3., uncertaintyConstantD = 3.; + double winnerRS; + /* initial benchmarking, gives exact ratio and memory, warms up future runs */ + CBENCHMARK(1, benchres, tmp, BMK_both, BMK_iterMode, 1); + + winnerRS = resultScore(*winnerResult, buf.srcSize, target); + DEBUGOUTPUT("WinnerScore: %f\n ", winnerRS); + + *resultPtr = benchres; + + /* calculate uncertainty in compression / decompression runs */ + if(benchres.cSpeed) { + loopDurationC = ((buf.srcSize * TIMELOOP_NANOSEC) / benchres.cSpeed); + uncertaintyConstantC = ((loopDurationC + (double)(2 * g_clockGranularity))/loopDurationC); + } + + if(benchres.dSpeed) { + loopDurationD = ((buf.srcSize * TIMELOOP_NANOSEC) / benchres.dSpeed); + uncertaintyConstantD = ((loopDurationD + (double)(2 * g_clockGranularity))/loopDurationD); + } + + /* anything with worse ratio in feas is definitely worse, discard */ + if(feas && benchres.cSize < winnerResult->cSize && !g_optmode) { + return WORSE_RESULT; + } + + /* second run, if first run is too short, gives approximate cSpeed + dSpeed */ + CBENCHMARK(loopDurationC < TIMELOOP_NANOSEC / 10, benchres, tmp, BMK_compressOnly, BMK_iterMode, 1); + CBENCHMARK(loopDurationD < TIMELOOP_NANOSEC / 10, benchres, tmp, BMK_decodeOnly, BMK_iterMode, 1); + + *resultPtr = benchres; + + /* optimistic assumption of benchres */ + resultMax = benchres; + resultMax.cSpeed *= uncertaintyConstantC * VARIANCE; + resultMax.dSpeed *= uncertaintyConstantD * VARIANCE; + + /* disregard infeasible results in feas mode */ + /* disregard if resultMax < winner in infeas mode */ + if((feas && !feasible(resultMax, target)) || + (!feas && (winnerRS > resultScore(resultMax, buf.srcSize, target)))) { + return WORSE_RESULT; + } + + CBENCHMARK(loopDurationC < TIMELOOP_NANOSEC, benchres, tmp, BMK_compressOnly, BMK_timeMode, 1); + CBENCHMARK(loopDurationD < TIMELOOP_NANOSEC, benchres, tmp, BMK_decodeOnly, BMK_timeMode, 1); + + *resultPtr = benchres; + + /* compare by resultScore when in infeas */ + /* compare by compareResultLT when in feas */ + if((!feas && (resultScore(benchres, buf.srcSize, target) > resultScore(*winnerResult, buf.srcSize, target))) || + (feas && (compareResultLT(*winnerResult, benchres, target, buf.srcSize))) ) { + return BETTER_RESULT; + } else { + return WORSE_RESULT; + } +} + +#define INFEASIBLE_THRESHOLD 200 +/* Memoized benchmarking, won't benchmark anything which has already been benchmarked before. */ +static int benchMemo(BMK_result_t* resultPtr, + const buffers_t buf, const contexts_t ctx, + const paramValues_t cParams, + const constraint_t target, + BMK_result_t* winnerResult, memoTable_t* const memoTableArray, + const int feas) { + static int bmcount = 0; + int res; + + if(memoTableGet(memoTableArray, cParams) >= INFEASIBLE_THRESHOLD || redundantParams(cParams, target, buf.maxBlockSize)) { return WORSE_RESULT; } + + res = allBench(resultPtr, buf, ctx, cParams, target, winnerResult, feas); + + if(DEBUG && !(bmcount % 250)) { + DISPLAY("Count: %d\n", bmcount); + bmcount++; + } + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, *resultPtr, cParams, target, buf.srcSize); + + if(res == BETTER_RESULT || feas) { + memoTableSet(memoTableArray, cParams, 255); /* what happens if collisions are frequent */ + } + return res; +} + +typedef struct { + U64 cSpeed_min; + U64 dSpeed_min; U32 windowLog_max; ZSTD_strategy strategy_max; } level_constraints_t; @@ -243,15 +1659,14 @@ static void BMK_init_level_constraints(int bytePerSec_level1) } } } -static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters params, - const void* srcBuffer, size_t srcSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx) +static int BMK_seed(winnerInfo_t* winners, const paramValues_t params, + const buffers_t buf, const contexts_t ctx) { BMK_result_t testResult; int better = 0; int cLevel; - BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, dctx, params); + BMK_benchParam(&testResult, buf, ctx, params); for (cLevel = 1; cLevel <= NB_LEVELS_TRACKED; cLevel++) { @@ -259,32 +1674,32 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para continue; /* not fast enough for this level */ if (testResult.dSpeed < g_level_constraint[cLevel].dSpeed_min) continue; /* not fast enough for this level */ - if (params.windowLog > g_level_constraint[cLevel].windowLog_max) + if (params.vals[wlog_ind] > g_level_constraint[cLevel].windowLog_max) continue; /* too much memory for this level */ - if (params.strategy > g_level_constraint[cLevel].strategy_max) + if (params.vals[strt_ind] > g_level_constraint[cLevel].strategy_max) continue; /* forbidden strategy for this level */ if (winners[cLevel].result.cSize==0) { /* first solution for this cLevel */ winners[cLevel].result = testResult; winners[cLevel].params = params; - BMK_printWinner(stdout, cLevel, testResult, params, srcSize); + BMK_printWinner(stdout, cLevel, testResult, params, buf.srcSize); better = 1; continue; } if ((double)testResult.cSize <= ((double)winners[cLevel].result.cSize * (1. + (0.02 / cLevel))) ) { /* Validate solution is "good enough" */ - double W_ratio = (double)srcSize / testResult.cSize; - double O_ratio = (double)srcSize / winners[cLevel].result.cSize; + double W_ratio = (double)buf.srcSize / testResult.cSize; + double O_ratio = (double)buf.srcSize / winners[cLevel].result.cSize; double W_ratioNote = log (W_ratio); double O_ratioNote = log (O_ratio); - size_t W_DMemUsed = (1 << params.windowLog) + (16 KB); - size_t O_DMemUsed = (1 << winners[cLevel].params.windowLog) + (16 KB); + size_t W_DMemUsed = (1 << params.vals[wlog_ind]) + (16 KB); + size_t O_DMemUsed = (1 << winners[cLevel].params.vals[wlog_ind]) + (16 KB); double W_DMemUsed_note = W_ratioNote * ( 40 + 9*cLevel) - log((double)W_DMemUsed); double O_DMemUsed_note = O_ratioNote * ( 40 + 9*cLevel) - log((double)O_DMemUsed); - size_t W_CMemUsed = (1 << params.windowLog) + ZSTD_estimateCCtxSize_usingCParams(params); - size_t O_CMemUsed = (1 << winners[cLevel].params.windowLog) + ZSTD_estimateCCtxSize_usingCParams(winners[cLevel].params); + size_t W_CMemUsed = (1 << params.vals[wlog_ind]) + ZSTD_estimateCCtxSize_usingCParams(pvalsToCParams(params)); + size_t O_CMemUsed = (1 << winners[cLevel].params.vals[wlog_ind]) + ZSTD_estimateCCtxSize_usingCParams(pvalsToCParams(winners[cLevel].params)); double W_CMemUsed_note = W_ratioNote * ( 50 + 13*cLevel) - log((double)W_CMemUsed); double O_CMemUsed_note = O_ratioNote * ( 50 + 13*cLevel) - log((double)O_CMemUsed); @@ -314,16 +1729,16 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para /* too large compression speed difference for the compression benefit */ if (W_ratio > O_ratio) DISPLAY ("Compression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n", - W_ratio, testResult.cSpeed / 1000000, - O_ratio, winners[cLevel].result.cSpeed / 1000000., cLevel); + W_ratio, (double)testResult.cSpeed / (1 MB), + O_ratio, (double)winners[cLevel].result.cSpeed / (1 MB), cLevel); continue; } if (W_DSpeed_note < O_DSpeed_note ) { /* too large decompression speed difference for the compression benefit */ if (W_ratio > O_ratio) DISPLAY ("Decompression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n", - W_ratio, testResult.dSpeed / 1000000., - O_ratio, winners[cLevel].result.dSpeed / 1000000., cLevel); + W_ratio, (double)testResult.dSpeed / (1 MB), + O_ratio, (double)winners[cLevel].result.dSpeed / (1 MB), cLevel); continue; } @@ -332,7 +1747,7 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para winners[cLevel].result = testResult; winners[cLevel].params = params; - BMK_printWinner(stdout, cLevel, testResult, params, srcSize); + BMK_printWinner(stdout, cLevel, testResult, params, buf.srcSize); better = 1; } } @@ -340,158 +1755,72 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para return better; } - -/* nullified useless params, to ensure count stats */ -static ZSTD_compressionParameters* sanitizeParams(ZSTD_compressionParameters params) -{ - g_params = params; - if (params.strategy == ZSTD_fast) - g_params.chainLog = 0, g_params.searchLog = 0; - if (params.strategy == ZSTD_dfast) - g_params.searchLog = 0; - if (params.strategy != ZSTD_btopt && params.strategy != ZSTD_btultra) - g_params.targetLength = 0; - return &g_params; -} - - -static void paramVariation(ZSTD_compressionParameters* ptr) -{ - ZSTD_compressionParameters p; - U32 validated = 0; - while (!validated) { - U32 nbChanges = (FUZ_rand(&g_rand) & 3) + 1; - p = *ptr; - for ( ; nbChanges ; nbChanges--) { - const U32 changeID = FUZ_rand(&g_rand) % 14; - switch(changeID) - { - case 0: - p.chainLog++; break; - case 1: - p.chainLog--; break; - case 2: - p.hashLog++; break; - case 3: - p.hashLog--; break; - case 4: - p.searchLog++; break; - case 5: - p.searchLog--; break; - case 6: - p.windowLog++; break; - case 7: - p.windowLog--; break; - case 8: - p.searchLength++; break; - case 9: - p.searchLength--; break; - case 10: - p.strategy = (ZSTD_strategy)(((U32)p.strategy)+1); break; - case 11: - p.strategy = (ZSTD_strategy)(((U32)p.strategy)-1); break; - case 12: - p.targetLength *= 1 + ((double)(FUZ_rand(&g_rand)&255)) / 256.; break; - case 13: - p.targetLength /= 1 + ((double)(FUZ_rand(&g_rand)&255)) / 256.; break; - } - } - validated = !ZSTD_isError(ZSTD_checkCParams(p)); - } - *ptr = p; -} - +/*-************************************ +* Compression Level Table Generation Functions +**************************************/ #define PARAMTABLELOG 25 #define PARAMTABLESIZE (1<> 3) & PARAMTABLEMASK] - +static BYTE* NB_TESTS_PLAYED(paramValues_t p) { + ZSTD_compressionParameters p2 = pvalsToCParams(sanitizeParams(p)); + return &g_alreadyTested[(XXH64((void*)&p2, sizeof(p2), 0) >> 3) & PARAMTABLEMASK]; +} static void playAround(FILE* f, winnerInfo_t* winners, - ZSTD_compressionParameters params, - const void* srcBuffer, size_t srcSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx) + paramValues_t p, + const buffers_t buf, const contexts_t ctx) { - int nbVariations = 0; + int nbVariations = 0, i; UTIL_time_t const clockStart = UTIL_getTime(); while (UTIL_clockSpanMicro(clockStart) < g_maxVariationTime) { - ZSTD_compressionParameters p = params; + BYTE* b; if (nbVariations++ > g_maxNbVariations) break; - paramVariation(&p); + + do { for(i = 0; i < 4; i++) { paramVaryOnce(FUZ_rand(&g_rand) % (strt_ind + 1), ((FUZ_rand(&g_rand) & 1) << 1) - 1, &p); } } + while(!paramValid(p)); /* exclude faster if already played params */ - if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(p))-1)) + if (FUZ_rand(&g_rand) & ((1 << *NB_TESTS_PLAYED(p))-1)) continue; /* test */ - NB_TESTS_PLAYED(p)++; - if (!BMK_seed(winners, p, srcBuffer, srcSize, ctx, dctx)) continue; + b = NB_TESTS_PLAYED(p); + (*b)++; + if (!BMK_seed(winners, p, buf, ctx)) continue; /* improvement found => search more */ - BMK_printWinners(f, winners, srcSize); - playAround(f, winners, p, srcBuffer, srcSize, ctx, dctx); + BMK_printWinners(f, winners, buf.srcSize); + playAround(f, winners, p, buf, ctx); } } - -static ZSTD_compressionParameters randomParams(void) -{ - ZSTD_compressionParameters p; - U32 validated = 0; - while (!validated) { - /* totally random entry */ - p.chainLog = (FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN)) + ZSTD_CHAINLOG_MIN; - p.hashLog = (FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN)) + ZSTD_HASHLOG_MIN; - p.searchLog = (FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN)) + ZSTD_SEARCHLOG_MIN; - p.windowLog = (FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN)) + ZSTD_WINDOWLOG_MIN; - p.searchLength=(FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN)) + ZSTD_SEARCHLENGTH_MIN; - p.targetLength=(FUZ_rand(&g_rand) % (512)); - p.strategy = (ZSTD_strategy) (FUZ_rand(&g_rand) % (ZSTD_btultra +1)); - validated = !ZSTD_isError(ZSTD_checkCParams(p)); - } - return p; -} - static void BMK_selectRandomStart( FILE* f, winnerInfo_t* winners, - const void* srcBuffer, size_t srcSize, - ZSTD_CCtx* ctx, ZSTD_DCtx* dctx) + const buffers_t buf, const contexts_t ctx) { U32 const id = FUZ_rand(&g_rand) % (NB_LEVELS_TRACKED+1); - if ((id==0) || (winners[id].params.windowLog==0)) { + if ((id==0) || (winners[id].params.vals[wlog_ind]==0)) { /* use some random entry */ - ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), srcSize, 0); - playAround(f, winners, p, srcBuffer, srcSize, ctx, dctx); + paramValues_t const p = adjustParams(cParamsToPVals(pvalsToCParams(randomParams())), /* defaults nonCompression parameters */ + buf.srcSize, 0); + playAround(f, winners, p, buf, ctx); } else { - playAround(f, winners, winners[id].params, srcBuffer, srcSize, ctx, dctx); + playAround(f, winners, winners[id].params, buf, ctx); } } - -static void BMK_benchOnce(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* srcBuffer, size_t srcSize) +static void BMK_benchFullTable(const buffers_t buf, const contexts_t ctx) { - BMK_result_t testResult; - g_params = ZSTD_adjustCParams(g_params, srcSize, 0); - BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, dctx, g_params); - DISPLAY("Compression Ratio: %.3f Compress Speed: %.1f MB/s Decompress Speed: %.1f MB/s\n", (double)srcSize / testResult.cSize, - testResult.cSpeed / 1000000, testResult.dSpeed / 1000000); - return; -} - -static void BMK_benchFullTable(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* srcBuffer, size_t srcSize) -{ - ZSTD_compressionParameters params; + paramValues_t params; winnerInfo_t winners[NB_LEVELS_TRACKED+1]; const char* const rfName = "grillResults.txt"; FILE* const f = fopen(rfName, "w"); - const size_t blockSize = g_blockSize ? g_blockSize : srcSize; /* cut by block or not ? */ /* init */ assert(g_singleRun==0); @@ -499,12 +1828,12 @@ static void BMK_benchFullTable(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* src if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); } if (g_target) { - BMK_init_level_constraints(g_target*1000000); + BMK_init_level_constraints(g_target * (1 MB)); } else { /* baseline config for level 1 */ - ZSTD_compressionParameters const l1params = ZSTD_getCParams(1, blockSize, 0); + paramValues_t const l1params = cParamsToPVals(ZSTD_getCParams(1, buf.maxBlockSize, ctx.dictSize)); BMK_result_t testResult; - BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, dctx, l1params); + BMK_benchParam(&testResult, buf, ctx, l1params); BMK_init_level_constraints((int)((testResult.cSpeed * 31) / 32)); } @@ -512,251 +1841,543 @@ static void BMK_benchFullTable(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* src { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel(); int i; for (i=0; i<=maxSeeds; i++) { - params = ZSTD_getCParams(i, blockSize, 0); - BMK_seed(winners, params, srcBuffer, srcSize, cctx, dctx); + params = cParamsToPVals(ZSTD_getCParams(i, buf.maxBlockSize, 0)); + BMK_seed(winners, params, buf, ctx); } } - BMK_printWinners(f, winners, srcSize); + BMK_printWinners(f, winners, buf.srcSize); /* start tests */ - { const time_t grillStart = time(NULL); + { const UTIL_time_t grillStart = UTIL_getTime(); do { - BMK_selectRandomStart(f, winners, srcBuffer, srcSize, cctx, dctx); - } while (BMK_timeSpan(grillStart) < g_grillDuration_s); + BMK_selectRandomStart(f, winners, buf, ctx); + } while (BMK_timeSpan(grillStart) < g_timeLimit_s); } /* end summary */ - BMK_printWinners(f, winners, srcSize); + BMK_printWinners(f, winners, buf.srcSize); DISPLAY("grillParams operations completed \n"); /* clean up*/ fclose(f); } -static void BMK_benchMem_usingCCtx(ZSTD_CCtx* const cctx, ZSTD_DCtx* const dctx, const void* srcBuffer, size_t srcSize) -{ - if (g_singleRun) - return BMK_benchOnce(cctx, dctx, srcBuffer, srcSize); - else - return BMK_benchFullTable(cctx, dctx, srcBuffer, srcSize); -} +/*-************************************ +* Single Benchmark Functions +**************************************/ -static void BMK_benchMemCCtxInit(const void* srcBuffer, size_t srcSize) -{ - ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); - if (cctx==NULL || dctx==NULL) { DISPLAY("Context Creation failed \n"); exit(1); } - BMK_benchMem_usingCCtx(cctx, dctx, srcBuffer, srcSize); - ZSTD_freeCCtx(cctx); -} +static int benchOnce(const buffers_t buf, const contexts_t ctx, const int cLevel) { + BMK_result_t testResult; + g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevel, buf.maxBlockSize, ctx.dictSize)), g_params), buf.maxBlockSize, ctx.dictSize); + if(BMK_benchParam(&testResult, buf, ctx, g_params)) { + DISPLAY("Error during benchmarking\n"); + return 1; + } -static int benchSample(void) -{ - const char* const name = "Sample 10MB"; - size_t const benchedSize = 10000000; + BMK_printWinner(stdout, CUSTOM_LEVEL, testResult, g_params, buf.srcSize); - void* origBuff = malloc(benchedSize); - if (!origBuff) { perror("not enough memory"); return 12; } - - /* Fill buffer */ - RDG_genBuffer(origBuff, benchedSize, g_compressibility, 0.0, 0); - - /* bench */ - DISPLAY("\r%79s\r", ""); - DISPLAY("using %s %i%%: \n", name, (int)(g_compressibility*100)); - BMK_benchMemCCtxInit(origBuff, benchedSize); - - free(origBuff); return 0; } +static int benchSample(double compressibility, int cLevel) +{ + const char* const name = "Sample 10MB"; + size_t const benchedSize = 10 MB; + void* srcBuffer = malloc(benchedSize); + int ret = 0; + + buffers_t buf; + contexts_t ctx; + + if(srcBuffer == NULL) { + DISPLAY("Out of Memory\n"); + return 2; + } + + RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); + + if(createBuffersFromMemory(&buf, srcBuffer, 1, &benchedSize)) { + DISPLAY("Buffer Creation Error\n"); + free(srcBuffer); + return 3; + } + + if(createContexts(&ctx, NULL)) { + DISPLAY("Context Creation Error\n"); + freeBuffers(buf); + return 1; + } + + /* bench */ + DISPLAY("\r%79s\r", ""); + DISPLAY("using %s %i%%: \n", name, (int)(compressibility*100)); + + if(g_singleRun) { + ret = benchOnce(buf, ctx, cLevel); + } else { + BMK_benchFullTable(buf, ctx); + } + + freeBuffers(buf); + freeContexts(ctx); + + return ret; +} /* benchFiles() : * note: while this function takes a table of filenames, * in practice, only the first filename will be used */ -int benchFiles(const char** fileNamesTable, int nbFiles) +int benchFiles(const char** fileNamesTable, int nbFiles, const char* dictFileName, const int cLevel) { - int fileIdx=0; + buffers_t buf; + contexts_t ctx; + int ret = 0; - /* Loop for each file */ - while (fileIdx inFileSize) benchedSize = (size_t)inFileSize; - if (benchedSize < inFileSize) - DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", inFileName, (int)(benchedSize>>20)); - origBuff = malloc(benchedSize); - if (origBuff==NULL) { - DISPLAY("\nError: not enough memory!\n"); - fclose(inFile); - return 12; - } - - /* Fill input buffer */ - DISPLAY("Loading %s... \r", inFileName); - { size_t const readSize = fread(origBuff, 1, benchedSize, inFile); - fclose(inFile); - if(readSize != benchedSize) { - DISPLAY("\nError: problem reading file '%s' !! \n", inFileName); - free(origBuff); - return 13; - } } - - /* bench */ - DISPLAY("\r%79s\r", ""); - DISPLAY("using %s : \n", inFileName); - BMK_benchMemCCtxInit(origBuff, benchedSize); - - /* clean */ - free(origBuff); + if(createBuffers(&buf, fileNamesTable, nbFiles)) { + DISPLAY("unable to load files\n"); + return 1; } - return 0; -} - - -static void BMK_translateAdvancedParams(ZSTD_compressionParameters params) -{ - DISPLAY("--zstd=windowLog=%u,chainLog=%u,hashLog=%u,searchLog=%u,searchLength=%u,targetLength=%u,strategy=%u \n", - params.windowLog, params.chainLog, params.hashLog, params.searchLog, params.searchLength, params.targetLength, (U32)(params.strategy)); -} - -/* optimizeForSize(): - * targetSpeed : expressed in MB/s */ -int optimizeForSize(const char* inFileName, U32 targetSpeed) -{ - FILE* const inFile = fopen( inFileName, "rb" ); - U64 const inFileSize = UTIL_getFileSize(inFileName); - size_t benchedSize = BMK_findMaxMem(inFileSize*3) / 3; - void* origBuff; - /* Init */ - if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; } - if (inFileSize == UTIL_FILESIZE_UNKNOWN) { - DISPLAY("Pb evaluatin size of %s \n", inFileName); - fclose(inFile); - return 11; + if(createContexts(&ctx, dictFileName)) { + DISPLAY("unable to load dictionary\n"); + freeBuffers(buf); + return 2; } - /* Memory allocation & restrictions */ - if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize; - if (benchedSize < inFileSize) { - DISPLAY("Not enough memory for '%s' \n", inFileName); - fclose(inFile); - return 11; - } - - /* Alloc */ - origBuff = malloc(benchedSize); - if(!origBuff) { - DISPLAY("\nError: not enough memory!\n"); - fclose(inFile); - return 12; - } - - /* Fill input buffer */ - DISPLAY("Loading %s... \r", inFileName); - { size_t const readSize = fread(origBuff, 1, benchedSize, inFile); - fclose(inFile); - if(readSize != benchedSize) { - DISPLAY("\nError: problem reading file '%s' !! \n", inFileName); - free(origBuff); - return 13; - } } - - /* bench */ DISPLAY("\r%79s\r", ""); - DISPLAY("optimizing for %s - limit speed %u MB/s \n", inFileName, targetSpeed); - targetSpeed *= 1000000; - { ZSTD_CCtx* const ctx = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); - winnerInfo_t winner; - BMK_result_t candidate; - const size_t blockSize = g_blockSize ? g_blockSize : benchedSize; + if(nbFiles == 1) { + DISPLAY("using %s : \n", fileNamesTable[0]); + } else { + DISPLAY("using %d Files : \n", nbFiles); + } - /* init */ - if (ctx==NULL) { DISPLAY("\n ZSTD_createCCtx error \n"); free(origBuff); return 14;} - memset(&winner, 0, sizeof(winner)); - winner.result.cSize = (size_t)(-1); + if(g_singleRun) { + ret = benchOnce(buf, ctx, cLevel); + } else { + BMK_benchFullTable(buf, ctx); + } - /* find best solution from default params */ - { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel(); - int i; - for (i=1; i<=maxSeeds; i++) { - ZSTD_compressionParameters const CParams = ZSTD_getCParams(i, blockSize, 0); - BMK_benchParam(&candidate, origBuff, benchedSize, ctx, dctx, CParams); - if (candidate.cSpeed < (double)targetSpeed) { + freeBuffers(buf); + freeContexts(ctx); + return ret; +} + + +/*-************************************ +* Local Optimization Functions +**************************************/ + +/* One iteration of hill climbing. Specifically, it first tries all + * valid parameter configurations w/ manhattan distance 1 and picks the best one + * failing that, it progressively tries candidates further and further away (up to #dim + 2) + * if it finds a candidate exceeding winnerInfo, it will repeat. Otherwise, it will stop the + * current stage of hill climbing. + * Each iteration of hill climbing proceeds in 2 'phases'. Phase 1 climbs according to + * the resultScore function, which is effectively a linear increase in reward until it reaches + * the constraint-satisfying value, it which point any excess results in only logarithmic reward. + * This aims to find some constraint-satisfying point. + * Phase 2 optimizes in accordance with what the original function sets out to maximize, with + * all feasible solutions valued over all infeasible solutions. + */ + +/* sanitize all params here. + * all generation after random should be sanitized. (maybe sanitize random) + */ +static winnerInfo_t climbOnce(const constraint_t target, + memoTable_t* mtAll, + const buffers_t buf, const contexts_t ctx, + const paramValues_t init) { + /* + * cparam - currently considered 'center' + * candidate - params to benchmark/results + * winner - best option found so far. + */ + paramValues_t cparam = init; + winnerInfo_t candidateInfo, winnerInfo; + int better = 1; + int feas = 0; + + winnerInfo = initWinnerInfo(init); + candidateInfo = winnerInfo; + + { + winnerInfo_t bestFeasible1 = initWinnerInfo(cparam); + DEBUGOUTPUT("Climb Part 1\n"); + while(better) { + + int offset; + size_t i, dist; + const size_t varLen = mtAll[cparam.vals[strt_ind]].varLen; + better = 0; + DEBUGOUTPUT("Start\n"); + cparam = winnerInfo.params; + candidateInfo.params = cparam; + /* all dist-1 candidates */ + for(i = 0; i < varLen; i++) { + for(offset = -1; offset <= 1; offset += 2) { + CHECKTIME(winnerInfo); + candidateInfo.params = cparam; + paramVaryOnce(mtAll[cparam.vals[strt_ind]].varArray[i], offset, &candidateInfo.params); + + if(paramValid(candidateInfo.params)) { + int res; + res = benchMemo(&candidateInfo.result, buf, ctx, + sanitizeParams(candidateInfo.params), target, &winnerInfo.result, mtAll, feas); + DEBUGOUTPUT("Res: %d\n", res); + if(res == BETTER_RESULT) { /* synonymous with better when called w/ infeasibleBM */ + winnerInfo = candidateInfo; + better = 1; + if(compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) { + bestFeasible1 = winnerInfo; + } + } + } + } + } + + if(better) { + continue; + } + + for(dist = 2; dist < varLen + 2; dist++) { /* varLen is # dimensions */ + for(i = 0; i < (1 << varLen) / varLen + 2; i++) { + int res; + CHECKTIME(winnerInfo); + candidateInfo.params = cparam; + /* param error checking already done here */ + paramVariation(&candidateInfo.params, mtAll, (U32)dist); + + res = benchMemo(&candidateInfo.result, buf, ctx, + sanitizeParams(candidateInfo.params), target, &winnerInfo.result, mtAll, feas); + DEBUGOUTPUT("Res: %d\n", res); + if(res == BETTER_RESULT) { /* synonymous with better in this case*/ + winnerInfo = candidateInfo; + better = 1; + if(compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) { + bestFeasible1 = winnerInfo; + } + break; + } + + } + if(better) { break; } - if ( (candidate.cSize < winner.result.cSize) - | ((candidate.cSize == winner.result.cSize) & (candidate.cSpeed > winner.result.cSpeed)) ) - { - winner.params = CParams; - winner.result = candidate; - BMK_printWinner(stdout, i, winner.result, winner.params, benchedSize); - } } + } + + if(!better) { /* infeas -> feas -> stop */ + if(feas) { return winnerInfo; } + + feas = 1; + better = 1; + winnerInfo = bestFeasible1; /* note with change, bestFeasible may not necessarily be feasible, but if one has been benchmarked, it will be. */ + DEBUGOUTPUT("Climb Part 2\n"); + } } - - BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize); - - BMK_translateAdvancedParams(winner.params); - - /* start tests */ - { time_t const grillStart = time(NULL); - do { - ZSTD_compressionParameters params = winner.params; - paramVariation(¶ms); - if ((FUZ_rand(&g_rand) & 31) == 3) params = randomParams(); /* totally random config to improve search space */ - params = ZSTD_adjustCParams(params, blockSize, 0); - - /* exclude faster if already played set of params */ - if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(params))-1)) continue; - - /* test */ - NB_TESTS_PLAYED(params)++; - BMK_benchParam(&candidate, origBuff, benchedSize, ctx, dctx, params); - - /* improvement found => new winner */ - if ( (candidate.cSpeed > targetSpeed) - & ( (candidate.cSize < winner.result.cSize) - | ((candidate.cSize == winner.result.cSize) & (candidate.cSpeed > winner.result.cSpeed)) ) ) - { - winner.params = params; - winner.result = candidate; - BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize); - BMK_translateAdvancedParams(winner.params); - } - } while (BMK_timeSpan(grillStart) < g_grillDuration_s); - } - /* end summary */ - - BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize); - BMK_translateAdvancedParams(winner.params); - DISPLAY("grillParams size - optimizer completed \n"); - - /* clean up*/ - ZSTD_freeCCtx(ctx); - ZSTD_freeDCtx(dctx); + winnerInfo = bestFeasible1; } - free(origBuff); - return 0; + return winnerInfo; +} + +/* Optimizes for a fixed strategy */ + +/* flexible parameters: iterations of failed climbing (or if we do non-random, maybe this is when everything is close to visitied) + weight more on visit for bad results, less on good results/more on later results / ones with more failures. + allocate memoTable here. + */ +static winnerInfo_t optimizeFixedStrategy( + const buffers_t buf, const contexts_t ctx, + const constraint_t target, paramValues_t paramTarget, + const ZSTD_strategy strat, + memoTable_t* memoTableArray, const int tries) { + int i = 0; + + paramValues_t init; + winnerInfo_t winnerInfo, candidateInfo; + winnerInfo = initWinnerInfo(emptyParams()); + /* so climb is given the right fixed strategy */ + paramTarget.vals[strt_ind] = strat; + /* to pass ZSTD_checkCParams */ + paramTarget = cParamUnsetMin(paramTarget); + + init = paramTarget; + + for(i = 0; i < tries; i++) { + DEBUGOUTPUT("Restart\n"); + do { randomConstrainedParams(&init, memoTableArray, strat); } while(redundantParams(init, target, buf.maxBlockSize)); + candidateInfo = climbOnce(target, memoTableArray, buf, ctx, init); + if(compareResultLT(winnerInfo.result, candidateInfo.result, target, buf.srcSize)) { + winnerInfo = candidateInfo; + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, winnerInfo.params, target, buf.srcSize); + i = 0; + continue; + } + CHECKTIME(winnerInfo); + i++; + } + return winnerInfo; +} + +/* goes best, best-1, best+1, best-2, ... */ +/* return 0 if nothing remaining */ +static int nextStrategy(const int currentStrategy, const int bestStrategy) { + if(bestStrategy <= currentStrategy) { + int candidate = 2 * bestStrategy - currentStrategy - 1; + if(candidate < 1) { + candidate = currentStrategy + 1; + if(candidate > (int)ZSTD_btultra) { + return 0; + } else { + return candidate; + } + } else { + return candidate; + } + } else { /* bestStrategy >= currentStrategy */ + int candidate = 2 * bestStrategy - currentStrategy; + if(candidate > (int)ZSTD_btultra) { + candidate = currentStrategy - 1; + if(candidate < 1) { + return 0; + } else { + return candidate; + } + } else { + return candidate; + } + } +} + +/* experiment with playing with this and decay value */ + +/* main fn called when using --optimize */ +/* Does strategy selection by benchmarking default compression levels + * then optimizes by strategy, starting with the best one and moving + * progressively moving further away by number + * args: + * fileNamesTable - list of files to benchmark + * nbFiles - length of fileNamesTable + * dictFileName - name of dictionary file if one, else NULL + * target - performance constraints (cSpeed, dSpeed, cMem) + * paramTarget - parameter constraints (i.e. restriction search space to where strategy = ZSTD_fast) + * cLevel - compression level to exceed (all solutions must be > lvl in cSpeed + ratio) + */ + +static int g_maxTries = 5; +#define TRY_DECAY 1 + +static int optimizeForSize(const char* const * const fileNamesTable, const size_t nbFiles, const char* dictFileName, constraint_t target, paramValues_t paramTarget, + const int cLevelOpt, const int cLevelRun, const U32 memoTableLog) +{ + varInds_t varArray [NUM_PARAMS]; + int ret = 0; + const size_t varLen = variableParams(paramTarget, varArray, dictFileName != NULL); + winnerInfo_t winner = initWinnerInfo(emptyParams()); + memoTable_t* allMT = NULL; + paramValues_t paramBase; + contexts_t ctx; + buffers_t buf; + g_time = UTIL_getTime(); + + if(createBuffers(&buf, fileNamesTable, nbFiles)) { + DISPLAY("unable to load files\n"); + return 1; + } + + if(createContexts(&ctx, dictFileName)) { + DISPLAY("unable to load dictionary\n"); + freeBuffers(buf); + return 2; + } + + if(nbFiles == 1) { + DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[0]); + } else { + DISPLAYLEVEL(2, "Loading %lu Files... \r", (unsigned long)nbFiles); + } + + /* sanitize paramTarget */ + optimizerAdjustInput(¶mTarget, buf.maxBlockSize); + paramBase = cParamUnsetMin(paramTarget); + + allMT = createMemoTableArray(paramTarget, varArray, varLen, memoTableLog); + + if(!allMT) { + DISPLAY("MemoTable Init Error\n"); + ret = 2; + goto _cleanUp; + } + + /* default strictnesses */ + if(g_strictness == PARAM_UNSET) { + if(g_optmode) { + g_strictness = 100; + } else { + g_strictness = 90; + } + } else { + if(0 >= g_strictness || g_strictness > 100) { + DISPLAY("Strictness Outside of Bounds\n"); + ret = 4; + goto _cleanUp; + } + } + + /* use level'ing mode instead of normal target mode */ + if(g_optmode) { + winner.params = cParamsToPVals(ZSTD_getCParams(cLevelOpt, buf.maxBlockSize, ctx.dictSize)); + if(BMK_benchParam(&winner.result, buf, ctx, winner.params)) { + ret = 3; + goto _cleanUp; + } + + g_lvltarget = winner.result; + g_lvltarget.cSpeed *= ((double)g_strictness) / 100; + g_lvltarget.dSpeed *= ((double)g_strictness) / 100; + g_lvltarget.cSize /= ((double)g_strictness) / 100; + + target.cSpeed = (U32)g_lvltarget.cSpeed; + target.dSpeed = (U32)g_lvltarget.dSpeed; + + BMK_printWinnerOpt(stdout, cLevelOpt, winner.result, winner.params, target, buf.srcSize); + } + + /* Don't want it to return anything worse than the best known result */ + if(g_singleRun) { + BMK_result_t res; + g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevelRun, buf.maxBlockSize, ctx.dictSize)), g_params), buf.maxBlockSize, ctx.dictSize); + if(BMK_benchParam(&res, buf, ctx, g_params)) { + ret = 45; + goto _cleanUp; + } + if(compareResultLT(winner.result, res, relaxTarget(target), buf.srcSize)) { + winner.result = res; + winner.params = g_params; + } + } + + /* bench */ + DISPLAYLEVEL(2, "\r%79s\r", ""); + if(nbFiles == 1) { + DISPLAYLEVEL(2, "optimizing for %s", fileNamesTable[0]); + } else { + DISPLAYLEVEL(2, "optimizing for %lu Files", (unsigned long)nbFiles); + } + + if(target.cSpeed != 0) { DISPLAYLEVEL(2," - limit compression speed %u MB/s", target.cSpeed >> 20); } + if(target.dSpeed != 0) { DISPLAYLEVEL(2, " - limit decompression speed %u MB/s", target.dSpeed >> 20); } + if(target.cMem != (U32)-1) { DISPLAYLEVEL(2, " - limit memory %u MB", target.cMem >> 20); } + + DISPLAYLEVEL(2, "\n"); + findClockGranularity(); + + { + paramValues_t CParams; + + /* find best solution from default params */ + { + /* strategy selection */ + const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel(); + DEBUGOUTPUT("Strategy Selection\n"); + if(paramTarget.vals[strt_ind] == PARAM_UNSET) { + BMK_result_t candidate; + int i; + for (i=1; i<=maxSeeds; i++) { + int ec; + CParams = overwriteParams(cParamsToPVals(ZSTD_getCParams(i, buf.maxBlockSize, ctx.dictSize)), paramTarget); + ec = BMK_benchParam(&candidate, buf, ctx, CParams); + BMK_printWinnerOpt(stdout, i, candidate, CParams, target, buf.srcSize); + + if(!ec && compareResultLT(winner.result, candidate, relaxTarget(target), buf.srcSize)) { + winner.result = candidate; + winner.params = CParams; + } + + CHECKTIMEGT(ret, 0, _displayCleanUp); /* if pass time limit, stop */ + /* if the current params are too slow, just stop. */ + if(target.cSpeed > candidate.cSpeed * 3 / 2) { break; } + } + + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winner.result, winner.params, target, buf.srcSize); + } + } + + DEBUGOUTPUT("Real Opt\n"); + /* start 'real' optimization */ + { + int bestStrategy = (int)winner.params.vals[strt_ind]; + if(paramTarget.vals[strt_ind] == PARAM_UNSET) { + int st = bestStrategy; + int tries = g_maxTries; + + { + /* one iterations of hill climbing with the level-defined parameters. */ + winnerInfo_t w1 = climbOnce(target, allMT, buf, ctx, winner.params); + if(compareResultLT(winner.result, w1.result, target, buf.srcSize)) { + winner = w1; + } + CHECKTIMEGT(ret, 0, _displayCleanUp); + } + + while(st && tries > 0) { + winnerInfo_t wc; + DEBUGOUTPUT("StrategySwitch: %s\n", g_stratName[st]); + + wc = optimizeFixedStrategy(buf, ctx, target, paramBase, st, allMT, tries); + + if(compareResultLT(winner.result, wc.result, target, buf.srcSize)) { + winner = wc; + tries = g_maxTries; + bestStrategy = st; + } else { + st = nextStrategy(st, bestStrategy); + tries -= TRY_DECAY; + } + CHECKTIMEGT(ret, 0, _displayCleanUp); + } + } else { + winner = optimizeFixedStrategy(buf, ctx, target, paramBase, paramTarget.vals[strt_ind], allMT, g_maxTries); + } + + } + + /* no solution found */ + if(winner.result.cSize == (size_t)-1) { + ret = 1; + DISPLAY("No feasible solution found\n"); + goto _cleanUp; + } + /* end summary */ +_displayCleanUp: + if(g_displayLevel >= 0) { BMK_displayOneResult(stdout, winner, buf.srcSize); } + BMK_translateAdvancedParams(stdout, winner.params); + DISPLAYLEVEL(1, "grillParams size - optimizer completed \n"); + + } +_cleanUp: + freeContexts(ctx); + freeBuffers(buf); + freeMemoTableArray(allMT); + return ret; +} + +/*-************************************ +* CLI parsing functions +**************************************/ + +/** longCommandWArg() : + * check if *stringPtr is the same as longCommand. + * If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand. + * @return 0 and doesn't modify *stringPtr otherwise. + * from zstdcli.c + */ +static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) +{ + size_t const comSize = strlen(longCommand); + int const result = !strncmp(*stringPtr, longCommand, comSize); + if (result) *stringPtr += comSize; + return result; } static void errorOut(const char* msg) @@ -772,7 +2393,9 @@ static void errorOut(const char* msg) static unsigned readU32FromChar(const char** stringPtr) { const char errorMsg[] = "error: numeric value too large"; + unsigned sign = 1; unsigned result = 0; + if(**stringPtr == '-') { sign = (unsigned)-1; (*stringPtr)++; } while ((**stringPtr >='0') && (**stringPtr <='9')) { unsigned const max = (((unsigned)(-1)) / 10) - 1; if (result > max) errorOut(errorMsg); @@ -790,6 +2413,22 @@ static unsigned readU32FromChar(const char** stringPtr) if (**stringPtr=='i') (*stringPtr)++; if (**stringPtr=='B') (*stringPtr)++; } + return result * sign; +} + +static double readDoubleFromChar(const char** stringPtr) +{ + double result = 0, divide = 10; + while ((**stringPtr >='0') && (**stringPtr <='9')) { + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + } + if(**stringPtr!='.') { + return result; + } + (*stringPtr)++; + while ((**stringPtr >='0') && (**stringPtr <='9')) { + result += (double)(**stringPtr - '0') / divide, divide *= 10, (*stringPtr)++ ; + } return result; } @@ -806,15 +2445,16 @@ static int usage(const char* exename) static int usage_advanced(void) { DISPLAY( "\nAdvanced options :\n"); - DISPLAY( " -T# : set level 1 speed objective \n"); - DISPLAY( " -B# : cut input into blocks of size # (default : single block) \n"); - DISPLAY( " -i# : iteration loops [1-9](default : %i) \n", NBLOOPS); - DISPLAY( " -O# : find Optimized parameters for # MB/s compression speed (default : 0) \n"); - DISPLAY( " -S : Single run \n"); - DISPLAY( " --zstd : Single run, parameter selection same as zstdcli \n"); - DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100); - DISPLAY( " -t# : Caps runtime of operation in seconds (default : %u seconds (%.1f hours)) \n", (U32)g_grillDuration_s, g_grillDuration_s / 3600); - DISPLAY( " -v : Prints Benchmarking output\n"); + DISPLAY( " -T# : set level 1 speed objective \n"); + DISPLAY( " -B# : cut input into blocks of size # (default : single block) \n"); + DISPLAY( " --optimize= : same as -O with more verbose syntax (see README.md)\n"); + DISPLAY( " -S : Single run \n"); + DISPLAY( " --zstd : Single run, parameter selection same as zstdcli \n"); + DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100); + DISPLAY( " -t# : Caps runtime of operation in seconds (default : %u seconds (%.1f hours)) \n", g_timeLimit_s, (double)g_timeLimit_s / 3600); + DISPLAY( " -v : Prints Benchmarking output\n"); + DISPLAY( " -D : Next argument dictionary file\n"); + DISPLAY( " -s : Seperate Files\n"); return 0; } @@ -825,41 +2465,87 @@ static int badusage(const char* exename) return 1; } +#define PARSE_SUB_ARGS(stringLong, stringShort, variable) { if (longCommandWArg(&argument, stringLong) || longCommandWArg(&argument, stringShort)) { variable = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } } +/* 1 if successful parse, 0 otherwise */ +static int parse_params(const char** argptr, paramValues_t* pv) { + int matched = 0; + const char* argOrig = *argptr; + varInds_t v; + for(v = 0; v < NUM_PARAMS; v++) { + if(longCommandWArg(argptr,g_shortParamNames[v]) || longCommandWArg(argptr, g_paramNames[v])) { + if(**argptr == '=') { + (*argptr)++; + pv->vals[v] = readU32FromChar(argptr); + matched = 1; + break; + } + } + /* reset and try again */ + *argptr = argOrig; + } + return matched; +} + +/*-************************************ +* Main +**************************************/ + int main(int argc, const char** argv) { int i, filenamesStart=0, result; const char* exename=argv[0]; - const char* input_filename=0; - U32 optimizer = 0; + const char* input_filename = NULL; + const char* dictFileName = NULL; U32 main_pause = 0; - U32 targetSpeed = 0; + int cLevelOpt = 0, cLevelRun = 0; + int seperateFiles = 0; + double compressibility = COMPRESSIBILITY_DEFAULT; + U32 memoTableLog = PARAM_UNSET; + constraint_t target = { 0, 0, (U32)-1 }; + + paramValues_t paramTarget = emptyParams(); + g_params = emptyParams(); assert(argc>=1); /* for exename */ - /* Welcome message */ - DISPLAY(WELCOME_MESSAGE); - for(i=1; i fastCover dictionary builder : advanced options " + +TESTFILE=../programs/zstdcli.c +./datagen > tmpDict +$ECHO "- Create first dictionary" +$ZSTD --train-fastcover=k=46,d=8,f=15,split=80 *.c ../programs/*.c -o tmpDict +cp $TESTFILE tmp +$ZSTD -f tmp -D tmpDict +$ZSTD -d tmp.zst -D tmpDict -fo result +$DIFF $TESTFILE result +$ECHO "- Create second (different) dictionary" +$ZSTD --train-fastcover=k=56,d=8 *.c ../programs/*.c ../programs/*.h -o tmpDictC +$ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" +$ECHO "- Create dictionary with short dictID" +$ZSTD --train-fastcover=k=46,d=8,f=15,split=80 *.c ../programs/*.c --dictID=1 -o tmpDict1 +cmp tmpDict tmpDict1 && die "dictionaries should have different ID !" +$ECHO "- Create dictionary with size limit" +$ZSTD --train-fastcover=steps=8 *.c ../programs/*.c -o tmpDict2 --maxdict=4K +$ECHO "- Compare size of dictionary from 90% training samples with 80% training samples" +$ZSTD --train-fastcover=split=90 -r *.c ../programs/*.c +$ZSTD --train-fastcover=split=80 -r *.c ../programs/*.c +$ECHO "- Create dictionary using all samples for both training and testing" +$ZSTD --train-fastcover=split=100 -r *.c ../programs/*.c +$ECHO "- Create dictionary using f=16" +$ZSTD --train-fastcover=f=16 -r *.c ../programs/*.c +$ECHO "- Create dictionary using accel=2" +$ZSTD --train-fastcover=accel=2 -r *.c ../programs/*.c +$ECHO "- Create dictionary using accel=10" +$ZSTD --train-fastcover=accel=10 -r *.c ../programs/*.c +$ECHO "- Create dictionary with multithreading" +$ZSTD --train-fastcover -T4 -r *.c ../programs/*.c +$ECHO "- Test -o before --train-fastcover" +rm -f tmpDict dictionary +$ZSTD -o tmpDict --train-fastcover *.c ../programs/*.c +test -f tmpDict +$ZSTD --train-fastcover *.c ../programs/*.c +test -f dictionary +rm tmp* dictionary + + $ECHO "\n===> legacy dictionary builder " TESTFILE=../programs/zstdcli.c diff --git a/tests/roundTripCrash.c b/tests/roundTripCrash.c index 7d937fcee..90afcd4b2 100644 --- a/tests/roundTripCrash.c +++ b/tests/roundTripCrash.c @@ -212,7 +212,7 @@ static void loadFile(void* buffer, const char* fileName, size_t fileSize) static void fileCheck(const char* fileName, int testCCtxParams) { size_t const fileSize = getFileSize(fileName); - void* buffer = malloc(fileSize); + void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */); if (!buffer) { fprintf(stderr, "not enough memory \n"); exit(4); diff --git a/tests/symbols.c b/tests/symbols.c index c0bed2e5d..b37082131 100644 --- a/tests/symbols.c +++ b/tests/symbols.c @@ -144,6 +144,8 @@ static const void *symbols[] = { /* zdict.h: advanced functions */ &ZDICT_trainFromBuffer_cover, &ZDICT_optimizeTrainFromBuffer_cover, + &ZDICT_trainFromBuffer_fastCover, + &ZDICT_optimizeTrainFromBuffer_fastCover, &ZDICT_finalizeDictionary, &ZDICT_trainFromBuffer_legacy, &ZDICT_addEntropyTablesFromBuffer, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 3d61394a7..1ca1bd080 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -600,7 +600,6 @@ static int basicUnitTests(U32 seed, double compressibility) size_t const initError = ZSTD_initCStream_usingCDict(zc, cdict); DISPLAYLEVEL(5, "ZSTD_initCStream_usingCDict result : %u ", (U32)initError); if (ZSTD_isError(initError)) goto _output_error; - cSize = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; @@ -718,7 +717,6 @@ static int basicUnitTests(U32 seed, double compressibility) ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem); size_t const initError = ZSTD_initCStream_usingCDict_advanced(zc, cdict, fParams, CNBufferSize); if (ZSTD_isError(initError)) goto _output_error; - cSize = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index a4dfbb6e8..d2d6073f9 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -573,10 +573,10 @@ static size_t BMK_findMaxMem(U64 requiredMem) do { testmem = (BYTE*)malloc((size_t)requiredMem); requiredMem -= step; - } while (!testmem); + } while (!testmem && requiredMem); /* do not allocate zero bytes */ free(testmem); - return (size_t)(requiredMem); + return (size_t)(requiredMem+1); /* avoid zero */ } static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, @@ -734,7 +734,7 @@ static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; if (benchedSize < totalSizeToLoad) DISPLAY("Not enough memory; testing %u MB only...\n", (U32)(benchedSize >> 20)); - srcBuffer = malloc(benchedSize); + srcBuffer = malloc(benchedSize + !benchedSize); if (!srcBuffer) EXM_THROW(12, "not enough memory"); /* Load input buffer */ diff --git a/zlibWrapper/gzlib.c b/zlibWrapper/gzlib.c index 8235cff4f..3070dd8b4 100644 --- a/zlibWrapper/gzlib.c +++ b/zlibWrapper/gzlib.c @@ -111,7 +111,7 @@ local gzFile gz_open(path, fd, mode) return NULL; /* allocate gzFile structure to return */ - state = (gz_statep)(gz_state*)malloc(sizeof(gz_state)); + state.state = (gz_state*)malloc(sizeof(gz_state)); if (state.state == NULL) return NULL; state.state->size = 0; /* no buffers allocated yet */ @@ -266,7 +266,7 @@ local gzFile gz_open(path, fd, mode) gz_reset(state); /* return stream */ - return (gzFile)state.file; + return state.file; } /* -- see zlib.h -- */ diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index d1250b900..21d5f8472 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -6,6 +6,8 @@ * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html */ +#include + #include "gzguts.h" /* Local functions */ @@ -24,7 +26,7 @@ local int gz_init(state) z_streamp strm = &(state.state->strm); /* allocate input buffer (double size for gzprintf) */ - state.state->in = (unsigned char *)malloc(state.state->want << 1); + state.state->in = (unsigned char*)malloc(state.state->want << 1); if (state.state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; @@ -33,7 +35,7 @@ local int gz_init(state) /* only need output buffer and deflate state if compressing */ if (!state.state->direct) { /* allocate output buffer */ - state.state->out = (unsigned char *)malloc(state.state->want); + state.state->out = (unsigned char*)malloc(state.state->want); if (state.state->out == NULL) { free(state.state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); @@ -284,6 +286,7 @@ z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) gz_statep state; /* get internal structure */ + assert(size != 0); if (file == NULL) return 0; state = (gz_statep)file; @@ -294,7 +297,7 @@ z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) /* compute bytes to read -- error on overflow */ len = nitems * size; - if (size && len / size != nitems) { + if (size && (len / size != nitems)) { gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); return 0; }