1 // Copyright 2011 Google Inc. All Rights Reserved.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
13 // * Neither the name of Google Inc. nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 // Various stubs for the unit tests for the open-source version of Snappy.
31 #include "snappy-test.h"
34 #define WIN32_LEAN_AND_MEAN
40 DEFINE_bool(run_microbenchmarks, true,
41 "Run microbenchmarks before doing anything else.");
45 string ReadTestDataFile(const string& base) {
47 const char* srcdir = getenv("srcdir"); // This is set by Automake.
49 File::ReadFileToStringOrDie(
50 string(srcdir) + "/testdata/" + base, &contents);
52 File::ReadFileToStringOrDie("testdata/" + base, &contents);
57 string StringPrintf(const char* format, ...) {
61 vsnprintf(buf, sizeof(buf), format, ap);
66 bool benchmark_running = false;
67 int64 benchmark_real_time_us = 0;
68 int64 benchmark_cpu_time_us = 0;
69 string *benchmark_label = NULL;
70 int64 benchmark_bytes_processed = 0;
72 void ResetBenchmarkTiming() {
73 benchmark_real_time_us = 0;
74 benchmark_cpu_time_us = 0;
78 LARGE_INTEGER benchmark_start_real;
79 FILETIME benchmark_start_cpu;
81 struct timeval benchmark_start_real;
82 struct rusage benchmark_start_cpu;
85 void StartBenchmarkTiming() {
87 QueryPerformanceCounter(&benchmark_start_real);
89 CHECK(GetProcessTimes(
90 GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_start_cpu));
92 gettimeofday(&benchmark_start_real, NULL);
93 if (getrusage(RUSAGE_SELF, &benchmark_start_cpu) == -1) {
94 perror("getrusage(RUSAGE_SELF)");
98 benchmark_running = true;
101 void StopBenchmarkTiming() {
102 if (!benchmark_running) {
107 LARGE_INTEGER benchmark_stop_real;
108 LARGE_INTEGER benchmark_frequency;
109 QueryPerformanceCounter(&benchmark_stop_real);
110 QueryPerformanceFrequency(&benchmark_frequency);
112 double elapsed_real = static_cast<double>(
113 benchmark_stop_real.QuadPart - benchmark_start_real.QuadPart) /
114 benchmark_frequency.QuadPart;
115 benchmark_real_time_us += elapsed_real * 1e6 + 0.5;
117 FILETIME benchmark_stop_cpu, dummy;
118 CHECK(GetProcessTimes(
119 GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_stop_cpu));
121 ULARGE_INTEGER start_ulargeint;
122 start_ulargeint.LowPart = benchmark_start_cpu.dwLowDateTime;
123 start_ulargeint.HighPart = benchmark_start_cpu.dwHighDateTime;
125 ULARGE_INTEGER stop_ulargeint;
126 stop_ulargeint.LowPart = benchmark_stop_cpu.dwLowDateTime;
127 stop_ulargeint.HighPart = benchmark_stop_cpu.dwHighDateTime;
129 benchmark_cpu_time_us +=
130 (stop_ulargeint.QuadPart - start_ulargeint.QuadPart + 5) / 10;
132 struct timeval benchmark_stop_real;
133 gettimeofday(&benchmark_stop_real, NULL);
134 benchmark_real_time_us +=
135 1000000 * (benchmark_stop_real.tv_sec - benchmark_start_real.tv_sec);
136 benchmark_real_time_us +=
137 (benchmark_stop_real.tv_usec - benchmark_start_real.tv_usec);
139 struct rusage benchmark_stop_cpu;
140 if (getrusage(RUSAGE_SELF, &benchmark_stop_cpu) == -1) {
141 perror("getrusage(RUSAGE_SELF)");
144 benchmark_cpu_time_us += 1000000 * (benchmark_stop_cpu.ru_utime.tv_sec -
145 benchmark_start_cpu.ru_utime.tv_sec);
146 benchmark_cpu_time_us += (benchmark_stop_cpu.ru_utime.tv_usec -
147 benchmark_start_cpu.ru_utime.tv_usec);
150 benchmark_running = false;
153 void SetBenchmarkLabel(const string& str) {
154 if (benchmark_label) {
155 delete benchmark_label;
157 benchmark_label = new string(str);
160 void SetBenchmarkBytesProcessed(int64 bytes) {
161 benchmark_bytes_processed = bytes;
164 struct BenchmarkRun {
169 struct BenchmarkCompareCPUTime {
170 bool operator() (const BenchmarkRun& a, const BenchmarkRun& b) const {
171 return a.cpu_time_us < b.cpu_time_us;
175 void Benchmark::Run() {
176 for (int test_case_num = start_; test_case_num <= stop_; ++test_case_num) {
177 // Run a few iterations first to find out approximately how fast
179 const int kCalibrateIterations = 100;
180 ResetBenchmarkTiming();
181 StartBenchmarkTiming();
182 (*function_)(kCalibrateIterations, test_case_num);
183 StopBenchmarkTiming();
185 // Let each test case run for about 200ms, but at least as many
186 // as we used to calibrate.
187 // Run five times and pick the median.
188 const int kNumRuns = 5;
189 const int kMedianPos = kNumRuns / 2;
190 int num_iterations = 0;
191 if (benchmark_real_time_us > 0) {
192 num_iterations = 200000 * kCalibrateIterations / benchmark_real_time_us;
194 num_iterations = max(num_iterations, kCalibrateIterations);
195 BenchmarkRun benchmark_runs[kNumRuns];
197 for (int run = 0; run < kNumRuns; ++run) {
198 ResetBenchmarkTiming();
199 StartBenchmarkTiming();
200 (*function_)(num_iterations, test_case_num);
201 StopBenchmarkTiming();
203 benchmark_runs[run].real_time_us = benchmark_real_time_us;
204 benchmark_runs[run].cpu_time_us = benchmark_cpu_time_us;
207 nth_element(benchmark_runs,
208 benchmark_runs + kMedianPos,
209 benchmark_runs + kNumRuns,
210 BenchmarkCompareCPUTime());
211 int64 real_time_us = benchmark_runs[kMedianPos].real_time_us;
212 int64 cpu_time_us = benchmark_runs[kMedianPos].cpu_time_us;
213 int64 bytes_per_second = benchmark_bytes_processed * 1000000 / cpu_time_us;
215 string heading = StringPrintf("%s/%d", name_.c_str(), test_case_num);
216 string human_readable_speed;
217 if (bytes_per_second < 1024) {
218 human_readable_speed = StringPrintf("%dB/s", bytes_per_second);
219 } else if (bytes_per_second < 1024 * 1024) {
220 human_readable_speed = StringPrintf(
221 "%.1fkB/s", bytes_per_second / 1024.0f);
222 } else if (bytes_per_second < 1024 * 1024 * 1024) {
223 human_readable_speed = StringPrintf(
224 "%.1fMB/s", bytes_per_second / (1024.0f * 1024.0f));
226 human_readable_speed = StringPrintf(
227 "%.1fGB/s", bytes_per_second / (1024.0f * 1024.0f * 1024.0f));
232 "%-18s %10I64d %10I64d %10d %s %s\n",
234 "%-18s %10lld %10lld %10d %s %s\n",
237 static_cast<long long>(real_time_us * 1000 / num_iterations),
238 static_cast<long long>(cpu_time_us * 1000 / num_iterations),
240 human_readable_speed.c_str(),
241 benchmark_label->c_str());
249 uncomp_init_(false) {
254 if (comp_init_) { deflateEnd(&comp_stream_); }
255 if (uncomp_init_) { inflateEnd(&uncomp_stream_); }
258 void ZLib::Reinit() {
259 compression_level_ = Z_DEFAULT_COMPRESSION;
260 window_bits_ = MAX_WBITS;
261 mem_level_ = 8; // DEF_MEM_LEVEL
263 deflateEnd(&comp_stream_);
267 inflateEnd(&uncomp_stream_);
268 uncomp_init_ = false;
277 // --------- COMPRESS MODE
279 // Initialization method to be called if we hit an error while
280 // compressing. On hitting an error, call this method before returning
282 void ZLib::CompressErrorInit() {
283 deflateEnd(&comp_stream_);
288 int ZLib::DeflateInit() {
289 return deflateInit2(&comp_stream_,
297 int ZLib::CompressInit(Bytef *dest, uLongf *destLen,
298 const Bytef *source, uLong *sourceLen) {
301 comp_stream_.next_in = (Bytef*)source;
302 comp_stream_.avail_in = (uInt)*sourceLen;
303 if ((uLong)comp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
304 comp_stream_.next_out = dest;
305 comp_stream_.avail_out = (uInt)*destLen;
306 if ((uLong)comp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
308 if ( !first_chunk_ ) // only need to set up stream the first time through
311 if (comp_init_) { // we've already initted it
312 err = deflateReset(&comp_stream_);
314 LOG(WARNING) << "ERROR: Can't reset compress object; creating a new one";
315 deflateEnd(&comp_stream_);
319 if (!comp_init_) { // first use
320 comp_stream_.zalloc = (alloc_func)0;
321 comp_stream_.zfree = (free_func)0;
322 comp_stream_.opaque = (voidpf)0;
324 if (err != Z_OK) return err;
330 // In a perfect world we'd always have the full buffer to compress
331 // when the time came, and we could just call Compress(). Alas, we
332 // want to do chunked compression on our webserver. In this
333 // application, we compress the header, send it off, then compress the
334 // results, send them off, then compress the footer. Thus we need to
335 // use the chunked compression features of zlib.
336 int ZLib::CompressAtMostOrAll(Bytef *dest, uLongf *destLen,
337 const Bytef *source, uLong *sourceLen,
338 int flush_mode) { // Z_FULL_FLUSH or Z_FINISH
341 if ( (err=CompressInit(dest, destLen, source, sourceLen)) != Z_OK )
344 // This is used to figure out how many bytes we wrote *this chunk*
345 int compressed_size = comp_stream_.total_out;
347 // Some setup happens only for the first chunk we compress in a run
348 if ( first_chunk_ ) {
349 first_chunk_ = false;
352 // flush_mode is Z_FINISH for all mode, Z_SYNC_FLUSH for incremental
354 err = deflate(&comp_stream_, flush_mode);
356 const uLong source_bytes_consumed = *sourceLen - comp_stream_.avail_in;
357 *sourceLen = comp_stream_.avail_in;
359 if ((err == Z_STREAM_END || err == Z_OK)
360 && comp_stream_.avail_in == 0
361 && comp_stream_.avail_out != 0 ) {
362 // we processed everything ok and the output buffer was large enough.
364 } else if (err == Z_STREAM_END && comp_stream_.avail_in > 0) {
365 return Z_BUF_ERROR; // should never happen
366 } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
370 } else if (comp_stream_.avail_out == 0) { // not enough space
374 assert(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR);
375 if (err == Z_STREAM_END)
378 // update the crc and other metadata
379 compressed_size = comp_stream_.total_out - compressed_size; // delta
380 *destLen = compressed_size;
385 int ZLib::CompressChunkOrAll(Bytef *dest, uLongf *destLen,
386 const Bytef *source, uLong sourceLen,
387 int flush_mode) { // Z_FULL_FLUSH or Z_FINISH
389 CompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
390 if (ret == Z_BUF_ERROR)
395 // This routine only initializes the compression stream once. Thereafter, it
396 // just does a deflateReset on the stream, which should be faster.
397 int ZLib::Compress(Bytef *dest, uLongf *destLen,
398 const Bytef *source, uLong sourceLen) {
400 const uLongf orig_destLen = *destLen;
401 if ( (err=CompressChunkOrAll(dest, destLen, source, sourceLen,
404 Reset(); // reset for next call to Compress
410 // --------- UNCOMPRESS MODE
412 int ZLib::InflateInit() {
413 return inflateInit2(&uncomp_stream_, MAX_WBITS);
416 // Initialization method to be called if we hit an error while
417 // uncompressing. On hitting an error, call this method before
418 // returning the error.
419 void ZLib::UncompressErrorInit() {
420 inflateEnd(&uncomp_stream_);
421 uncomp_init_ = false;
425 int ZLib::UncompressInit(Bytef *dest, uLongf *destLen,
426 const Bytef *source, uLong *sourceLen) {
429 uncomp_stream_.next_in = (Bytef*)source;
430 uncomp_stream_.avail_in = (uInt)*sourceLen;
431 // Check for source > 64K on 16-bit machine:
432 if ((uLong)uncomp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
434 uncomp_stream_.next_out = dest;
435 uncomp_stream_.avail_out = (uInt)*destLen;
436 if ((uLong)uncomp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
438 if ( !first_chunk_ ) // only need to set up stream the first time through
441 if (uncomp_init_) { // we've already initted it
442 err = inflateReset(&uncomp_stream_);
445 << "ERROR: Can't reset uncompress object; creating a new one";
446 UncompressErrorInit();
450 uncomp_stream_.zalloc = (alloc_func)0;
451 uncomp_stream_.zfree = (free_func)0;
452 uncomp_stream_.opaque = (voidpf)0;
454 if (err != Z_OK) return err;
460 // If you compressed your data a chunk at a time, with CompressChunk,
461 // you can uncompress it a chunk at a time with UncompressChunk.
462 // Only difference bewteen chunked and unchunked uncompression
463 // is the flush mode we use: Z_SYNC_FLUSH (chunked) or Z_FINISH (unchunked).
464 int ZLib::UncompressAtMostOrAll(Bytef *dest, uLongf *destLen,
465 const Bytef *source, uLong *sourceLen,
466 int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH
469 if ( (err=UncompressInit(dest, destLen, source, sourceLen)) != Z_OK ) {
470 LOG(WARNING) << "UncompressInit: Error: " << err << " SourceLen: "
475 // This is used to figure out how many output bytes we wrote *this chunk*:
476 const uLong old_total_out = uncomp_stream_.total_out;
478 // This is used to figure out how many input bytes we read *this chunk*:
479 const uLong old_total_in = uncomp_stream_.total_in;
481 // Some setup happens only for the first chunk we compress in a run
482 if ( first_chunk_ ) {
483 first_chunk_ = false; // so we don't do this again
485 // For the first chunk *only* (to avoid infinite troubles), we let
486 // there be no actual data to uncompress. This sometimes triggers
487 // when the input is only the gzip header, say.
488 if ( *sourceLen == 0 ) {
494 // We'll uncompress as much as we can. If we end OK great, otherwise
495 // if we get an error that seems to be the gzip footer, we store the
496 // gzip footer and return OK, otherwise we return the error.
498 // flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode.
499 err = inflate(&uncomp_stream_, flush_mode);
501 // Figure out how many bytes of the input zlib slurped up:
502 const uLong bytes_read = uncomp_stream_.total_in - old_total_in;
503 CHECK_LE(source + bytes_read, source + *sourceLen);
504 *sourceLen = uncomp_stream_.avail_in;
506 if ((err == Z_STREAM_END || err == Z_OK) // everything went ok
507 && uncomp_stream_.avail_in == 0) { // and we read it all
509 } else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) {
511 << "UncompressChunkOrAll: Received some extra data, bytes total: "
512 << uncomp_stream_.avail_in << " bytes: "
513 << string(reinterpret_cast<const char *>(uncomp_stream_.next_in),
514 min(int(uncomp_stream_.avail_in), 20));
515 UncompressErrorInit();
516 return Z_DATA_ERROR; // what's the extra data for?
517 } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
519 LOG(WARNING) << "UncompressChunkOrAll: Error: " << err
520 << " avail_out: " << uncomp_stream_.avail_out;
521 UncompressErrorInit();
523 } else if (uncomp_stream_.avail_out == 0) {
527 assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END);
528 if (err == Z_STREAM_END)
531 *destLen = uncomp_stream_.total_out - old_total_out; // size for this call
536 int ZLib::UncompressChunkOrAll(Bytef *dest, uLongf *destLen,
537 const Bytef *source, uLong sourceLen,
538 int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH
540 UncompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
541 if (ret == Z_BUF_ERROR)
542 UncompressErrorInit();
546 int ZLib::UncompressAtMost(Bytef *dest, uLongf *destLen,
547 const Bytef *source, uLong *sourceLen) {
548 return UncompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH);
551 // We make sure we've uncompressed everything, that is, the current
552 // uncompress stream is at a compressed-buffer-EOF boundary. In gzip
553 // mode, we also check the gzip footer to make sure we pass the gzip
554 // consistency checks. We RETURN true iff both types of checks pass.
555 bool ZLib::UncompressChunkDone() {
556 assert(!first_chunk_ && uncomp_init_);
557 // Make sure we're at the end-of-compressed-data point. This means
558 // if we call inflate with Z_FINISH we won't consume any input or
560 Bytef dummyin, dummyout;
562 if ( UncompressChunkOrAll(&dummyout, &dummylen, &dummyin, 0, Z_FINISH)
567 // Make sure that when we exit, we can start a new round of chunks later
573 // Uncompresses the source buffer into the destination buffer.
574 // The destination buffer must be long enough to hold the entire
575 // decompressed contents.
577 // We only initialize the uncomp_stream once. Thereafter, we use
578 // inflateReset, which should be faster.
580 // Returns Z_OK on success, otherwise, it returns a zlib error code.
581 int ZLib::Uncompress(Bytef *dest, uLongf *destLen,
582 const Bytef *source, uLong sourceLen) {
584 if ( (err=UncompressChunkOrAll(dest, destLen, source, sourceLen,
585 Z_FINISH)) != Z_OK ) {
586 Reset(); // let us try to compress again
589 if ( !UncompressChunkDone() ) // calls Reset()
591 return Z_OK; // stream_end is ok
596 } // namespace snappy