Image

API NumPy на GPU?

Он уже представлен компанией Nvidia и называется cuNumeric.

Делиться

2dcc946fe0b011e3f4dfb9c31ededf08

Это ли будущее численных вычислений на Python?

В конце прошлого года NVIDIA сделала важное заявление о будущем численных вычислений на базе Python. Не удивлюсь, если вы его пропустили. В конце концов, каждое второе заявление любой компании, занимающейся ИИ, как тогда, так и сейчас, кажется невероятно важным.

В этом объявлении была представлена библиотека cuNumeric готовая замена повсеместной библиотеке NumPy, созданной на основе фреймворка Legate .

Кто такие Nvidia?

Большинство людей, вероятно, знают Nvidia по её сверхбыстрым чипам, которые обеспечивают работу компьютеров и центров обработки данных по всему миру. Возможно, вы также знакомы с харизматичным генеральным директором Nvidia, любителем кожаных курток, Дженсеном Хуаном, который в последнее время, кажется, появляется на сцене каждой конференции по искусственному интеллекту.

Многие не знают, что Nvidia также разрабатывает и создаёт инновационные архитектуры устройств и соответствующее программное обеспечение. Один из её самых ценных продуктов — Compute Unified Device Architecture.   (CUDA) . CUDA — это собственная платформа параллельных вычислений и модель программирования NVIDIA. С момента своего запуска в 2007 году она превратилась в комплексную экосистему, включающую драйверы, среду выполнения, компиляторы, математические библиотеки, инструменты отладки и профилирования, а также образы контейнеров. Результатом является тщательно настроенный аппаратный и программный цикл, который позволяет графическим процессорам NVIDIA оставаться в центре современных высокопроизводительных задач и задач искусственного интеллекта.

Что такое Легат?

Legate — это среда выполнения с открытым исходным кодом от NVIDIA, которая позволяет запускать знакомые библиотеки Python для анализа данных (NumPy, cuNumeric, API в стиле Pandas, разреженные ядра линейной алгебры и т. д.) на многоядерных процессорах, узлах с одним или несколькими графическими процессорами и даже в многоузловых кластерах без изменения кода Python. Она преобразует высокоуровневые операции с массивами в граф мелкозернистых задач и передает этот граф среде выполнения C++ Legion , которая планирует задачи, разбивает данные на разделы и перемещает фрагменты между центральными процессорами, графическими процессорами и сетевыми соединениями.

Вкратце, Legate позволяет привычным одноузловым библиотекам Python прозрачно масштабироваться до многоузловых машин с несколькими GPU.

Что такое cuNumeric?

cuNumeric — это готовая замена NumPy, операции с массивами которой выполняются движком задач Legate и ускоряются на одном или нескольких графических процессорах NVIDIA (или, при отсутствии графического процессора, на всех ядрах центрального процессора). На практике, cuNumeric устанавливается и требует лишь изменения одной строки импорта, чтобы начать использовать его вместо обычного кода NumPy. Например…

# старый импорт numpy как np … … # новый импорт cupynumeric как np # все остальное остается прежним … …

… и запустите свой скрипт на терминале с помощью команды legate.

В фоновом режиме cuNumeric преобразует каждый ваш вызов NumPy, например, np.sin, np.linalg.svd, сложную индексацию, широковещательную рассылку, свертки и т. д., в задачи Legate. Эти задачи будут…

  1. Разделите ваши массивы на плитки, размер которых соответствует размеру памяти графического процессора.
  2. Запланируйте каждую плитку на наилучшем доступном устройстве (GPU или CPU).
  3. Совмещение вычислений с коммуникацией, когда рабочая нагрузка охватывает несколько графических процессоров или узлов.
  4. Автоматически переносите тайлы на NVMe/SSD, когда ваш набор данных превышает объем оперативной памяти графического процессора.

Поскольку API cuNumeric практически полностью копирует API NumPy, существующий научный или аналитический код можно масштабировать с ноутбука до кластера с несколькими GPU без необходимости его переписывания.

Преимущества производительности

Итак, всё это кажется замечательным, правда? Но это имеет смысл только в том случае, если это приведёт к ощутимому повышению производительности по сравнению с использованием NumPy, и Nvidia убедительно заявляет об этом. Поскольку специалисты по анализу данных, инженеры машинного обучения и специалисты по работе с данными обычно активно используют NumPy, мы понимаем, что это может быть критически важным аспектом систем, которые мы разрабатываем и поддерживаем.

