为什么我的卷积实现与 Tensorflow 的卷积实现相比如此慢?



我在C++中使用SIMD指令实现了VGG19网络,仅用于推理。我想优化一个推理请求的延迟。

由于VGG19主要由卷积层组成,我主要关注于实现高效的卷积层。我在做这篇论文的时候关注了这篇论文:SIMD体系结构上的高性能深度学习卷积剖析。

我的实施提供了正确的结果。我使用SIMD本质和本文中描述的算法。所有砝码都预先加载。在运行实际推理之前,分配每一层的输入和输出缓冲区。


作为一个例子,让我们看看VGG19网络的第二个卷积层:

  • 输入:(22422464((填充后的22622664(
  • 输出:(22422464(
  • 内核:(3,3,64,64((KH,KW,C_IN,C_OUT(

这是代码对应的代码:

void conv2d_block1_conv2(const float* in, const float* weights, float* out) {
constexpr int VLEN = 8; // to use _mm256_* intrisics
constexpr int C_OUT_B = VLEN;
constexpr int C_IN_B = VLEN;
constexpr int H = 226;           // Input Height
constexpr int W = 226;           // Input Width
constexpr int C_IN = 64;         // Input Channels
constexpr int KH = 3;            // Kernel Height
constexpr int KW = 3;            // Kernel Width
constexpr int H_OUT = 224;       // Output Height
constexpr int W_OUT = 224;       // Output Width
constexpr int C_OUT = 64;        // Output Channels
__m256 in_vec, weights_vec, out_vec;
for (int c_out = 0; c_out < C_OUT / C_OUT_B; c_out++)
for (int c_in_b = 0; c_in_b < C_IN / C_IN_B; c_in_b++)
for (int h_out = 0; h_out < H_OUT; h_out++)
for (int w_out = 0; w_out < W_OUT; w_out++){
const int outIdx = LINEAR_4(c_out, h_out, w_out, 0, H_OUT, W_OUT, C_OUT_B);
out_vec = _mm256_load_ps (&out[outIdx]);
for (int kh = 0; kh < KH; kh++)
for (int kw = 0; kw < KW; kw++)
for (int c_in = 0; c_in < C_IN_B; c_in++){
const int inIdx = LINEAR_4(c_in_b, h_out + kh, w_out + kw, c_in, H, W, C_IN_B);
const int weightsIdx = LINEAR_6(c_out, c_in_b, kh, kw, c_in, 0, C_IN / C_IN_B, KH, KW, C_IN_B, C_OUT_B);
in_vec = _mm256_set1_ps (in[inIdx]);
weights_vec = _mm256_load_ps(&weights[weightsIdx]); 
out_vec = _mm256_fmadd_ps (in_vec, weights_vec, out_vec);
_mm256_store_ps(&out[outIdx], out_vec);
}
}
}

注意:我正在处理一个线性地址空间。函数CCD_ 1和CCD_ 2将多维索引映射为一维索引。

array[c_out][h_out][w_out][0]         <-> LINEAR_4(c_out, h_out, w_out, 0, H_OUT, W_OUT, C_OUT_B); 
array[c_out][c_in_b][kh][kw][c_in][0] <-> LINEAR_6(c_out, c_in_b, kh, kw, c_in, 0, C_IN / C_IN_B, KH, KW, C_IN_B, C_OUT_B);

我为每个卷积层创建了一个如上所述的函数,为编译器提供了最佳的优化可能性。

然而,执行时间相当糟糕。对于整个VGG19网络(均为单线程执行(:

  • 我的实现:2400毫秒
  • 使用model.predict(image)的Tensorflow后端Keras:600ms

这种巨大的性能差距让我怀疑自己做错了什么。我使用带有-O3标志的clang。

所以我的问题是:

  1. 是否有我没有考虑的关键因素
  2. Keras/TensorFlow使用哪个实现。它们怎么这么快

我找到了性能不佳的原因。clang编译器只使用了2个SSE寄存器,而不是所有可用的寄存器。这导致对一级缓存进行不必要的写入和读取。

我手动展开了两个内部循环,编译器现在使用了所有可用的16个SSE寄存器。性能急剧提高。

如果使用SSE Intristics,请确保检查生成的程序集。

最新更新