DJL is an open source framework to support distributed inference in Java for deep learning models such as MXNet, Tensor flow or PyTorch.
The training of deep learning models may require a very significant amount of floating computations which are best supported by GPUs. However, the memory model in JVM is incompatible with column-based resident memory requires by the GPU.
Vectorization libraries such as Blast are implemented in C/C++ and support fast execution of linear algebra operations. The ubiquitous Python numerical library, numpy [ref 2] commonly used in data science is a wrapper around these low level math functions. The ND interface, used in DJL, provide Java developers with similar functionality.
Notes: The code snippets in this post are written in Scala but can be easily reworked in Java
The basics
- Resident Set Size (RSS) is the portion of the memory used by a process that is held in RAM memory and cannot be swapped.
- Heap is the section of memory used by object dynamically allocated
- Non-heap is the section encompassing static memory and stack allocation
Tensor representation
import ai.djl.ndarray.NDManager
// Set up the memory manager
val ndManager = ndManager.newBaseManager()
val input = Array.fill(1000)(Random.nexFloat())
// Allocate resources outside JVM
val ndInput = ndManager.create(input)
val ndMean = ndInput.means()
val mean = ndMean.toFloatArray.head
// Release ND resources
ndManager.close()
- instantiates the root resource manager, ndManager
- creates an array of 1000 random floating point values
- convert into a ND array, ndInput
- computes the mean, ndMean
- convert back to Java data types
- and finally close the root manager.
import ai.djl.ndarray.NDManager
def computeMean(input: Array[Float], ndManager: NDManager): Float =
if(input.nonEmpty) {
val subNDManager = ndManager.newSubManager()
val ndInput = ndManager.create(input)
val ndMean = ndInput.means()
val mean = ndMean.toFloatArray.head
subNDManager.close()
mean
////f// Local resources, ndInput and ndMean are released
// when going out of scope
}
else
0.0F
JMX to the rescue
The JVM provides developers with the ability to access operating system metrics such as CPU, or heap consumption through the Java Management Extension (JMX) interface [ref 4]
System.setProperty("collect-memory", "true")
def computeMean(
input: Array[Float],
ndManager: NDManager,
metricName: String): Float = {
val manager = ndManager.newSubManager()
// Initialize a new metrics
val metrics = new Metrics()
// Initialize the collection of memory related metrics
MemoryTrainingListener.collectMemoryInfo(metrics)
val initVal = metrics.latestMetric(metricName).getValue.longValue
val ndInput = ndManager.create(input)
val ndMean = ndInput.mean()
collectMetric(metrics, initVal, metricName)
val mean = ndMean.toFloatArray.head
// Close the output array and collect metrics
ndMean.close()
collectMetric(metrics, initVal, metricName)
// Close the input array and collect metrics
ndInput.close()
collectMetric(metrics, originalValue, metricName)
// Close the sub manager and collect metrics
ndManager.close()
collectMetric(metrics, initVal, metricName)
mean
}
Here is a simple snapshot method which compute the increase/decrease in heap memory from the base line.
def collectMetric(
metrics: Metrics,
initVal: Long,
metricName: String): Unit = {
MemoryTrainingListener.collectMemoryInfo(metrics)
val newVal = metrics.latestMetric(metricName).getValue.longValue
println(s"$metricName: ${(newVal - initVal)/1024.0} KB")
}
Memory leaks detection
MemoryTrainingListener.debugDump(metrics, outputFile)
NDManager.cap
// Protect the parent/root manager from
// accidental allocation of NDArray objects
ndManager.cap()
// Set up the memory manager
val ndManager = ndManager.newBaseManager()
val ndInput = ndManager.create(input)
VisualVM
We select VisualVM [ref 6] among the various JVM profiling solutions to highlight some key statistics in investigating a memory leak. VisualVM is a utility that is to be downloaded for Oracle site. It is not bundled with JDK.
He has been director of data engineering at Aideo Technologies since 2017 and he is the author of "Scala for Machine Learning" Packt Publishing ISBN 978-1-78712-238-3