У меня нет кластера графических процессоров или суперкомпьютера, чтобы проверить это, но мой настольный ПК оснащен графическим процессором Nvidia GeForce RTX 4070, и мы собираемся использовать его для проверки некоторых заявлений Nvidia.

(база) tom@tpr-desktop:~$ nvidia-smi вс июн 15 15:26:36 2025 +——————————————————————————————+ | Версия драйвера NVIDIA-SMI 565.75: 566.24 Версия CUDA: 12.7 | |——————————————+————————+————————+ | Сохранение имени графического процессора-M | Отображение идентификатора шины A | Энергозависимый некорр. ECC | | Температура вентилятора Производительность Питание:Использование/Емкость | Использование памяти | GPU-Util Вычисления M. | | | | MIG M. | |=============================================+=========================+=========================| | 0 NVIDIA GeForce RTX 4070 Ti Вкл | 00000000:01:00.0 Вкл | Н/Д | | 32% 29C P8 9 Вт / 285 Вт | 1345 МБ / 12282 МБ | 2% По умолчанию | | | Н/Д | +——————————————+————————+———————-+ +——————————————————————————————+ | Процессы: | | GPU GI CI PID Тип Имя процесса GPU Память | | ID ID Использование | |================================================================================================| | Запущенных процессов не найдено | +—————————————————————————————-+

Я установлю cuNumeric и NumPy на свой компьютер для проведения сравнительных тестов. Это поможет нам оценить точность заявлений Nvidia и понять разницу в производительности между двумя библиотеками.

Настройка среды разработки.

Как всегда, я предпочитаю настраивать отдельную среду разработки для запуска тестов. Таким образом, никакие действия в этой среде не повлияют на другие мои проекты. На момент написания статьи cuNumeric недоступен для установки в Windows, поэтому я буду использовать WSL2 Ubuntu для Windows.

Я буду использовать Miniconda для настройки своей среды, но вы можете использовать любой удобный для вас инструмент.

$ conda create cunumeric-env python=3.10 -c conda-forge $ conda active cunumeric-env $ conda install -c conda-forge -c legate cupynumeric $ conda install -c conda-forge ucx cuda-cudart cuda-version=12

Пример кода 1 — Простое умножение матриц

Умножение матриц — это основа математических операций, лежащих в основе многих систем искусственного интеллекта, поэтому имеет смысл сначала попробовать именно эту операцию.

Обратите внимание, что во всех моих примерах я запускаю фрагменты кода NumPy и cuNumeric пять раз подряд и усредняю время, затраченное на каждый из них. Я также выполняю «шаг разогрева» графического процессора перед замером времени, чтобы учесть накладные расходы, такие как JIT-компиляция.

import time import gc import argparse import sys def benchmark_numpy(n, runs): «»»Запускает тест производительности умножения матриц с использованием стандартного NumPy на ЦП.»»» import numpy as np print(f»— NumPy (CPU) Benchmark —«) print(f»Умножение двух матриц {n}×{n} ({runs} runs)n») # 1. Сгенерируйте данные ОДИН РАЗ перед циклом синхронизации. print(f»Генерация двух случайных матриц {n}x{n} на ЦП…») A = np.random.rand(n, n).astype(np.float32) B = np.random.rand(n, n).astype(np.float32) # 2. Выполните один несинхронизированный прогревочный запуск. print(«Выполнение разминочного прогона…») _ = np.matmul(A, B) print(«Разминка завершена.n») # 3. Выполнение хронометрируемых прогонов. times = [] for i in range(runs): start = time.time() # Операция хронометрируется. Оператор @ — удобное сокращение для np.matmul. C = A @ B end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.4f}s») del C # Очищаем матрицу результата gc.collect() avg = sum(times) / len(times) print(f»nNumPy average: {avg:.4f}sn») return avg def benchmark_cunumeric(n, runs): «»»Запускает тест умножения матриц с использованием cuNumeric на GPU.»»» import cupynumeric as cn import numpy as np # Импортируем numpy для канонической синхронизации print(f»— cuNumeric (GPU) Benchmark —«) print(f»Умножение двух матриц {n}×{n} ({runs} runs)n») # 1. Сгенерировать данные ОДИН РАЗ на GPU перед циклом синхронизации. print(f»Generating two {n}x{n} random matrix on GPU…») A = cn.random.rand(n, n).astype(np.float32) B = cn.random.rand(n, n).astype(np.float32) # 2. Выполните важный несинхронизированный прогревочный прогон для JIT-компиляции. print(«Performing warm-up run…») C_warmup = cn.matmul(A, B) # Лучший способ синхронизации: принудительно отправить копию обратно на ЦП. _ = np.array(C_warmup) print(«Warm-up complete.n») # 3. Выполните хронометрированные прогоны. times = [] for i in range(runs): start = time.time() # Запустите операцию на ГП C = A @ B # Синхронизируйте, преобразуя результат в массив NumPy на стороне хоста. np.array(C) end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.4f}s») del C gc.collect() avg = sum(times) / len(times) print(f»ncuNumeric average: {avg:.4f}sn») return avg if __name__ == «__main__»: parser = argparse.ArgumentParser( description=»Сравнительный анализ умножения матриц на NumPy (CPU) и cuNumeric (GPU).» ) parser.add_argument( «-n», «—n», type=int, default=3000, help=»Размер матрицы (nxn)» ) parser.add_argument( «-r», «—runs», type=int, default=5, help=»Количество запусков измерения времени» ) parser.add_argument( «—cunumeric», action=»store_true», help=»Запустить версию cuNumeric (GPU)» ) args, unknown = parser.parse_known_args() # Логика диспетчера, если args.cunumeric или «—cunumeric» в неизвестном: benchmark_cunumeric(args.n, args.runs) else: benchmark_numpy(args.n, args.runs)

Запуск NumPy-компоненты использует стандартный синтаксис командной строки Python example1.py . Для запуска с использованием Legate синтаксис более сложный. Он отключает автоматическую настройку Legate и затем запускает скрипт example1.py в Legate с одним ЦП, одной видеокартой и нулевым количеством потоков OpenMP, используя бэкенд cuNumeric.

Вот что получилось.

(cunumeric-env) tom@tpr-desktop:~$ python example1.py — NumPy (ЦП) Benchmark — Умножение двух матриц 3000×3000 (5 запусков) Генерация двух случайных матриц 3000×3000 на ЦП… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,0976 с Запуск 2: время = 0,0987 с Запуск 3: время = 0,0957 с Запуск 4: время = 0,1063 с Запуск 5: время = 0,0989 с Среднее значение NumPy: 0,0994 с (cunumeric-env) tom@tpr-desktop:~$ LEGATE_AUTO_CONFIG=0 legate —cpus 1 —gpus 1 —omps 0 example1.py —cunumeric [0 — 7f2e8fcc8480] 0,000000 {5}{module_config}: Модуль numa не может обнаружить ресурсы. [0 — 7f2e8fcc8480] 0,000000 {4}{topology}: невозможно открыть /sys/devices/system/node/ [0 — 7f2e8fcc8480] 0,000049 {4}{threads}: резервирование ('GPU ctxsync 0x55cd5fd34530') не может быть удовлетворено — cuNumeric (GPU) Benchmark — Умножение двух матриц 3000×3000 (5 запусков) Генерация двух случайных матриц 3000×3000 на GPU… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,0113 с Запуск 2: время = 0,0089 с Запуск 3: время = 0,0086 с Запуск 4: время = 0,0090 с Запуск 5: время = 0,0087 с Среднее арифметическое: 0,0093 с

Что ж, это впечатляющее начало. cuNumeric демонстрирует 10-кратное ускорение по сравнению с NumPy.

Предупреждения, выводимые Legate, можно игнорировать. Они носят информационный характер и указывают на то, что Legate не удалось найти информацию о структуре ЦП/памяти (NUMA) компьютера или о недостаточном количестве ядер ЦП для управления графическим процессором.

Пример кода 2 — Логистическая регрессия

Логистическая регрессия является основополагающим инструментом в науке о данных, поскольку она обеспечивает простой и интерпретируемый способ моделирования и прогнозирования бинарных результатов (да/нет, пройдено/не пройдено, щелчок/нет щелчка). В этом примере мы измерим, сколько времени требуется для обучения простого бинарного классификатора на синтетических данных. Для каждого из пяти запусков он сначала генерирует N образцов с D признаками (X) и соответствующий случайный вектор меток 0/1 (Y). Он инициализирует вектор весов w нулями, затем выполняет 500 итераций пакетного градиентного спуска: вычисляет линейные предсказания z = X.dot(w) , применяет сигмоиду p = 1/(1+exp(–z)) , вычисляет градиент grad = XTdot(p – y) / N , и обновляет веса с помощью w -= 0.1 * grad . Скрипт записывает прошедшее время для каждого запуска, очищает память и, наконец, выводит среднее время обучения.

