Skip to content

Commit 72331d8

Browse files
authored
Merge pull request #2951 from PaulBoersma/master
guide for how to parallelize an existing function
2 parents ddc96f5 + 19a1cd3 commit 72331d8

2 files changed

Lines changed: 86 additions & 5 deletions

File tree

fon/Sound_to_Pitch.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ autoPitch Sound_to_Pitch_any (Sound me,
436436

437437
autoMelderProgress progress (U"Sound to Pitch...");
438438

439-
MelderThread_PARALLELIZE(numberOfFrames, 5, false, threadNumber)
439+
MelderThread_PARALLELIZE (numberOfFrames, 5, false, threadNumber)
440440

441441
autoMAT frame;
442442
autoNUMFourierTable fftTable;
@@ -453,7 +453,7 @@ autoPitch Sound_to_Pitch_any (Sound me,
453453
autoINTVEC imax = zero_INTVEC (maxnCandidates);
454454
autoVEC localMean = zero_VEC (my ny);
455455

456-
MelderThread_FOR (iframe)
456+
MelderThread_FOR (iframe) {
457457

458458
Pitch_Frame pitchFrame = & thy frames [iframe];
459459
const double time = Sampled_indexToX (thee.get(), iframe);
@@ -473,7 +473,7 @@ autoPitch Sound_to_Pitch_any (Sound me,
473473
r, imax.get(), localMean.get()
474474
);
475475

476-
MelderThread_ENDFOR
476+
} MelderThread_ENDFOR
477477

478478
Melder_progress (0.95, U"Sound to Pitch: path finder");
479479
Pitch_pathFinder (thee.get(), silenceThreshold, voicingThreshold,

melder/MelderThread.h

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ void MelderThread_run (
214214
autoINTVEC imax = zero_INTVEC (maxnCandidates);
215215
autoVEC localMean = zero_VEC (my ny);
216216
217-
MelderThread_FOR (iframe)
217+
MelderThread_FOR (iframe) {
218218
219219
Pitch_Frame pitchFrame = & thy frames [iframe];
220220
Sound_into_PitchFrame (me, pitchFrame, maxnCandidates,
@@ -231,7 +231,7 @@ void MelderThread_run (
231231
);
232232
}
233233
234-
MelderThread_ENDFOR
234+
} MelderThread_ENDFOR
235235
236236
Melder_progress (0.95, U"Sound to Pitch: path finder");
237237
return thee;
@@ -241,5 +241,86 @@ void MelderThread_run (
241241
}
242242
*/
243243

244+
/*
245+
How to parallelize an existing analysis function
246+
================================================
247+
248+
Suppose you have something like
249+
250+
autoPitch Sound_to_Pitch (Sound me) {
251+
try {
252+
autoPitch thee = ...;
253+
autoVEC window = ...; // the window shape
254+
autoMAT frame = zero_MAT (my ny, ...);
255+
autoNUMFourierTable fftTable = NUMFourierTable_create (...);
256+
autoVEC ac = zero_VEC (...);
257+
autoVEC rbuffer = zero_VEC (...);
258+
double *r = & rbuffer [...];
259+
autoVEC windowR = ...; // the autocorrelation of the window shape
260+
autoINTVEC imax = zero_INTVEC (maxnCandidates);
261+
autoVEC localMean = zero_VEC (my ny);
262+
autoMelderProgress progress (U"Sound to Pitch...");
263+
for (integer iframe = 1; iframe <= thy nx; iframe ++) {
264+
Pitch_Frame pitchFrame = & thy frames [iframe];
265+
const double fractionAnalysed = (double) iframe / thy nx;
266+
Melder_progress (0.1 + 0.8 * fractionAnalysed,
267+
U"Sound to Pitch: analysed ", iframe,
268+
U" out of ", thy nx, U" frames"
269+
);
270+
Sound_into_PitchFrame (me, pitchFrame, maxnCandidates,
271+
window.get(), windowR.get(),
272+
frame.get(), fftTable.get(), ac.get(),
273+
r, imax.get(), localMean.get()
274+
);
275+
}
276+
Melder_progress (0.95, U"Sound to Pitch: path finder");
277+
return thee;
278+
} catch (MelderError) {
279+
Melder_throw (me, U": pitch analysis not performed.");
280+
}
281+
}
282+
283+
The first step is to divide all those initializations into two sets:
284+
1. the ones that all threads can use at the same time;
285+
these are:
286+
1a. the target Pitch object, because each threads will write a separate part of it:
287+
autoPitch thee = ...;
288+
1b. the "constant" things that are computed only once, before the loop over the frames:
289+
autoVEC window = ...; // the window shape
290+
autoVEC windowR = ...; // the autocorrelation of the window shape
291+
1c. the initialization of the progress bar:
292+
autoMelderProgress progress (U"Sound to Pitch...");
293+
2. the ones of which each thread need its own copy, like buffers:
294+
autoMAT frame = zero_MAT (my ny, ...);
295+
autoNUMFourierTable fftTable = NUMFourierTable_create (...);
296+
autoVEC ac = zero_VEC (...);
297+
autoVEC rbuffer = zero_VEC (...);
298+
double *r = & rbuffer [...];
299+
autoINTVEC imax = zero_INTVEC (maxnCandidates);
300+
autoVEC localMean = zero_VEC (my ny);
301+
302+
This reordering is the preparation. You then make the following simple changes:
303+
3. between 1abc and 2 above you insert:
304+
MelderThread_PARALLELIZE (thy nx, 5, false, threadNumber)
305+
4. instead of
306+
for (integer iframe = 1; iframe <= thy nx; iframe ++) {
307+
you write
308+
MelderThread_FOR (iframe) {
309+
5. instead of the frame-loop-closing "}" you write
310+
} MelderThread_ENDFOR
311+
6. you update the progress bar only in the master thread, by inserting
312+
if (threadNumber == 0) {
313+
and
314+
}
315+
7. instead of computing the progress as
316+
const double fractionAnalysed = (double) iframe / thy nx;
317+
you write
318+
const double fractionAnalysed = MelderThread_ESTIMATE_PROGRESS (iframe);
319+
and you may reword the variable name and the text to acknowledge that this progress
320+
is just an estimate;
321+
8. you tune the "thresholdNumberOfElementsPerThread" to what turns out to be fastest
322+
for low numbers of threads; see `manually/Sound_to_Pitch.praat` for an example.
323+
*/
324+
244325
/* End of file MelderThread.h */
245326
#endif

0 commit comments

Comments
 (0)