diff options
author | KyleJHarper <KyleJHarper@gmail.com> | 2015-10-26 01:38:14 -0500 |
---|---|---|
committer | KyleJHarper <KyleJHarper@gmail.com> | 2015-10-26 01:38:14 -0500 |
commit | 02be6631beae8afa7140f6eece45333e91d22e4f (patch) | |
tree | 94a72c816f9e2675bcf3425eb4cdd38bc95b627a /examples | |
parent | 67b3a24707750d2f09d278cd46439ad6d94b93cb (diff) | |
download | lz4-02be6631beae8afa7140f6eece45333e91d22e4f.tar.gz lz4-02be6631beae8afa7140f6eece45333e91d22e4f.tar.bz2 lz4-02be6631beae8afa7140f6eece45333e91d22e4f.zip |
Took out the basics and placed them into basics.c. Added decompression and a wrapper for the generic call. I will likely break this file up into 2 examples before submission.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/Makefile | 5 | ||||
-rw-r--r-- | examples/basics.c | 83 | ||||
-rw-r--r-- | examples/compress_functions.c (renamed from examples/argPerformanceTesting.c) | 220 |
3 files changed, 232 insertions, 76 deletions
diff --git a/examples/Makefile b/examples/Makefile index e00bb58..012c020 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -68,9 +68,12 @@ lineCompress: $(LZ4DIR)/lz4.c blockStreaming_lineByLine.c frameCompress: frameCompress.c $(CC) $(FLAGS) $^ -o $@$(EXT) -L$(LZ4DIR) -llz4 -argPerformanceTesting: $(LZ4DIR)/lz4.c argPerformanceTesting.c +compressFunctions: $(LZ4DIR)/lz4.c compress_functions.c $(CC) $(FLAGS) $^ -o $@$(EXT) -lrt +basics: $(LZ4DIR)/lz4.c basics.c + $(CC) $(FLAGS) $^ -o $@$(EXT) + test : all ./printVersion$(EXT) ./doubleBuffer$(EXT) $(TESTFILE) diff --git a/examples/basics.c b/examples/basics.c new file mode 100644 index 0000000..c8c674a --- /dev/null +++ b/examples/basics.c @@ -0,0 +1,83 @@ +/* + * basics.c + * Copyright : Kyle Harper + * License : Follows same licensing as the lz4.c/lz4.h program at any given time. Currently, BSD 2. + * Description: Example program to demonstrate the basic usage of the compress/decompress functions within lz4.c/lz4.h. + * The functions you'll likely want are LZ4_compress_default and LZ4_compress_fast. Both of these are documented in + * the lz4.h header file; I guess reading them. + */ + +/* Includes, for Power! */ +#include "lz4.h" // This is all that is required to expose the prototypes for basic compression and decompression. +#include <stdio.h> // For printf() +#include <string.h> // For memcmp() +#include <stdlib.h> // For exit() + +/* + * Easy show-error-and-bail function. + */ +void run_screaming(const char *message, const int code) { + printf("%s\n", message); + exit(code); + return; +} + + +/* + * main + */ +int main(void) { + /* Compression */ + // We'll store some text into a variable pointed to by *src to be compressed later. + const char *src = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + // The compression function needs to know how many bytes of data we're sending. The string above has 57 characters == 57 bytes. + const int src_size = 57; + // LZ4 provides a function that will tell you the maximum size of compressed output based on input data via LZ4_compressBound. + const int max_dst_size = LZ4_compressBound(src_size); + // We will use that size for our destination boundary when allocating space. + char *compressed_data = malloc(max_dst_size); + if (compressed_data == NULL) + run_screaming("Failed to allocate memory for *compressed_data.", 1); + // That's all the information and preparation LZ4 needs to compress *src into *compressed_data. Invoke LZ4_compress_default now + // with our size values and pointers to our memory locations. Save the return value for error checking. + int return_value = 0; + return_value = LZ4_compress_default(src, compressed_data, src_size, max_dst_size); + // Check return_value to determine what happened. + if (return_value < 0) + run_screaming("A negative result from LZ4_compress_default indicates a failure trying to compress the data. See exit code (echo $?) for value returned.", return_value); + if (return_value == 0) + run_screaming("A result of 0 means compression worked, but was stopped because the destination buffer couldn't hold all the information.", 1); + if (return_value > 0) + printf("We successfully compressed some data!\n"); + // Not only does a positive return_value mean success, the value returned == the number of bytes required. You can use this to + // realloc() *compress_data to free up memory, if desired. We'll do so just to demonstrate the concept. + const int compressed_data_size = return_value; + compressed_data = (char *)realloc(compressed_data, compressed_data_size); + if (compressed_data == NULL) + run_screaming("Failed to re-alloc memeory for compressed_data. Sad :(", 1); + + /* Decompression */ + // Now that we've successfully compressed the information from *src to *compressed_data, let's do the opposite! We'll create a + // *new_src location of size src_size since we know that value. + char *new_src = malloc(src_size); + if (new_src == NULL) + run_screaming("Failed to allocate memory for *new_src.", 1); + // The LZ4_decompress_fast function needs to know where the compressed data is, where the new_src memory location is, and how + // large the new_src (uncompressed) output will be. Again, save the return_value. + return_value = LZ4_decompress_fast(compressed_data, new_src, src_size); + if (return_value < 0) + run_screaming("A negative result from LZ4_decompress_fast indicates a failure trying to decompress the data. See exit code (echo $?) for value returned.", return_value); + if (return_value == 0) + run_screaming("I'm not sure this function can ever return 0. Documentation in lz4.h doesn't indicate so.", 1); + if (return_value > 0) + printf("We successfully decompressed some data!\n"); + // Not only does a positive return value mean success, the value returned == the number of bytes read from the compressed_data + // stream. I'm not sure there's ever a time you'll need to know this in most cases... + + /* Validation */ + // We should be able to compare our original *src with our *new_src and be byte-for-byte identical. + if (memcmp(src, new_src, src_size) != 0) + run_screaming("Validation failed. *src and *new_src are not identical.", 1); + printf("Validation done. The string we ended up with is:\n%s\n", new_src); + return 0; +} diff --git a/examples/argPerformanceTesting.c b/examples/compress_functions.c index b999683..302f03f 100644 --- a/examples/argPerformanceTesting.c +++ b/examples/compress_functions.c @@ -1,9 +1,35 @@ /* - * Temporary example to confirm/disprove a performance optimization I believe might exist by re-using arguments instead of making - * LZ4_compress_generic re-evaluate them repeatedly between calls. + * compress_functions.c + * Copyright : Kyle Harper + * License : Follows same licensing as the lz4.c/lz4.h program at any given time. Currently, BSD 2. + * Description: A program to demonstrate the various compression functions involved in when using LZ4_compress_default(). The idea + * is to show how each step in the call stack can be used directly, if desired. There is also some benchmarking for + * each function to demonstrate the (probably lack of) performance difference when jumping the stack. + * + * The call stack (before theoretical compiler optimizations) for LZ4_compress_default is as follows: + * LZ4_compress_default + * LZ4_compress_fast + * LZ4_compress_fast_extState + * LZ4_compress_generic + * + * LZ4_compress_default() + * This is the recommended function for compressing data. It will serve as the baseline for comparison. + * LZ4_compress_fast() + * Despite its name, it's not a "fast" version of compression. It simply decides if HEAPMODE is set and either + * allocates memory on the heap for a struct or creates the struct directly on the stack. Stack access is generally + * faster but this function itself isn't giving that advantage, it's just some logic for compile time. + * LZ4_compress_fast_extState() + * This simply accepts all the pointers and values collected thus far and adds logic to determine how + * LZ4_compress_generic should be invoked; specifically: can the source fit into a single pass as determined by + * LZ4_64Klimit. + * LZ4_compress_generic() + * As the name suggests, this is the generic function that ultimately does most of the heavy lifting. Calling this + * directly can help avoid some test cases and branching which might be useful in some implementation-specific + * situations. + * */ -/* Since lz4 compiles with c99 we need to enable posix linking for time.h structs and functions. */ +/* Since lz4 compiles with c99 and not gnu/std99 we need to enable posix linking for time.h structs and functions. */ #if __STDC_VERSION__ >= 199901L #define _XOPEN_SOURCE 600 #else @@ -23,11 +49,18 @@ /* We need to know what one billion is for clock timing. */ #define BILLION 1000000000L -/* Create a crude set of test IDs so we can switch on them later. */ +/* Create a crude set of test IDs so we can switch on them later (Can't switch() on a char[] or char*. */ #define ID__LZ4_COMPRESS_DEFAULT 1 #define ID__LZ4_COMPRESS_FAST 2 #define ID__LZ4_COMPRESS_FAST_EXTSTATE 3 #define ID__LZ4_COMPRESS_GENERIC 4 +#define ID__LZ4_DECOMPRESS_FAST 5 + +/* Copy these to be syntactically accurate to lz4.c */ +typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive; +typedef enum { byPtr, byU32, byU16 } tableType_t; +typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; @@ -51,55 +84,54 @@ void usage(const char *message) { } + /* * Runs the benchmark for LZ4_compress_* based on function_id. */ -uint64_t bench(char *known_good_dst, const int function_id, const int iterations, const char *src, char *dst, const int src_size, const int max_dst_size) { +uint64_t bench(const char *known_good_dst, const int function_id, const int iterations, const char *src, char *dst, const int src_size, const int max_dst_size) { uint64_t time_taken = 0; int rv = 0; + const int warm_up = 5000; struct timespec start, end; const int acceleration = 1; LZ4_stream_t state; - // Select the right function to perform the benchmark on. For simplicity, start the timer here. We perform 2 initial loops to - // ensure that dst remains matching to known_good_dst between successive calls. + // Select the right function to perform the benchmark on. For simplicity, start the timer here. We perform 5000 initial loops + // to warm the cache and ensure that dst remains matching to known_good_dst between successive calls. clock_gettime(CLOCK_MONOTONIC, &start); switch(function_id) { case ID__LZ4_COMPRESS_DEFAULT: printf("Starting benchmark for function: LZ4_compress_default()\n"); - for(int junk=0; junk<2; junk++) { + for(int junk=0; junk<warm_up; junk++) rv = LZ4_compress_default(src, dst, src_size, max_dst_size); - if (rv < 1) - run_screaming("Couldn't run LZ4_compress_default()... error code received is in exit code.", rv); - if (memcmp(known_good_dst, dst, max_dst_size) != 0) - run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); - } + if (rv < 1) + run_screaming("Couldn't run LZ4_compress_default()... error code received is in exit code.", rv); + if (memcmp(known_good_dst, dst, max_dst_size) != 0) + run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); for (int i=1; i<=iterations; i++) LZ4_compress_default(src, dst, src_size, max_dst_size); break; case ID__LZ4_COMPRESS_FAST: printf("Starting benchmark for function: LZ4_compress_fast()\n"); - for(int junk=0; junk<2; junk++) { + for(int junk=0; junk<warm_up; junk++) rv = LZ4_compress_fast(src, dst, src_size, max_dst_size, acceleration); - if (rv < 1) - run_screaming("Couldn't run LZ4_compress_fast()... error code received is in exit code.", rv); - if (memcmp(known_good_dst, dst, max_dst_size) != 0) - run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); - } + if (rv < 1) + run_screaming("Couldn't run LZ4_compress_fast()... error code received is in exit code.", rv); + if (memcmp(known_good_dst, dst, max_dst_size) != 0) + run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); for (int i=1; i<=iterations; i++) LZ4_compress_fast(src, dst, src_size, max_dst_size, acceleration); break; case ID__LZ4_COMPRESS_FAST_EXTSTATE: printf("Starting benchmark for function: LZ4_compress_fast_extState()\n"); - for(int junk=0; junk<2; junk++) { + for(int junk=0; junk<warm_up; junk++) rv = LZ4_compress_fast_extState(&state, src, dst, src_size, max_dst_size, acceleration); - if (rv < 1) - run_screaming("Couldn't run LZ4_compress_fast_extState()... error code received is in exit code.", rv); - if (memcmp(known_good_dst, dst, max_dst_size) != 0) - run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); - } + if (rv < 1) + run_screaming("Couldn't run LZ4_compress_fast_extState()... error code received is in exit code.", rv); + if (memcmp(known_good_dst, dst, max_dst_size) != 0) + run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); for (int i=1; i<=iterations; i++) LZ4_compress_fast_extState(&state, src, dst, src_size, max_dst_size, acceleration); break; @@ -107,20 +139,32 @@ uint64_t bench(char *known_good_dst, const int function_id, const int iterations case ID__LZ4_COMPRESS_GENERIC: printf("Starting benchmark for function: LZ4_compress_generic()\n"); LZ4_resetStream((LZ4_stream_t*)&state); - for(int junk=0; junk<2; junk++) { + for(int junk=0; junk<warm_up; junk++) { LZ4_resetStream((LZ4_stream_t*)&state); - rv = LZ4_compress_generic_wrapper(&state, src, dst, src_size, acceleration); - if (rv < 1) - run_screaming("Couldn't run LZ4_compress_generic()... error code received is in exit code.", rv); - if (memcmp(known_good_dst, dst, max_dst_size) != 0) - run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); + rv = LZ4_compress_generic_wrapper(&state, src, dst, src_size, max_dst_size, notLimited, byU16, noDict, noDictIssue, 1); } + if (rv < 1) + run_screaming("Couldn't run LZ4_compress_generic()... error code received is in exit code.", rv); + if (memcmp(known_good_dst, dst, max_dst_size) != 0) + run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); for (int i=1; i<=iterations; i++) { LZ4_resetStream((LZ4_stream_t*)&state); - LZ4_compress_generic_wrapper(&state, src, dst, src_size, acceleration); + LZ4_compress_generic_wrapper(&state, src, dst, src_size, max_dst_size, notLimited, byU16, noDict, noDictIssue, 1); } break; + case ID__LZ4_DECOMPRESS_FAST: + printf("Starting benchmark for function: LZ4_decompress_fast()\n"); + for(int junk=0; junk<warm_up; junk++) + rv = LZ4_decompress_fast(src, dst, src_size); + if (rv < 1) + run_screaming("Couldn't run LZ4_compress_generic()... error code received is in exit code.", rv); + if (memcmp(known_good_dst, dst, src_size) != 0) + run_screaming("According to memcmp(), the compressed dst we got doesn't match the known_good_dst... ruh roh.", 1); + for (int i=1; i<=iterations; i++) + LZ4_decompress_fast(src, dst, src_size); + break; + default: run_screaming("The test specified isn't valid. Please check your code.", 1); break; @@ -136,73 +180,97 @@ uint64_t bench(char *known_good_dst, const int function_id, const int iterations /* - * Run tests. Call stack (before theoretical compiler optimizations) for LZ4_compress_default is as follows: - * LZ4_compress_default - * LZ4_compress_fast - * LZ4_compress_fast_extState - * LZ4_compress_generic - * + * main() + * We will demonstrate the use of each function for simplicity sake. Then we will run 2 suites of benchmarking: * Test suite A) Uses generic Lorem Ipsum text which should be generally compressible insomuch as basic human text is * compressible for such a small src_size * Test Suite B) For the sake of testing, see what results we get if the data is drastically easier to compress. IF there are * indeed losses and IF more compressible data is faster to process, this will exacerbate the findings. - * - * Test 1) LZ4_compress_default. - * A. No expectations here obviously. - * Test 2) LZ4_compress_fast. - * A. Compiler should have optimized the 'acceleration' constant expression (1), so no gains are expected here. That - * said, if nothing else ever calls this with a different acceleration, it could be eliminated. - * Test 3) LZ4_compress_fast_extState. - * A. This requires an LZ4_stream_t struct to track the compression stream, however it is initialized via a call to - * LZ4_resetStream() which ultimately just memset()s the memeory to 0. Avoiding creating this struct repeatedly - * might yield a minor improvement, but I doubt it. - * B. There is then an integer check on acceleration that has to run each iteration. (safety check) - * C. A call to LZ4_compressBound() is required to determine if output should be limited . (safety check) - * Test 4) LZ4_compress_generic. - * !. This is a STATIC INLINE function, which means it's probably slurped into the caller: LZ4_compress_fast_extState. - * There's likely nothing to test here but I'll create a wrapper to test it out. - * A. Calling this directly does NOT allow us to avoid calling resetStream() each time, but we do get to call it with - * our own local variable which might help... or not. - * B. We can avoid checking acceleration each time, which isn't very helpful. - * C. Since we can guarantee a few things we will avoid a few if..else checks. But this isn't fair as the function is - * serving a purpose and we're avoiding that safety check. */ int main(int argc, char **argv) { - // Get and verify options. This isn't user friendly but I don't care for a test. - int iterations = atoi(argv[1]); - if (argc < 2) - usage("Must specify at least 1 argument."); + // Get and verify options. There's really only 1: How many iterations to run. + int iterations = 1000000; + if (argc > 1) + iterations = atoi(argv[1]); if (iterations < 1) usage("Argument 1 (iterations) must be > 0."); - // Setup source data to work with. 500 bytes. Let LZ4 tell us the safest size for dst. - const int src_size = 2000; - const int max_dst_size = LZ4_compressBound(src_size); + // First we will create 2 sources (char *) of 2000 bytes each. One normal text, the other highly-compressible text. const char *src = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed luctus purus et risus vulputate, et mollis orci ullamcorper. Nulla facilisi. Fusce in ligula sed purus varius aliquet interdum vitae justo. Proin quis diam velit. Nulla varius iaculis auctor. Cras volutpat, justo eu dictum pulvinar, elit sem porttitor metus, et imperdiet metus sapien et ante. Nullam nisi nulla, ornare eu tristique eu, dignissim vitae diam. Nulla sagittis porta libero, a accumsan felis sagittis scelerisque. Integer laoreet eleifend congue. Etiam rhoncus leo vel dolor fermentum, quis luctus nisl iaculis. Praesent a erat sapien. Aliquam semper mi in lorem ultrices ultricies. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In feugiat risus sed enim ultrices, at sodales nulla tristique. Maecenas eget pellentesque justo, sed pellentesque lectus. Fusce sagittis sit amet elit vel varius. Donec sed ligula nec ligula vulputate rutrum sed ut lectus. Etiam congue pharetra leo vitae cursus. Morbi enim ante, porttitor ut varius vel, tincidunt quis justo. Nunc iaculis, risus id ultrices semper, metus est efficitur ligula, vel posuere risus nunc eget purus. Ut lorem turpis, condimentum at sem sed, porta aliquam turpis. In ut sapien a nulla dictum tincidunt quis sit amet lorem. Fusce at est egestas, luctus neque eu, consectetur tortor. Phasellus eleifend ultricies nulla ac lobortis. Morbi maximus quam cursus vehicula iaculis. Maecenas cursus vel justo ut rutrum. Curabitur magna orci, dignissim eget dapibus vitae, finibus id lacus. Praesent rhoncus mattis augue vitae bibendum. Praesent porta mauris non ultrices fermentum. Quisque vulputate ipsum in sodales pulvinar. Aliquam nec mollis felis. Donec vitae augue pulvinar, congue nisl sed, pretium purus. Fusce lobortis mi ac neque scelerisque semper. Pellentesque vel est vitae magna aliquet aliquet. Nam non dolor. Nulla facilisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi ac lacinia felis metus."; const char *hc_src = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + // Set and derive sizes. + const int src_size = 2000; + const int max_dst_size = LZ4_compressBound(src_size); + int bytes_returned = 0; + // Now build allocations for the data we'll be playing with. char *dst = calloc(1, max_dst_size); char *known_good_dst = calloc(1, max_dst_size); char *known_good_hc_dst = calloc(1, max_dst_size); + if (dst == NULL || known_good_dst == NULL || known_good_hc_dst == NULL) + run_screaming("Couldn't allocate memory for the destination buffers. Sad :(", 1); - // Pre-load dst with the compressed data so we can memcmp() it in our bench() function to ensure we're getting matching responses outputs. + // Create known-good buffers to verify our tests with other functions will produce the same results. if (LZ4_compress_default(src, known_good_dst, src_size, max_dst_size) < 0) run_screaming("Couldn't create a known-good destination buffer for comparison... this is bad.", 1); if (LZ4_compress_default(hc_src, known_good_hc_dst, src_size, max_dst_size) < 0) run_screaming("Couldn't create a known-good (highly compressible) destination buffer for comparison... this is bad.", 1); + + /* LZ4_compress_default() */ + // This is the default function so we don't need to demonstrate how to use it. See basics.c if you need more basal information. + + /* LZ4_compress_fast() */ + // Using this function is identical to LZ4_compress_default except we need to specify an "acceleration" value. Defaults to 1. + memset(dst, 0, max_dst_size); + bytes_returned = LZ4_compress_fast(src, dst, src_size, max_dst_size, 1); + if (bytes_returned < 1) + run_screaming("Failed to compress src using LZ4_compress_fast. echo $? for return code.", bytes_returned); + if (memcmp(dst, known_good_dst, bytes_returned) != 0) + run_screaming("According to memcmp(), the value we got in dst from LZ4_compress_fast doesn't match the known-good value. This is bad.", 1); + + /* LZ4_compress_fast_extState() */ + // Using this function directly requires that we build an LZ4_stream_t struct ourselves. We do NOT have to reset it ourselves. + memset(dst, 0, max_dst_size); + LZ4_stream_t state; + bytes_returned = LZ4_compress_fast_extState(&state, src, dst, src_size, max_dst_size, 1); + if (bytes_returned < 1) + run_screaming("Failed to compress src using LZ4_compress_fast_extState. echo $? for return code.", bytes_returned); + if (memcmp(dst, known_good_dst, bytes_returned) != 0) + run_screaming("According to memcmp(), the value we got in dst from LZ4_compress_fast_extState doesn't match the known-good value. This is bad.", 1); + + /* LZ4_compress_generic */ + // When you can exactly control the inputs and options of your LZ4 needs, you can use LZ4_compress_generic and fixed (const) + // values for the enum types such as dictionary and limitations. Any other direct-use is probably a bad idea. + memset(dst, 0, max_dst_size); + // LZ4_stream_t state: is already declared above. We can reuse it BUT we have to reset the stream ourselves between each call. + LZ4_resetStream((LZ4_stream_t *)&state); + // Since src size is small we know the following enums will be used: notLimited (0), byU16 (2), noDict (0), noDictIssue (0). + // They are hard-coded into the LZ4_compress_generic_wrapper() function that THIS program provides. + bytes_returned = LZ4_compress_generic_wrapper(&state, src, dst, src_size, max_dst_size, notLimited, byU16, noDict, noDictIssue, 1); + if (bytes_returned < 1) + run_screaming("Failed to compress src using LZ4_compress_generic. echo $? for return code.", bytes_returned); + if (memcmp(dst, known_good_dst, bytes_returned) != 0) + run_screaming("According to memcmp(), the value we got in dst from LZ4_compress_generic doesn't match the known-good value. This is bad.", 1); + + + /* Now we'll run a few benchmarks with each function to demonstrate differences in speed based on the function used. */ // Suite A - Normal Compressibility - printf("\nStarting suite A: Normal compressible text.\n"); - uint64_t time_taken__default = bench(known_good_dst, ID__LZ4_COMPRESS_DEFAULT, iterations, src, dst, src_size, max_dst_size); - uint64_t time_taken__fast = bench(known_good_dst, ID__LZ4_COMPRESS_FAST, iterations, src, dst, src_size, max_dst_size); - uint64_t time_taken__fast_extstate = bench(known_good_dst, ID__LZ4_COMPRESS_FAST_EXTSTATE, iterations, src, dst, src_size, max_dst_size); - uint64_t time_taken__generic = bench(known_good_dst, ID__LZ4_COMPRESS_GENERIC, iterations, src, dst, src_size, max_dst_size); + char *dst_d = calloc(1, src_size); memset(dst, 0, max_dst_size); + printf("\nStarting suite A: Normal compressible text.\n"); + uint64_t time_taken__default = bench(known_good_dst, ID__LZ4_COMPRESS_DEFAULT, iterations, src, dst, src_size, max_dst_size); + uint64_t time_taken__fast = bench(known_good_dst, ID__LZ4_COMPRESS_FAST, iterations, src, dst, src_size, max_dst_size); + uint64_t time_taken__fast_extstate = bench(known_good_dst, ID__LZ4_COMPRESS_FAST_EXTSTATE, iterations, src, dst, src_size, max_dst_size); + uint64_t time_taken__generic = bench(known_good_dst, ID__LZ4_COMPRESS_GENERIC, iterations, src, dst, src_size, max_dst_size); + uint64_t time_taken__decomp = bench(src, ID__LZ4_DECOMPRESS_FAST, iterations, known_good_dst, dst_d, src_size, max_dst_size); // Suite B - Highly Compressible + memset(dst, 0, max_dst_size); printf("\nStarting suite B: Highly compressible text.\n"); - uint64_t time_taken_hc__default = bench(known_good_hc_dst, ID__LZ4_COMPRESS_DEFAULT, iterations, hc_src, dst, src_size, max_dst_size); - uint64_t time_taken_hc__fast = bench(known_good_hc_dst, ID__LZ4_COMPRESS_FAST, iterations, hc_src, dst, src_size, max_dst_size); - uint64_t time_taken_hc__fast_extstate = bench(known_good_hc_dst, ID__LZ4_COMPRESS_FAST_EXTSTATE, iterations, hc_src, dst, src_size, max_dst_size); - uint64_t time_taken_hc__generic = bench(known_good_hc_dst, ID__LZ4_COMPRESS_GENERIC, iterations, hc_src, dst, src_size, max_dst_size); + uint64_t time_taken_hc__default = bench(known_good_hc_dst, ID__LZ4_COMPRESS_DEFAULT, iterations, hc_src, dst, src_size, max_dst_size); + uint64_t time_taken_hc__fast = bench(known_good_hc_dst, ID__LZ4_COMPRESS_FAST, iterations, hc_src, dst, src_size, max_dst_size); + uint64_t time_taken_hc__fast_extstate = bench(known_good_hc_dst, ID__LZ4_COMPRESS_FAST_EXTSTATE, iterations, hc_src, dst, src_size, max_dst_size); + uint64_t time_taken_hc__generic = bench(known_good_hc_dst, ID__LZ4_COMPRESS_GENERIC, iterations, hc_src, dst, src_size, max_dst_size); + uint64_t time_taken_hc__decomp = bench(hc_src, ID__LZ4_DECOMPRESS_FAST, iterations, known_good_hc_dst, dst_d, src_size, max_dst_size); // Report and leave. const char *format = "|%-16s|%-32s|%16.9f|%16d|%16d|%13.2f%%|\n"; @@ -216,11 +284,13 @@ int main(int argc, char **argv) { printf(format, "Normal Text", "LZ4_compress_fast()", (double)time_taken__fast / BILLION, (int)(iterations / ((double)time_taken__fast /BILLION)), time_taken__fast / iterations, (double)time_taken__fast * 100 / time_taken__default); printf(format, "Normal Text", "LZ4_compress_fast_extState()", (double)time_taken__fast_extstate / BILLION, (int)(iterations / ((double)time_taken__fast_extstate /BILLION)), time_taken__fast_extstate / iterations, (double)time_taken__fast_extstate * 100 / time_taken__default); printf(format, "Normal Text", "LZ4_compress_generic()", (double)time_taken__generic / BILLION, (int)(iterations / ((double)time_taken__generic /BILLION)), time_taken__generic / iterations, (double)time_taken__generic * 100 / time_taken__default); + printf(format, "Normal Text", "LZ4_decompress_fast()", (double)time_taken__decomp / BILLION, (int)(iterations / ((double)time_taken__decomp /BILLION)), time_taken__decomp / iterations, (double)time_taken__decomp * 100 / time_taken__default); printf(header_format, "", "", "", "", "", ""); printf(format, "Compressible", "LZ4_compress_default()", (double)time_taken_hc__default / BILLION, (int)(iterations / ((double)time_taken_hc__default /BILLION)), time_taken_hc__default / iterations, (double)time_taken_hc__default * 100 / time_taken_hc__default); printf(format, "Compressible", "LZ4_compress_fast()", (double)time_taken_hc__fast / BILLION, (int)(iterations / ((double)time_taken_hc__fast /BILLION)), time_taken_hc__fast / iterations, (double)time_taken_hc__fast * 100 / time_taken_hc__default); printf(format, "Compressible", "LZ4_compress_fast_extState()", (double)time_taken_hc__fast_extstate / BILLION, (int)(iterations / ((double)time_taken_hc__fast_extstate /BILLION)), time_taken_hc__fast_extstate / iterations, (double)time_taken_hc__fast_extstate * 100 / time_taken_hc__default); printf(format, "Compressible", "LZ4_compress_generic()", (double)time_taken_hc__generic / BILLION, (int)(iterations / ((double)time_taken_hc__generic /BILLION)), time_taken_hc__generic / iterations, (double)time_taken_hc__generic * 100 / time_taken_hc__default); + printf(format, "Compressible", "LZ4_decompress_fast()", (double)time_taken_hc__decomp / BILLION, (int)(iterations / ((double)time_taken_hc__decomp /BILLION)), time_taken_hc__decomp / iterations, (double)time_taken_hc__decomp * 100 / time_taken_hc__default); printf("%s", separator); printf("\n"); printf("All done, ran %d iterations per test.\n", iterations); |