В соответствии с вашими шагами 1 и 2, вы не делаете работу с основным ядром gpu. Какова сложность вашего ядра? Каково ваше использование gpu? Вы проверили с программами мониторинга, такими как afterburner? Средние настольные игровые карты могут получать потоки 10 тыс., Каждый из которых выполняет итерации 10 тыс.
Поскольку вы работаете только с соседями, размер данных/размер вычислений слишком велик, и ваши ядра могут быть узким местом для vram bandiwdth. Ваш основной системный RAM может быть таким же быстрым, как и пропускная способность pci-e, и это может быть проблемой.
1) Использование выделенного кэша может быть причиной фактической ячейки ячейки потока в частных регистрах, что является самым быстрым. Затем соседи в __local массив, поэтому сравнения/вычисления выполняются только в чипе.
Ток нагрузки клеток в __private
нагрузки соседей в __local
начала зацикливания для локального массива
получить следующий сосед в __private от __local
вычислений
конца цикла
(если у него много соседей, строки после «Load neighbors in __local» могут быть в другом цикле, который поступает из основной памяти с помощью патчей)
Каков ваш gpu? Приятно, что это GTX660. У вас должен быть 64 кб управляемый кеш на единицу расчета. Процессоры имеют только регистры 1kB и не адресуются для операций с массивами.
2) Более короткая индексация может использовать один байт в качестве индекса соседнего хранилища вместо int.
Пример:
0=neighbour from left
1=neighbour from right
2=neighbour from up
3=neighbour from down
4=neighbour from front
5=neighbour from back
6=neighbour from upper left
...
...
так что можно просто вывести индекс соседа из одного байта вместо 4-байтовой Int который уменьшает основную память доступа, по крайней мере, соседа доступ.Ваше ядро будет выводить индекс соседа из верхней таблицы, используя его вычислительную мощность, а не мощность памяти, потому что вы сделаете это из основных регистров (__ privates). Если ваш общий размер сетки постоянный, это очень просто, например, просто добавление 1 фактического идентификатора ячейки, добавление 256 к id или добавление 256 * 256 к id или около того.
3) Оптимальный размер объекта может сделать размер вашей структуры/ячейки более кратным 4 байтам. Если ваш общий размер объекта составляет около 200 байт, вы можете поместить его или увеличить его с помощью пустых байтов, чтобы сделать ровно 200 байтов, 220 бит или 256 байтов.
4) Безветрный код может использовать меньше if-утверждений. Использование if-statement делает вычисления намного медленнее. Вместо того, чтобы проверять -1 как конец индекса нити, вы можете использовать другой способ. Бесполезный легкий сердечник не способен выдерживать тяжеловес. Вы можете использовать поверхностные-буферные ячейки для обертывания поверхности, поэтому вычисленные ячейки всегда будут иметь 6-соседей, поэтому вы избавитесь от if (elem.neighbors [i]! = -1). Стоит попробовать особенно для GPU.
Просто вычисление всех соседей выполняется быстрее, чем выполнение if-statement. Просто умножьте результат на ноль, если он не является допустимым соседом. Как мы можем знать, что это не действительный сосед? Используя байтовый массив из 6 элементов на ячейку (параллельно с массивом соседних идентификаторов) (invalid = 0, valid = 1 -> умножьте результат на это)
Оператор if находится внутри цикла, который рассчитывается для шесть раз. Развертка петли дает аналогичное ускорение, если рабочая нагрузка в контуре относительно проста.
5) Элементы данных Изменение порядка можно перемещать INT [8] элемент к самой верхней стороне структуры, чтобы доступ к памяти может стать более с получением таким образом меньшего размера элементов к нижней стороне можно прочитать в одной операции считывания.
6) Размер рабочей группы при попытке разного размера рабочей группы может дать производительность 2-3 раза. Начиная с 16 до 512 дает разные результаты.
7) Разделение переменных (только если вы не можете избавиться от if-statements) Разделение элементов сравнения из struct. Таким образом вам не нужно загружать целую структуру из основной памяти только для сравнения int или логического. Когда требуется сравнение, затем загружает структуру из основной памяти (если у вас уже есть локальная оптимизация памяти, тогда вы должны поместить эту операцию до нее, так что загрузка в локальный mem выполняется только для выбранных соседей)
Эта оптимизация делает наилучший случай (ни соседка, ни только один сосед) значительно быстрее. Не влияет на худший случай (случай с максимальными соседями).
8a) Magic Использование сдвига вместо деления по мощности 2. Выполнение подобных по модулю. Помещение «f» в конец плавающих литералов (1.0f вместо 1.0), чтобы избежать автоматического преобразования из double в float.
8b) Magic-2 -cl-mad-enable Опция компилятора может увеличить умножение + добавить скорость работы.
9) Скрытность с задержкой Оптимизация конфигурации выполнения. Вам нужно скрыть задержку доступа к памяти и позаботиться о занятости.
Get maximum cycles of latency for instructions and global memory access.
Then divide memory latency by instruction latency.
Now you have the ratio of: arithmetic instruction number per memory access to hide latency.
If you have to use N instructions to hide mem latency and you have only M instructions in your code, then you will need N/M warps(wavefronts?) to hide latency because a thread in gpu can do arithmetics while other thread getting things from mem.
10) Смешанный тип Вычислительный После доступа к памяти оптимизировано, подкачка или переместить некоторые инструкции, где это применимо, чтобы получить лучшее размещение, используйте половину типа, чтобы помочь операций с плавающей точкой, где точность не важна.
11) Задержка Скрытие снова Попробуйте код ядра только с арифметикой (закомментируйте все мем доступ и начать их с 0 или sometihng вы хотите), то попробуйте код ядра с инструкциями доступа только памяти (закомментировать расчеты/сослагательного наклонения)
Сравнение времени ядра с исходным временем ядра. Что еще больше влияет на исходное время? Сконцентрируйтесь на том, что ..
12) Lane & Bank Конфликты Правильно ли конфликты LDS переулками и глобальные банковские памяти конфликты, поскольку одни и те же адреса accessings можно сделать в процессе замедления serialed пути (новые карты имеют транслируют способность уменьшить это)
13) Использование регистров Попробуйте заменить любых независимых местных жителей частными лицами, так как ваш GPU может использовать пропускную способность почти 10 ТБ/с с использованием регистров.
14) Использование регистров Не используйте слишком много регистров, иначе они будут разливаться в глобальную память и замедлить процесс.
15) Минималистский подход к занятиям Посмотрите местное/частное использование, чтобы получить представление о профессии. Если вы используете гораздо больше локальных и рядовых, тогда меньше потоков можно использовать в одном вычислительном блоке и вести меньшее занятие. Меньше использование ресурсов приводит больше шансов оккупации (если у вас есть достаточно общие темы)
16) Сбор Scatter Когда соседи разные частицы (например, в Nbody NNS) от случайных адресов памяти, его может быть трудно применить, но, build read Оптимизация может дать 2x-3x скорость перед оптимизацией (требуется оптимизация локальной памяти для работы), поэтому она считывает в порядке от памяти вместо случайного и переупорядочивает по мере необходимости в локальной памяти для разделения между (разбросом) и потоки.
17) Разделяй и властвуй Только в случае, когда буфер слишком велик, и копировать между хостом и устройством таким образом делает GPU ожидания простоя, а затем разделить его на две части, отправить их по отдельности, начинают вычисления, как только один приходит, отправить результаты возвращаются одновременно в конце. Даже параллелизм на уровне процесса может подтолкнуть gpu к своим пределам таким образом.
18)Пропускная способность из памяти квалификаторов. Когда для ядра требуется дополнительная пропускная способность «read», вы можете использовать ключевое слово «__constant» (вместо __global) для некоторых параметров, которые меньше по размеру и только для чтения. Если эти параметры слишком велики, вы можете по-прежнему иметь хороший поток из квалификатора '__read_only' (после квалификатора '__global'). Similary '__write_only' увеличивает пропускную способность, но в основном это производительность оборудования. Если это серия HD5000 от Amd, постоянная хорошая. Возможно, GTX660 быстрее работает с кешем, поэтому __read_only может стать более удобным (или Nvidia использует кеш для __constant?).
Имейте три части одного буфера с одним __global __read_only, один как __constant и один как __global (если их строить не штрафует больше, чем читает).
Только что протестировал мою карту с использованием примеров AMD APP SDK, полоса пропускания LDS показывает 2 ТБ/с, а постоянная - 5 ТБ/с (такая же индексация вместо линейной/случайной), а основная память - 120 ГБ/с.
19)Современные функции аппаратного трансцендентные быстрее, чем старый бит взломать (например, Quake-3 быстро обратными квадратный корень) версии
20)теперь есть OpenCL 2.0, который позволяет нерестилища ядра внутри так что вы можете дополнительно увеличить разрешение в 2-й точке сетки и разгрузить его в рабочую группу при необходимости (что-то вроде увеличения детализации завихрения на краях жидкости динамически)
Профилировщик может помочь в r все те, но любой индикатор FPS может делать, если для каждого шага выполняется только одна оптимизация.
Даже если бенчмаркинг не предназначен для кодовых путей, зависящих от архитектуры, вы можете попробовать иметь в своем вычислительном пространстве несколько десятков точек в строке, поскольку ваш gpu имеет несколько из этого количества ядер и эталон, что если он делает gpu более заняты и имеют больше операций gigafloatingpoint в секунду.
Должно быть место для оптимизации после всех этих опций, но idk, если оно повредит вашу карту или возможно для продления времени ваших проектов.
Anymore imporvement? –
На этой неделе я немного купил другие вещи, я перевел общие данные в локальную память, но не успел проверить производительность. Я постараюсь сделать это в наши дни. После этого я хочу попытаться уменьшить условия IF, используя тот метод, который вы указали в четвертой оптимизации. Я сохраню это обновление :) – Alex
Так это сейчас 72 FPS? От 8x до 9,2x? –