В чем отличие Block от Warp и как увеличить occupancy?

Решил разобраться с SM, Cores, Blocks, Warps и Threads. Читал: NVIDIA_CUDA_Programming_Guide_2.0.pdf, NVIDIA_Fermi_Compute_Architecture_Whitepaper.pdf, CUDA_Optimizations.pdf, CUDA_C_Best_Practices_Guide.pdf

Допустим у меня GeForce GTX 460SE, 6 SM x 288 CUDA Cores.
http://ru.wikipedia.org/wiki/GeForce_400
Понятно, что реально исполнятся могут только 288 Threads, по 48 Threads в каждом Block.
На сколько я понимаю, если я вызываю кернел используя 6 блоков по 1024 потока, то в каждом блоке создатся по 32 варпа (32х32=1024), и в этих 32 варпах общее количество реально одновременно исполняемых нитей будет 48?

Вопрос в том, чем отличается Warp от Block и сколько реально одновременно может исполняться Warp-ов и Block-ов?

И что такое occupancy и как его посмотреть в MS VS 2010 + Nsight2.0?

Forums: 

Нет, все не вполне так. Warp

Нет, все не вполне так.

Warp - это единица исполнения. Весь SM исполняет одновременно (одну и ту же команду) один Warp.
При этом на SM может почти одновременно (перемежаясь по инструкциям) исполняться несколько Warp-ов. Это происходит со всеми Warp одного блока.
Иметь размер блока больше чем размер Warp (т.е. несколько warp в блоке) - выгодно т.к. время на обращения к глобальной памяти при этом от вас "прячутся". Захотел warp что-то прочесть или записать - запустил команду чтения и его отложили в уголок, взяли следующий warp.

На одном SM могут исполняться и несколько block. Сколько именно - зависит от ресурсов (shared memory, регистры) и от числа потоков в блоке. Вот, к примеру, если у вас в block 256 потоков и затребовано мало ресурсов, то на 460SE на одном SM будет исполняться 6 блоков (т.к. максимальное число потоков на этом чипе в одном SM - 1536.

Да я понимаю для

Да я понимаю для (Blocks/Warps/Thread) разницу между "реально исполняемыми" и "резидентными", первые увеличивают вычислительную производительность, вторые скрывают задержки обращения к памяти.
Т.е. на GTX460SE для 1 SM можно создать максимум 6 блоков и все 6 будут реально исполняться одновременно.

А сколько максимальное количество Warp-ов можно создать на 1 SM для GTX460SE, при минимальном использовании регистров и разделяемой памяти и только 1 из этих Warp-ов будет исполняться реально одновременно?

Почему "максимум 6 блоков"?

Почему "максимум 6 блоков"? В CC 2.x ограничение - 8 блоков.

Максимальное количество одновременных варпов = 1536/32 = 48. Каждый варп будет активизироваться раз в 48 тактов, это "почти реально одновременно". А остальные 47 тактов - простаивать, пряча латентность global memory

Т.е. получается так, 48 Warps

Т.е. получается так, 48 Warps x 6 SM = 288 CUDA Cores. Но т.к. реально одновременно работает только 1 Warp на SM, то получается 32 Threads x 1 Warp x 6 SM = 192 реально одновременно исполняемых команды. Но это несколько ниже числа 288 CUDA Cores :)
Тут где-то у меня непонимание.

А вообще сколько реально одновременно может исполняться SIMD и сколько SISD потоков на 288 CUDA Cores?

Нет, вы не то умножаете. На

Нет, вы не то умножаете.

На одном SM (в котором 32 cuda cores) могут выполняться 1536 потоков. Почти одновременно, если по тактам:
- инструкция warp 0 (32 потока одновременно)
- инструкция warp 1
....
- инструкция warp 46
- инструкция warp47
- инструкция warp 0
- инструкция warp 1

Т.е. за 480 тактов SM будут исполнены по 10 инструкций из каждого варпа.

А SM - всего 6. Т.е. количество (почти)одновременно исполняемых потоков в пределе 1536x6

