2015-04-01 9 views
2

Рассмотрим эту функцию, предназначенную для векторизации:Является ли указатель на C++ наложением угрозы, если указатели точно такие же?

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 + 1, не допускаются, конечно. Но если указатели одинаковые, не должно быть проблем, или я что-то упускаю?

Редактировать: ограничить ключевое слово компилятора Intel C++, MSVC имеет __restrict.

Я считаю, что я не вижу способа, каким образом любая векционализация может пойти не так: поскольку каждое значение dst зависит от единственного значения src либо совершенно другого (без какого-либо наложения псевдонимов), либо ТОЧНО, тот же адрес, когда dst изменен, значение src больше никогда не понадобится, потому что тот факт, что он был записан, означает, что результат был рассчитан. Единственным случаем было бы, если бы компилятор использовал сам dst в качестве временного буфера, который, я думаю, не верен.

+3

Я думаю [Можете ли вы использовать ограниченные указатели для доступа к одному и тому же объекту в некоторых случаях?] (Http://stackoverflow.com/q/18059205/1708801) отвечает на ваш вопрос –

+8

Разве это не вопрос C? У C++ нет 'ограничения'. – Angew

+1

Я не знаю, C, но под моим чтением стандарта, выше UB. Компилятор может свободно испускать патологический код, предполагающий, что 'a [7]' никогда не изменяется, даже после того, как написано 'b [7]'. Есть ли причина испускать этот код? Нет, но все еще допустимый вывод. – Yakk

ответ

2

В C ваш код вызывает неопределенное поведение, нарушая определение restrict, поскольку он записывает один объект через dst, но читает тот же объект через src.

Не имеет значения, существует ли смещение между dst и src; условие состоит в том, что существует объект float, который записывается через один указатель и считывается через другой.

0

Ограничение - это ключевое слово, позволяющее некоторым оптимизациям, которые действительны только в том случае, если два указателя не мешают друг другу.

Для вашего очень простого случая маловероятно наличие каких-либо оптимизаций, которые потерпят неудачу, если два указателя идентичны, поэтому при проверке ничего плохого не произойдет.

Но в более общем случае ключевое слово restrict означает, что вы утверждаете, что два указателя различны, а структуры данных, на которые они указывают, различны. Компилятор может свободно использовать это утверждение, чтобы разрешить любую его оптимизацию, особенно те, которые сделают вашу программу катастрофически неудачной, если ваше утверждение неверно.

Этот отказ называется «неопределенным поведением», поскольку стандарт C не определяет, что происходит, когда утверждение неверно. Поскольку это утверждение оптимизации полностью непредсказуемое поведение, обычно называемое «носовые демоны», является разумным поведением для компилятора C для определения.

0

Спасибо за ответы всем людям. Итак: - По стандарту C++ это действительно неверно. - Однако я получил ответ от Intel, что все в порядке.

Мой первоначальный вопрос действительно был не о том, если он «следует правилам», но если есть вероятность, что это может пойти не так. Массивы 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]; 
}; 

Это должно решить потенциальную проблему и даже предоставляет некоторые дополнительные возможности оптимизации, как и в случае указатели являются одинаковыми, компилятор может использовать только один регистр (или не использовать регистр смещения) для таргетинга массивов.