Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

[Clojure] Add resource scope to clojure package #13993

Merged
merged 12 commits into from
Feb 5, 2019

Conversation

gigasquid
Copy link
Member

Description

Ports ResourceScope from the Scala package and introduces using macro to automatically close any resource in the enclosing forms.

#13442

and see https://cwiki.apache.org/confluence/display/MXNET/JVM+Memory+Management for more context

Checklist

Essentials

Please feel free to remove inapplicable items for your PR.

  • Changes are complete (i.e. I finished coding on this PR)
  • All changes have test coverage:
  • Unit tests are added for small changes to verify correctness (e.g. adding a new operator)
  • Nightly tests are added for complicated/long-running ones (e.g. changing distributed kvstore)
  • Build tests will be added for build configuration changes (e.g. adding a new build option with NCCL)
  • Code is well-documented:
  • To the my best knowledge, examples are either not affected by this change, or have been fixed to be compatible with this change

Changes

Also added the resource scope to the imclassification example.

New runs to the example do not show any warnings of memory leaks of this nature:
WARN org.apache.mxnet.WarnIfNotDisposed: LEAK: [one-time warning] ...

Copy link

@benkamphaus benkamphaus left a comment

Choose a reason for hiding this comment

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

The implementation looks good to me. The tests are technically sound and illustrate the desired behavior. My only comment is just a nit and question: is there a use case (perhaps one someone encountered) that would make sense as a more natural example for the tests but could still be easily tested? (.e.g with the isDisposed method or otherwise).

(is (true? (.isDisposed (:temp-x @native-resources))))
(is (false? (.isDisposed x)))))

