Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move off deprecated OpenMP API, fix FFmpeg code paths #530

Merged
merged 4 commits into from Oct 16, 2020

Conversation

ferdnyc
Copy link
Contributor

@ferdnyc ferdnyc commented Jun 8, 2020

This was primarily inspired by Codecov flagging some code as unreachable, turned out it has a bit of history. But this corrects that, moves us off of a deprecated OpenMP API call, and makes some tweaks to FFmpegWriter/Reader along with Timeline, while I was touching them.

I'll explain some of the changes in inline comments.

- Replacement openmp_set_max_active_levels() takes an int argument
  - In OpenMP 5.0 it can be set to openmp_get_supported_active_levels()
  - Below 5.0, we just set it to our processor count
- Move configuration of OpenMP from ad-hoc locations to constructors
  for FFmpegWriter/Reader and Timeline
Comment on lines 698 to 702
// Write the frames once it reaches the correct cache size
if ((int)spooled_video_frames.size() == cache_size || (int)spooled_audio_frames.size() == cache_size) {
// Is writer currently writing?
if (!is_writing)
// Write frames to video file
write_queued_frames();

else {
// Write frames to video file
write_queued_frames();
}
// Write frames to video file
write_queued_frames();
}
Copy link
Contributor Author

@ferdnyc ferdnyc Jun 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the bit Codecov flagged as invalid branching. I should certainly say so! Turns out this block has a bit of history.

Once upon a time it looked basically exactly as it will after my change. Then, 8 years ago in 0f3758b, it became this:

	if (spooled_video_frames.size() == cache_size || spooled_audio_frames.size() == cache_size)
	{
		// Is writer currently writing?
		if (!is_writing)
			// Write frames to video file
			write_queued_frames();

		else
		{
			// YES, WRITING... so wait until it finishes, before writing again
			while (is_writing)
				usleep(250 * 1000); // sleep for 250 milliseconds

			// Write frames to video file
			write_queued_frames();
		}

	}

It trundled along like that with only minor changes until 851ad86, when (despite the commit message being "Changing some sleep() calls to usleep()", the sleep was removed entirely, making the two branches accidentally equivalent.

Maybe the right change is to put the sleep call back, I don't know. But in its current state the code is nonsensical, both branches are identical so there's no reason to check is_writing there.

Copy link
Contributor Author

@ferdnyc ferdnyc Jun 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I might also point out that even if we want to put the sleep-before-write back in, the cleanest form would be...)

if ((int)spooled_video_frames.size() == cache_size || (int)spooled_audio_frames.size() == cache_size) {
    // Wait until an in-progress write finishes
    while (is_writing) {
        std::this_thread::sleep_for(std::chrono::milliseconds(250));
    }
    write_queued_frames();
}

There's no reason for the preceding if(), when is_writing is false execution will fall straight through the while() without a single iteration.

@ferdnyc ferdnyc added the resources Issues involving consumption of memory, CPU, disk, etc. label Jun 8, 2020
@codecov
Copy link

codecov bot commented Jun 8, 2020

Codecov Report

Merging #530 into develop will increase coverage by 0.04%.
The diff coverage is 75.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #530      +/-   ##
===========================================
+ Coverage    48.81%   48.85%   +0.04%     
===========================================
  Files          129      129              
  Lines        10042    10033       -9     
===========================================
  Hits          4902     4902              
+ Misses        5140     5131       -9     
Impacted Files Coverage Δ
include/FFmpegReader.h 33.33% <ø> (ø)
include/FFmpegWriter.h 25.00% <ø> (ø)
include/Timeline.h 68.75% <ø> (ø)
src/Timeline.cpp 38.60% <40.00%> (-0.11%) ⬇️
src/FFmpegReader.cpp 69.46% <100.00%> (+0.67%) ⬆️
src/FFmpegWriter.cpp 62.78% <100.00%> (+0.02%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c29174f...3f13ed8. Read the comment docs.

/// or throws an exception.
/// @param path The filesystem location to load
/// @param inspect_reader if true (the default), automatically open the media file and loads frame 1.
FFmpegReader(const std::string& path, bool inspect_reader=true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously the two constructors for FFmpegReader differed only in that one required an inspect_reader argument, which turned off the Open(); Close(); cycle that was always performed by the one-arg version. (IOW, there was no reason to use the 2-arg version unless inspect_reader was false.) By making it one constructor with an optional inspect_reader argument that defaults to true, I was able to eliminate an entire constructor's worth of duplicate code.

While I was touching these constructors, I changed their string args to take a const reference, so that we can try it out as a change. Especially for arguments that are simply copied to member variables in the initializer list, there should be no down side, only a slightly more efficient method that avoids one unnecessary redundant copy of a class that's just going to be copied again by the initializer list. I anticipate submitting a much larger PR that updates a significant porition of our class methods in the same manner, so consider this a trial run.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for a bunch of these constructors also kept ending on, "...throws one of the following exceptions." with no list of exceptions after it. There's nothing worse than documentation that teases you and doesn't follow through, so I changed it to not over-promise.

Comment on lines -901 to -904
// Set the number of threads in OpenMP
omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
// Allow nested OpenMP sections
omp_set_nested(true);
Copy link
Contributor Author

@ferdnyc ferdnyc Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last but not least, I noticed there were a few places scattered around the code that kept making these calls. They were always in very ad-hoc locations, generally right before a nested #pragma section was actually used. But that meant (a) they weren't necessarily in effect before that point, and (b) they'd keep getting called every time that block of code was run.

I have no idea how expensive omp_set_num_threads() and omp_set_nested() are. I assume not very (probably not at all, really), but they can't be FREE and there's no reason to call them more than once. So, I moved this initialization into the class constructors where it can get done once and never needs to be fiddled wih again.

There's also the fact that omp_set_nested() was deprecated in OpenMP 4.5 due to its lack of granularity. OpenMP has always (since 1.0) supported omp_set_max_active_levels(), which allows control over the amount of nesting possible in the code, rather than just offering a boolean switch. So the code I moved to the constructors uses that, instead.

@jonoomph
Copy link
Member

Good catch! I think this implementation is fine for now. I'll do some local testing as well.

@jonoomph jonoomph merged commit ef80439 into OpenShot:develop Oct 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
resources Issues involving consumption of memory, CPU, disk, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants