编译时间数据特定的优化

时间:2015-11-01 10:52:14

标签: c optimization

在某些情况下,人们在编译时知道特定的算法数据是什么样的,因此可能希望将此信息传达给编译器。这个问题是关于如何最好地实现这一点。

举例来说,考虑以下稀疏矩阵乘法的例子,其中矩阵是常数并且在编译时是已知的:

matrix = [  0, 210,   0, 248, 137]
         [  0,   0,   0,   0, 239]
         [  0,   0,   0,   0,   0]
         [116, 112,   0,   0,   7]
         [  0,   0,   0,   0, 165]

在这种情况下,可以编写完全无分支的实现来实现任意输入向量的矩阵向量乘法:

#include <stdio.h>

#define ARRAY_SIZE 8
static const int matrix[ARRAY_SIZE] = {210, 248, 137, 239, 116, 112, 7, 165};
static const int input_indices[ARRAY_SIZE] = {1, 3, 4, 4, 0, 1, 4, 4};
static const int output_indices[ARRAY_SIZE] = {0, 0, 0, 1, 3, 3, 3, 4};

static void matrix_multiply(int *input_array, int *output_array)
{
    for (int i=0; i<ARRAY_SIZE; ++i){
        output_array[output_indices[i]] += (
                matrix[i] * input_array[input_indices[i]]);
    }
}

int main()
{
    int test_input[5] = {36, 220, 212, 122,  39};
    int output[5] = {0};

    matrix_multiply(test_input, output);

    for (int i=0; i<5; ++i){
        printf("%d\n", output[i]);
    }

}

打印矩阵向量乘法(81799, 9321, 0, 29089, 6435)的正确结果。

可以设想进一步优化,以建立关于参考记忆位置的数据特定知识。

现在,显然这是一种可以使用的方法,但是当数据大小变大时(在我的情况下大约为100MB),它开始变得难以处理,而且在任何现实世界的情况下都会依赖于元编程生成相关的数据相关知识。

在数据特定知识中烘焙的一般策略是否在优化方面具有里程数?如果是这样,最好的办法是什么?

在给出的示例中,在一个级别上,整个事情要归结为在运行时设置数组的ARRAY_SIZE知识。这让我觉得这种方法是有限的(实际上是一个数据结构问题),但我很想知道数据派生编译时优化的一般方法在任何情况下都是有用的。

1 个答案:

答案 0 :(得分:2)

我不认为这是对这个问题的一个很好的答案,但无论如何我都会尝试提供它。它也更像是寻找相同的基本答案。

我在3D VFX中工作,包括光线追踪,在一小时内构建的数据结构采用相当适度的输入并且随后在用户可能的点之后进行大量处理并不罕见等待数小时的高质量生产渲染在困难的光线条件下。

至少在理论上,如果我们能够进行这些特定于数据的优化,那么这可能会更快。#34;变量可以变成文字常量,可能需要明显减少分支,已知总是具有45个元素的上限的数据可以在堆栈上而不是堆上分配或使用预先预先分配的另一种形式的存储器,参考的位置可以比以往任何时候都更容易被利用,矢量化可以更容易应用,实现线程安全和效率可以更容易等等。

这对我来说很尴尬的是,这需要有关用户输入的信息,这些信息只能在&#34;编译时&#34;的通常概念之后提供。因此,我的很多兴趣都与应用程序运行时的代码生成技术有关。

  

现在,显然这是一种可以使用的方法,但它开始了   当数据大小变大时变得笨拙(比如我的数据大约100MB)   案例)以及任何现实世界的情况都将取决于   元编程,用于生成相关的数据依赖知识。

我认为,除此之外,如果数据量过大,那么我们通常需要分享和变量的良好份额,以避免生成如此多的代码,以至于我们开始因icache misses而陷入瓶颈。

然而,甚至能够将经常访问的十二个变量转换为编译时常量并允许少数数据结构利用对指定输入的更多知识(以及借助于积极的优化器)可能会产生很大的里程数,特别是考虑到优化者的表现如何,他们提前提供了必要的信息。

其中一些可以通过越来越精细和通用的代码,元编程技术等正常解决,但是我们可以在多大程度上达到峰值:优化器只能根据信息的优化进行优化提前。这里的困难是以实际的方式提供这些信息。而且,正如您已经猜到的那样,这可能很快变得难以操作,难以维护,并且生产力开始变得与效率一样大(如果不是更大)。

所以对我来说最有前途的技术围绕着针对特定问题域调整的代码生成技术,而不是针对特定输入(针对特定输入进行优化将更多地依赖于优化器,代码生成就在那里,以便我们可以更轻松/更恰当地提供优化器所需的更多信息。一个已经做过类似事情的适度例子是Open Shading Language,它使用JIT编译将这个想法发挥到适度水平:

  

OSL使用LLVM编译器框架将着色器网络转换为   机器代码即时(恰好及时,或#34; JIT&#34;),并在此过程中   充分了解着色器和网络,大大优化了着色器和网络   着色器参数和其他无法运行的运行时值   着色器是从源代码编译时知道的。结果,我们   我们看到我们的OSL着色网络的执行速度比   用C手工制作的等效着色器! (这就是我们的旧着色器   在我们的渲染器中工作。)

虽然与手写代码相比有25%的改进是适度的,但在制作渲染器中这仍然是一个大问题,看起来我们可以远远超出这个范围。

使用节点作为可视化编程语言还提供了一个更具限制性的环境,有助于减少人为错误,允许在更高级别表达解决方案,查看动态变化的结果(即时周转)等 - - 因此它不仅增加了效率,还增加了我们需要避免在这种优化中迷失的生产力。维护和构建代码生成器可能有点复杂,但它只需要最少量的代码,并且不会随着使用它生成的代码量而在复杂性上进行扩展。

所以道歉 - 这不是一个完整的答案,你的问题作为评论,但我认为我们正在寻找类似的东西。

相关问题