/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* this small program is used to measure the performance of zlib's inflate * algorithm... */ /* most code lifted from the public-domain http://www.zlib.net/zpipe.c */ #include <zlib.h> #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/time.h> #define CHUNK 32768 int def(FILE *source, FILE *dest, int level) { int ret, flush; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, level); if (ret != Z_OK) return ret; /* compress until end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)deflateEnd(&strm); return Z_ERRNO; } flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; strm.next_in = in; /* run deflate() on input until output buffer not full, finish compression if all of source has been read in */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = deflate(&strm, flush); /* no bad return value */ have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)deflateEnd(&strm); return Z_ERRNO; } } while (strm.avail_out == 0); /* done when last data in file processed */ } while (flush != Z_FINISH); /* clean up and return */ (void)deflateEnd(&strm); return Z_OK; } int inf(FILE *source) { int ret; unsigned have; z_stream strm; static unsigned char in[CHUNK]; static unsigned char out[CHUNK]; /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) return ret; /* decompress until deflate stream ends or end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)inflateEnd(&strm); return Z_ERRNO; } if (strm.avail_in == 0) break; strm.next_in = in; /* run inflate() on input until output buffer not full */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } } while (strm.avail_out == 0); /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } #define DEFAULT_REPEAT 10 #define DEFAULT_LEVEL 9 static void usage(void) { fprintf(stderr, "usage: test_zlib [options] filename [filename2 ...]\n" ); fprintf(stderr, "options: -r NN repeat count (default %d)\n", DEFAULT_REPEAT ); fprintf(stderr, " -N set compression level (default %d)\n", DEFAULT_LEVEL ); exit(1); } static double get_time_usec( void ) { #ifdef HAVE_ANDROID_OS struct timespec ts; if ( clock_gettime( CLOCK_MONOTONIC, &ts ) < 0 ) fprintf(stderr, "clock_gettime: %s\n", strerror(errno) ); return ts.tv_sec*1e6 + ts.tv_nsec*1e-3; #else struct timeval tv; if (gettimeofday( &tv, NULL ) < 0) fprintf(stderr, "gettimeofday: %s\n", strerror(errno) ); return tv.tv_sec*1000000. + tv.tv_usec*1.0; #endif } int main( int argc, char** argv ) { FILE* f; char tempfile[256]; int repeat_count = DEFAULT_REPEAT; int compression_level = DEFAULT_LEVEL; double usec0, usec1; if (argc < 2) usage(); for ( ; argc > 1 && argv[1][0] == '-'; argc--, argv++) { const char* arg = &argv[1][1]; switch (arg[0]) { case 'r': if (arg[1] == 0) { if (argc < 3) usage(); arg = argv[2]; argc--; argv++; } else arg += 1; repeat_count = strtol(arg, NULL, 10); if (repeat_count <= 0) repeat_count = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': compression_level = arg[0] - '0'; break; default: usage(); } } sprintf(tempfile, "/tmp/ztest.%d", getpid() ); for ( ; argc > 1; argc--, argv++ ) { /* first, compress the file into a temporary storage */ FILE* f = fopen(argv[1], "rb"); FILE* out = NULL; long fsize; int ret, rr; if (f == NULL) { fprintf(stderr, "could not open '%s': %s\n", argv[1], strerror(errno) ); continue; } printf( "testing %s\n", argv[1] ); fseek( f, 0, SEEK_END ); fsize = ftell(f); fseek( f, 0, SEEK_SET ); out = fopen( tempfile, "wb" ); if (out == NULL) { fprintf(stderr, "could not create '%s': %s\n", tempfile, strerror(errno)); fclose(f); continue; } usec0 = get_time_usec(); ret = def( f, out, compression_level ); usec1 = get_time_usec() - usec0; printf( "compression took: %10.3f ms (%.2f KB/s)\n", usec1/1e3, fsize*(1e6/1024)/usec1 ); fclose( out ); fclose(f); usec0 = get_time_usec(); f = fopen( tempfile, "rb" ); for ( rr = repeat_count; rr > 0; rr -- ) { fseek( f, 0, SEEK_SET ); inf(f); } fclose( f ); usec1 = get_time_usec() - usec0; printf( "decompression took: %10.3f ms (%.2f KB/s, %d passes)\n", usec1/1e3, fsize*(1e6/1024)*repeat_count/usec1, repeat_count ); } unlink(tempfile); return 0; }