Для начала все таки хотелось

Для начала все таки хотелось бы понять сколько реально одновременно.
Но у вас получается одномоментно выполняется 1 варп на 1 SM, за который исполняются 32 потока одновременно, т.е. одномоментно на 6 SM исполняется 32 х 6 = 192 потока.

А вообще сколько реально одновременно может исполняться SIMD и сколько SISD потоков на 288 CUDA Cores?

Что такое "реально

Что такое "реально одновременно"?

Пусть у нас всего 1 SM. На нем работает 1536 потоков (скажем, 6 блоков по 256 threads). Они исполняются ну вот примерно реально одновременно, 48 варпов интерливятся и раз в 48 тактов варп получает управление. Чем эта одновременность нереальна? Вот если потоки разных варпов (не суть важно, одного блока или разных) полезут в один адрес в памяти - вы получите (можете получить) data race

"реально одновременно" - это

"реально одновременно" - это когда "одновременно", а не "почти", "примерно" и т.д. :)
У вас в описании варпы выполняются не одновременно, а поочередно-последовательно.
Причем у вас в примере в каждый 1 так работает только 32 потока(1 варп) несмотря на то, что есть 48 ядер :) Т.е. 16 ядер простаивают. Ну это ладно. Меня интересует как варпы с числом потоков кратным 32 ложатся на блок с числом ядер не кратным 32, т.е. 48.

А один и тот же Thread может выполняться на разных CUDA-Cores, т.е. сейчас на одном, а через 48 тактов на другом?

Обратите внимание - я

Обратите внимание - я рассматриваю один SM, а не несколько. И CC 2.0 (а не 2.1) с 32-мя cuda cores на SM. Для простоты.

А ну тогда в принципе все

А ну тогда в принципе все понятно в ваших объяснениях.
Но мой вопрос как раз про GTX460 SE, с CC 2.1 и 6 SM :)

Ну а CC 2.1 исполняет три

Ну а CC 2.1 исполняет три полуварпа за такт на одном SM. Т.е. 1536 потоков в 48 варпах выполняются одновременно, с интерливом в 32 тактов.

На 6 SM - соответственно около 10 тыс потоков "в практическом смысле одновременно".

Хм, интересно :) А в СС 2.1

Хм, интересно :)
А в СС 2.1 по прежнему все потоки одного варпа всегда синхронизованы или один из полуварпов может уйти вперед больше чем на 1 инструкцию по сравнению со вторым полуварпом этого же варпа?

Наука говорит об этом -

Наука говорит об этом - "хотите синхронизироваться - используйте syncthreads()"

Потому что конечно можно написать такой код, который сильно разойдется. Например, один полуварп идет по одной ветке if(), а второй - по другой.

syncthreads() к сожалению

syncthreads() к сожалению синхронизирует все 1500 потоков, а не только 32 потока одного варпа, от чего производительность может просесть :)
А у меня уже часть кода написана под СС 2.0 с учетом того, что все потоки одного варпа всегда сами синхронизованы, но в пределах if/else это в моем коде не важно.

А в чем, собственно,

А в чем, собственно, проблема?

У вас поток одного варпа использует данные, порожденные другим потоком того же варпа и вы хотите быть уверенным, что данные уже порождены?

Я вот боюсь, что в этом месте могут быть сюрпризы, именно что полуварп убежит относительно другого (необязательно по причине if/else, скажем у одной половинки доступ к глобальной памяти случился из кэша, а у другой - пришлось ждать настоящего доступа).

Вообще, похоже я не прав -

Вообще, похоже я не прав - руководство (раздел 4.1 programming manual) утверждает, что если нет ветвления то исполняется одна и та же инструкция во всем варпе.

Да, я в одном из Best

Да, я в одном из Best Practice-ов вычитал про эту оптимизацию учитывающую автоматическую синхронизацию потоков в пределах одного варпа :)

А все таки один и тот же Thread может выполняться на разных CUDA-Cores, т.е. сейчас на одном, а через 48 тактов на другом?