将CUDA PRINTF重定向到C 流

Redirecting CUDA printf to a C++ stream

本文关键字:重定向 CUDA PRINTF      更新时间:2023-10-16

问题语句

我正在研究一个大型项目,该项目使用记录仪进行调试。由于我喜欢保留某些CUDA内核中发生的事情的踪迹,因此我试图找到一种将我的CUDA内核printf重定向到stringstream(或任何流)的方法,然后可以将其转发到Logger。h2>可能的解决方案

我通过使用以下代码设法进行操作:

#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 - startn");
    /// 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 - endn");
    cudaMemcpy(h_B, d_B, size, cudaMemcpyDeviceToHost);
    cudaFree(d_A);
    cudaFree(d_B);
    free (h_A);
    free (h_B);
}

我使用以下问题来实现这一目标:

  • 将COUT和STDOUT重定向到C 的字符串进行单位测试
  • 如何在freopen(" out.txt"," a",stdout)之后将输出重定向回屏幕

运行程序,我们进入控制台:

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()运行格式字符串和参数,并将结果传递到Logger。