做输入&输出张量到TensorFlow的OpKernel :: Compute()函数在多个函数调用中改变它们的地址?

时间:2016-12-22 18:50:01

标签: tensorflow

我正致力于在新架构上支持TensorFlow。

考虑以下TensorFlow代码:

import tensorflow as tf
import random as r

def random_10x10():
  return [[r.normalvariate(1.0,1.0) for i in range(10)] for j in range(10)]

a = tf.placeholder(tf.float32, shape=[10, 10])
b = tf.placeholder(tf.float32, shape=[10, 10]) 
c = tf.placeholder(tf.float32, shape=[10, 10]) 
d = tf.placeholder(tf.float32, shape=[10, 10]) 

with tf.device('/device:CPU:0'):
  mm1 = tf.matmul(a,b)
  mm2 = tf.matmul(c,d)
  output = tf.add(mm1,mm2)

sess = tf.Session() 
for i in xrange(10):
  print sess.run(output, 
      feed_dict={ a:random_10x10(), b:random_10x10(),
                  c:random_10x10(), d:random_10x10()} )

TensorFlow程序的执行图中存在两个matmul块,在执行期间,它们使用OpKernel的MatMulOp子类的两个实例表示,在tensorflow / core / kernels / matmul_op.cc中找到。在MatMulOp :: Compute()中完成的第一件事是获取输入张量的地址:

  void Compute(OpKernelContext* ctx) override {
    const Tensor& a = ctx->input(0);
    const Tensor& b = ctx->input(1);
...

我对TensorFlow的理解是,在上面的sess.run()的每次迭代中,MatMulOp的两个实例都不会改变。对于每个MatMul块,我可以期望输入的地址在迭代中保持不变,或者可能是在sess.run()调用的第七次迭代中,ctx-> input(0)将具有不同的价值比第六次?

Compute()方法还调用ctx-> allocate_output(),它最终包装了我们自己的架构的分配器。是否可以分配输出块一次,然后在同一会话中的未来运行中继续使用相同的块?

1 个答案:

答案 0 :(得分:2)

感谢您的提问!

TL; DR:是的,输入和输出张量将在Run()调用的不同调用中更改其地址。

详细说明:

如果图中有两个不同的MatMul操作,则会有两个MatMulOp OpKernel实例(保存在OpSegment中,在图中为Op查找/创建,并在以后的每个调用中进行后续缓存)会话::运行())。

不保证输入的地址在迭代期间保持不变。实际上,几乎可以肯定的是,情况并非如此,部分原因是数据流图的执行是动态的,而不是确定性的。

ctx-> input(0)是可能来自输入op" allocate_output"的另一个调用分配的内存。函数,所以输入和输出内存位置的答案是相同的。 allocate_output函数最终委托给设备"分配器"实现分配适当大小(和对齐)的内存。在CPU上,当前实现委托给malloc(),所以就像malloc()一样,每次调用Run()时都可能获得不同的内存。在GPU上,我们使用自定义GPU分配器(BFCAllocator类)来动态分配内存,其属性类似于malloc(),因为可以通过分配器根据内存的分配/释放顺序返回不同的内存。 p>

因此,通常,调用allocate_output()返回的内存由设备的Allocator实现处理。 CPU和GPU实现不提供跨同一图形的运行的稳定指针保证。

但是,如果您要实现自定义设备,则可能必须为您的设备实现自定义分配器,并且可能可以以这样的方式编写分配器图表中相同输出的相同内存。但这需要弄清楚如何将op的标识符传递给Allocator,以便每次都可以返回相同的内存。

TensorFlow故意进行动态内存分配至少有几个原因:

1)数据流图的执行顺序可能取决于外部输入,因此定制严格的计划可能会导致不必要的停顿。动态执行顺序确保操作仅在所有输入都准备就绪时执行。

2)TensorFlow中的形状可以是动态的(例如,您的图形可以处理可变的批量大小),这意味着op可能需要为Run()的相同op输出分配不同的内存量跑()!这是我们不能也不提供这种保证的一个重要原因。

我们理解,有些设备希望针对图形的一个实例化(具有固定大小)进行优化,因此设备可以预先规划整个数据流图并在多次尝试中分摊其执行。这些情况通常更适合XLA编译器框架(https://www.tensorflow.org/versions/master/resources/xla_prerelease),但有可能在没有使用现有设备框架的XLA的情况下使其适用于有限情况/图形。