Использование ограничения с массивами?

Есть ли способ сообщить компилятору C99, что единственный способ получить доступ к данному массиву - это использовать myarray[index] ? Скажите что-нибудь вроде этого:

int heavy_calcualtions(float* restrict range1, float* restrict range2)
{
    float __I promise I won't alias this__ tmpvalues[1000] = {0};

    ....
    heavy calculations using range1, range2 and tmpvalues;
    ....
}

Используя ограничение, я пообещал, что не буду использовать псевдонимы range1 и range2, но как мне сделать то же самое для массива, объявленного внутри моей функции?


person Piotr Lopusiewicz    schedule 26.09.2013    source источник
comment
Какое предупреждение выдает?   -  person dhein    schedule 28.09.2013
comment
недопустимое использование ограничения - его предполагается использовать с указателями, а не с массивами (насколько я понимаю). Я мог бы использовать float* limited tmpvalues ​​= malloc(sizeof(float)*1000), но тогда я не выделяю память в стеке, что также может повлиять на производительность. Помимо сообщения компилятору, что доступ к индексам массива безопасен (поэтому защитные чтения не требуются), кажется очень естественным расширением ограничения использования с помощью указателей, поэтому интуитивно должен быть способ сделать это.   -  person Piotr Lopusiewicz    schedule 28.09.2013
comment
Я сделал: float* limited tmpvalues ​​= alloca(sizeof(float)*1000); memset(tmpvalues, 0, sizeof(float)*1000); и это измеримое улучшение, но я бы предпочел сделать это стандартно (как в жалобах C99)   -  person Piotr Lopusiewicz    schedule 28.09.2013
comment
@PiotrLopusiewicz, можете ли вы опубликовать реальный код, в котором float *restrict tmpvalues превосходит VLA? (предпочтительно с уже установленным временным кодом)   -  person M.M    schedule 23.05.2014
comment
Доступ к массиву с помощью указателя ограничения.   -  person Jeff Hammond    schedule 26.04.2015


Ответы (2)


Хотя ответ Джеффа правильный, т. е. вы всегда можете сделать указатель на выделенный массив, факт в том, что компилятор знает во время компиляции, что значения tmp не будут иметь псевдоним, потому что переменная объявлена ​​как фактический массив, а не указатель. Единственная возможность создать псевдоним массива — это объявить указатель на него, поэтому, если вы этого не сделаете, нет необходимости объявлять его как restrict. Это более очевидно, если tmpvalues — единственная переменная, которую вы будете иметь в функции.

Проблема может возникнуть, если вы передаете указатель другой функции, тогда там вы должны указать, ограничен ли полученный указатель или нет.

Документация, связанная с этой темой, с которой я столкнулся, включает C99:

Пусть D будет объявлением обычного идентификатора, который предоставляет средства для обозначения объекта P как указателя с ограничением на тип T.

Обратите внимание, что это относится только к указателям.

Этот другой документ от TI содержит некоторые советы по настройке производительности с использованием restrict ключевое слово. В дополнение ко всем подсказкам в разделе 3.3 приведены примеры, когда можно применять этот квалификатор типа, а когда нет. Найдите объявление массива x в середине страницы 16, в нем говорится, что он не объявляет указатель и, следовательно, не может быть квалифицирован restrict.

person Harald    schedule 25.08.2016
comment
Если указатель на массив передается внешнему коду, нет четкого способа указать, что внешний код не будет хранить копию указателя и использовать эту копию для произвольных целей при следующем вызове внешнего кода. ИМХО, для C было бы полезно иметь квалификатор, аналогичный restrict, но для использования с переменными, чей адрес был взят, но будет использоваться очень ограниченным образом, но такая функция не определена. - person supercat; 08.09.2016
comment
@supercat проблема в том, как вы определяете эти «различные способы»? У gcc есть атрибуты (такие как «чистый»), которые можно прикрепить к функциям, которые обещают, что функция не будет делать определенные классы вещей. Кроме того, теперь у нас есть «оптимизация времени компоновки», когда набор инструментов компилятора может в основном посмотреть, что на самом деле делает функция, и использовать эту информацию. - person greggo; 17.05.2017
comment
@greggo: Если бы я писал правила, я бы сказал, что адрес объекта с квалификацией register может быть получен только при оценке аргумента функции, а поведение будет определяться только в тех случаях, когда все виды использования результирующего указателя или указателей производный от него происходит до возврата функции, и во время выполнения функции либо (1) доступ к объекту осуществляется исключительно через результирующий указатель или другие производные от него указатели, либо (2) объект не модифицируется никакими средствами. Если процесс сборки начинается с создания списка всех внешних символов, используемых в каждой функции... - person supercat; 17.05.2017
comment
... компилятор может с уверенностью предположить, что если ни foo, ни какая-либо функция, вызываемая foo, не использует панель символов, связанную с объектом, квалифицированным регистром, доступ к bar может рассматриваться как неупорядоченный по отношению ко всему, что происходит внутри функции [ определение каждой функции с заданной сигнатурой будет рассматриваться как вызов универсальной функции этого типа сигнатуры к этой функции, а косвенный вызов указателя сигнатурного типа будет вызовом универсальной функции этого типа, и, таким образом, вызов каждой функции, соответствующей этой сигнатуре]. - person supercat; 17.05.2017

Почему нельзя сделать следующее? Вы не получаете доступ к данным, связанным с tmpvalues, через эту переменную, поэтому допустимо использовать указатель ограничения в части кода, интенсивно использующей вычисления.

#include <stdio.h>
#include <stdlib.h>

int heavy_calcs(int n, float* restrict range1, float* restrict range2)
{
    if (n>1000) return 1;
    float tmpvalues[1000] = {0};
    {
        float * restrict ptv = tmpvalues;
        for (int i=0; i<n; i++) {
            ptv[i] = range1[i] + range2[i];
        }
    }
    return 0;
}

int main(int argc, char * argv[])
{
    int n = (argc>1) ? atoi(argv[1]) : 1000;
    float * r1 = (float*)malloc(n*sizeof(float));
    float * r2 = (float*)malloc(n*sizeof(float));
    int rc = heavy_calcs(n,r1,r2);
    free(r1);
    free(r2);
    return rc;
}

Я пропустил это через компилятор Intel 15, и у него не было проблем с векторизацией цикла. Конечно, этот цикл тривиален по сравнению с тем, что я предполагаю у вас, поэтому ваш пробег может отличаться.

person Jeff Hammond    schedule 28.04.2015
comment
Также нет необходимости в {} вокруг задания ptv. - person Jeff Hammond; 29.08.2016