Почему видеокарта, имеющая неплохие вычислительные возможности, в Stable Diffusion работает в 20 раз медленнее, чем RTX 3060? Почему в LM Studio она становится фаворитом, а в ComfyUI карета превращается в тыкву? Почему FurMark на CMP 90HX тормозит, а на CMP 50HX «бублик» крутится почти нормально? Разгадки в разных программных ограничениях, которые можно найти с помощью экспериментов. Я купил три майнинговые карты Nvidia, чтобы понять, можно ли заставить их эффективно работать.
В этот раз мы рассмотрим:
статистику производительности в LM Studio
как всё печально в ComfyUI и Stable Diffusion
анатомию программного кода GPU
почему оптимизации производительности дают на CMP обратный эффект
какие режимы вычислений могут раскрыть их потенциал.

LM Studio – здесь новый фаворит
Эта статья – продолжение предыдущего обзора возможностей майнинговых видеокарт Nvidia CMP40 HX, CMP 50HX, CMP 90HX при их работе в LLM и попытка исследования ограничений CMP на уровне софта в поисках лазеек. Искусственно созданные ограничения производительности CMP могут быть частично преодолены путем принудительного отключения программных оптимизаций и использования специфичных режимов вычислительных библиотек.
В прошлой статье все тесты я проводил в ollama и лишь мельком упомянул про LM Studio. А здесь ситуация заметно отличается. В основном для CMP 40HX, и, что приятно, в положительную сторону.
Для начала замечу, что в LM Studio можно выбирать разные движки для работы моделей. Я использовал «Vulkan» и «CUDA 12». CPU нас не интересует, а просто «CUDA» вроде особо ничем не отличается. CMP 40HX и CMP 50HX работают на PCIexpress x8, CMP 90HX – x4.


Вот скорость работы некоторых из таких моделей (ток/с)
CMP 40HX |
CMP 50HX |
CMP 90HX |
CMP 50+90 |
RTX 3060 12G |
RTX 3060 + CMP 40HX |
||||||||
Модель LLM |
Размер GB |
CUDA |
Vulk |
CUDA |
Vulk |
CUDA |
Vulk |
CUDA |
Vulk |
CUDA |
Vulk |
CUDA |
Vulk |
Dolphin3.0 8B Q4_K_S |
4.69 |
25 |
55.5 |
30 |
14 |
28 |
18 |
28.5 |
15 |
60 |
53 |
36 |
52 |
Llama 3.1 8B Instruct |
4.92 |
26 |
53 |
30 |
14.5 |
29 |
17.5 |
29 |
15 |
55.5 |
51.5 |
37 |
51 |
qwen3-8b |
5.03 |
26 |
52 |
29 |
14 |
27 |
16.5 |
27.5 |
15 |
54.5 |
50 |
35 |
47.5 |
deepseek-r1-0528-qwen3-8b |
6.25 |
25 |
49 |
28.5 |
14 |
28 |
17 |
27 |
15 |
54 |
48 |
35 |
45 |
gemma-3-12b |
8.15 |
9.5 |
9 |
18 |
9 |
18 |
10.5 |
17.5 |
9.5 |
35 |
31 |
24 |
31 |
qwen3-14b |
7.9 |
10.5 |
9.5 |
13.5 |
12.5 |
19 |
9 |
18.5 |
10 |
24 |
23.5 |
21.5 |
23 |
gpt-oss-20b |
12.11 |
12 |
17 |
17 |
22 |
13 |
21 |
65 |
37.5 |
30 |
31 |
62 |
53 |
qwen3-30b-a3b-2507 |
18.56 |
14 |
15 |
20.5 |
17 |
16.5 |
16 |
44 |
28 |
24 |
26 |
50 |
70 |


Обратите внимание на соотношение производительности видеокарт в режимах CUDA и Vulkan. Видно, что CMP 40HX ведет себя не так, как остальные видеокарты – у неё максимальная производительность в режиме Vulkan, а у других – в CUDA. По всей видимости, это происходит из-за разной природы вычислительных ограничений.
К сожалению, из-за этого она не очень «сочетается» с RTX 3060, а то могла быть идеальным дополнением к «нормальной» видеокарте для расширения общего объема VRAM. Но это и так неплохо работает – на больших моделях Vulkan везде «догоняет» CUDA и пара RTX 3060 + CMP 40HX сильно выигрывает у одной RTX. При этом на маленьких моделях CMP 40HX можно отключать в настойках, чтобы не терять скорость RTX в режиме CUDA.
В общем, CMP 40HX показала себя здесь лучше, чем в Ollama и очень хороша за свою цену, по производительности примерно соответствует RTX 2060/3060 на 8 ГБ. Ей бы ещё памяти побольше.
При работе в LM Studio для CMP 40HX необходимо всегда использовать Vulkan.
ComfyUI – вот, где печаль…