import time import gc import argparse import sys # — Повторно используемая функция обучения — # Помещая цикл обучения в отдельную функцию, мы избегаем дублирования кода. # Аргумент `np` позволяет нам передавать модуль numpy или cupynumeric. def train_logistic_regression(np, X, y, iters, alpha): «»»Выполняет заданное количество итераций градиентного спуска.»»» # Убедитесь, что w запускается на правильном устройстве (ЦП или ГП) w = np.zeros(X.shape[1]) for _ in range(iters): z = X.dot(w) p = 1.0 / (1.0 + np.exp(-z)) grad = XTdot(p — y) / X.shape[0] w -= alpha * grad return w def benchmark_numpy(n_samples, n_features, iters, alpha): «»»Запускает тест логистической регрессии с использованием стандартного NumPy на ЦП.»»» import numpy as np print(f»— NumPy (ЦП) Benchmark —«) print(f»Обучение на {n_samples} выборках, {n_features} признаков для {iters} итерацийn») # 1. Сгенерировать данные ОДИН РАЗ перед циклом синхронизации. print(«Генерация случайного набора данных на ЦП…») X = np.random.rand(n_samples, n_features) y = (np.random.rand(n_samples) > 0.5).astype(np.float64) # 2. Выполнить один несинхронизированный прогревочный запуск. print(«Выполнение прогревательного запуска…») _ = train_logistic_regression(np, X, y, iters, alpha) print(«Прогрев завершен.n») # 3. Выполнить синхронизированные запуски. times = [] for i in range(args.runs): start = time.time() # Операция, время которой измеряется _ = train_logistic_regression(np, X, y, iters, alpha) end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.3f}s») gc.collect() avg = sum(times) / len(times) print(f»nNumPy average: {avg:.3f}sn») return avg def benchmark_cunumeric(n_samples, n_features, iters, alpha): «»»Запускает тест логистической регрессии с использованием cuNumeric на GPU.»»» import cupynumeric as cn import numpy as np # Также импортируйте numpy для канонической синхронизации print(f»— cuNumeric (GPU) Benchmark —«) print(f»Обучение на {n_samples} выборках, {n_features} признаках для {iters} итерацийn») # 1. Сгенерируйте данные ОДИН РАЗ на GPU перед циклом синхронизации. print(«Генерация случайного набора данных на GPU…») X = cn.random.rand(n_samples, n_features) y = (cn.random.rand(n_samples) > 0.5).astype(np.float64) # 2. Выполните важный несинхронизированный прогревочный прогон для JIT-компиляции. print(«Выполнение прогревательного прогона…») w_warmup = train_logistic_regression(cn, X, y, iters, alpha) # Лучшая практика для синхронизации: принудительно отправить копию обратно на CPU. _ = np.array(w_warmup) print(«Прогрев завершен.n») # 3. Выполните синхронизированные прогоны. times = [] for i in range(args.runs): start = time.time() # Запустить операцию на GPU w = train_logistic_regression(cn, X, y, iters, alpha) # Синхронизировать, преобразуя конечный результат обратно в массив NumPy. np.array(w) end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.3f}s») del w gc.collect() avg = sum(times) / len(times) print(f»ncuNumeric average: {avg:.3f}sn») return avg if __name__ == «__main__»: # Более надежная настройка разбора аргументов parser = argparse.ArgumentParser( description=»Тест логистической регрессии на NumPy (ЦП) по сравнению с cuNumeric (ГП).» ) # Гиперпараметры для модели parser.add_argument( «-n», «—n_samples», type=int, default=2_000_000, help=»Количество выборок данных» ) parser.add_argument( «-d», «—n_features», type=int, default=10, help=»Количество признаков» ) parser.add_argument( «-i», «—iters», type=int, default=500, help=»Количество итераций градиентного спуска» ) parser.add_argument( «-a», «—alpha», type=float, default=0.1, help=»Скорость обучения» ) # Контроль производительности parser.add_argument( «-r», «—runs», type=int, default=5, help=»Количество прогонов измерения времени» ) parser.add_argument( «—cunumeric», action=»store_true», help=»Запустить версию cuNumeric (GPU)» ) args, unknown = parser.parse_known_args() # Логика диспетчера if args.cunumeric или «—cunumeric» в неизвестном значении: benchmark_cunumeric(args.n_samples, args.n_features, args.iters, args.alpha) иначе: benchmark_numpy(args.n_samples, args.n_features, args.iters, args.alpha)

