diff --git a/CMakeLists.txt b/CMakeLists.txt index ee838358..a2a45731 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -888,7 +888,8 @@ foreach(libtype ${TEST_LIBTYPES}) ${CMAKE_COMMAND} -E copy_if_different ${TESTIMAGES}/testorig.ppm testout_tile.ppm) add_test(tjbench-${libtype}-tile - tjbench${suffix} testout_tile.ppm 95 -rgb -quiet -tile -benchtime 0.01) + tjbench${suffix} testout_tile.ppm 95 -rgb -quiet -tile -benchtime 0.01 + -warmup 0) set_tests_properties(tjbench-${libtype}-tile PROPERTIES DEPENDS tjbench-${libtype}-tile-cp) @@ -915,7 +916,7 @@ foreach(libtype ${TEST_LIBTYPES}) testout_tilem.ppm) add_test(tjbench-${libtype}-tilem tjbench${suffix} testout_tilem.ppm 95 -rgb -fastupsample -quiet -tile - -benchtime 0.01) + -benchtime 0.01 -warmup 0) set_tests_properties(tjbench-${libtype}-tilem PROPERTIES DEPENDS tjbench-${libtype}-tilem-cp) diff --git a/ChangeLog.md b/ChangeLog.md index d59e90f5..ab1e7691 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -127,6 +127,11 @@ more easily defend against one of the progressive JPEG exploits (LJT-01-004) identified in [this report](http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf). +10. TJBench will now run each benchmark for 1 second prior to starting the +timer, in order to improve the consistency of the results. Furthermore, the +`-warmup` option is now used to specify the amount of warmup time rather than +the number of warmup iterations. + 1.5.1 ===== diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 8c66c4b5..3246eaaf 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -1091,7 +1091,7 @@ Variables jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees this.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored. @@ -1189,7 +1189,7 @@ If you choose option 1, *jpegSize should be set to the size of your jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees this.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored. @@ -1286,7 +1286,7 @@ If you choose option 1, *jpegSize should be set to the size of your jpegBufaddress of a pointer to an image buffer that will receive the JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set *jpegBuf to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees this.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize(). This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.)
If you choose option 1, *jpegSize should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check *jpegBuf upon return from this function, as it may have changed. jpegSizepointer to an unsigned long variable that holds the size of the JPEG image buffer. If *jpegBuf points to a pre-allocated buffer, then *jpegSize should be set to the size of the buffer. Upon return, *jpegSize will contain the size of the JPEG image (in bytes.) If *jpegBuf points to a JPEG image buffer that is being reused from a previous call to one of the JPEG compression functions, then *jpegSize is ignored. @@ -2385,7 +2385,7 @@ If you choose option 1, *jpegSize should be set to the size of your dstBufspointer to an array of n image buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG buffer to accommodate the size of the JPEG image. Thus, you can choose to:
  1. pre-allocate the JPEG buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. This should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees this.)
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated (setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the output image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
If you choose option 1, dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed. dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the JPEG image (in bytes.) diff --git a/java/TJBench.java b/java/TJBench.java index e76f1a22..6f5f3355 100644 --- a/java/TJBench.java +++ b/java/TJBench.java @@ -34,7 +34,7 @@ import org.libjpegturbo.turbojpeg.*; class TJBench { - static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvpad = 1, warmup = 1; + static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvpad = 1; static boolean compOnly, decompOnly, doTile, doYUV, write = true; static final String[] pixFormatStr = { @@ -55,7 +55,7 @@ class TJBench { static TJScalingFactor sf; static int xformOp = TJTransform.OP_NONE, xformOpt = 0; - static double benchTime = 5.0; + static double benchTime = 5.0, warmup = 1.0; static final double getTime() { @@ -162,7 +162,7 @@ class TJBench { } /* Benchmark */ - iter -= warmup; + iter = -1; elapsed = elapsedDecode = 0.0; while (true) { int tile = 0; @@ -184,11 +184,14 @@ class TJBench { tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); } } - iter++; - if (iter >= 1) { - elapsed += getTime() - start; + elapsed += getTime() - start; + if (iter >= 0) { + iter++; if (elapsed >= benchTime) break; + } else if (elapsed >= warmup) { + iter = 0; + elapsed = elapsedDecode = 0.0; } } if(doYUV) @@ -321,7 +324,7 @@ class TJBench { } /* Benchmark */ - iter = -warmup; + iter = -1; elapsed = elapsedEncode = 0.0; while (true) { int tile = 0; @@ -346,11 +349,14 @@ class TJBench { totalJpegSize += jpegSize[tile]; } } - iter++; - if (iter >= 1) { - elapsed += getTime() - start; + elapsed += getTime() - start; + if (iter >= 0) { + iter++; if (elapsed >= benchTime) break; + } else if (elapsed >= warmup) { + iter = 0; + elapsed = elapsedEncode = 0.0; } } if (doYUV) @@ -541,17 +547,20 @@ class TJBench { } } - iter = -warmup; + iter = -1; elapsed = 0.; while (true) { start = getTime(); tjt.transform(jpegBuf, t, flags); jpegSize = tjt.getTransformedSizes(); - iter++; - if (iter >= 1) { - elapsed += getTime() - start; + elapsed += getTime() - start; + if (iter >= 0) { + iter++; if (elapsed >= benchTime) break; + } else if (elapsed >= warmup) { + iter = 0; + elapsed = 0.0; } } t = null; @@ -661,8 +670,9 @@ class TJBench { System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression"); System.out.println(" test (can be combined with the other transforms above)"); System.out.println("-benchtime = Run each benchmark for at least seconds (default = 5.0)"); - System.out.println("-warmup = Execute each benchmark times to prime the cache before"); - System.out.println(" taking performance measurements (default = 1)"); + System.out.println("-warmup = Run each benchmark for seconds (default = 1.0) prior to"); + System.out.println(" starting the timer, in order to prime the caches and thus improve the"); + System.out.println(" consistency of the results."); System.out.println("-componly = Stop after running compression tests. Do not test decompression."); System.out.println("-nowrite = Do not write reference or output images (improves consistency"); System.out.println(" of performance measurements.)"); @@ -833,14 +843,15 @@ class TJBench { if (argv[i].equalsIgnoreCase("-nowrite")) write = false; if (argv[i].equalsIgnoreCase("-warmup") && i < argv.length - 1) { - int temp = -1; + double temp = -1; try { - temp = Integer.parseInt(argv[++i]); + temp = Double.parseDouble(argv[++i]); } catch (NumberFormatException e) {} - if (temp >= 0) { + if (temp >= 0.0) { warmup = temp; - System.out.format("Warmup runs = %d\n\n", warmup); - } + System.out.format("Warmup time = %.1f seconds\n\n", warmup); + } else + usage(); } if (argv[i].equalsIgnoreCase("-stoponwarning")) flags |= TJ.FLAG_STOPONWARNING; diff --git a/tjbench.c b/tjbench.c index 36ea6cf2..a5be7d42 100644 --- a/tjbench.c +++ b/tjbench.c @@ -50,7 +50,7 @@ #define _throwbmp(m) _throw(m, bmpgeterr()) int flags=TJFLAG_NOREALLOC, componly=0, decomponly=0, doyuv=0, quiet=0, - dotile=0, pf=TJPF_BGR, yuvpad=1, warmup=1, dowrite=1; + dotile=0, pf=TJPF_BGR, yuvpad=1, dowrite=1; char *ext="ppm"; const char *pixFormatStr[TJ_NUMPF]= { @@ -68,7 +68,7 @@ const char *subName[TJ_NUMSAMP]={"444", "422", "420", "GRAY", "440", "411"}; tjscalingfactor *scalingfactors=NULL, sf={1, 1}; int nsf=0; int xformop=TJXOP_NONE, xformopt=0; int (*customFilter)(short *, tjregion, tjregion, int, int, tjtransform *); -double benchtime=5.0; +double benchtime=5.0, warmup=1.0; char *formatName(int subsamp, int cs, char *buf) @@ -150,7 +150,7 @@ int decomp(unsigned char *srcbuf, unsigned char **jpegbuf, } /* Benchmark */ - iter=-warmup; + iter=-1; elapsed=elapsedDecode=0.; while(1) { @@ -180,12 +180,17 @@ int decomp(unsigned char *srcbuf, unsigned char **jpegbuf, _throwtj("executing tjDecompress2()"); } } - iter++; - if(iter>=1) + elapsed+=gettime()-start; + if(iter>=0) { - elapsed+=gettime()-start; + iter++; if(elapsed>=benchtime) break; } + else if(elapsed>=warmup) + { + iter=0; + elapsed=elapsedDecode=0.; + } } if(doyuv) elapsed-=elapsedDecode; @@ -344,7 +349,7 @@ int fullTest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual, } /* Benchmark */ - iter=-warmup; + iter=-1; elapsed=elapsedEncode=0.; while(1) { @@ -378,12 +383,17 @@ int fullTest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual, totaljpegsize+=jpegsize[tile]; } } - iter++; - if(iter>=1) + elapsed+=gettime()-start; + if(iter>=0) { - elapsed+=gettime()-start; + iter++; if(elapsed>=benchtime) break; } + else if(elapsed>=warmup) + { + iter=0; + elapsed=elapsedEncode=0.; + } } if(doyuv) elapsed-=elapsedEncode; @@ -627,7 +637,7 @@ int decompTest(char *filename) } } - iter=-warmup; + iter=-1; elapsed=0.; while(1) { @@ -635,12 +645,17 @@ int decompTest(char *filename) if(tjTransform(handle, srcbuf, srcsize, _ntilesw*_ntilesh, jpegbuf, jpegsize, t, flags)==-1) _throwtj("executing tjTransform()"); - iter++; - if(iter>=1) + elapsed+=gettime()-start; + if(iter>=0) { - elapsed+=gettime()-start; + iter++; if(elapsed>=benchtime) break; } + else if(elapsed>=warmup) + { + iter=0; + elapsed=0.; + } } free(t); t=NULL; @@ -771,8 +786,9 @@ void usage(char *progname) printf("-grayscale = Perform lossless grayscale conversion prior to decompression\n"); printf(" test (can be combined with the other transforms above)\n"); printf("-benchtime = Run each benchmark for at least seconds (default = 5.0)\n"); - printf("-warmup = Execute each benchmark times to prime the cache before\n"); - printf(" taking performance measurements (default = 1)\n"); + printf("-warmup = Run each benchmark for seconds (default = 1.0) prior to\n"); + printf(" starting the timer, in order to prime the caches and thus improve the\n"); + printf(" consistency of the results.\n"); printf("-componly = Stop after running compression tests. Do not test decompression.\n"); printf("-nowrite = Do not write reference or output images (improves consistency of\n"); printf(" performance measurements.)\n"); @@ -894,13 +910,10 @@ int main(int argc, char *argv[]) } if(!strcasecmp(argv[i], "-warmup") && i=0) - { - warmup=temp; - printf("Warmup runs = %d\n\n", warmup); - } + double temp=atof(argv[++i]); + if(temp>=0.0) warmup=temp; else usage(argv[0]); + printf("Warmup time = %.1f seconds\n\n", warmup); } if(!strcmp(argv[i], "-?")) usage(argv[0]); if(!strcasecmp(argv[i], "-alloc")) flags&=(~TJFLAG_NOREALLOC); diff --git a/turbojpeg.h b/turbojpeg.h index 4edcc680..8b4a4dad 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -681,7 +681,7 @@ DLLEXPORT tjhandle DLLCALL tjInitCompress(void); * for you, or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees this.) + * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, *jpegSize should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, @@ -750,7 +750,7 @@ DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, const unsigned char *srcBuf, * for you, or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees this.) + * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, *jpegSize should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, @@ -821,7 +821,7 @@ DLLEXPORT int DLLCALL tjCompressFromYUV(tjhandle handle, * for you, or * -# pre-allocate the buffer to a "worst case" size determined by calling * #tjBufSize(). This should ensure that the buffer never has to be - * re-allocated (setting #TJFLAG_NOREALLOC guarantees this.) + * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) * . * If you choose option 1, *jpegSize should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC, @@ -1439,9 +1439,13 @@ DLLEXPORT tjhandle DLLCALL tjInitTransform(void); * -# set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer * for you, or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize() with the transformed or cropped width and height. This should - * ensure that the buffer never has to be re-allocated (setting - * #TJFLAG_NOREALLOC guarantees this.) + * #tjBufSize() with the transformed or cropped width and height. Under normal + * circumstances, this should ensure that the buffer never has to be + * re-allocated (setting #TJFLAG_NOREALLOC guarantees that it won't be.) Note, + * however, that there are some rare cases (such as transforming images with a + * large amount of embedded EXIF or ICC profile data) in which the output image + * will be larger than the worst-case size, and #TJFLAG_NOREALLOC cannot be + * used in those cases. * . * If you choose option 1, dstSizes[i] should be set to the size of * your pre-allocated buffer. In any case, unless you have set