Здесь даже говорить нечего – вот значения скорости и времени генерации для нескольких значений разрешения картинки. Все замеры проводились в режиме SD, в Stable Diffusion получается примерно так же.
GPU |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
||||
Разрешение |
t, сек |
it/s |
t, сек |
it/s |
t,сек |
it/s |
t,сек |
it/s |
512x512 |
43 |
0.55 |
16.5 |
1.77 |
16 |
1.55 |
2.70 |
9.21 |
768x768 |
110 |
0.22 |
42 |
0.66 |
42 |
0.59 |
6.1 |
4.11 |
1024x1024 |
243 |
0.1 |
88 |
0.32 |
92 |
0.27 |
12.15 |
2.05 |
1920x1080 |
711 |
0.03 |
243 |
0.11 |
268 |
0.09 |
34.2 |
0.72 |


CMP 50/90HX проигрывают RTX 3060 в 6-7 раз, а CMP 40HX – около двадцати раз.
В LM Studio CMP 40HX летает, а в ComfyUI — тормоз. Почему? Что же там так тормозит? Непонятно – и там и там используются матричные вычисления.
Самый главный «козырь» Nvidia в работе с матрицами – это тензорные ядра (Tensor Cores). У GPU AMD их нет и у них те же проблемы, что и у наших CMP. Здесь они формально есть, но очень сильно «заторможены».
Итак, копаем глубже
Конечно, наиболее эффективный и радикальный метод – разобраться в BIOS-ах и драйверах этих карт, но мне это, пожалуй, не под силу – Nvidia очень старается и у неё большой опыт по части сокрытия своих коммерческих тайн и введения программных и аппаратных ограничений. Но дальнейшие рассуждения могут помочь как-то использовать имеющиеся неплохие возможности для применения CMP в каких-то частных случаях или сделать патчи, которые помогут ускорить их работу в той или иной программе.
Что мы можем «выжать» из CMP 50/90HX по части вычислений в FP32 и всех CMP в INT8? Напомню здесь грустный фрагмент таблицы с замерами производительности из предыдущей статьи.
Параметр |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
FP32 |
8.22 TFLOPs/s (1x) |
0.43 TFLOPs/s (1/24) |
0.72 TFLOPs/s (1/32) |
13.04 TFLOPs/s (1x) |
FP32 теор. |
7.18 TFLOPs/s |
11.07 TFLOPS |
21.89 TFLOPS |
12.74 TFLOPs/s |
INT8 |
0.92 TIOPs/s (1/8) |
1.72 TIOPs/s (1/8) |
1.44 TIOPs/s (1/16) |
25.38 TIOPs/s (2x) |
INT8 теор. |
27.8 TIOPs/s (4х) |
43.97 TIOPs/s (4x) |
39.24 TIOPs/s (4х) |
26.8 TIOPs/s (4х) |
Не пугайтесь отличия замеров от теоретических значений. Главное, что для разных GPU измеренные «попугаи» одинаковы.
Ещё раз вспоминаем, как здесь всё очень плохо, но почему-то на практике плохо далеко не настолько.
Дело в том, что использованный мной бенчмарк на Open CL измеряет максимальную пиковую производительность как раз в матричных и векторных вычислениях. Здесь начинается интересное. Хотя мы не сможем получить от них полную производительность, хорошей новостью является то, что не все операции FP32 и INT8 «заторможены».
При измерении максимальной производительности в FP32 используется операция FMA (Fused Multiply-Add), которая выполняет операцию умножения с последующим сложением (d = a + b * c) за один такт (умножение с накоплением) – самая часто используемая операция в графике и нейросетях.
При измерении максимальной производительности в INT8 используется операция dp4a (Dot Product 4 Accumulate), которая выполняет 4 умножения + 3 сложения + аккумуляцию чисел INT8 за один такт (умножение с накоплением), так же часто используемая операция в нейросетях, она особенно важна при работе с сильно квантованными моделями.
Именно эти операции и «заторможены» на CMP и при попытке выполнения на них стандартных матричных и векторных вычислений они дико тормозят. Но «обычная» математика всё ещё может работать нормально. Для этого нужно заменить операцию FMA на 2 последовательные операции – умножение, а затем сложение. Да, это медленнее, чем одна операция, но не в 24-32 раза! Если покопаться, здесь есть некоторые нюансы, но в целом вариант рабочий.
То же самое и с dp4a – её можно разложить на несколько отдельных операций и выполнить их последовательно.
Как это выглядит на практике?
Вместо d = fma(a, b, c);
пишем d = a + b * c;
вместо d = dp4a(a, b, c);
пишем что-то вроде d = c + a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
После этого, предположительно, скорость вычислений RTX 3060 и CMP 40HX для FP32 должна вдвое уменьшиться, а для остальных должно получиться что-то около половины теоретического значения. По INT8 для RTX 3060 скорость должна в несколько раз упасть, для остальных – вырасти. Естественно, в реальных программах этот прием нужно применять избирательно – только для тех GPU, где он дает выигрыш.
Кроме того, есть ещё ряд операций для вычислений с упакованными INT8:
dp4a – поддерживаются с Pascal (SM 6.1+).
add4, sub4, mul4, max4, min4, abs4 – с Turing (SM 7.5+).
satadd4 и INT4-версии dp4a — только на Hopper (SM 9.0+).
Все они используются в TensorRT, PyTorch, TensorFlow Lite, и других фреймворках для квантованного инференса и их, наверное, при необходимости тоже нужно будет эмулировать – написать небольшую библиотеку таких функций.
Итак, всё просто! Пробуем, запускаем бенчмарк, и получаем не совсем то, что ожидалось…
Параметр |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
FP32 с FMA |
8.22 TFLOPs/s (1x) |
0.43 TFLOPs/s (1/24) |
0.72 TFLOPs/s (1/32) |
13.04 TFLOPs/s (1x ) |
FP32 без FMA |
8.20 TFLOPs/s (1x) |
0.43 TFLOPs/s (1/24) |
0.70 TFLOPs/s (1/32) |
13.05 TFLOPs/s (1x) |
INT8 с dp4a |
0.92 TIOPs/s (1/8) |
1.72 TIOPs/s (1/8) |
1.44 TIOPs/s (1/16) |
24.88 TIOPs/s (2x) |
INT8 без dp4a |
0.93 TIOPs/s (1/8) |
1.71 TIOPs/s (1/8) |
1.43 TIOPs/s (1/16) |
16.76 TIOPs/s (1x) |
Что за чертовщина?? Ничего не изменилось… Разве что на RTX 3060 хуже стало. Кстати, не обращайте внимания на небольшой разброс значений – это погрешности измерений и устранять её не имеет особого смысла, нас везде интересует только существенная разница – в разы.
Если пораскинуть мозгами, то всё ясно – это оптимизации компилятора. Если он видит операцию, похожую на FMA или dp4a, он заменяет её на FMA или dp4a соответственно. Ладно, ищем дальше.
Можно выйти за рамки Си и написать прямые инструкции для GPU, используя ассемблерные вставки. Для маленьких коротких операций это будет несложно и эффективно.
Эквивалент dp4a выглядит так
int ndp4a(const char4 a, const char4 b, const int c)
{
int result = c;
asm("mad.lo.s32 %0,%1,%2,%0;":"+r"(result):"r"((int)a.x),"r"((int)b.x));
asm("mad.lo.s32 %0,%1,%2,%0;":"+r"(result):"r"((int)a.y),"r"((int)b.y));
asm("mad.lo.s32 %0,%1,%2,%0;":"+r"(result):"r"((int)a.z),"r"((int)b.z));
asm("mad.lo.s32 %0,%1,%2,%0;":"+r"(result):"r"((int)a.w),"r"((int)b.w));
return result;
}
Эквивалент fma выглядит так
float nfma(float a, float b, float c)
{
float result;
asm("mul.f32 %0,%1,%2;":"=f"(result):"f"(a),"f"(b)); // Сначала a * b
asm("add.f32 %0,%1,%2;":"=f"(result):"f"(result),"f"(c)); // Затем + c
return result;
}