И результаты.

(cunumeric-env) tom@tpr-desktop:~$ python example2.py — NumPy (ЦП) Benchmark — Обучение на 2 000 000 образцах, 10 признаков на 500 итераций Генерация случайного набора данных на ЦП… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 12,292 с Запуск 2: время = 11,830 с Запуск 3: время = 11,903 с Запуск 4: время = 12,843 с Запуск 5: время = 11,964 с Среднее значение NumPy: 12,166 с (cunumeric-env) tom@tpr-desktop:~$ LEGATE_AUTO_CONFIG=0 legate —cpus 1 —gpus 1 —omps 0 example2.py —cunumeric [0 — 7f04b535c480] 0,000000 {5}{module_config}: Модуль numa не может обнаружить ресурсы. [0 — 7f04b535c480] 0,000000 {4}{topology}: невозможно открыть /sys/devices/system/node/ [0 — 7f04b535c480] 0,001149 {4}{threads}: резервирование ('GPU ctxsync 0x55fb037cf140') не может быть удовлетворено — cuNumeric (GPU) Benchmark — Обучение на 2000000 образцах, 10 признаков на 500 итераций Генерация случайного набора данных на GPU… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 1,964 с Запуск 2: время = 1,957 с Запуск 3: время = 1,968 с Запуск 4: время = 1,955 с Запуск 5: время = 1,960 с Среднее арифметическое: 1,961 с

Не столь впечатляюще, как наш первый пример, но ускорение в 5–6 раз для и без того быстрой программы NumPy — это нечто особенное.

Пример кода 3 — решение линейных уравнений

Этот скрипт измеряет время решения плотной системы уравнений линейной алгебры размером 3000×3000. Это фундаментальная операция в линейной алгебре, используемая для решения уравнения типа Ax = b, где A — гигантская сетка чисел (в данном случае матрица 3000×3000), а b — список чисел (вектор).

Цель — найти неизвестный список чисел x, при котором уравнение становится верным. Это ресурсоёмкая вычислительная задача, лежащая в основе многих научных симуляций, инженерных задач, финансовых моделей и даже некоторых алгоритмов искусственного интеллекта.

import time import gc import argparse import sys # Импорт sys для проверки аргументов # Примечание: импорт библиотек (numpy и cupynumeric) теперь выполняется *внутри* # соответствующих функций, чтобы разделить их и избежать ошибок импорта. def benchmark_numpy(n, runs): «»»Запускает тест линейного решения с использованием стандартного NumPy на ЦП.»»» import numpy as np print(f»— NumPy (ЦП) Benchmark —«) print(f»Решение {n}×{n} A x = b ({runs} runs)n») # 1. Сгенерировать данные ОДИН РАЗ перед циклом синхронизации. print(«Генерация случайной системы на ЦП…») A = np.random.randn(n, n).astype(np.float32) b = np.random.randn(n).astype(np.float32) # 2. Выполняем один прогревочный прогон без ограничения по времени. Это хорошая практика даже для ЦП, чтобы убедиться, что кэши прогреты, и любая однократная настройка выполнена. print(«Выполнение прогревательного прогона…») _ = np.linalg.solve(A, b) print(«Прогрев завершён.n») # 3. Выполняем прогоны с ограничением по времени. times = [] for i in range(runs): start = time.time() # Операция хронометрируется x = np.linalg.solve(A, b) end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.6f}s») # Очищаем результат для безопасного использования памяти del x gc.collect() avg = sum(times) / len(times) print(f»nNumPy average: {avg:.6f}sn») return avg def benchmark_cunumeric(n, runs): «»»Запускает тест линейного решения с использованием cuNumeric на GPU.»»» import cupynumeric as cn import numpy as np # Также import numpy для канонической синхронизации print(f»— cuNumeric (GPU) Benchmark —«) print(f»Solving {n}×{n} A x = b ({runs} runs)n») # 1. Сгенерировать данные ОДИН РАЗ на GPU перед циклом синхронизации. # Это гарантирует, что мы не будем хронометрировать передачу данных в нашем основном цикле. print(«Генерация случайной системы на GPU…») A = cn.random.randn(n, n).astype(np.float32) b = cn.random.randn(n).astype(np.float32) # 2. Выполнить важный несинхронизированный прогревочный прогон. Это обрабатывает JIT-компиляцию и другие одноразовые затраты на настройку GPU. print(«Выполнение прогревательного прогона…») x_warmup = cn.linalg.solve(A, b) # Лучший способ синхронизации: принудительно вернуть копию на CPU. _ = np.array(x_warmup) print(«Прогрев завершен.n») # 3. Выполнить хронометрированные прогоны. times = [] for i in range(runs): start = time.time() # Запустить операцию на GPU x = cn.linalg.solve(A, b) # Синхронизировать, преобразуя результат в массив NumPy на стороне хоста. # Это гарантированно блокирует выполнение до тех пор, пока GPU не завершит работу. np.array(x) end = time.time() duration = end — start times.append(duration) print(f»Run {i+1}: time = {duration:.6f}s») # Очищаем массив GPU result del x gc.collect() avg = sum(times) / len(times) print(f»ncuNumeric average: {avg:.6f}sn») return avg if __name__ == «__main__»: # Более надежная настройка разбора аргументов parser = argparse.ArgumentParser( description=»Тест линейного решения на NumPy (CPU) и cuNumeric (GPU).» ) parser.add_argument( «-n», «—n», type=int, default=3000, help=»Размер матрицы (nxn)» ) parser.add_argument( «-r», «—runs», type=int, default=5, help=»Number of timing runs» ) # Используйте parse_known_args() для обработки возможных дополнительных аргументов из Legate args, unknown = parser.parse_known_args() # Логика диспетчера: проверьте, есть ли «—cunumeric» в командной строке # Это простой и эффективный способ переключения между режимами. если «—cunumeric» в sys.argv или «—cunumeric» в unknown: benchmark_cunumeric(args.n, args.runs) else: benchmark_numpy(args.n, args.runs)

