Skip to content

Commit e2663fa

Browse files
authored
Option to time all IBA functions (#1885)
* Option to time all IBA functions All ImageBufAlgo functions (for now; other functionality may be added to logging down the line) are outfitted with optional timers to count the number of executions and total time spent for each. This is enabled by a new global OIIO attribute "log_times", which can be enabled and disabled at any time (for example, to only log a certain phase of execution of an app). The default is 0, meaning not to log -- and I believe that when turned off, there is no added runtime cost (except for checking the flag once per IBA function). The initial default can be changed with the OPENIMAGEIO_LOG_TIMES environment variable. When log_times is nonzero, logging will occur, with a slight increase in runtime expense to check the times and to lock and access the map that stores all the running totals (stored separately, indexed by name). This extra cost is probably not significant for whole-image operations such as IBA calls, but it would not be an especially accurate way to time fine-grained operations that work on one or a few pixels at a time. The global getattribute() call may be used to retrieve "timing_report", which will return a string containing a list of all the timing totals, alphabetized by name, like this: IBA::computePixelStats 2 0.003s (avg 1.34ms) IBA::make_texture 1 0.074s (avg 74.05ms) IBA::mul 8 0.002s (avg 0.30ms) IBA::over 10 0.024s (avg 2.38ms) IBA::resize 20 0.240s (avg 12.18ms) IBA::zero 8 0.001s (avg 0.08ms) If the log_times option has a value > 1 at the time that the program terminates, the destructor of the timing data structure will automatically print the timing report to stdout. So, even for a program (that uses OIIO ImageBufAlgo internally) that has no exposed option to retrieve and print the report, you can still find out how it's spending its time by launching as OPENIMAGEIO_LOG_TIMES=2 my_application Some caveats to keep in mind: * It's possible that in situations where one IBA function ends up calling another, the time could be doubled-counted. * This measures "wall clock time", so interpret carefully in the presence of multithreading.
1 parent 5d248d0 commit e2663fa

20 files changed

+271
-7
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ Public API changes:
5656
* ImageBufAlgo::colorconvert variety deprecated since 1.7.
5757
* ImageCache::clear, deprecated since 1.7.
5858
* ImageCache::add_tile variety deprecated since 1.6.
59+
* Global OIIO attribute "log_times" (which defaults to 0 but can be overridden
60+
by setting the `OPENIMAGEIO_LOG_TIMES` environment variable), when nonzero,
61+
instruments ImageBufAlgo functions to record the number of times they are
62+
called and how much time they take to execute. A report of these times
63+
can be retrieved as a string as the "timing_report" attribute, or it will
64+
be printed to stdout automatically if the value of log_times is 2 or more
65+
at the time that the application exits. #1885 (1.9.2)
5966

6067
Performance improvements:
6168
* ImageBufAlgo::computePixelStats is now multithreaded and should improve by

src/doc/imageioapi.tex

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,8 +999,53 @@ \section{Miscellaneous Utilities}
999999
of 0 indicates that it should try to read the whole image if possible.
10001000
\apiend
10011001

1002+
\apiitem{int debug}
1003+
\vspace{10pt}
1004+
\index{debug}
1005+
When nonzero, various debug messages may be printed. The default is 0 for
1006+
release builds, 1 for {\cf DEBUG} builds, but also may be overridden by the
1007+
{\cf OPENIMAGEIO\_DEBUG} env variable.
1008+
\apiend
1009+
1010+
\apiitem{int log_times \\
1011+
string timing_report}
1012+
\vspace{10pt}
1013+
\index{log_times} \index{timing_report}
1014+
\NEW % 1.9
1015+
1016+
When the \qkw{log_times} attribute is nonzero, {\cf ImageBufAlgo} functions
1017+
are instrumented to record the number of times they were called and the
1018+
total amount of time spent executing them. When enabled, there is a slight
1019+
runtime performance cost due to checking the time at the start and end of
1020+
each of those function calls, and the locking and recording of the data
1021+
structure that holds the log information. When the {\cf log_times} attribute
1022+
is disabled, there is no additional performance cost.
1023+
1024+
The {\cf log_times} attribute is initialized to 0, but may be overridden by
1025+
environment variable {\cf OPENIMAGEIO_LOG_TIMES}.
1026+
\index{OPENIMAGEIO_LOG_TIMES}
1027+
1028+
The report of totals can be retrieved as the value of the
1029+
\qkw{timing_report} attribute, which is a string and is read-only. The
1030+
report is sorted alphabetically and for each named instrumentation region,
1031+
prints the number of times it executed, the total runtime, and the average
1032+
per call, like this:
1033+
\begin{code}
1034+
IBA::computePixelStats 2 2.69ms (avg 1.34ms)
1035+
IBA::make_texture 1 74.05ms (avg 74.05ms)
1036+
IBA::mul 8 2.42ms (avg 0.30ms)
1037+
IBA::over 10 23.82ms (avg 2.38ms)
1038+
IBA::resize 20 0.24s (avg 12.18ms)
1039+
IBA::zero 8 0.66ms (avg 0.08ms)
1040+
\end{code}
1041+
1042+
If the value of {\cf log_times} is 2 or more when the application terminates,
1043+
the timing report will be printed to {\cf stdout} upon exit.
10021044
\apiend
10031045

1046+
\apiend
1047+
1048+
10041049
\apiitem{bool {\ce attribute} (string_view name, int val) \\
10051050
bool {\ce attribute} (string_view name, float val) \\
10061051
bool {\ce attribute} (string_view name, const char *val) \\

src/include/OpenImageIO/imagebufalgo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1979,7 +1979,7 @@ bool OIIO_API render_text (ImageBuf &dst, int x, int y, string_view text,
19791979
int shadow = 0,
19801980
ROI roi = ROI::All(), int nthreads = 0);
19811981

1982-
// Old style (pre-1.8) -- will eventually be deprecated.
1982+
// DEPRECATED: Old style (pre-1.8) -- will eventually be deprecated.
19831983
bool OIIO_API render_text (ImageBuf &dst, int x, int y, string_view text,
19841984
int fontsize, string_view fontname,
19851985
const float *textcolor);

src/include/OpenImageIO/imageio.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,12 @@ OIIO_API std::string geterror ();
12781278
/// The default is 0 for release builds, 1 for DEBUG builds,
12791279
/// but also may be overridden by the OPENIMAGEIO_DEBUG env
12801280
/// variable.
1281+
/// int log_times
1282+
/// When nonzero, various internals will record how much total
1283+
/// time they spend in execution. If the value is >= 2, these
1284+
/// times will be printed upon exit. Thd default is 0, but will
1285+
/// be initialized to the value of the OPENIMAGEIO_LOG_TIMES
1286+
/// environment variable, if it exists.
12811287
/// int tiff:half
12821288
/// When nonzero, allows TIFF to write 'half' pixel data.
12831289
/// N.B. Most apps may not read these correctly, but OIIO will.
@@ -1326,6 +1332,8 @@ inline bool attribute (string_view name, string_view val) {
13261332
/// the library. Semicolons separate the lists for formats. For
13271333
/// example,
13281334
/// "jpeg:jpeg-turbo 1.5.1;png:libpng 1.6.29;gif:gif_lib 5.1.4"
1335+
/// string "timing_report"
1336+
/// A string containing the report of all the log_times.
13291337
/// string "oiio:simd"
13301338
/// Comma-separated list of the SIMD-related capabilities
13311339
/// enabled when the OIIO library was built. For example,

src/libOpenImageIO/color_ocio.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <OpenImageIO/color.h>
4343
#include <OpenImageIO/imagebufalgo.h>
4444
#include <OpenImageIO/imagebufalgo_util.h>
45+
#include "imageio_pvt.h"
4546

4647
#ifdef USE_OCIO
4748
#include <OpenColorIO/OpenColorIO.h>
@@ -1104,6 +1105,7 @@ ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src,
11041105
ColorConfig *colorconfig,
11051106
ROI roi, int nthreads)
11061107
{
1108+
pvt::LoggedTimer logtime("IBA::colorconvert");
11071109
if (from.empty() || from == "current") {
11081110
from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear");
11091111
}
@@ -1129,6 +1131,8 @@ ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src,
11291131
return false;
11301132
}
11311133
}
1134+
1135+
logtime.stop(); // transition to other colorconvert
11321136
bool ok = colorconvert (dst, src, processor.get(), unpremult, roi, nthreads);
11331137
if (ok)
11341138
dst.specmod().attribute ("oiio:ColorSpace", to);
@@ -1266,6 +1270,7 @@ ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src,
12661270
const ColorProcessor* processor, bool unpremult,
12671271
ROI roi, int nthreads)
12681272
{
1273+
pvt::LoggedTimer logtime("IBA::colorconvert");
12691274
// If the processor is NULL, return false (error)
12701275
if (!processor) {
12711276
dst.error ("Passed NULL ColorProcessor to colorconvert() [probable application bug]");
@@ -1281,11 +1286,11 @@ ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src,
12811286
return false;
12821287

12831288
// If the processor is a no-op (and it's not an in-place conversion),
1284-
// use paste() to simplify the operation.
1289+
// use copy() to simplify the operation.
12851290
if (processor->isNoOp()) {
12861291
roi.chend = std::max (roi.chbegin+4, roi.chend);
1287-
return ImageBufAlgo::paste (dst, roi.xbegin, roi.ybegin, roi.zbegin,
1288-
roi.chbegin, src, roi, nthreads);
1292+
logtime.stop(); // transition to copy
1293+
return ImageBufAlgo::copy (dst, src, TypeUnknown, roi, nthreads);
12891294
}
12901295

12911296
if (unpremult && src.spec().alpha_channel >= 0 &&
@@ -1319,6 +1324,7 @@ ImageBufAlgo::ociolook (ImageBuf &dst, const ImageBuf &src,
13191324
ColorConfig *colorconfig,
13201325
ROI roi, int nthreads)
13211326
{
1327+
pvt::LoggedTimer logtime("IBA::ociolook");
13221328
if (from.empty() || from == "current") {
13231329
from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear");
13241330
}
@@ -1346,6 +1352,8 @@ ImageBufAlgo::ociolook (ImageBuf &dst, const ImageBuf &src,
13461352
return false;
13471353
}
13481354
}
1355+
1356+
logtime.stop(); // transition to colorconvert
13491357
bool ok = colorconvert (dst, src, processor.get(), unpremult, roi, nthreads);
13501358
if (ok)
13511359
dst.specmod().attribute ("oiio:ColorSpace", to);
@@ -1363,6 +1371,7 @@ ImageBufAlgo::ociodisplay (ImageBuf &dst, const ImageBuf &src,
13631371
ColorConfig *colorconfig,
13641372
ROI roi, int nthreads)
13651373
{
1374+
pvt::LoggedTimer logtime("IBA::ociodisplay");
13661375
if (from.empty() || from == "current") {
13671376
from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear");
13681377
}
@@ -1386,6 +1395,8 @@ ImageBufAlgo::ociodisplay (ImageBuf &dst, const ImageBuf &src,
13861395
return false;
13871396
}
13881397
}
1398+
1399+
logtime.stop(); // transition to colorconvert
13891400
bool ok = colorconvert (dst, src, processor.get(), unpremult, roi, nthreads);
13901401
return ok;
13911402
}
@@ -1397,6 +1408,7 @@ ImageBufAlgo::ociofiletransform (ImageBuf &dst, const ImageBuf &src,
13971408
string_view name, bool inverse, bool unpremult,
13981409
ColorConfig *colorconfig, ROI roi, int nthreads)
13991410
{
1411+
pvt::LoggedTimer logtime("IBA::ociofiletransform");
14001412
if (name.empty()) {
14011413
dst.error ("Unknown filetransform name");
14021414
return false;
@@ -1417,6 +1429,8 @@ ImageBufAlgo::ociofiletransform (ImageBuf &dst, const ImageBuf &src,
14171429
return false;
14181430
}
14191431
}
1432+
1433+
logtime.stop(); // transition to colorconvert
14201434
bool ok = colorconvert (dst, src, processor.get(), unpremult, roi, nthreads);
14211435
if (ok)
14221436
dst.specmod().attribute ("oiio:ColorSpace", name);

src/libOpenImageIO/imagebufalgo_addsub.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <OpenImageIO/imagebufalgo_util.h>
4444
#include <OpenImageIO/deepdata.h>
4545
#include <OpenImageIO/dassert.h>
46+
#include "imageio_pvt.h"
4647

4748

4849

@@ -113,6 +114,7 @@ bool
113114
ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B,
114115
ROI roi, int nthreads)
115116
{
117+
pvt::LoggedTimer logtime("IBA::add");
116118
if (! IBAprep (roi, &dst, &A, &B))
117119
return false;
118120
ROI origroi = roi;
@@ -145,6 +147,7 @@ bool
145147
ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, const float *b,
146148
ROI roi, int nthreads)
147149
{
150+
pvt::LoggedTimer logtime("IBA::add");
148151
if (! IBAprep (roi, &dst, &A,
149152
IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
150153
return false;
@@ -167,6 +170,7 @@ bool
167170
ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, float b,
168171
ROI roi, int nthreads)
169172
{
173+
pvt::LoggedTimer logtime("IBA::add");
170174
if (! IBAprep (roi, &dst, &A,
171175
IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
172176
return false;
@@ -202,6 +206,7 @@ bool
202206
ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B,
203207
ROI roi, int nthreads)
204208
{
209+
pvt::LoggedTimer logtime("IBA::sub");
205210
if (! IBAprep (roi, &dst, &A, &B))
206211
return false;
207212
ROI origroi = roi;
@@ -234,6 +239,7 @@ bool
234239
ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, const float *b,
235240
ROI roi, int nthreads)
236241
{
242+
pvt::LoggedTimer logtime("IBA::sub");
237243
if (! IBAprep (roi, &dst, &A,
238244
IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
239245
return false;
@@ -261,6 +267,7 @@ bool
261267
ImageBufAlgo::sub (ImageBuf &dst, const ImageBuf &A, float b,
262268
ROI roi, int nthreads)
263269
{
270+
pvt::LoggedTimer logtime("IBA::sub");
264271
if (! IBAprep (roi, &dst, &A,
265272
IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
266273
return false;

src/libOpenImageIO/imagebufalgo_channels.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <OpenImageIO/imagebufalgo_util.h>
4444
#include <OpenImageIO/deepdata.h>
4545
#include <OpenImageIO/thread.h>
46+
#include "imageio_pvt.h"
4647

4748

4849

@@ -81,6 +82,7 @@ ImageBufAlgo::channels (ImageBuf &dst, const ImageBuf &src,
8182
const std::string *newchannelnames,
8283
bool shuffle_channel_names, int nthreads)
8384
{
85+
pvt::LoggedTimer logtime("IBA::channels");
8486
// Not intended to create 0-channel images.
8587
if (nchannels <= 0) {
8688
dst.error ("%d-channel images not supported", nchannels);
@@ -229,6 +231,7 @@ ImageBufAlgo::channel_append (ImageBuf &dst, const ImageBuf &A,
229231
const ImageBuf &B, ROI roi,
230232
int nthreads)
231233
{
234+
pvt::LoggedTimer logtime("IBA::channel_append");
232235
// If the region is not defined, set it to the union of the valid
233236
// regions of the two source images.
234237
if (! roi.defined())

src/libOpenImageIO/imagebufalgo_compare.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
#include <OpenImageIO/dassert.h>
4545
#include <OpenImageIO/thread.h>
4646
#include <OpenImageIO/SHA1.h>
47-
47+
#include "imageio_pvt.h"
4848

4949

5050
OIIO_NAMESPACE_BEGIN
@@ -187,6 +187,7 @@ bool
187187
ImageBufAlgo::computePixelStats (PixelStats &stats, const ImageBuf &src,
188188
ROI roi, int nthreads)
189189
{
190+
pvt::LoggedTimer logtimer("IBA::computePixelStats");
190191
if (! roi.defined())
191192
roi = get_roi (src.spec());
192193
else
@@ -318,6 +319,7 @@ ImageBufAlgo::compare (const ImageBuf &A, const ImageBuf &B,
318319
ImageBufAlgo::CompareResults &result,
319320
ROI roi, int nthreads)
320321
{
322+
pvt::LoggedTimer logtimer("IBA::compare");
321323
// If no ROI is defined, use the union of the data windows of the two
322324
// images.
323325
if (! roi.defined())
@@ -378,6 +380,7 @@ bool
378380
ImageBufAlgo::isConstantColor (const ImageBuf &src, float *color,
379381
ROI roi, int nthreads)
380382
{
383+
pvt::LoggedTimer logtimer("IBA::isConstantColor");
381384
// If no ROI is defined, use the data window of src.
382385
if (! roi.defined())
383386
roi = get_roi(src.spec());
@@ -415,6 +418,7 @@ bool
415418
ImageBufAlgo::isConstantChannel (const ImageBuf &src, int channel, float val,
416419
ROI roi, int nthreads)
417420
{
421+
pvt::LoggedTimer logtimer("IBA::isConstantChannel");
418422
// If no ROI is defined, use the data window of src.
419423
if (! roi.defined())
420424
roi = get_roi(src.spec());
@@ -455,6 +459,7 @@ isMonochrome_ (const ImageBuf &src, ROI roi, int nthreads)
455459
bool
456460
ImageBufAlgo::isMonochrome (const ImageBuf &src, ROI roi, int nthreads)
457461
{
462+
pvt::LoggedTimer logtimer("IBA::isMonochrome");
458463
// If no ROI is defined, use the data window of src.
459464
if (! roi.defined())
460465
roi = get_roi(src.spec());
@@ -511,6 +516,7 @@ ImageBufAlgo::color_count (const ImageBuf &src, imagesize_t *count,
511516
const float *eps,
512517
ROI roi, int nthreads)
513518
{
519+
pvt::LoggedTimer logtimer("IBA::color_count");
514520
// If no ROI is defined, use the data window of src.
515521
if (! roi.defined())
516522
roi = get_roi(src.spec());
@@ -577,6 +583,7 @@ ImageBufAlgo::color_range_check (const ImageBuf &src, imagesize_t *lowcount,
577583
const float *low, const float *high,
578584
ROI roi, int nthreads)
579585
{
586+
pvt::LoggedTimer logtimer("IBA::color_range_check");
580587
// If no ROI is defined, use the data window of src.
581588
if (! roi.defined())
582589
roi = get_roi(src.spec());
@@ -628,6 +635,7 @@ deep_nonempty_region (const ImageBuf &src, ROI roi)
628635
ROI
629636
ImageBufAlgo::nonzero_region (const ImageBuf &src, ROI roi, int nthreads)
630637
{
638+
pvt::LoggedTimer logtimer("IBA::nonzero_region");
631639
roi = roi_intersection (roi, src.roi());
632640

633641
if (src.deep()) {
@@ -737,6 +745,7 @@ ImageBufAlgo::computePixelHashSHA1 (const ImageBuf &src,
737745
string_view extrainfo,
738746
ROI roi, int blocksize, int nthreads)
739747
{
748+
pvt::LoggedTimer logtimer("IBA::computePixelHashSHA1");
740749
if (! roi.defined())
741750
roi = get_roi (src.spec());
742751

@@ -834,6 +843,7 @@ ImageBufAlgo::histogram (const ImageBuf &A, int channel,
834843
float min, float max, imagesize_t *submin,
835844
imagesize_t *supermax, ROI roi)
836845
{
846+
pvt::LoggedTimer logtimer("IBA::histogram");
837847
if (A.spec().format != TypeFloat) {
838848
A.error ("Unsupported pixel data format '%s'", A.spec().format);
839849
return false;
@@ -876,6 +886,7 @@ bool
876886
ImageBufAlgo::histogram_draw (ImageBuf &R,
877887
const std::vector<imagesize_t> &histogram)
878888
{
889+
pvt::LoggedTimer logtimer("IBA::histogram_draw");
879890
// Fail if there are no bins to draw.
880891
int bins = histogram.size();
881892
if (bins == 0) {

0 commit comments

Comments
 (0)