Skip to content

Commit c610e48

Browse files
authored
fix(oiiotool): Allow thread control for --parallel-frames (#4818)
Fixes #4811 This patch makes oiiotool's initial cursory scan of the command line notice a `--threads` and carfully honor it when parallelizing the frame loop. Explanatory background info: The cited issue shows something subtle happening with oiiotool's `--parallel-frames` argument. The symptom was that it was using too many threads and running out of memory, and seemed impervious to the use of `--threads` to properly control it. The reason is that oiiotool first scans the command line args in a cursory way looking for signs of a "sequence", like a `--frames` or any filename containing frame wildcards (like `linear.%04d.exr`) and also noting if there is a `--parallel-frames`, but not actually taking any actions for any of the commands yet. Having seen that there are frame wildcards, what it then does is execute the whole command line, once for each frame number that matched the pattern. And since it noted that a `--parallel-frames` argument was present somewhere, it does it with a parallel for loop. But how many threads does it use? Well, enough for all the cores, since in some sense it has not yet noticed the `--threads` argument. Moving the `--threads` to be placed prior to the `--parallel-frames` doesn't help, either. The suggested workaround was to set the default number of threads with the environment variable `OPENIMAGEIO_THREADS` prior to launching oiiotool. But this patch is a more robust solution. Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 09250af commit c610e48

2 files changed

Lines changed: 47 additions & 10 deletions

File tree

src/oiiotool/oiiotool.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <OpenImageIO/deepdata.h>
2929
#include <OpenImageIO/filesystem.h>
3030
#include <OpenImageIO/filter.h>
31+
#include <OpenImageIO/fmath.h>
3132
#include <OpenImageIO/imagebuf.h>
3233
#include <OpenImageIO/imagebufalgo.h>
3334
#include <OpenImageIO/imagebufalgo_util.h>
@@ -616,9 +617,11 @@ Oiiotool::extract_options(string_view command)
616617

617618
// --threads
618619
static void
619-
set_threads(Oiiotool&, cspan<const char*> argv)
620+
set_threads(Oiiotool& ot, cspan<const char*> argv)
620621
{
621622
OIIO_DASSERT(argv.size() == 2);
623+
if (ot.in_parallel_frame_loop())
624+
return;
622625
int nthreads = Strutil::stoi(argv[1]);
623626
OIIO::attribute("threads", nthreads);
624627
OIIO::attribute("exr_threads", nthreads);
@@ -7228,9 +7231,10 @@ one_sequence_iteration(Oiiotool& otmain, size_t i, int frame_number,
72287231
}
72297232

72307233
Oiiotool otit; // Oiiotool for this iteration
7231-
otit.imagecache = otmain.imagecache;
7232-
otit.frame_number = frame_number;
7233-
otit.parent_oiiotool = &otmain;
7234+
otit.imagecache = otmain.imagecache;
7235+
otit.frame_number = frame_number;
7236+
otit.parent_oiiotool = &otmain;
7237+
otit.m_in_parallel_frame_loop = otmain.in_parallel_frame_loop();
72347238
otit.getargs((int)seq_argv.size(), (char**)&seq_argv[0]);
72357239

72367240
if (otit.ap.aborted()) {
@@ -7305,8 +7309,9 @@ handle_sequence(Oiiotool& ot, int argc, const char** argv)
73057309
int framepadding = 0;
73067310
std::vector<int> sequence_args; // Args with sequence numbers
73077311
std::vector<bool> sequence_is_output;
7308-
bool is_sequence = false;
7309-
bool wildcard_on = true;
7312+
int parallel_frame_threads = OIIO::get_int_attribute("threads");
7313+
bool is_sequence = false;
7314+
bool wildcard_on = true;
73107315
for (int a = 1; a < argc; ++a) {
73117316
bool is_output = false;
73127317
bool is_output_all = false;
@@ -7336,6 +7341,11 @@ handle_sequence(Oiiotool& ot, int argc, const char** argv)
73367341
} else if ((strarg == "--views" || strarg == "-views")
73377342
&& a < argc - 1) {
73387343
Strutil::split(argv[++a], views, ",");
7344+
} else if ((strarg == "--threads" || strarg == "-threads")
7345+
&& a < argc - 1) {
7346+
int t = Strutil::stoi(argv[++a]);
7347+
t = OIIO::clamp(t, 0, int(Sysutil::hardware_concurrency()));
7348+
parallel_frame_threads = t;
73397349
} else if (strarg == "--wildcardoff" || strarg == "-wildcardoff") {
73407350
wildcard_on = false;
73417351
} else if (strarg == "--parallel-frames"
@@ -7441,17 +7451,21 @@ handle_sequence(Oiiotool& ot, int argc, const char** argv)
74417451
// every time.
74427452
// Note: nfilenames really means, number of frame number iterations.
74437453
if (ot.parallel_frames) {
7444-
// If --parframes was used, run the iterations in parallel.
7454+
// If --parframes was used, run the iterations in parallel, but
7455+
// each iteration should itself not try to internally parallelize.
74457456
if (ot.debug)
7446-
print("Running {} frames in parallel\n", nfilenames);
7457+
print("Running {} frames in parallel with {} threads\n", nfilenames,
7458+
parallel_frame_threads);
7459+
ot.begin_parallel_frame_loop(parallel_frame_threads);
74477460
parallel_for(
74487461
uint64_t(0), uint64_t(nfilenames),
74497462
[&](uint64_t i) {
74507463
one_sequence_iteration(ot, i, frame_numbers[0][i],
74517464
sequence_args, filenames,
74527465
{ argv, argv + argc });
74537466
},
7454-
paropt().minitems(1));
7467+
paropt().minitems(1).maxthreads(parallel_frame_threads));
7468+
ot.end_parallel_frame_loop();
74557469
} else {
74567470
// Fully serialized over the frame range, multithreaded for each frame
74577471
// individually.
@@ -7465,6 +7479,24 @@ handle_sequence(Oiiotool& ot, int argc, const char** argv)
74657479

74667480

74677481

7482+
void
7483+
Oiiotool::begin_parallel_frame_loop(int nthreads)
7484+
{
7485+
OIIO::attribute("threads", nthreads);
7486+
OIIO::attribute("exr_threads", 1);
7487+
m_in_parallel_frame_loop = true;
7488+
}
7489+
7490+
7491+
7492+
void
7493+
Oiiotool::end_parallel_frame_loop()
7494+
{
7495+
m_in_parallel_frame_loop = false;
7496+
}
7497+
7498+
7499+
74687500
int
74697501
main(int argc, char* argv[])
74707502
{

src/oiiotool/oiiotool.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ class Oiiotool {
158158
int frame_number = 0;
159159
bool enable_function_timing = true;
160160
bool input_config_set = false;
161-
bool printed_info = false; // printed info at some point
161+
bool printed_info = false; // printed info at some point
162+
bool m_in_parallel_frame_loop = false; // True when in parallel frame loop
162163
// Remember the first input dataformats we encountered
163164
TypeDesc input_dataformat;
164165
int input_bitspersample = 0;
@@ -397,6 +398,10 @@ class Oiiotool {
397398
m_first_input_dimensions.copy_dimensions(dims);
398399
}
399400

401+
void begin_parallel_frame_loop(int nthreads);
402+
void end_parallel_frame_loop();
403+
bool in_parallel_frame_loop() const { return m_in_parallel_frame_loop; }
404+
400405
private:
401406
CallbackFunction m_pending_callback;
402407
std::vector<const char*> m_pending_argv;

0 commit comments

Comments
 (0)