-
Notifications
You must be signed in to change notification settings - Fork 6.8k
[Clojure] Add resource scope to clojure package #13993
Changes from 10 commits
444f6f4
a44e5a6
329611d
f4a38d9
32c9bfe
a5db1f2
5ae5085
e26836e
fdb1e73
d8ab1b7
7aa65f9
9e5cfc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
;; | ||
;; Licensed to the Apache Software Foundation (ASF) under one or more | ||
;; contributor license agreements. See the NOTICE file distributed with | ||
;; this work for additional information regarding copyright ownership. | ||
;; The ASF licenses this file to You under the Apache License, Version 2.0 | ||
;; (the "License"); you may not use this file except in compliance with | ||
;; the License. You may obtain a copy of the License at | ||
;; | ||
;; http://www.apache.org/licenses/LICENSE-2.0 | ||
;; | ||
;; Unless required by applicable law or agreed to in writing, software | ||
;; distributed under the License is distributed on an "AS IS" BASIS, | ||
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
;; See the License for the specific language governing permissions and | ||
;; limitations under the License. | ||
;; | ||
|
||
(ns org.apache.clojure-mxnet.resource-scope | ||
(:require [org.apache.clojure-mxnet.util :as util]) | ||
(:import (org.apache.mxnet ResourceScope))) | ||
|
||
(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. | ||
Example: | ||
(resource-scope/using | ||
(let [temp-x (ndarray/ones [3 1]) | ||
temp-y (ndarray/ones [3 1])] | ||
(ndarray/+ temp-x temp-y))) " | ||
[& forms] | ||
`(ResourceScope/using (new ResourceScope) (util/forms->scala-fn ~@forms))) | ||
|
||
|
||
(defmacro | ||
with-do | ||
"Alias for a do within a resource scope using. | ||
Example: | ||
(resource-scope/with-do | ||
(ndarray/ones [3 1]) | ||
:all-cleaned-up) | ||
" | ||
[& forms] | ||
`(using (do ~@forms))) | ||
|
||
(defmacro | ||
with-let | ||
"Alias for a let within a resource scope using. | ||
Example: | ||
(resource-scope/with-let [temp-x (ndarray/ones [3 1]) | ||
temp-y (ndarray/ones [3 1])] | ||
(ndarray/+ temp-x temp-y))" | ||
[& forms] | ||
`(using (let ~@forms))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
;; | ||
;; Licensed to the Apache Software Foundation (ASF) under one or more | ||
;; contributor license agreements. See the NOTICE file distributed with | ||
;; this work for additional information regarding copyright ownership. | ||
;; The ASF licenses this file to You under the Apache License, Version 2.0 | ||
;; (the "License"); you may not use this file except in compliance with | ||
;; the License. You may obtain a copy of the License at | ||
;; | ||
;; http://www.apache.org/licenses/LICENSE-2.0 | ||
;; | ||
;; Unless required by applicable law or agreed to in writing, software | ||
;; distributed under the License is distributed on an "AS IS" BASIS, | ||
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
;; See the License for the specific language governing permissions and | ||
;; limitations under the License. | ||
;; | ||
|
||
(ns org.apache.clojure-mxnet.resource-scope-test | ||
(:require [org.apache.clojure-mxnet.ndarray :as ndarray] | ||
[org.apache.clojure-mxnet.symbol :as sym] | ||
[org.apache.clojure-mxnet.resource-scope :as resource-scope] | ||
[clojure.test :refer :all])) | ||
|
||
|
||
(deftest test-resource-scope-with-ndarray | ||
(let [native-resources (atom {}) | ||
x (ndarray/ones [2 2]) | ||
return-val (resource-scope/using | ||
gigasquid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(let [temp-x (ndarray/ones [3 1]) | ||
temp-y (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(swap! native-resources assoc :temp-y temp-y) | ||
(ndarray/+ temp-x 1)))] | ||
(is (true? (ndarray/is-disposed (:temp-x @native-resources)))) | ||
(is (true? (ndarray/is-disposed (:temp-y @native-resources)))) | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (false? (ndarray/is-disposed x))) | ||
(is (= [2.0 2.0 2.0] (ndarray/->vec return-val))))) | ||
|
||
(deftest test-nested-resource-scope-with-ndarray | ||
(let [native-resources (atom {}) | ||
x (ndarray/ones [2 2]) | ||
return-val (resource-scope/using | ||
(let [temp-x (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(resource-scope/using | ||
(let [temp-y (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-y temp-y)))))] | ||
(is (true? (ndarray/is-disposed (:temp-y @native-resources)))) | ||
(is (true? (ndarray/is-disposed (:temp-x @native-resources)))) | ||
(is (false? (ndarray/is-disposed x))))) | ||
|
||
(deftest test-resource-scope-with-sym | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you also add a unit test for nested with sym? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one more unit test idea (similar in spirit to scala api): many There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
(let [native-resources (atom {}) | ||
x (sym/ones [2 2]) | ||
return-val (resource-scope/using | ||
(let [temp-x (sym/ones [3 1]) | ||
temp-y (sym/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(swap! native-resources assoc :temp-y temp-y) | ||
(sym/+ temp-x 1)))] | ||
(is (true? (sym/is-disposed (:temp-x @native-resources)))) | ||
(is (true? (sym/is-disposed (:temp-y @native-resources)))) | ||
(is (false? (sym/is-disposed return-val))) | ||
(is (false? (sym/is-disposed x))))) | ||
|
||
(deftest test-nested-resource-scope-with-ndarray | ||
(let [native-resources (atom {}) | ||
x (ndarray/ones [2 2]) | ||
return-val (resource-scope/using | ||
(let [temp-x (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(resource-scope/using | ||
(let [temp-y (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-y temp-y)))))] | ||
(is (true? (ndarray/is-disposed (:temp-y @native-resources)))) | ||
(is (true? (ndarray/is-disposed (:temp-x @native-resources)))) | ||
(is (false? (ndarray/is-disposed x))))) | ||
|
||
(deftest test-nested-resource-scope-with-sym | ||
(let [native-resources (atom {}) | ||
x (sym/ones [2 2]) | ||
return-val (resource-scope/using | ||
(let [temp-x (sym/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(resource-scope/using | ||
(let [temp-y (sym/ones [3 1])] | ||
(swap! native-resources assoc :temp-y temp-y)))))] | ||
(is (true? (sym/is-disposed (:temp-y @native-resources)))) | ||
(is (true? (sym/is-disposed (:temp-x @native-resources)))) | ||
(is (false? (sym/is-disposed x))))) | ||
|
||
;;; Note that if first is returned the rest of the collection ndarrays will | ||
;;; NOT be disposed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. update comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yes - the curse of the leftover misleading comments :) |
||
(deftest test-list-creation-with-returning-first | ||
(let [native-resources (atom []) | ||
return-val (resource-scope/using | ||
(let [temp-ndarrays (doall (repeatedly 3 #(ndarray/ones [3 1]))) | ||
_ (reset! native-resources temp-ndarrays)] | ||
(first temp-ndarrays)))] | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (= [false true true] (mapv ndarray/is-disposed @native-resources))))) | ||
|
||
;;; collections not referenced are disposed | ||
(deftest test-list-creation | ||
(let [native-resources (atom []) | ||
return-val (resource-scope/using | ||
(let [temp-ndarrays (doall (repeatedly 3 #(ndarray/ones [3 1]))) | ||
_ (reset! native-resources temp-ndarrays)] | ||
(ndarray/ones [3 1])))] | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (= [true true true] (mapv ndarray/is-disposed @native-resources))))) | ||
|
||
(deftest test-list-creation-without-let | ||
(let [native-resources (atom []) | ||
return-val (resource-scope/using | ||
(first (doall (repeatedly 3 #(do | ||
(let [x (ndarray/ones [3 1])] | ||
(swap! native-resources conj x) | ||
x))))))] | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (= [false true true] (mapv ndarray/is-disposed @native-resources))))) | ||
|
||
(deftest test-with-let | ||
(let [native-resources (atom {}) | ||
x (ndarray/ones [2 2]) | ||
return-val (resource-scope/with-let [temp-x (ndarray/ones [3 1]) | ||
temp-y (ndarray/ones [3 1])] | ||
(swap! native-resources assoc :temp-x temp-x) | ||
(swap! native-resources assoc :temp-y temp-y) | ||
(ndarray/+ temp-x 1))] | ||
(is (true? (ndarray/is-disposed (:temp-x @native-resources)))) | ||
(is (true? (ndarray/is-disposed (:temp-y @native-resources)))) | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (false? (ndarray/is-disposed x))) | ||
(is (= [2.0 2.0 2.0] (ndarray/->vec return-val))))) | ||
|
||
(deftest test-with-do | ||
(let [native-resources (atom {}) | ||
x (ndarray/ones [2 2]) | ||
return-val (resource-scope/with-do | ||
(swap! native-resources assoc :temp-x (ndarray/ones [3 1])) | ||
(swap! native-resources assoc :temp-y (ndarray/ones [3 1])) | ||
(ndarray/ones [3 1]))] | ||
(is (true? (ndarray/is-disposed (:temp-x @native-resources)))) | ||
(is (true? (ndarray/is-disposed (:temp-y @native-resources)))) | ||
(is (false? (ndarray/is-disposed return-val))) | ||
(is (false? (ndarray/is-disposed x))) | ||
(is (= [1.0 1.0 1.0] (ndarray/->vec return-val))))) |
There was a problem hiding this comment.
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 ascope
to be passed in?There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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? ^