Параметр |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
FP32 с FMA |
8.21 TFLOPs/s (1x) |
0.42 TFLOPs/s (1/24) |
0.71 TFLOPs/s (1/32) |
13.05 TFLOPs/s (1x) |
FP32 без FMA |
4.12 TFLOPs/s (1/2) |
6.88 TFLOPs/s (2/3) |
0.36 TFLOPs/s (1/64) |
6.51 TFLOPs/s (1/2) |
FP32 теор. |
7.18 TFLOPs/s |
11.07 TFLOPS |
21.89 TFLOPS |
12.74 TFLOPs/s |
INT8 с dp4a |
0.92 TIOPs/s (1/8) |
1.70 TIOPs/s (1/8) |
1.44 TIOPs/s (1/16) |
25.39 TIOPs/s (2x) |
INT8 без dp4a |
2.61 TIOPs/s (1/8) |
4.17 TIOPs/s (1/3) |
3.62 TIOPs/s (1/8) |
2.184 TIOPs/s (1/8) |
INT8 теор. |
27.8 TIOPs/s (4х) |
43.97 TIOPs/s (4x) |
39.24 TIOPs/s (4х) |
26.8 TIOPs/s (4х) |
CMP 50HX, кажется, победили! Полученная производительность FP32 примерно соответствует ожидаемой. Да, это примерно вдвое хуже теоретически возможной, но в 16 раз выше, чем было!
У CMP 40HX и RTX 3060 ожидаемое падение производительности FP32 вдвое.
INT8 у всех CMP худо-бедно, но улучшилось в 2,5 раза. Неплохо бы ещё «подтянуть», но пусть пока так.
Но что же с CMP 90HX? Всё стало ещё вдвое хуже! Оптимизации компилятора исключены – напрямую используются инструкции GPU. Дальнейшие поиски привели к тому, что в Си и Open CL есть команды, запрещающие автоматическую подстановку команд FMA при оптимизации.
#pragma fma off
// Здесь — FMA будет отключен
#pragma fma on // Включить обратно (опционально)
#pragma OPENCL FP_CONTRACT OFF // То же для Open CL
Их использование приводит к тому, что для CMP 40HX и CMP 50HX обычная запись d = a + b * c; начинает работать как надо и функция nfma не нужна. Но для CMP 90HX и RTX 3060 это ничего не меняет и любое добавление любой арифметической операции для FP32 (add, mul, sub) пропорционально увеличивает время обработки.
Чем отличаются эти видеокарты? Архитектурой: CMP 40HX и CMP 50HX – это Turing, а CMP 90HX и RTX 3060 – Ampere, следующее поколение. Немного поразмыслив, я пришел к предположению, что, начиная с некоторого момента, Nvidia «унифицировала» все вычислительные блоки и заменила все сумматоры и умножители на FMA. Ведь эта операция так же выполняется за один такт и из неё можно получить и сложение и умножение.
И действительно, никакие мои ухищрения ничего не изменили. Теперь-то я понял, почему измеренная производительность FP32 у CMP 50HX ниже, чем у CMP 90HX, но «бублик» FurMark на CMP 50HX крутится более-менее резво (30 fps), а на CMP 90HX дико тормозит (5-8 fps). Большая часть вычислений на CMP 50HX выполняется нормально, а часть «оптимизируется» в FMA и тормозит. На CMP 90HX, похоже, «оптимизируется» в FMA и тормозит большинство операций и в ней разделение FMA на умножение и сложение превращается в две «неполные» FMA.
Из CUDA C++ Programming Guide :
Most compute devices implement multiplication and addition operations using a single fused multiply-add (FMA) instruction... The compiler and hardware combine multiply and add operations into a single FMA operation whenever possible.
Посмотрим, где происходят невидимые нам изменения кода и как код приложения попадает на вычислительные блоки GPU. Помните, ведь я писал
asm("mul.f32 %0, %1, %2;" : "=f"(result) : "f"(a), "f"(b));
а получалось FMA. Но ведь ASM же… А на самом деле, это не ASM.
Анатомия кода GPU для «чайников»
То, что мы видим, как «ассемблерные» вставки и то, что генерируется компиляторами CUDA (или Open CL) и войдет в скомпилированный файл – это PTX (Parallel Thread Execution) – низкоуровневый, но аппаратно-независимый язык виртуальной GPU-машины. Его можно рассматривать как аналог байт-кода в Java или промежуточного представления (IR) в LLVM. PTX-код не исполняется напрямую GPU-ядрами, а компилируется потом в бинарный машинный код, специфичный для конкретной архитектуры GPU (например, для конкретного числа CUDA ядер, shared memory и т.п.). Это JIT-компиляция (Just-In-Time), на этом этапе так же происходят внутренние оптимизации.
При загрузке кода на GPU PTX-код компилируется на лету драйвером NVIDIA. Это позволяет адаптировать код под конкретную архитектуру GPU, даже если она отличается от той, под которую изначально компилировался код.
Во время JIT-компиляции могут происходить различные оптимизации: перестановка инструкций, замена более эффективными, свёртка констант, устранение мёртвого кода и т.п.
Эти оптимизации зависят от версии драйвера, архитектуры GPU и версии PTX.
NVIDIA поддерживает несколько версий PTX, каждая из которых соответствует определённому уровню возможностей GPU.
Здесь происходит преобразование PTX в SASS – нативный, аппаратно-зависимый машинный код для конкретной микроархитектуры GPU (например, Turing, Ampere, Hopper). Это бинарный формат, который непосредственно понимают Streaming Multiprocessors (SMs) внутри GPU.
PTX — это как «универсальный ассемблер для всех GPU», а SASS — настоящий машинный код, который понимает только конкретная карта. Компиляция PTX в SASS происходит непосредственно при запуске программы – именно там Nvidia может «подменить» наши инструкции.
Когда вы запускаете приложение, драйвер видит, что бинарник содержит код PTX. Затем он выполняет JIT-компиляцию из PTX в SASS, специфичный для установленной в системе видеокарты.
Процесс компиляции PTX в SASS:
1. Декодирование и анализ: Драйвер загружает PTX-код.
2. Оптимизация: Выполняются оптимизации, специфичные для целевой архитектуры (переупорядочивание инструкций, разворот циклов и т.д.).
3. Распределение регистров: Виртуальные регистры PTX распределяются на ограниченное количество физических регистров конкретного GPU – это одна из самых сложных задач.
4. Инструкционное планирование (Scheduling): Инструкции переупорядочиваются для максимальной загрузки исполнительных блоков GPU (например, чтобы операции с float и integer могли выполняться параллельно).
5. Генерация кода: На выходе получается бинарный образ SASS, готовый к загрузке в память видеокарты и исполнению.
Скомпилированный код SASS загружается в память GPU (константную или кеш инструкций), где происходит его выполнение:
1. Warp Execution: GPU выполняет инструкции варпами (warps) — группами из 32 потоков. Все 32 потока в варпе выполняют одну и ту же инструкцию одновременно (SIMT — Single Instruction, Multiple Threads).
2. Instruction Dispatch: Диспетчер (scheduler) на каждом Streaming Multiprocessor (SM) выбирает готовый к выполнению варп и отправляет его инструкцию на соответствующий исполнительный блок.
3. Исполнительные блоки (Execution Units): В зависимости от инструкции, она выполняется на одном из многочисленных специализированных блоков внутри SM:
CUDA Cores: для арифметики с плавающей запятой и целочисленных операций.
Tensor Cores: для специализированных матричных вычислений (AI/ML).
RT Cores: для трассировки лучей.
SFUs (Special Function Units): для трансцендентных функций (sin, cos, log, etc.).
Load/Store Units: для работы с памятью.
Поднимаемся выше
Мы говорили о замене отдельных инструкций GPU в коде на Си, но что делать на более высоком уровне?
Все знают, что при работе с нейросетями на Python используется PyTorch. А как он обрабатывает данные? Под капотом он использует CUDA-библиотеки Nvidia, такие, как cuBLAS, cuBLASLt, cuTENSOR, CUTLASS.
cuBLAS – это CUDA Basic Linear Algebra Subroutines – библиотека стандартных математических операций, оптимизированная для выполнения на GPU NVIDIA.
cuBLASLt — Linear Algebra Library with Tuning
Это новая, более гибкая версия cuBLAS, построенная поверх старой.
Позволяет явно выбирать алгоритмы, настраивать параметры, использовать планировщики.
Поддерживает автоматический подбор оптимального алгоритма для нужной архитектуры и размеров матриц.
Используется внутри TensorRT, PyTorch, TensorFlow.
cuTENSOR — Tensor Operations on GPU – более широкий фреймворк, чем cuBLAS. Работает с тензорами произвольной размерности (не только матрицы 2D).
-
Поддерживает:
Элементные операции (+, *, relu)
Свертки (conv)
Тензорные произведения (einsum)
Перестановки осей (transpose, reshape)
Индексированные операции
Используется в PyTorch, JAX, TensorFlow, NVIDIA Triton. Внутри использует cuBLASLt и cuDNN для базовых операций.
CUTLASS — CUDA Templates for Linear Algebra Subroutines — это библиотека шаблонов C++, написанная на уровне PTX/ассемблера, которая позволяет писать собственные высокооптимизированные ядра для GEMM, конволюций, attention и других операций.
Использует аппаратные возможности (Tensor Cores, FMA, dp4a, add4 и т.д.) максимально эффективно.
Основана на метапрограммировании C++ — шаблоны, специализации, макросы.
Не использует cublasGemmEx — вместо этого она генерирует PTX-код на лету.
Давайте посмотрим на скорость выполнения матричных операций двумя функциями библитотеки cuBLAS: cublasHgemm и cublasGemmEx. Они обе выполняют классическую операцию GEMM (General Matrix Multiplication)
C = α·A·B + β·C
cublasHgemm — это функция, которая выполняет матричное умножение только для данных типа half (FP16).
cublasGemmEx — это универсальная функция для выполнения обобщённого матричного умножения с поддержкой произвольных типов данных и точности вычислений.
cublasHgemm считается устаревшей функцией, но всё ещё используется. Она выглядит попроще, поэтому начнем с неё. Её работа определяется установленным предварительно режимом вычислений MathMode. Посмотрим зависимость производительности FP16 наших «пациентов» от этого режима. Дальнейшие значения производительности скорее не в терафлопсах, а в «попугаях» – только для сравнения между собой.
MathMode |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
DEFAULT |
0.98 |
3.17 |
2.82 |
50.53 |
TENSOR_OP |
0.98 |
3.17 |
2.82 |
47.09 |
PEDANTIC |
15.25 |
24.35 |
22.17 |
13.08 |
TF32_TENSOR_OP |
0.98 |
3.17 |
2.82 |
49.85 |
FP32_EMULATED_BF16X9 |
0.98 |
3.17 |
2.82 |
46.90 |
DISABLE_REDUCED_PREC. |
0.98 |
3.17 |
2.82 |
46.82 |
В режиме DEFAULT автоматически выбирается оптимальный режим, если GPU поддерживает Tensor Cores, то используются они. Для всех CMP он далеко не оптимальный…
В режиме TENSOR_OP используются Tensor Cores.
В режиме PEDANTIC запрещено использование Tensor Cores и FMA, выполняются чисто программные вычисления.
В режимах TF32_TENSOR_OP, FP32_EMULATED_BF16X9, DISABLE_REDUCED_PREC используются вычисления в FP32 и FMA.
Что мы видим? Использование тензорных ядер и FP32 вместо того, чтобы повысить, катастрофически снижает производительность CMP. Маркетологи Nvidia создали инструменты для «забивания гвоздей микроскопом». Хотя, возможно, всё не настолько кощунственно, но при этом хуже для нас. Возможно, на эти карты пошла отбраковка чипов с неисправными FMA и тензорными блоками, которым для устойчивой работы в десятки раз снизили тактовые частоты. Но я всё же склоняюсь к мнению, что с чипами не настолько всё плохо, но BIOS загружает сильно ухудшенные настройки в некоторые блоки и/или передает драйверам неверные тайминги операций.
Здесь тоже использование программной эмуляции на CMP сильно помогает. Естественно, для «нормальных» GPU результат обратный. Конечно, если бы, да кабы, да тензорные ядра на CMP работали бы нормально, любая из CMP уделала бы RTX 3060 (даже CMP 40HX), так как их теоретическая производительность более, чем втрое выше полученной. Обидно, да? Но мы всё же смогли поднять их со дна на более-менее удовлетворительную высоту.
Большинство приложений используют режим DEFAULT и, к сожалению, эти карты бодро рапортуют, что тензорные операции поддерживаются, и, в результате, всё работает очень медленно. А ведь CMP 50HX и CMP 90HX могут примерно половину от RTX 3060. Возможно, секрет нормальной работы llama.cpp (движка Ollama и LM Studio) как раз в том, что он умеет выбирать нужные режимы, а Stable Diffusion – нет.
Теперь cublasGemmEx, – она может обрабатывать FP16, FP32, FP64 и другие форматы чисел, в том числе и целых. Её работа определяется двумя параметрами: computeType – тип вычислений и cublasGemmAlgo – условный номер алгоритма вычислений.
computeType — тип данных, используемый для выполнения арифметических операций внутри GEMM
cublasGemmAlgo — это выбор алгоритма, по которому будет выполняться матричное умножение.
Он определяет:
Использовать ли Tensor Cores
Как организовать вычислительный паттерн (размер блока, порядок доступа к памяти)
Использовать ли CUDA Cores вместо Tensor Cores
Зависимость производительности при вычислениях матриц в FP16 от ComputeType и GemmAlgo
ComputeType |
GemmAlgo |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
16F |
DEFAULT |
0.97 |
3.2 |
2.8 |
48 |
0 |
- |
- |
2.8 |
48 |
|
1–4 |
13–15 |
22–24 |
2.8 |
48 |
|
5-23 |
- |
- |
2.8 |
48 |
|
TENSOR_OPs |
0.97–1.0 |
3.2–3.5 |
2.8 |
48 |
|
TENS. AUTOTUNE |
0.97 |
3.2 |
2.8 |
48 |
|
16F_PEDANTIC |
DEFAULT |
15 |
24 |
22 |
13 |
0 |
- |
- |
22 |
13 |
|
1–4 |
13–15 |
22–24 |
22 |
13 |
|
5-23 |
- |
- |
22 |
13 |
|
TENSOR_OPs 99 |
15 |
24 |
22 |
13 |
|
TENSOR_OPs |
0.96–1.0 |
3.2–3.5 |
22 |
13 |
|
TENS. AUTOTUNE |
15 |
24 |
22 |
13 |
|
32F_PEDANTIC |
DEFAULT |
7.7 |
0.43 |
0.71 |
7.9 |
0,1 |
- |
- |
0.71 |
7.9 |
|
2 |
4.6 |
0.43 |
0.71 |
7.9 |
|
3 |
6.9 |
0.43 |
0.71 |
7.9 |
|
4 |
5.3 |
0.43 |
0.71 |
7.9 |
|
5,6,10,11 |
7.3-7.7 |
0.43 |
0.71 |
7.9 |
|
18 |
4.4 |
0.43 |
0.71 |
7.9 |
|
19 |
5.6 |
0.43 |
0.71 |
7.9 |
|
20 |
6.5 |
0.43 |
0.71 |
7.9 |
|
7-9,12-17,21-23 |
- |
- |
0.71 |
7.9 |
|
TENSOR_OPs 99 |
7.6 |
0.43 |
0.71 |
7.9 |
|
TENSOR_OPs |
0.95–1.0 |
3.2–3.5 |
0.71 |
7.9 |
|
TENS. AUTOTUNE |
7.7 |
0.43 |
0.71 |
7.9 |
|
32F 32F_FAST_16F 32F_FAST_16BF 32F_FAST_TF32 32F_EMULATED_16BFX9 |
DEFAULT |
0.96 |
3.2 |
2.7 |
25 |
0,1 |
- |
- |
2.7 |
25 |
|
2 |
4.6 |
0.43 |
2.7 |
25 |
|
3 |
6.9 |
0.43 |
2.7 |
25 |
|
4 |
5.3 |
0.43 |
2.7 |
25 |
|
5,6,10,11 |
7.4-7.7 |
0.43 |
2.7 |
25 |
|
18 |
4.4 |
0.43 |
2.7 |
25 |
|
19 |
5.5 |
0.43 |
2.7 |
25 |
|
20 |
6.5 |
0.43 |
2.7 |
25 |
|
7-9,12-17,21-23 |
- |
- |
2.7 |
25 |
|
TENSOR_OPs |
0.95–1.0 |
3.2–3.5 |
2.7 |
25 |
|
TENS. AUTOTUNE |
0.96 |
3.2 |
2.7 |
25 |
Лучшим выбором для CMP здесь будет ComputeType = 16F_PEDANTIC, в котором запрещено использование Tensor Cores и FMA. Здесь производительность CMP 50HX и CMP 90HX примерно равна половине от RTX 3060. CMP 40HX не блещет, но и не около нуля – около четверти от RTX 3060.
При ComputeType = 16F на CMP 40HX и CMP 50HX можно пользоваться режимами 1-4 с аналогичным результатом но алгоритм DEFAULT включает тензоры, что для наших CMP недопустимо.
Любыми режимами, где используется внутреннее накопление данных в FP32, на CMP 50HX и CMP 90HX пользоваться нельзя, а на CMP 40HX не стоит без крайней необходимости, особенно с выбором алгоритма DEFAULT. А многие программы, скорее всего, как раз и будут выбирать DEFAULT, ведь «нормальной» видеокарте всё равно.
Посмотрим повнимательнее на CMP 90HX. Ей приходится хуже всего: в любых режимах, кроме PEDANTIC у неё обязательно включаются тормозные тензоры, а в режиме 32F_PEDANTIC, хотя их и нет, но тут всё убивает уже известная нам FMA. Хотя CMP 50HX во всех режимах с использованием 32F не лучше, но для неё ещё есть надежда…
Для поддержки CMP в приложениях необходимо добавить проверку на vendor ID и model ID этих карт и при их обнаружении принудительно использовать режим CUBLAS_COMPUTE_16F_PEDANTIC.
Теперь посмотрим на такую же таблицу для вычисления матриц в FP32
ComputeType |
GemmAlgo |
CMP 40HX |
CMP 50HX |
CMP 90HX |
RTX 3060 |
32F_PEDANTIC |
DEFAULT |
7.1 |
0.42 |
0.64 |
7.0 |
0,1 |
- |
- |
0.64 |
7.3 |
|
2 |
4.5 |
0.40 |
0.64 |
7.3 |
|
3 |
6.9 |
0.33 |
0.64 |
7.3 |
|
4 |
5.3 |
0.40 |
0.64 |
7.3 |
|
5 |
7.3 |
0.33 |
0.64 |
7.3 |
|
6 |
7.5 |
0.25 |
0.64 |
7.3 |
|
7 |
4.4 |
0.40 |
0.64 |
7.3 |
|
8 |
6.7 |
0.40 |
0.64 |
7.3 |
|
9 |
5.2 |
0.40 |
0.64 |
7.3 |
|
10 |
7.1 |
0.40 |
0.64 |
7.3 |
|
11 |
7.3 |
0.33 |
0.64 |
7.3 |
|
18 |
4.4 |
0.42 |
0.64 |
7.2 |
|
19 |
5.5 |
0.40 |
0.64 |
7.3 |
|
20 |
6.1 |
0.40 |
0.64 |
7.2 |
|
21 |
4.0 |
0.43 |
0.64 |
7.2 |
|
22,23 |
5.2–5.5 |
0.40–0.42 |
0.64 |
7.3 |
|
TENSOR_OP 99 |
7.1 |
0.42 |
0.64 |
7.3 |
|
TENS. 110,112 |
0.98 |
2.6 |
0.64 |
7.3 |
|
TENSOR_OPs |
0.98 |
2.0 |
0.64 |
7.3 |
|
T. AUTOTUNE |
- |
- |
- |
- |
|
32F 32F_FAST_16F 32F_FAST_16BF 32F_FAST_TF32 |
DEFAULT |
0.98 |
2.0 |
1.8 |
19 |
0,1 |
- |
- |
1.8 |
19 |
|
2 |
4.5 |
0.40 |
1.8 |
19 |
|
3 |
6.9 |
0.33 |
1.8 |
19 |
|
4 |
5.3 |
0.40 |
1.8 |
19 |
|
5 |
7.2 |
0.33 |
1.8 |
19 |
|
6 |
7.4 |
0.25 |
1.8 |
19 |
|
7 |
4.4 |
0.40 |
1.8 |
19 |
|
8 |
6.8 |
0.40 |
1.8 |
19 |
|
9 |
5.2 |
0.40 |
1.8 |
19 |
|
10 |
7.1 |
0.40 |
1.8 |
19 |
|
11 |
7.2 |
0.33 |
1.8 |
19 |
|
18 |
4.5 |
0.42 |
1.8 |
19 |
|
19 |
5.5 |
0.40 |
1.8 |
19 |
|
20 |
6.2 |
0.40 |
1.8 |
19 |
|
21 |
4.0 |
0.43 |
1.8 |
19 |
|
22,23 |
5.2–5.5 |
0.40–0.42 |
1.8 |
19 |
|
TENS. 110,112 |
0.98 |
2.6 |
1.8 |
19 |
|
TENSOR_OPs |
0.98 |
2.0 |
1.8 |
19 |
|
T. AUTOTUNE |
- |
- |
- |
- |
Как и ожидалось, с матричными вычислениями FP32 на CMP 50HX и CMP 90HX всё плохо. Была надежда, что CMP 50HX в режиме 32F_PEDANTIC сможет показать неплохой результат, но нет… Хотя это странно, ведь заявлено, что в режиме PEDANTIC запрещено использование Tensor Cores и FMA, а при этом она должна выдавать примерно половину производительности RTX 3060. Но что-то где-то в cuBLAS не срослось и, судя по скорости, там используется FMA. Чёрт!
CMP 40HX во многих режимах показала неплохую для неё производительность – примерно треть от RTX 3060. По сочетанию производительности FP16 и FP32, она «порезана» меньше всего. Но что же там со Stable Diffusion? Ведь CMP 40HX по своим возможностям не в 20 раз хуже RTX 3060.
Очевидно, что где-то выбираются неподходящие для неё алгоритмы вычислений. И это тот самый DEFAULT. Посмотрим на последнюю таблицу для FP32. Конечно, никому в голову не пришло использовать режим 32F_PEDANTIC, а при использовании любых других режимов и выборе алгоритма DEFAULT она именно в 20 раз проигрывает RTX 3060.
А если посмотреть на предыдущую таблицу производительности при вычислениях матриц в FP16, то там дела ещё хуже: при выборе алгоритма DEFAULT в неподходящих для неё режимах она проигрывает в 25-50 раз. Так вот, зачем понадобилась опция «--no-half»… Да не FP16 с ней отключать надо, а режим вычислений переключать на 16F_PEDANTIC, это гораздо эффективнее! И при вычислении матриц в FP32 обязательно включать 32F_PEDANTIC. Тогда она должна работать не в 20, а раза в 3 медленнее RTX 3060 и при этом лучше, чем CMP 50HX и CMP 90HX. Так что, если поковыряться в ComfyUI и Stable Diffusion, они с ней подружатся. Хотя, возможно, здесь ещё и INT8 сказывается.
Итак, CMP 40HX весьма неплоха, просто не все умеют её правильно готовить.
CMP 50HX всё же подает надежды, что её FP32 можно научиться использовать и тогда она станет лучшей.
А вот CMP 90HX выглядит всё безнадежнее. Прямо как микроскоп, подхо��ящий только для забивания гвоздей… Хотя, пусть это будет вызовом для энтузиастов, ведь если взломать её защиту, это будет реальный прорыв! У кого ещё есть идеи, как её «оживить»?