运行 在 Delphi 程序中使用 CUDA 调用的 C 函数
Run C function with CUDA calls in Delphi program
我的objective是有一个Delphi(或freepascal)代码,它将调用C函数func 喜欢这个:
C/Cuda 文件:
/* this is the "progcuda.cu" file */
#include <stdio.h>
__global__ void foo(int *a, int *b, int *c, int n){
/*
add all the vector's element
*/
}
void func(int *a, int *b, int *c,int n){
int *da,*db,*dc;
cudaMalloc(&da, n*sizeof(int));
cudaMalloc(&db, n*sizeof(int));
cudaMalloc(&dc, n*sizeof(int));
cudaMemcpy(da,a,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(db,b,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(dc,c,sizeof(int)*n,cudaMemcpyHostToDevice);
foo<<<1,256>>>(da,db,dc);
cudaMemcpy(c,dc,sizeof(int),cudaMemcpyDeviceToHost);
/* do other stuff and call another Host and Device functions*/
return;
}
pascal 主文件:
// this is the "progpas.pas" file
program progpas;
{$mode objfpc}{$H+}
uses unitpas;
var
...
begin
...
func(a, b, c, len);
...
end.
帕斯卡单元文件:
// this is the "unitpas.pas" file
unit unitpas;
{$link progcuda.o}
interface
uses ctypes;
procedure func(a, b, c : cpint32 , n:cint32); cdecl; external;
procedure foo(a, b, c : cpint32 , n:cint32);cdecl; external;
implementation
end.
我找到了 post Programming CUDA using Delphi or FreePascal
,但它显示了更多在 delphi.
中对 CUDA 进行编程的方法
我不想在 Delphi 中对 CUDA 进行编程,我想在 CUDA 中使用纯 C/C++ 代码进行编程,并且只在 delphi 中调用该 C 函数。
有什么问题?
我怎样才能 link .cu 代码到 delphi 代码?
我正在使用 linux ubuntu 16.04 LTS,但如有必要,我在 windows 中也有 CUDA 和 VS。
注意:如果你们能详细解释如何操作,将会有所帮助(pascal 和 linking 文件的新手)
我已经尝试生成 .o 目标文件并link它在免费的 Pascal 中
$ nvcc progcuda.cu -c -o progcuda.o
然后 $fpc progpas.pas
但它在 linking.
处失败
注意:我曾经尝试过 link 将 C 代码生成的普通 .o 转换为 Pascal 代码,使用 gcc 和 freepascal 编译器,它成功了,但是如果我使用 nvcc 而不是 gcc 并重命名.cu 的扩展(仍然是相同的代码),linking 失败。
注意:堆栈溢出中的新帐户,我还不能回复答案。
我对 Delphi 和 FreePascal 一无所知,但我知道 CUDA、C 和 C++,所以也许我的解决方案也适合你。
我将用一个简单的问题来演示它:
f.cu
的内容:
int f() { return 42; }
main.c
的内容:
extern int f();
int main() {
return f();
}
以下作品:
$ gcc -c -xc f.cu # need -xc to tell gcc it's a C file
$ gcc main.c f.o
(no errors emitted)
现在,当我们尝试用 nvcc
替换 gcc
时:
$ nvcc -c f.cu
$ gcc main.c f.o
/tmp/ccI3tBM1.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
f.o: In function `__cudaUnregisterBinaryUtil()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x52): undefined reference to `__cudaUnregisterFatBinary'
f.o: In function `__nv_init_managed_rt_with_module(void**)':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x6d): undefined reference to `__cudaInitModule'
f.o: In function `__sti____cudaRegisterAll()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0xa9): undefined reference to `__cudaRegisterFatBinary'
collect2: error: ld returned 1 exit status
这里的问题是 nvcc
在编译 f.cu
时添加了对 CUDA runtime API 中的一些符号的引用,这些符号必须 linked最后的可执行文件。我的 CUDA 安装在 /opt/cuda
中,因此我将使用它,但您必须将其替换为系统上安装 CUDA 的任何位置。所以如果我们 link libcudart.so
在编译库时我们得到:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
/tmp/ccUeDZcb.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
collect2: error: ld returned 1 exit status
这看起来好多了,没有奇怪的错误,但仍然找不到函数 f
。那是因为 nvcc
将 f.cu
视为 C++ 文件,因此它在创建目标文件时会进行名称修改,我们必须指定我们希望 f
具有 C,而不是 C++ link年龄(在此处查看更多信息:http://en.cppreference.com/w/cpp/language/language_linkage)。
为此,我们必须像这样修改 f.cu
:
extern "C" int f() { return 42; }
现在当我们这样做时:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
(no errors emitted)
我希望你设法修改它以使用你的语言。
编辑:我尝试了一个更复杂的例子:
// f.cu
#include <stdio.h>
__global__ void kernel() {
printf("Running kernel\n");
}
extern "C" void f() {
kernel<<<1, 1>>>();
// make sure the kernel completes before exiting
cudaDeviceSynchronize();
}
// main.c
extern void f();
int main() {
f();
return 0;
}
编译时得到:
f.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
要修复它,您还需要将标准 C++ 库添加到 linker 标志:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart -lstdc++
$ ./a.out
Running kernel
我按照@Goran Flegar 的解释修复了这些文件:
将 extern "C" int func(...);
添加到 .cu 文件。然后尝试 compile/link .cu 代码,但没有设备调用(但有设备代码),并且一切正常。
但是当我添加一个设备调用(foo<<<Nb,Nt>>>(...)
)并编译时:
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
我得到:
Free Pascal Compiler version 3.0.4 [2017/12/13] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling prog1.pas
Linking sum.exe
/usr/bin/ld: aviso: link.res contém seções de saída; você se esqueceu -T?
/usr/bin/ld: sum.o: undefined reference to symbol '_Unwind_Resume@@GCC_3.0'
//lib/x86_64-linux-gnu/libgcc_s.so.1: error adding symbols: DSO missing from command line
prog1.pas(16,1) Error: Error while linking
prog1.pas(16,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
所以仍然缺少一些库。
解决方案:
发现将 stdc++ 和 gcc_s lib 链接到 pascal 解决了编译问题。
unit unitpas;
// file "unitpas.pas"
{$LINK progcuda.o}
{$LINKLIB c}
{$LINKLIB cudart}
{$linklib stdc++}
{$linklib gcc_s}
interface
uses ctypes;
function func(x,y: cint32): cint32; cdecl; external;
implementation
end.
运行
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
一切正常。
我的objective是有一个Delphi(或freepascal)代码,它将调用C函数func 喜欢这个:
C/Cuda 文件:
/* this is the "progcuda.cu" file */
#include <stdio.h>
__global__ void foo(int *a, int *b, int *c, int n){
/*
add all the vector's element
*/
}
void func(int *a, int *b, int *c,int n){
int *da,*db,*dc;
cudaMalloc(&da, n*sizeof(int));
cudaMalloc(&db, n*sizeof(int));
cudaMalloc(&dc, n*sizeof(int));
cudaMemcpy(da,a,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(db,b,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(dc,c,sizeof(int)*n,cudaMemcpyHostToDevice);
foo<<<1,256>>>(da,db,dc);
cudaMemcpy(c,dc,sizeof(int),cudaMemcpyDeviceToHost);
/* do other stuff and call another Host and Device functions*/
return;
}
pascal 主文件:
// this is the "progpas.pas" file
program progpas;
{$mode objfpc}{$H+}
uses unitpas;
var
...
begin
...
func(a, b, c, len);
...
end.
帕斯卡单元文件:
// this is the "unitpas.pas" file
unit unitpas;
{$link progcuda.o}
interface
uses ctypes;
procedure func(a, b, c : cpint32 , n:cint32); cdecl; external;
procedure foo(a, b, c : cpint32 , n:cint32);cdecl; external;
implementation
end.
我找到了 post Programming CUDA using Delphi or FreePascal ,但它显示了更多在 delphi.
中对 CUDA 进行编程的方法我不想在 Delphi 中对 CUDA 进行编程,我想在 CUDA 中使用纯 C/C++ 代码进行编程,并且只在 delphi 中调用该 C 函数。
有什么问题? 我怎样才能 link .cu 代码到 delphi 代码?
我正在使用 linux ubuntu 16.04 LTS,但如有必要,我在 windows 中也有 CUDA 和 VS。
注意:如果你们能详细解释如何操作,将会有所帮助(pascal 和 linking 文件的新手)
我已经尝试生成 .o 目标文件并link它在免费的 Pascal 中
$ nvcc progcuda.cu -c -o progcuda.o
然后 $fpc progpas.pas
但它在 linking.
注意:我曾经尝试过 link 将 C 代码生成的普通 .o 转换为 Pascal 代码,使用 gcc 和 freepascal 编译器,它成功了,但是如果我使用 nvcc 而不是 gcc 并重命名.cu 的扩展(仍然是相同的代码),linking 失败。
注意:堆栈溢出中的新帐户,我还不能回复答案。
我对 Delphi 和 FreePascal 一无所知,但我知道 CUDA、C 和 C++,所以也许我的解决方案也适合你。
我将用一个简单的问题来演示它:
f.cu
的内容:
int f() { return 42; }
main.c
的内容:
extern int f();
int main() {
return f();
}
以下作品:
$ gcc -c -xc f.cu # need -xc to tell gcc it's a C file
$ gcc main.c f.o
(no errors emitted)
现在,当我们尝试用 nvcc
替换 gcc
时:
$ nvcc -c f.cu
$ gcc main.c f.o
/tmp/ccI3tBM1.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
f.o: In function `__cudaUnregisterBinaryUtil()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x52): undefined reference to `__cudaUnregisterFatBinary'
f.o: In function `__nv_init_managed_rt_with_module(void**)':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x6d): undefined reference to `__cudaInitModule'
f.o: In function `__sti____cudaRegisterAll()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0xa9): undefined reference to `__cudaRegisterFatBinary'
collect2: error: ld returned 1 exit status
这里的问题是 nvcc
在编译 f.cu
时添加了对 CUDA runtime API 中的一些符号的引用,这些符号必须 linked最后的可执行文件。我的 CUDA 安装在 /opt/cuda
中,因此我将使用它,但您必须将其替换为系统上安装 CUDA 的任何位置。所以如果我们 link libcudart.so
在编译库时我们得到:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
/tmp/ccUeDZcb.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
collect2: error: ld returned 1 exit status
这看起来好多了,没有奇怪的错误,但仍然找不到函数 f
。那是因为 nvcc
将 f.cu
视为 C++ 文件,因此它在创建目标文件时会进行名称修改,我们必须指定我们希望 f
具有 C,而不是 C++ link年龄(在此处查看更多信息:http://en.cppreference.com/w/cpp/language/language_linkage)。
为此,我们必须像这样修改 f.cu
:
extern "C" int f() { return 42; }
现在当我们这样做时:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
(no errors emitted)
我希望你设法修改它以使用你的语言。
编辑:我尝试了一个更复杂的例子:
// f.cu
#include <stdio.h>
__global__ void kernel() {
printf("Running kernel\n");
}
extern "C" void f() {
kernel<<<1, 1>>>();
// make sure the kernel completes before exiting
cudaDeviceSynchronize();
}
// main.c
extern void f();
int main() {
f();
return 0;
}
编译时得到:
f.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
要修复它,您还需要将标准 C++ 库添加到 linker 标志:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart -lstdc++
$ ./a.out
Running kernel
我按照@Goran Flegar 的解释修复了这些文件:
将 extern "C" int func(...);
添加到 .cu 文件。然后尝试 compile/link .cu 代码,但没有设备调用(但有设备代码),并且一切正常。
但是当我添加一个设备调用(foo<<<Nb,Nt>>>(...)
)并编译时:
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
我得到:
Free Pascal Compiler version 3.0.4 [2017/12/13] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling prog1.pas
Linking sum.exe
/usr/bin/ld: aviso: link.res contém seções de saída; você se esqueceu -T?
/usr/bin/ld: sum.o: undefined reference to symbol '_Unwind_Resume@@GCC_3.0'
//lib/x86_64-linux-gnu/libgcc_s.so.1: error adding symbols: DSO missing from command line
prog1.pas(16,1) Error: Error while linking
prog1.pas(16,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
所以仍然缺少一些库。
解决方案:
发现将 stdc++ 和 gcc_s lib 链接到 pascal 解决了编译问题。
unit unitpas;
// file "unitpas.pas"
{$LINK progcuda.o}
{$LINKLIB c}
{$LINKLIB cudart}
{$linklib stdc++}
{$linklib gcc_s}
interface
uses ctypes;
function func(x,y: cint32): cint32; cdecl; external;
implementation
end.
运行
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
一切正常。