2

考虑这个用于矢量化的函数:

void AddSqr(float* restrict dst, float* restrict src, int cnt)
{
    for (int i=0; i<cnt; i++) dst[i] = src[i] * src[i];
};

如果 src & dst 当然没有别名,这将起作用。但是如果 src == dst 呢?像 src == dst+1 这样的极端情况当然是不允许的。但是,如果指针相同,则应该没有问题,还是我遗漏了什么?

编辑:restrict 是 Intel C++ 编译器关键字,MSVC 有 __restrict。

我对这个问题的看法是,我看不出任何一种矢量化如何出错:由于每个 dst 值都依赖于完全不同(没有任何别名)或完全相同地址的单个 src 值,当dst 改变了,src 值就不再需要了,因为它已经被写入意味着输出已经被计算了。唯一的情况是编译器将 dst 本身用作临时缓冲区,我认为这甚至不正确。

4

3 回答 3

2

在 C 中,您的代码通过违反restrict定义导致未定义的行为,因为它dst通过src.

dst和之间是否有偏移无关紧要src;条件是存在一个float通过一个指针写入并通过另一个指针读取的对象。

于 2015-04-01T12:51:09.890 回答
0

Restrict 是一个关键字,允许某些优化仅在两个指针不相互干扰时才有效。

对于您非常简单的情况,当两个指针相同时,任何可用的优化都不太可能失败,因此当您测试时不会发生任何不良情况。

但在更一般的情况下,restrict关键字意味着您断言这两个指针是不同的,并且它们指向的数据结构是不同的。编译器可以自由地使用这个断言来允许它想要的任何优化,尤其是那些如果你的断言不正确会使你的程序灾难性地失败的优化。

这种失败被称为“未定义的行为”,因为 C 标准没有定义断言错误时会发生什么。由于这是一个优化断言,完全不可预测的行为,通常称为“鼻恶魔”,是 C 编译器定义的合理行为。

于 2015-04-01T13:40:49.810 回答
0

感谢所有回答的人。所以: - 根据标准 C++ 定义,这确实是不正确的。- 但是我直接从英特尔那里得到答复,这没关系。

我最初的问题确实不是关于它是否“遵守规则”,而是它是否有可能出错。src/dst 数组是 1:1 映射的,因此数组要么完全不同,要么完全相同,因此每个项目要么依赖于某个完全不相关的项目,要么依赖于自身。因此,如果该项目被重写,它的最终值已经被计算存储并且在循环期间将不再需要。

无论如何,我做了一些额外的处理:

void AddSqr(float* restrict dst, float* restrict src, int cnt)
{
    if (dst == src)
        for (int i=0; i<cnt; i++) dst[i] = dst[i] * dst[i];
    else
        for (int i=0; i<cnt; i++) dst[i] = src[i] * src[i];
};

这应该可以解决潜在问题,甚至提供一些额外的优化可能性,因为在指针相同的情况下,编译器可以只使用一个寄存器(或不使用偏移寄存器)来定位数组。

于 2015-04-02T21:34:53.203 回答