Простота и сложность GPU.
Почему CPU просто программировать, а GPU сложно?
Каким образом на GPU достигается высокая производительность?
(Далее по тексту под GPU иногда понимается GPU поддерживающие CUDA)
Давайте взглянем на устройство CPU.
Центральное место - конвейер, а вокруг него множество "заплат" или "костылей" -
- переименование регистров
- предсказание переходов (спекулятивное исполнение+логика сброса конвейера)
- поддержка когерентности кэшей
- предвыборка данных
- hyper-threading
- страничная организация памяти
Эти функциональные блоки существуют для того, что бы программист шибко не задумывался о внутреннем устройстве процессора.
Очень редко кто спускается до оптимизации программ не на алгоритмическом уровне, а на аппаратном, и, обычно, дальше использования кэша дело не доходит.
Если же программист, наконец, доходит до программирования на ассемблере и соответствующего уровня оптимизации, то, конечно, он должен начать разбираться во всех деталях современных процессоров.
А деталей оказывается очень и очень много... и в этом случае уже нельзя сказать, что на CPU программировать проще чем GPU - порою кажется, что ещё сложнее.
А на GPU ничего такого нет - там все предельно просто с точки зрения проектирования самого GPU.
Единственные похожие функциональные элементы - планировщик потоков GPU и планировщик внеочередного исполнения CPU.
Одновременно с этим, планировщик потоков - единственный нетривиальный, и при этом неизвестно-как-устроенный функциональный блок мультипроцессора.
(Можно вспомнить про кэши констант и текстур на GPU, но они несравнимо малы, по сравнению с кэшами CPU)
В остальном, ничего предназначенного для упрощения программирования на GPU не имеется - все остальное управляемо. Однако от этой "простоты" программисту жить совсем не просто - надо знать почти все особенности GPU даже для написания небольших программ:
- выровненное использование глобальное памяти
- отсутствие конфликтов по банкам при использовании разделяемой памяти
- наличие самой разделяемой памяти (управляемый кэш)
- максимизация использования разделяемой памяти
- отсутствия полноценных кэшей
- блоки и сетка блоков потоков
- warp'ы и исполнение условных ветвлений
- однотактовое исполнение почти всех команд (4-х тактовое для warp'а)
- слабые средства синхронизации потоков (только барьеры внутри блока потоков)
- отсутствие полноценного отладчика
- нужна в огромном количестве потоков для эффективного исполнения
P.S. Явного вопроса нет. На обсуждение.