package org.nd4j.examples;
import org.nd4j.linalg.api.memory.MemoryWorkspace;
import org.nd4j.linalg.api.memory.conf.WorkspaceConfiguration;
import org.nd4j.linalg.api.memory.enums.AllocationPolicy;
import org.nd4j.linalg.api.memory.enums.LearningPolicy;
import org.nd4j.linalg.api.memory.enums.ResetPolicy;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.slf4j.Logger;
/**
*
* 这个例子展示了如何将内存工作空间与nd4j一起用于循环工作负载。
* 背景:
*
* ND4J工作间是一个内存块,分配一次,然后在上面重复使用。基本上,如果使用循环工作负载,它为避免堆外内存的垃圾收集提供了一种方法。
*请注意:工作间是可选的。如果你更喜欢使用原始的基于GC的内存管理器,那么你可以使用它而不会出现任何问题。
*请注意:使用工作间时,你负责跟踪作用域等。你不应该访问附加到某些工作间之外的任何INDArray。结果将是不可预测的,直到JVM崩溃。
*
* @author raver119@gmail.com
*/
public class Nd4jEx15_Workspaces {
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Nd4jEx15_Workspaces.class);
public static void main(String[] args) throws Exception {
/**
* 每个工作间都通过ID绑定到一个JVM线程。因此,不同线程中的相同ID将指向不同的实际工作区。
* 每个工作区都是使用某些配置创建的,不同的工作区既可以共享相同的配置,也可以拥有自己的配置。
*/
//我们创建了一个预先分配了10MB内存空间的配置
WorkspaceConfiguration initialConfig = WorkspaceConfiguration.builder()
.initialSize(10 * 1024L * 1024L)
.policyAllocation(AllocationPolicy.STRICT)
.policyLearning(LearningPolicy.NONE)
.build();
INDArray result = null;
// 我们使用
try(MemoryWorkspace ws = Nd4j.getWorkspaceManager().getAndActivateWorkspace(initialConfig, "SOME_ID")) {
//现在,在此try块中创建的每个INDArray都将从此工作区池中分配
INDArray array = Nd4j.rand(10, 10);
//查看此数组是否连接到某个工作间的最简单方法。我们希望这里打印出TRUE。
log.info("Array attached? {}", array.isAttached());
//请注意,新的数组平均值也将附加到此工作间
INDArray mean = array.mean(1);
/**
* 请注意:如果在工作间上执行了一些操作之后,你希望从中获得结果,那么应该利用它,或者分离
*/
result = mean.detach();
}
//因为我们分离了数组,所以我们希望在这里输出false。所以,结果数组现在由GC管理。
log.info("Array attached? {}", result.isAttached());
/**
*
* 工作间最初可以预先分配,如上图所示,或者可以随着时间的推移或在第一个循环之后学习其所需的大小。
*/
WorkspaceConfiguration learningConfig = WorkspaceConfiguration.builder()
//<--此选项禁用过度分配行为
.policyAllocation(AllocationPolicy.STRICT)
//<--此选项使工作间在第一个循环后学习
.policyLearning(LearningPolicy.FIRST_LOOP)
.build();
for (int x = 0; x < 10; x++) {
try (MemoryWorkspace ws = Nd4j.getWorkspaceManager().getAndActivateWorkspace(learningConfig, "OTHER_ID")) {
INDArray array = Nd4j.create(100);
/**
* 在第一次迭代时,工作间将所有配额作为独立的内存块,但在此次迭代完成后,工作间将被分配,以匹配所有
* 此次循环中要求的配额。所以,更多的迭代将一次次重复使用工作间内存。
*/
}
}
/**
*
* 工作间可以嵌套。如果需要,INDArrays可以在它们之间迁移
*/
try(MemoryWorkspace ws1 = Nd4j.getWorkspaceManager().getAndActivateWorkspace(initialConfig, "SOME_ID")) {
INDArray array = Nd4j.create(10, 10).assign(1.0f);
INDArray sumRes;
try(MemoryWorkspace ws2 = Nd4j.getWorkspaceManager().getAndActivateWorkspace(initialConfig, "THIRD_ID")) {
//请注意:只有当内存尚未关闭/重置时,我们才能从父工作间访问内存,而不会出现任何问题。
INDArray res = array.sum(1);
//数组在ws1处分配,而res在ws2中分配。但我们可以在需要时迁移它们。
sumRes = res.leverageTo("SOME_ID");
}
// at this point sumRes contains valid data, allocated in current workspace. We expect 100 printed here.
//此时,sumRes包含当前工作间中分配的有效数据。我们希望这里能打印100。
log.info("Sum: {}", sumRes.sumNumber().floatValue());
}
/**
*
* 如果出于某种原因需要用GC处理部分计算,则可以中断工作区流。
*/
try(MemoryWorkspace ws1 = Nd4j.getWorkspaceManager().getAndActivateWorkspace(initialConfig, "SOME_ID")) {
INDArray array1 = Nd4j.create(10, 10).assign(1.0f);
INDArray array2;
try(MemoryWorkspace ws = Nd4j.getWorkspaceManager().scopeOutOfWorkspaces()) {
//此try块中分配的任何内容都将由GC管理
array2 = Nd4j.create(10, 10).assign(2.0f);
}
//此时,sumRes包含当前工作间中分配的有效数据。我们希望这里能打印300。
log.info("Sum: {}", array1.addi(array2).sumNumber().floatValue());
}
/**
*
* 还可以构建充当循环缓冲区的工作间。
*/
WorkspaceConfiguration circularConfig = WorkspaceConfiguration.builder()
.initialSize(10 * 1024L * 1024L)
.policyAllocation(AllocationPolicy.STRICT)
// <--- 此选项将禁用工作间随时间的重新分配
.policyLearning(LearningPolicy.NONE)
//<---此选项使工作间充当循环缓冲区,请注意。
.policyReset(ResetPolicy.ENDOFBUFFER_REACHED)
.build();
for (int x = 0; x < 10; x++) {
//因为这个工作间是循环的,所以我们知道在缓冲区结束之前分配的所有指针都是可行的。
try (MemoryWorkspace ws1 = Nd4j.getWorkspaceManager().getAndActivateWorkspace(circularConfig, "CIRCULAR_ID")) {
INDArray array = Nd4j.create(100);
//所以,只要确定缓冲区没有重置,就可以在任何地方使用这个数组。
//换句话说:如果你负责流,它适合于生产者/消费者模式使用
}
}
}
}
翻译:风一样的男子
image
网友评论