Programming
C++
C
Concurrent computing
Rust
Comments 44
0
Логичным продолжением был бы пост про, собственно, программирование с использованием CUDA…
+2
В корень проекта добавим папку src в которую поместим файлы с исходным кодом. В папке src создадим четыре файла: main.rs — код основной программы, dot.cpp — С++ binding (обертка для CUDA C), dot_gpu.h, dot_gpu.cu — файл в котором содержится код выполняемый на GPU.

По сути, CUDA используется, как и раньше использовалась. Статей, как использовать, на хабре достаточно.

+4
Одного поста не хватит, что бы описать программирование с использованием CUDA. В данной статье я лишь показал как использовать CUDA и Rust вместе. И как заметили выше сама по себе CUDA используется как обычно. А так, возможно, в дальнейшем еще опишу некоторые интересные моменты именно программирования CUDA в связке с Rust.
+3

Мой вопрос всё же был не о том, что такое extern "C". Просто C++ тут только ради <iostream> и cout. Можно было и на C всё написать вполне.

+1
.cpp не ради iostream и cout, .cpp вообще здесь не ради чего то, в нем нет глубокого смысла и необходимости, можно вообще все запихнуть в один файл .c и написать в нем и функцию обертку которая будет вызываться из Rust и CUDA кернел. Я это сделал просто так, дабы показать возможность линковки нескольких исходных файлов (.cpp в частности) в добавок ко всему.
+1
Не совсем так даже. Скорее дело в том, что nvcc на самом деле компилятор (расширенный) C++, а не C, по каким-то причинам.
+1
Этот туториал на самом деле подойдёт для большинства других сишных библиотек.
+1
По сути да. И не только сишных, и с++ — шных тоже. В случае с CUDA самое главное это прописать пути к исполняемым файлам, что бы Rust корректно подгрузил их как динамическую либу.
0
что бы Rust корректно подгрузил их как динамическую либу.

Раст ничего никуда не подгружает. Проблема в отсутствии прописанных путей/кривой линковке. С растом это никак не связано.
+2
1. То есть непосредственно логику вычисления на GPU на расте написать не получится?
2. Есть ли под макось что-нибудь вроде стаба/мока для cuda? Я хочу разрабатывать и тестить на маке, но запускать на убунте где у меня 1080TI
0
1. Нет, не получится. Код для вычисления на GPU все так же пишется на CUDA C. Да и честно говоря не вижу смысла переносить CUDA на Rust если достаточно в данном случае написать кернел для необходимых вычислений, вызвать этот кернел из Rust и результат вернуть обратно.

2. Не совсем понял при чем тут stub/mock и макось. Если это то о чем я подумал, то оно вроде вообще не имеет привязки к операционной системе. Поэтому все что вы хотите сделать, все возможно. На Rust/С/С++/CUDA можно писать и запускать на разных платформах, главное собрать правильно.
0
Вообщем да, вы правы, видел подобное, например тут и тут. Но честно говоря, я так не делал, мне проще и понятней написать кернел код на чистом CUDA C. Да и даже в статье, что я выше скинул говорится, что на данный момент поддержка nvptx и CUDA в частности делается с большими костылями и не совсем стабильная. Если есть еще какая то новая инфа по этому поводу буду рад ее услышать.
+1
Не могу ничего сказать по поводу AMDGPU, посмотрите в сторону OpenCL если вам нужно более платформо-независимое решение.
0

Я имел в виду аналог амдшных hcc и HIP для C++, только для Rust.
SPIR-V интереснее, т.к. более переносимо, но должно быть сложнее в реализации (hcc, как и rustc использует LLVM).

0
вызвать этот кернел из Rust и результат вернуть обратно.

А где у вас вызов кернела из rust?
+3
Раз уж на то пошло, почему было не сделать безопасную функцию, которая принимает 2 вектора, проверяет их длины и потом вызывает unsafe функцию?
В данной реализации это выглядит как пишем на С на Расте.
+1
Так и надо сделать если писать полноценное приложение. Код из данного туториала конечно нельзя использовать в реальных задачах, да и зачем, ведь он просто считает произведение векторов, ничего полезного по сути он не делает. Это всего лишь небольшой пример, инструкция, как соединить Rust и CUDA. Далее на основе этого уже можно брать и писать «правильный» и «безопасный» unsafe код, оборачивать еще дополнительно во все то, что нужно.
0
Это не вызов кернела. Это вызов си-функции из раста. К куде, кернелу и прочему не имеет никакого отношения.

А вызов кернела вот:

	dot__<<<blocksPerGrid, threadsPerBlock>>>(dev_v1, dev_v2, dev_res, (int)N);
+1
Конечно, формально это вызов си-функции которая вызывает кернел, из раста нарямую нет смысла вызывать функцию кернела, просто потому, что помимо самого кернела, если вы посмотрите внимательно в файл dot_gpu.cu, есть предшествующий код в виде инициализации памяти на GPU, копирование переменных в GPU и тд, это все уже относится к СUDA.

Так что вызов си-функции обвязки из раста в которой находится CUDA-зависимый код в том числе вызов самого кернела и которая возвращает результат работы кернела, можно грубо назвать «вызовом кернела из раста».
0
формально это вызов си-функци

Это просто вызов си-функции. Какая она — это неважно.

что помимо самого кернела, если вы посмотрите внимательно в файл dot_gpu.cu, есть предшествующий код в виде инициализации памяти на GPU

Я знаю, строчка упомянутая мною именно оттуда.

это все уже относится к СUDA.

Это обычный сишный рантайм. К тому же это неважно, ведь даже если мы определяем по границе cuda, то границей является gpu_dot. И даже если принять вашу логику — кернелом будет gpu_dot, но вы её не вызываете — вы вызываете обёртку над gpu_dot.

Так что вызов си-функции обвязки из раста в которой находится CUDA-зависимый код в том числе вызов самого кернела и которая возвращает результат работы кернела, можно грубо назвать «вызовом кернела из раста».

Нельзя. Это вызов обёртки dot, которая вызывает обёртку, которая вызывает кернел. dot никоим образом не вызвает кернел.

Поэтому как это не называй — это обычный вызов сишной функции из раста. Этот вызов никак не связан с cuda, вообще никак.
0
Нельзя. Это вызов обёртки dot, которая вызывает обёртку, которая вызывает кернел. dot никоим образом не вызвает кернел.


Эту обертку поверх обертки можно убрать, не принципиально, тут уже на ваш вкус и зависит от архитектурного решения вашего приложения. В статье приведен лишь пример не более.

Поэтому как это не называй — это обычный вызов сишной функции из раста. Этот вызов никак не связан с cuda, вообще никак.


Да, это обычный вызов сишной функции в которой мы можем писать CUDA код (а можем и не писать, можем ее вообще не вызывать), спорить не буду, можете называть этот вызов функции как вам удобно)
0
Эту обертку поверх обертки можно убрать, не принципиально, тут уже на ваш вкус и зависит от архитектурного решения вашего приложения. В статье приведен лишь пример не более.

Это ничего не изменит.

Да, это обычный вызов сишной функции в которой мы можем писать CUDA код (а можем и не писать, можем ее вообще не вызывать), спорить не буду, можете называть этот вызов функции как вам удобно)

Ну это принципиальный момент. По-сути из статьи можно выкинуть cuda и ничего не изменится, т.к. статья чисто про вызов сишной функции из раста.
+1
Это ничего не изменит.


Изменит)

И даже если принять вашу логику — кернелом будет gpu_dot, но вы её не вызываете — вы вызываете обёртку над gpu_dot.


Если принять мою логику то gpu_dot будет кернелом а уже эту функцию я могу вызвать из раст) Так что если принять мою логику то это изменит кое что.

Ну это принципиальный момент. По-сути из статьи можно выкинуть cuda и ничего не изменится, т.к. статья чисто про вызов сишной функции из раста.


Этот туториал в том числе можно использовать как инструкцию для вызова обычного си кода из раст, в комментариях это уже упоминалось, посмотрите выше. В статье рассказывается как писать и собирать Rust + С + CUDA C.
0
Изменит)

Нет, это не будет вызовом кернела. Это доказано мною выше.

Если принять мою логику то gpu_dot будет кернелом а уже эту функцию я могу вызвать из раст) Так что если принять мою логику то это изменит кое что.

Это логика несостоятельна. К тому же, зачем задним числом пытаться что-то изменять? Пока у вас gpu_dot не вызвается из раста — всё эти рассуждения не имеют смысла, т.к. вы показывали не это.

Этот туториал в том числе можно использовать как инструкцию для вызова обычного си кода из раст, в комментариях это уже упоминалось, посмотрите выше. В статье рассказывается как писать и собирать Rust + С + CUDA C.


Какое отношение к вызову си кода из раст имеет куда? Никакого. Как минимум вы должны написать «вызов си кода из раста НА ПРИМЕРЕ куды», но опять же — это будет полной глупостью, т.к. куда тут вообще не при делах.

Тоже самое и со сборкой. «сборка С++ кода через cargo на примере куда», но опять же — куда тут никаким образом не относится к теме.

В статье рассказывается как писать и собирать Rust + С + CUDA C.

Опять же, неверно. В статье не рассказывается как писать на С++ + cuda, а даже если бы и рассказывалось — причём тут раст? Так и пишите «как писать на С++ + cuda».

Я вам дам правильный заголовок. «пишем на С++ + cuda c» + «вызываем написанный таким образом код из раста». То, что написано у вас — неверно.
0
Когда будете писать свою статью назовете её как захотите. Я больше спорить с вами не буду, потому что у меня сложилось впечатление, что вы просто придираетесь к словам и к постановке предложений, ищите какой то тайный смысл в них.
-2
Ну т.е. статья — это чисто желтуха для «похайпить» на популярном базворде. Никакой куды нет, никакого раста нет, никакого вызова кернелов нет. Статье «вызываем си-фукцию из раста» никто бы 100 плюсов не наставил бы.
0
Все с вами ясно, пустил в коменты на свою голову) вам заняться нечем? Так пишите свои статьи на чисто «похайпить», я вам что мешаю? Мне на это абсолютно все равно, не судите по себе других. Повторюсь, статья о том, как собрать Rust + С + CUDA и прописать зависимости, что бы все работало, а не о том как писать на CUDA, это все написано в самом начале. Это даже не статья, это небольшой туториал, все это так же отмечено в самом начале под заголовком, прочтите внимательно еще раз.
-1
Хорошо, про желтый заголовок я уже сказал — ответа не было. Пошло игнорирование. Теперь касательно текста:

И в качестве примера напишем небольшую программу на Rust для вычисления скалярного произведения векторов, вычисление скалярного произведения будет производиться на GPU с использованием CUDA C.

Где вы написали на rust программу для «вычисления скалярного произведения векторов»? Я отвечу — нигде. Вы написали программу вызова си-функции из rust.

Я слушаю про внимательность и тому подобное.
0
Где вы написали на rust программу для «вычисления скалярного произведения векторов»?

Открываем файл main.rs (.rs — означает, что он написан на языке Rust). Коментируем unsafe блок кода. Получаем такой файл:
main.rs
extern crate libc;
extern crate rand;

use libc::{c_float, size_t};
use rand::Rng;

const VEC_SIZE: usize = 10;
const MAX: f32 = 10.;
const MIN: f32 = 0.;


extern "C" {
  fn dot(v1: *mut c_float, v2: *mut c_float, N: size_t) -> c_float;
}

fn cpu_dot(v1: Vec<f32>, v2: Vec<f32>) -> f32 {
  let mut res: f32 = 0.;
  for i in 0..v1.len() {
    res += v1[i] * v2[i];
  }
  return res;
}

fn main() {
  let mut v1: Vec<f32> = Vec::new();
  let mut v2: Vec<f32> = Vec::new();
  let mut gpu_res: c_float;
  let mut cpu_res: f32 = 0.;

  let mut rng = rand::thread_rng();
  for _ in 0..VEC_SIZE {
    v1.push(rng.gen_range(MIN, MAX));
    v2.push(rng.gen_range(MIN, MAX));
  }

  println!("{:?}", v1);
  println!("{:?}", v2);

  println!("GPU computing started");
 // unsafe {
    //gpu_res = dot(v1.as_mut_ptr(), v2.as_mut_ptr(), VEC_SIZE);
//  }
  println!("GPU computing finished");
  println!("GPU dot product result: {}", gpu_res);
  
  cpu_res = cpu_dot(v1, v2);
  println!("CPU dot product result: {}", cpu_res);
}


в нем ищем функцию cpu_dot, которая вычисляет скалярное произведение двух векторов на языке Rust (dot product на английском означает скалярное произведение), формулу скалярного произведения я тоже привел.
Компилируем это и получаем простую программу для вычисления скалярного произведения на Rust.
Я отвечу — нигде.

Уверены? Берете свои слова обратно? Или сейчас вы скажете, что ее я не сам написал?
Я слушаю про внимательность и тому подобное.

Слушайте: читайте пожалуйста внимательно. Не надо придираться к словам.

И может хватит уже? Мне надоело повторять одно и то же, вы меня все равно не хотите слушать.
-2
Какая наивность, какое враньё.

в нем ищем функцию cpu_dot, которая вычисляет скалярное произведение двух векторов на языке Rust


И получаем файл, т.к. спастил я цитату не полностью:

И в качестве примера напишем небольшую программу на Rust для вычисления скалярного произведения векторов, вычисление скалярного произведения будет производиться на GPU


вычисление скалярного произведения будет производиться на GPU

Здесь уточнение, как именно будет происходить вычисление «скалярного произведения» в «программу на Rust».

А что вы пытаетесь мне подсунуть?
cpu_dot

Который вы только что придумали, но самом деле вы то имели ввиду другое, и я это знаю, и я это доказал выше.

0
и я это доказал выше.

поздравляю) удачи вам, обсуждение можно считать закрытым.
+2
Так просто — не получится. Это надо компилятор переписывать. На GPU вычисляется только часть кода, который ещё туда надо как-то загрузить. CUDA — только один из вариантов, есть ещё OpenCL, который чуть более переносимый, но тоже со своими проблемами.

Архитектура GPU немного другая, соответственно, код там нужен другой. Цикл примерно такой, host-приложение пишется на любом языке (чаще C), подготавливает данные, программу для GPU — отправляет туда, ждёт результата, потом вытягивает результат, и, например, печатает в терминал (или GUI/веб-сервис, или куда ещё вам надо). Цикл может повторяться несколько раз, и во время ожидания результата с GPU можно фактически ещё чем-то полезным заниматься в других потоках… тут не уверен — на CUDA не писал ничего сложнее простеньких приложений, скидывающих результат в консоль или csv-табличку, или рисующих график, например — поэтому необходимости такой не было.
+1
Это надо компилятор переписывать.

Бекенд, на самом деле.


Я году в 2016-м или 2017-м игрался с оффлоадингом вычислений из кода на плюсах на видеокарту, ну чтобы я писал вот обычный какой-нибудь цикл для перемножения двух векторов, а clang мне генерировал бинарь для CPU + код для GPU + код для связи между ними. Там даже что-то получалось и уходило на видеокарту, но производительность у этого решения была отвратная, я и забил. Может, сейчас получше стало.

0
Возможно, вектора слишком маленькие, а результат слишком большой. Может банально больше времени тратиться на пересылку данных на GPU + обратно, и смешное время на собственно перемножение. Перемножение (или сложение) векторов идёт как пример hello world, потому что его проще написать.
0
Я, увы, уже не помню подробностей, но масштабы размерностей там были порядка миллиона. Ручками написанный хелловорлд отрабатывал несопоставимо быстрее.

Собственно, это призвано проиллюстрировать, что эффективная автовекторизация, автопараллелизация, автооффлоадинг — это сложно.
Only those users with full accounts are able to leave comments. , please.