Выходы.

(cunumeric-env) tom@tpr-desktop:~$ python example4.py — NumPy (ЦП) Тест — Решение задачи 3000×3000 A x = b (5 запусков) Генерация случайной системы на ЦП… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,133075 с Запуск 2: время = 0,126129 с Запуск 3: время = 0,135849 с Запуск 4: время = 0,137383 с Запуск 5: время = 0,138805 с Среднее значение NumPy: 0,134248 с (cunumeric-env) tom@tpr-desktop:~$ LEGATE_AUTO_CONFIG=0 legate —cpus 1 —gpus 1 —omps 0 example4.py —cunumeric [0 — 7f29f42ce480] 0,000000 {5}{module_config}: Модуль numa не может обнаружить ресурсы. [0 — 7f29f42ce480] 0,000000 {4}{topology}: невозможно открыть /sys/devices/system/node/ [0 — 7f29f42ce480] 0,000053 {4}{threads}: резервирование ('GPU ctxsync 0x562e88c28700') не может быть удовлетворено — cuNumeric (GPU) Benchmark — Решение задачи 3000×3000 A x = b (5 запусков) Генерация случайной системы на GPU… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,009685 с Запуск 2: время = 0,010043 с Запуск 3: время = 0,009966 с Запуск 4: время = 0,009739 с Запуск 5: время = 0,009383 с Среднее арифметическое: 0,009763 с

Это потрясающий результат. Счётчик Nvidia cuNumeric в 100 раз быстрее, чем счётчик NumPy.

Пример кода 4 — Сортировка

Сортировка — фундаментальная часть всего, что происходит в вычислительной технике, а современные компьютеры настолько быстры, что большинство разработчиков даже не задумываются об этом. Но давайте посмотрим, насколько использование cuNumeric может изменить эту распространённую операцию. Мы отсортируем большой (30 000 000) одномерный массив чисел.

