[英]why tanh has different results in OpenCL and C++ function
这是我的OpenCL代码。
#include <iostream>
#include <cmath>
#include <CL/cl.hpp>
int main(){
std::vector<cl::Platform> all_platforms;
cl::Platform::get(&all_platforms);
cl::Platform default_platform=all_platforms[0];
std::vector<cl::Device> all_devices;
default_platform.getDevices(CL_DEVICE_TYPE_ALL, &all_devices);
cl::Device default_device=all_devices[0];
std::cout<< "Using device: "<<default_device.getInfo<CL_DEVICE_NAME>()<<"\n";
cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(default_platform)(), 0};
cl::Context context = cl::Context(CL_DEVICE_TYPE_ALL, properties);
cl::Program::Sources sources;
std::string kernel_code=
" void __kernel simple_tanh(__global const float *A, __global float *B){ "
" B[get_global_id(0)]=tanh(A[get_global_id(0)]); "
" } ";
sources.push_back({kernel_code.c_str(),kernel_code.length()});
cl::Program program(context,sources);
if(program.build({default_device})!=CL_SUCCESS){
std::cout<<" Error building: "<<program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(default_device)<<"\n";
exit(1);
}
cl::Buffer buffer_A(context,CL_MEM_READ_WRITE,sizeof(float));
cl::Buffer buffer_B(context,CL_MEM_READ_WRITE,sizeof(float));
float A[1]; A[0] = 0.0595172755420207977294921875000000000000f;
cl::CommandQueue queue(context,default_device);
queue.enqueueWriteBuffer(buffer_A,CL_TRUE,0,sizeof(float),A);
queue.finish();
cl::Kernel kernel=cl::Kernel(program,"simple_tanh");
kernel.setArg(0,buffer_A);
kernel.setArg(1,buffer_B);
queue.enqueueNDRangeKernel(kernel,cl::NullRange,cl::NDRange(1),cl::NullRange);
queue.finish();
float B[1];
queue.enqueueReadBuffer(buffer_B,CL_TRUE,0,sizeof(float),B);
printf("result: %.40f %.40f\n", tanh(A[0]), B[0]);
return 0;
}
在我使用以下cmd进行编译之后:g ++ -std = c ++ 0x hello.cc -lOpenCL -o hello,然后运行它。 我得到了tanh函数的不同结果。
使用设备:大溪地结果:0.0594470988394579374913817559900053311139 0.0594470985233783721923828125000000000000
第一个是cpu结果,第二个是OpenCL函数。 我应该信任哪一个?
当编译器(opencl)无法对内核进行向量化时,生成的指令可能是标量类型。 然后,x87 FPU计算80位。 SSE的精度更可与GPU媲美,因此内核中需要float4
或float8
,这样编译器才能产生与GPU精度更接近的SSE / AVX。
通常,英特尔的opencl编译器可实现更好的矢量化(至少对于某些旧CPU)。 您正在使用哪种实现? 即使GPU之间也可能存在差异,但是它们都遵守不超过ULP限制的规则。 如果您需要GPU(和SSE / AVX)更高的精度,那为什么不编写自己的系列扩展功能呢? 但这会使学习变得非常缓慢,但至少比单个FPU快。
你的CPU是多少? 您正在使用什么opencl平台? 您是否使用某些分析器软件或某些内核分析器检查了生成的内核代码?
最重要的是,您不应该这样做:
cl::NDRange(1)
除非是出于学习目的。 这将有%99的内核启动开销,%1的数据复制开销以及接近零的计算延迟。 也许这就是为什么它使用80位FPU而不是SSE(在CPU上)的原因。 尝试计算8个ndrange值的多个值,或在内核中使用float8
类型,以使编译器使用矢量化指令。
当全局ndrange值是数百万时,它将对学习时间产生重大影响,而不是需要反复迭代。 如果CPU可以在一天内完成1M次迭代的学习,那么即使GPU需要进行1000万次迭代,GPU也可以在1小时内完成学习。 Transcandental函数具有较高的计算数据比,因此,如果您使用更多函数,则与CPU的加速比会更高。
如果您导出自己的系列扩展功能以实现更高的精度,那么在此令人尴尬的并行内核代码中,它仍将比单个CPU内核快得多。
如果神经网络只有几个神经元,那么也许您可以同时进行N个网络训练并选择最佳的学习者(如果学习具有任何随机性)? 因此,它选择比CPU更好的结果吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.