Определение самой регистровой голодной части ядра - PullRequest
3 голосов
/ 28 сентября 2011

когда я получаю ядро, использующее слишком много регистров, я могу сделать 3 варианта:

  1. оставьте ядро ​​без изменений, что приведет к низкой загрузке
  2. установить компилятор для использования меньшего количества регистров, разливая их, что ухудшает производительность
  3. переписать ядро ​​

Для варианта 3 я хотел бы знать, какой части ядра требуется максимальное количество регистров. Есть ли какой-либо инструмент или метод, позволяющий мне идентифицировать эту часть? Чтение кода PTX (я разрабатываю на NVidia) бесполезно, регистры имеют различные большие числа и, честно говоря, лучшее, что я могу сделать, - это определить, какая часть кода сборки соответствует какой части кода C *. 1011 *

Просто комментировать некоторый код не так уж и много - например, я заметил, что если я просто помещаю код в цикл, число регистров резко возрастает, а не только на один для переменной управления циклом. Я лично подозреваю компилятор NVidia в несовершенном анализе живучести переменных, но, конечно, я не могу с этим многое сделать: -)

Ответы [ 2 ]

1 голос
/ 14 марта 2012

Если вы работаете на оборудовании NVidia, вы можете передать опцию -cl-nv-verbose для компиляции в clBuildProgram, а затем clGetProgramInfo CL_PROGRAM_BINARIES, чтобы получить читаемый человеком текст о компиляции. Там будет указано количество используемых регистров. Обратите внимание, что NVidia кеширует компиляцию и выдает эту информацию о регистре только при фактическом изменении источника ядра, так что вы можете захотеть внести некоторые лишние изменения в исходный код, чтобы заставить его выполнить полный анализ.

Если вы работаете на оборудовании AMD, просто установите переменную среды GPU_DUMP_DEVICE_KERNEL = 1. Во время компиляции будет создан текстовый файл IL. Не уверен, что в нем явно указано количество используемых регистров, но это то, что эквивалентно описанной выше методике NVidia.

Если посмотреть на этот вывод (по крайней мере, на nvidia), вы обнаружите, что он использует бесконечное количество регистров (если вы идете по номерам регистров). В действительности, он выполняет анализ потока и фактически повторно использует регистры способом, который совсем не очевиден при взгляде на IL.

0 голосов
/ 11 октября 2011

Это сложный вопрос на любом языке, и, вероятно, нет одного правильного ответа.Но вот некоторые вещи, о которых следует подумать:

  • Найдите код в «самой глубокой» области видимости, которую вы можете найти, имея в виду, что большинство функций, вероятно, встроено вашим компилятором OpenCL.Подсчитайте переменные, используемые в этой области, и пройдитесь по содержащимся областям.В каждой содержащей области, подсчитайте переменные, которые используются как до, так и после внутренней области.Они потенциально живы, пока выполняется внутренняя область.Этот процесс может помочь вам учесть текущие регистры в определенной части программы.
  • Попробуйте взять переменные, которые «охватывают» глубокие области действия, и, по возможности, не допускать их охвата областью действия.Например, если у вас есть что-то вроде этого:

    int i = func1();
    int j = func2();  // perhaps lots of live registers here
    int k = func3(i,j);
    

    , вы можете попробовать переупорядочить первые две строки, если в func2 много живых регистров.Это уберет i из набора живых регистров, пока func2 работает.Это, конечно, тривиальная модель, но, надеюсь, она иллюстративная.

  • Подумайте об избавлении от переменных, которые просто сохраняют результаты простых вычислений.Вы можете пересчитать их, когда они вам понадобятся.Например, если у вас есть что-то вроде int i = get_local_id (0), вы можете просто использовать get_local_id (0) везде, где бы вы использовали i.

  • Подумайте об избавлении от переменных, которыехранение значений, хранящихся в памяти.

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

...