# benchmark_sort.py import time import sys import gc # Размер массива n = 30_000_000 # 30 миллионов элементов def benchmark_numpy(): import numpy as np print(f»Сортировка массива из {n} элементов с помощью NumPy (5 запусков)n») times = [] for i in range(5): data = np.random.randn(n).astype(np.float32) start = time.time() _ = np.sort(data) end = time.time() duration = end — start times.append(duration) print(f»Выполнить {i+1}: time = {duration:.6f}s») del data gc.collect() avg = sum(times) / len(times) print(f»nNumPy average: {avg:.6f}sn») def benchmark_cunumeric(): import cupynumeric as np print(f»Сортировка массива из {n} элементов с помощью cuNumeric (5 запусков)n») times = [] for i in range(5): data = np.random.randn(n).astype(np.float32) start = time.time() _ = np.sort(data) # Принудительная синхронизация с GPU _ = np.linalg.norm(np.zeros(())) end = time.time() duration = end — start times.append(duration) print(f»Выполнение {i+1}: time = {duration:.6f}s») del data gc.collect() _ = np.linalg.norm(np.zeros(())) avg = sum(times) / len(times) print(f»ncuNumeric average: {avg:.6f}sn») if __name__ == «__main__»: if «—cunumeric» в sys.argv: benchmark_cunumeric() иначе: benchmark_numpy()

Выходы.

(cunumeric-env) tom@tpr-desktop:~$ python example5.py — NumPy (ЦП) Benchmark — Сортировка массива из 30000000 элементов (5 запусков) Создание случайного массива на ЦП… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,588777 с Запуск 2: время = 0,586813 с Запуск 3: время = 0,586745 с Запуск 4: время = 0,586525 с Запуск 5: время = 0,583783 с Среднее значение NumPy: 0,586529 с —————————— (cunumeric-env) tom@tpr-desktop:~$ LEGATE_AUTO_CONFIG=0 legate —cpus 1 —gpus 1 —omps 0 example5.py —cunumeric [0 — 7fd9e4615480] 0,000000 {5}{module_config}: Модуль numa не может обнаружить ресурсы. [0 — 7fd9e4615480] 0,000000 {4}{topology}: невозможно открыть /sys/devices/system/node/ [0 — 7fd9e4615480] 0,000082 {4}{threads}: резервирование ('GPU ctxsync 0x564489232fd0') не может быть удовлетворено — cuNumeric (GPU) Benchmark — Сортировка массива из 30000000 элементов (5 запусков) Создание случайного массива на GPU… Выполнение прогрева… Прогрев завершен. Запуск 1: время = 0,010857 с Запуск 2: время = 0,007927 с Запуск 3: время = 0,007921 с Запуск 4: время = 0,008240 с Запуск 5: время = 0,007810 с Среднее арифметическое: 0,008551 с ——————————-

Еще одно чрезвычайно впечатляющее выступление от cuNumeric и Legate.

Краткое содержание

В этой статье представлена библиотека cuNumeric от NVIDIA, разработанная как высокопроизводительная и удобная замена NumPy . Ключевой вывод заключается в том, что специалисты по работе с данными могут ускорить свой существующий код Python на графических процессорах NVIDIA с минимальными усилиями, часто просто изменив одну строку импорта и запустив скрипт с помощью команды «legate» .

В основе технологии лежат два основных компонента:

  1. Legate: среда выполнения с открытым исходным кодом от NVIDIA, которая автоматически преобразует высокоуровневые операции Python в задачи. Она интеллектуально распределяет эти задачи между одним или несколькими графическими процессорами, обеспечивает разбиение данных на разделы, управляет памятью (даже при необходимости сбрасывает данные на диск) и оптимизирует обмен данными.
  2. cuNumeric: пользовательская библиотека, зеркалирующая API NumPy. Когда вы делаете вызов, например, np.matmul(), cuNumeric преобразует его в задачу для движка Legate, которая выполняется на графическом процессоре.

Мне удалось подтвердить заявления Nvidia о производительности, выполнив четыре бенчмарк-теста на моем настольном ПК (с графическим процессором NVIDIA RTX 4070 Ti), сравнив стандартный NumPy на центральном процессоре с cuNumeric на графическом процессоре.

Результаты демонстрируют значительный прирост производительности для cuNumeric:

  • Умножение матриц: примерно в 10 раз быстрее, чем NumPy.
  • Обучение логистической регрессии: примерно в 6 раз быстрее.
  • Решение линейных уравнений: ускорение в 100+ раз .
  • Сортировка большого массива: еще одно огромное улучшение, работающее примерно в 70 раз быстрее.

В заключение я показал, что cuNumeric успешно справляется со своими задачами, делая огромную вычислительную мощь графических процессоров доступной для более широкого сообщества специалистов по данным Python без необходимости сложного обучения или полного переписывания кода.

Дополнительную информацию и ссылки на соответствующие ресурсы можно найти в оригинальном объявлении Nvidia на cuNumeric здесь.

Источник: towardsdatascience.com

✅ Найденные теги: API, новости
Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых