Я работаю над большим проектом, который использует журнал для отладки. Поскольку мне нравится следить за тем, что происходит в некоторых ядрах CUDA, я попытался найти способ перенаправить printf
моих ядер CUDA в stringstream
(или любой поток), который затем может быть перенаправлен в регистратор.
Мне удалось сделать это с помощью следующего кода:
#include <cuda.h>
#include <stdio.h>
#include <unistd.h> // dup
#include <iostream>
#include <sstream> // stringstream
#include <fstream> // ofstream
char* output_file = "printf_redirect.log";
__global__ void printf_redirect(int* src, int* res)
{
res[threadIdx.x] = threadIdx.x;
printf(" %i: Hello World!\n", res[threadIdx.x]);
}
int main()
{
using namespace std;
const uint N = 2;
// Note: dummy arrays are not actually used, but this should prevent the
// compiler from discarding the printf in the kernel.
int *d_A, *d_B, *h_A, *h_B;
size_t size = N * sizeof (int);
cudaMalloc (&d_A, size);
cudaMalloc (&d_B, size);
h_A = (int*) malloc (size);
h_B = (int*) malloc (size);
cudaMemcpy (d_A, h_A, size, cudaMemcpyHostToDevice);
std::cout << "std::cout - start" << std::endl;
printf ("stdout - start\n");
/// REGULAR PRINT
// Print to regular stdout
std::cout << "Output to stdout:" << std::endl;
printf_redirect<<<1,1>>> (d_A, d_B);
cudaDeviceSynchronize ();
/// REDIRECTION TO STRINGSTREAM
std::stringstream ss;
// Redirect std::cout to a stringstream
std::streambuf* backup_cout = std::cout.rdbuf ();
std::cout.rdbuf (ss.rdbuf ());
// Redirect stdout to a buffer
char buf[1024] = "";
int backup_stdout = dup (fileno (stdout));
freopen ("/dev/null", "w", stdout);
setbuf (stdout, buf);
std::cout << "Redirected output:" << std::endl;
printf_redirect<<<1,N>>> (d_A, d_B);
cudaDeviceSynchronize ();
// Add CUDA buffer to a stringstream
ss << buf;
// Write stringstream to file
std::ofstream outFile;
outFile.open (output_file);
outFile << ss.str ();
outFile.close ();
/// RESET REDIRECTION
// Redirect back to initial stdout
fflush (stdout);
setbuf (stdout, NULL);
fclose (stdout);
FILE *fp = fdopen (backup_stdout, "w");
fclose (stdout);
*stdout = *fp;
// Redirect back to initial std::cout
std::cout.rdbuf (backup_cout);
std::cout << "std::cout - end" << std::endl;
printf ("stdout - end\n");
cudaMemcpy(h_B, d_B, size, cudaMemcpyDeviceToHost);
cudaFree(d_A);
cudaFree(d_B);
free (h_A);
free (h_B);
}
Для этого я использовал следующие вопросы:
Запустив программу, мы попадаем в консоль:
std::cout - start
stdout - start
Output to stdout:
0: Hello World!
std::cout - end
stdout - end
И в printf_redirect.log
:
Redirected output:
0: Hello World!
1: Hello World!
Есть ли более простой способ добиться этого? (например, скрытая опция CUDA или хитрый трюк C / C ++)
Обратите внимание, что окончательное решение окажется в служебном классе, чтобы сделать его менее подробным в реальном коде.
Сторона устройства printf()
вызывает неявную сериализацию печатаемых потоков, поэтому вы, вероятно, не захотите использовать ее в производственном коде.
Сторона устройства printf()
работает с тем, чтобы ядро копировало сообщения в предварительно выделенный кольцевой буфер. При неявной или явной синхронизации устройства (cudaDeviceSynchronize ()) CUDA создает дамп любого содержимого в буфере для stdout
и затем очищает это.
Вы можете просто реализовать собственное устройство printf()
, Его производительность, вероятно, будет не хуже, чем встроенная. Единственным недостатком является то, что вам придется передавать кольцевой буфер ядру и добавлять вызов для его обработки после возвращения ядра.
Реализация будет примерно такой:
Создать буфер с комнатой для фиксированного числа printf()
форматирование строк и связанных с ними 32-битных или 64-битных параметров.
Создать функцию устройства, которая использует atomicInc()
отслеживать текущее местоположение печати и берет строку форматирования и параметры и копирует их в текущее местоположение.
Передайте кольцевой буфер ядру, которое затем передает его функции устройства вместе с параметрами печати.
Создайте функцию хоста, которая принимает кольцевой буфер, запускает строки форматирования и параметры на стороне хоста sprintf()
и передает результаты в регистратор.
Других решений пока нет …