(deftest test-resource-scope-with-sym
Copy link
Contributor

Choose a reason for hiding this comment

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

can you also add a unit test for nested with sym?

Copy link
Contributor

@kedarbellare kedarbellare Jan 26, 2019

Choose a reason for hiding this comment

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

one more unit test idea (similar in spirit to scala api): many ndarray/ones within a vector and only the first one is returned after a transformation (e.g. (+ (first v) 1))

Copy link
Member Author

Choose a reason for hiding this comment

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

interesting the result here - I put test cases in, but if a whole vector has been assigned to a let binding and you return the first, the whole vector is not disposed. You have to call copy on the first or not assign the whole vector to a let to have it disposed

Copy link
Member Author

Choose a reason for hiding this comment

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

There are some edge cases still I think there - but on the whole, it's an improvement for mem management of native resources.


(defn -main [& args]
(let [[dev dev-num] args
devs (if (= dev ":gpu")
(mapv #(context/gpu %) (range (Integer/parseInt (or dev-num "1"))))
(mapv #(context/cpu %) (range (Integer/parseInt (or dev-num "1")))))]
(start devs)))
(resource-scope/using (start devs))))
Copy link
Contributor

@kedarbellare kedarbellare Jan 26, 2019

Choose a reason for hiding this comment

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

having the whole thing encapsulated in one resource-scope seems weird to me. is it possible to wrap using around the individual pieces (e.g. mnist-iter, fit, fit-params) so that most of the code remains the same?

Copy link
Member Author

Choose a reason for hiding this comment

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

that's a good idea - thanks

@gigasquid
Copy link
Member Author

Thanks for your review @kedarbellare - I'll work on implementing the feedback 😸

(defmacro using
"Uses a Resource Scope for all forms. This is a way to manage all Native Resources like NDArray and Symbol - it will deallocate all Native Resources by calling close on them automatically. It will not call close on Native Resources returned from the form"
[& forms]
`(ResourceScope/using (new ResourceScope) (util/forms->scala-fn ~@forms)))
Copy link
Contributor

Choose a reason for hiding this comment

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

this may be a stupid question but is there any value in reusing a previously defined ResourceScope object? why does the scala API allow a scope to be passed in?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good question. From looking at the Scala api I would say that it would enable finer grained control - you could manually add or remove native resources from a scope and then manually call close on it. But I don't think that we need to expose that on the Clojure side, unless you have a different view on it.

Copy link
Contributor

@kedarbellare kedarbellare Jan 26, 2019

Choose a reason for hiding this comment

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

i don't see a big need for it -- at least for now -- but i was wondering whether the motivation was something like tf.variable_scope (e.g. reusing symbols).

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know - maybe @nswamy knows more about the context behind the design? ^

@gigasquid
Copy link
Member Author

I had some stuff come up - but plan to finish this up sometime this week :)

@gigasquid
Copy link
Member Author

I think all the feedback is addressed. Feel free to take another look. If there are no more changes needed, I will merge 😸

(swap! native-resources conj x)
x)))))]
(is (false? (ndarray/is-disposed return-val)))
(is (every? true? (mapv ndarray/is-disposed (:temp-ndarrays @native-resources))))))
Copy link
Contributor

Choose a reason for hiding this comment

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

@native-resources is a vector. just check (mapv ndarray/is-disposed @native-resources)? (also i didn't know that (:keyword vector) didn't raise an error)

Copy link
Member Author

Choose a reason for hiding this comment

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

If fixed some other problems with laziness in the tests with repeatedly. Evidently the weird behavior in the dispose of the first of the collection was just my fault on the tests. Now everything works as expected 🎆

:eval-data test-data
(resource-scope/with-let [_mod (m/module (get-symbol) {:contexts devs})]
(-> _mod
(m/fit {:train-data (mx-io/mnist-iter {:image (str data-dir "train-images-idx3-ubyte")
Copy link
Contributor

Choose a reason for hiding this comment

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

just for my understanding, does resource-scope not work when the train-data and eval-data are defined outside the with-let block?

Copy link
Member Author

Choose a reason for hiding this comment

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

It will work if they are defs outside, but you will still get the undisposed ndarray warning. I refactored to move it out to functions which does work and looks better too :)

(is (false? (sym/is-disposed x)))))

;;; Note that if first is returned the rest of the collection ndarrays will
;;; NOT be disposed
Copy link
Contributor

Choose a reason for hiding this comment

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

update comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

oh yes - the curse of the leftover misleading comments :)

Copy link
Contributor

@kedarbellare kedarbellare left a comment

Choose a reason for hiding this comment

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

neat!! looks great! 🎉

@kedarbellare
Copy link
Contributor

can't wait to use this 😃

@gigasquid gigasquid merged commit fbc5723 into apache:master Feb 5, 2019
@gigasquid gigasquid deleted the clojure-resource-scope branch February 5, 2019 02:01
stephenrawls pushed a commit to stephenrawls/incubator-mxnet that referenced this pull request Feb 16, 2019
* Add resource scope to clojure package

* add rat

* fix integration test

* feedback from @benkamphaus
- move from defs to atoms to make the tests a bit better

* adding alias with-do and with-let 
more tests

* another test

* Add examples in docstring

* refactor example and test to use resource-scope/with-let

* fix tests and problem with laziness 
now they work as expected!

* refactor to be a bit more modular

* remove comments
vdantu pushed a commit to vdantu/incubator-mxnet that referenced this pull request Mar 31, 2019
* Add resource scope to clojure package

* add rat

* fix integration test

* feedback from @benkamphaus
- move from defs to atoms to make the tests a bit better

* adding alias with-do and with-let 
more tests

* another test

* Add examples in docstring

* refactor example and test to use resource-scope/with-let

* fix tests and problem with laziness 
now they work as expected!

* refactor to be a bit more modular

* remove comments
haohuanw pushed a commit to haohuanw/incubator-mxnet that referenced this pull request Jun 23, 2019
* Add resource scope to clojure package

* add rat

* fix integration test

* feedback from @benkamphaus
- move from defs to atoms to make the tests a bit better

* adding alias with-do and with-let 
more tests

* another test

* Add examples in docstring

* refactor example and test to use resource-scope/with-let

* fix tests and problem with laziness 
now they work as expected!

* refactor to be a bit more modular

* remove comments
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants