Skip to content

Track external memory usage for matrices #602

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

Merged
merged 7 commits into from
Apr 4, 2018

Conversation

dstarke
Copy link
Contributor

@dstarke dstarke commented Jan 2, 2018

Adds tracking of the memory used by matrices to Node's external memory count, allowing the Javascript garbage collector to make better decisions about when to run.

Without these changes, Node only knows about the memory usage of the Javascript objects, and not the associated C and C++ objects. In the case of Matrix, those associated objects are quite large. As a result, it was possible to quickly allocate a large amount of memory without triggering garbage collection, for example: by reading multiple frames from a camera. This is especially problematic on low-memory systems, such as a Raspberry Pi, where memory usage could quickly grow past the available memory, triggering out-of-memory crashes.

To resolve this, this PR adds external memory adjustment calls to the Matrix wrapper class, and every location where the size of the referenced OpenCV matrix is altered, so that Node has a reasonable estimate of the memory usage of the Matrix objects. This causes garbage collection to run more often when needed, and prevents memory growth far outside of the bounds that Node is configured to run in.

It is still possible to use Matrix.release() to manually free memory, as this call has also been instrumented to adjust the external memory usage correctly.

Note that it is now important that every place that can change the size of the OpenCV Mat object referenced from a Matrix correctly adjusts the external memory usage (using Nan::AdjustExternalMemory()). If this is not done correctly, Node's external memory count could get out of sync with the library's actual memory usage, which could cause the garbage collector to run too often or not often enough.

I have attempted to find all the locations where these memory sizes could change, and have updated them in the least invasive way that I could. There might be better ways to restructure the code (for example, by strengthening the encapsulation of the referenced Mat within the Matrix wrapper class) to make some of this cleaner and safer, but that could be significantly more work and would probably change some of the usage patterns for the Matrix class.

In most cases, I was able to replace the code that creates a Javascript wrapped Matrix with a call to a helper method that does the same thing and correctly tracks the external memory size of the new object. A new destructor was also added to correctly reduce the tracked external memory size when the Matrix object is released.

I believe that this PR fixes #601, #411, #209, #167, and #101.

@codecov-io
Copy link

codecov-io commented Jan 2, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@48a4fc3). Click here to learn what that means.
The diff coverage is 64.67%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #602   +/-   ##
=========================================
  Coverage          ?   57.23%           
=========================================
  Files             ?       32           
  Lines             ?     4043           
  Branches          ?       30           
=========================================
  Hits              ?     2314           
  Misses            ?     1729           
  Partials          ?        0
Impacted Files Coverage Δ
src/Stereo.cc 20.48% <0%> (ø)
src/Calib3D.cc 7.27% <0%> (ø)
src/OpenCV.cc 88.88% <100%> (ø)
src/Features2d.cc 100% <100%> (ø)
src/LDAWrap.cc 70.73% <100%> (ø)
src/VideoWriterWrap.cc 96% <100%> (ø)
src/CascadeClassifierWrap.cc 89.28% <100%> (ø)
src/ImgProc.cc 38.94% <33.33%> (ø)
src/VideoCaptureWrap.cc 45.85% <50%> (ø)
src/Matrix.cc 56.52% <66.18%> (ø)
... and 1 more

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 48a4fc3...e1c0d8e. Read the comment docs.

@dstarke
Copy link
Contributor Author

dstarke commented Jan 29, 2018

After some extensive discussion with @btsimonh (which you can view in #601), I've made some changes to how the memory tracking works. The original version would double count memory when it was referenced from more than one JS object. The latest version now correctly takes into account how many JS objects are referencing an OpenCV object, so that memory is tracked more accurately, particularly when matrices are cloned or cropped.

The latest changes also fix some tracking issues that were missed earlier.

Note that for the tracking to work correctly, internal code must not independently retain references to Mat memory also referenced by a JS object (our Matrix wrapper class) past the end of an operation. In order to comply with this requirement, I've slightly changed the pattern for implementing asynchronous functions so that they retain our Matrix wrapper classes instead of the raw OpenCV Mat objects.

At this point, we are only tracking memory for Matrix objects, so the tracking code has been consolidated into the Matrix implementation in Matrix.cc.

@peterbraden
Copy link
Owner

Amazing work, thanks!

@peterbraden peterbraden merged commit 41730f8 into peterbraden:master Apr 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Informing Node of our memory usage
3 participants