- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
原始问题
我有以下内核执行不均匀节点的插值,我想优化它:
__global__ void interpolation(cufftDoubleComplex *Uj, double *points, cufftDoubleComplex *result, int N, int M)
{
int i = threadIdx.x + blockDim.x * blockIdx.x;
int PP;
double P;
const double alfa=(2.-1./cc)*pi_double-0.01;
double phi_cap_s;
cufftDoubleComplex temp;
double cc_points=cc*points[i];
double r_cc_points=rint(cc*points[i]);
temp = make_cuDoubleComplex(0.,0.);
if(i<M) {
for(int m=0; m<(2*K+1); m++) {
P = (K*K-(cc_points-(r_cc_points+m-K))*(cc_points-(r_cc_points+m-K)));
if(P>0.) phi_cap_s = (1./pi_double)*((sinh(alfa*sqrt(P)))/sqrt(P));
if(P<0.) phi_cap_s = (1./pi_double)*((sin(alfa*sqrt(-P)))/sqrt(-P));
if(P==0.) phi_cap_s = alfa/pi_double;
PP = modulo((r_cc_points + m -K ),(cc*N));
temp.x = temp.x+phi_cap_s*Uj[PP].x;
temp.y = temp.y+phi_cap_s*Uj[PP].y;
}
result[i] = temp;
}
}
K 和 cc 是常量,points 包含节点,Uj 是要插值的值。 modulo 是一个基本上作为 % 工作的函数,但适本地扩展为负值。对于某种安排,内核调用需要 2.3ms。我已经确认最昂贵的部分是
if(P>0.) phi_cap_s = (1./pi_double)*((sinh(alfa*sqrt(P)))/sqrt(P));
if(P<0.) phi_cap_s = (1./pi_double)*((sin(alfa*sqrt(-P)))/sqrt(-P));
if(P==0.) phi_cap_s = alfa/pi_double;
这大约占总时间的 40%,并且
PP = modulo((r_cc_points + m -K ),(cc*N));
temp.x = temp.x+phi_cap_s*Uj[PP].x;
temp.y = temp.y+phi_cap_s*Uj[PP].y;
这大约占 60%。通过 Visual Profiler,我已验证前者的性能不受 if
语句存在的影响。请注意,我想要 double ,所以我避免使用 __exp() 解决方案。我怀疑,对于后者,“随机”内存访问 Uj[PP] 可能是造成那么多计算百分比的原因。关于减少计算时间的技巧或评论有什么建议吗?提前致谢。
以下评论和答案的版本
根据答案和评论中提供的建议,我最终得到了以下代码:
__global__ void interpolation(cufftDoubleComplex *Uj, double *points, cufftDoubleComplex *result, int N, int M)
{
int i = threadIdx.x + blockDim.x * blockIdx.x;
int PP;
double P,tempd;
const double alfa=(2.-1./cc)*pi_double-0.01;
cufftDoubleComplex temp = make_cuDoubleComplex(0.,0.);
double cc_points=cc*points[i];
double r_cc_points=rint(cc_points);
cufftDoubleComplex rtemp[(2*K+1)];
double phi_cap_s[2*K+1];
if(i<M) {
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
PP = modulo(((int)r_cc_points + m -K ),(cc*N));
rtemp[m] = Uj[PP]; //2
P = (K*K-(cc_points-(r_cc_points+(double)(m-K)))*(cc_points-(r_cc_points+(double)(m-K))));
if(P<0.) {tempd=rsqrt(-P); phi_cap_s[m] = (1./pi_double)*((sin(alfa/tempd))*tempd); }
else if(P>0.) {tempd=rsqrt(P); phi_cap_s[m] = (1./pi_double)*((sinh(alfa/tempd))*tempd); }
else phi_cap_s[m] = alfa/pi_double;
}
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
temp.x = temp.x+phi_cap_s[m]*rtemp[m].x;
temp.y = temp.y+phi_cap_s[m]*rtemp[m].y;
}
result[i] = temp;
}
}
特别是:1) 我将全局内存变量 Uj 移动到大小为 2*K+1 的寄存器 rtemp 数组(在我的例子中,K 是一个等于 6 的常数);2) 我将变量 phi_cap_s 移动到一个 2*K+1 大小的寄存器中;3)我用if ... else语句代替了之前用的三个if语句(条件P<0.和P>0.的出现概率相同);3)我为平方根定义了额外的变量;4)我用的是rsqrt而不是sqrt(据我所知,sqrt()被CUDA计算为1/rsqrt());
我一次添加每个新功能,验证对原始版本的改进,但我必须说,它们都没有给我任何相关的改进。
执行速度受限于:1) sin/sinh 函数的计算(大约 40% 的时间);有没有办法通过某种方式利用内在数学作为“起始猜测”来用 double 算术计算它们?2) 由于映射索引 PP,许多线程最终访问相同的全局内存位置 Uj[PP];避免它的一种可能性是使用共享内存,但这意味着强大的线程协作。
我的问题是。我完成了吗?即,是否有改进代码的方法?我使用 NVIDIA Visual Profiler 分析了代码,结果如下:
IPC = 1.939 (compute capability 2.1);
Global Memory Load Efficiency = 38.9%;
Global Memory Store Efficiency = 18.8%;
Warp Execution Efficiency = 97%;
Instruction Replay Overhead = 0.7%;
最后,我想指出这个讨论链接到 CUDA: 1-dimensional cubic spline interpolation in CUDA 上的讨论
使用共享内存的版本
我已经对使用共享内存进行了可行性研究。我考虑过 N=64
以便整个 Uj
适合共享内存。下面是代码(基本上是我原来的版本)
__global__ void interpolation_shared(cufftDoubleComplex *Uj, double *points, cufftDoubleComplex *result, int N, int M)
{
int i = threadIdx.x + blockDim.x * blockIdx.x;
int PP;
double P;
const double alfa=(2.-1./cc)*pi_double-0.01;
double phi_cap_s;
cufftDoubleComplex temp;
double cc_points=cc*points[i];
double r_cc_points=rint(cc*points[i]);
temp = make_cuDoubleComplex(0.,0.);
__shared__ cufftDoubleComplex Uj_shared[128];
if (threadIdx.x < cc*N) Uj_shared[threadIdx.x]=Uj[threadIdx.x];
if(i<M) {
for(int m=0; m<(2*K+1); m++) {
P = (K*K-(cc_points-(r_cc_points+m-K))*(cc_points-(r_cc_points+m-K)));
if(P>0.) phi_cap_s = (1./pi_double)*((sinh(alfa*sqrt(P)))/sqrt(P));
if(P<0.) phi_cap_s = (1./pi_double)*((sin(alfa*sqrt(-P)))/sqrt(-P));
if(P==0.) phi_cap_s = alfa/pi_double;
PP = modulo((r_cc_points + m -K ),(cc*N));
temp.x = temp.x+phi_cap_s*Uj_shared[PP].x;
temp.y = temp.y+phi_cap_s*Uj_shared[PP].y;
}
result[i] = temp;
}
}
结果同样没有显着改善,尽管这可能取决于输入数组的小尺寸。
详细的 PTXAS 输出
ptxas : info : Compiling entry function '_Z13interpolationP7double2PdS0_ii' for 'sm_20'
ptxas : info : Function properties for _Z13interpolationP7double2PdS0_ii
352 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
ptxas : info : Used 55 registers, 456 bytes cumulative stack size, 52 bytes cmem[0]
P 的值,对于第一次扭曲和 m=0
0.0124300933082964
0.0127183892149176
0.0135847002913749
0.0161796378170038
0.0155488126345702
0.0138890822153499
0.0121163187739057
0.0119998374528905
0.0131600831194518
0.0109574866163769
0.00962949548477354
0.00695850974164358
0.00446426651940612
0.00423369284281705
0.00632921297092537
0.00655137618976198
0.00810202954519923
0.00597974034698723
0.0076811348379735
0.00604267951733561
0.00402922460255439
0.00111841719893846
-0.00180949615796777
-0.00246283218698551
-0.00183256444286428
-0.000462696661685413
0.000725108980390132
-0.00126793006072035
0.00152263101649197
0.0022499598348702
0.00463681632275836
0.00359856091027666
模函数
__device__ int modulo(int val, int modulus)
{
if(val > 0) return val%modulus;
else
{
int P = (-val)%modulus;
if(P > 0) return modulus -P;
else return 0;
}
}
根据答案优化的模函数
__device__ int modulo(int val, int _mod)
{
if(val > 0) return val&(_mod-1);
else
{
int P = (-val)&(_mod-1);
if(P > 0) return _mod -P;
else return 0;
}
}
最佳答案
//your code above
cufftDoubleComplex rtemp[(2*K+1)] //if it fits into available registers, assumes K is a constant
if(i<M) {
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
PP = modulo((r_cc_points + m -K ),(cc*N));
rtemp[m] = Uj[PP]; //2
}
#pragma unroll
for(nt m=0; m<(2*K+1); m++) {
P = (K*K-(cc_points-(r_cc_points+m-K))*(cc_points-(r_cc_points+m-K)));
// 1
if(P>0.) phi_cap_s = (1./pi_double)*((sinh(alfa*sqrt(P)))/sqrt(P));
else if(P<0.) phi_cap_s = (1./pi_double)*((sin(alfa*sqrt(-P)))/sqrt(-P));
else phi_cap_s = alfa/pi_double;
temp.x = temp.x+phi_cap_s*rtemp[m].x; //3
temp.y = temp.y+phi_cap_s*rtemp[m].y;
}
result[i] = temp;
}
添加了 else if 和 else,因为这些条件是互斥的,如果可以的话,您应该在发生概率之后对语句进行排序。例如。如果 P<0。大多数时候,您应该首先评估它。
这会将请求的内存提取到多个寄存器,您之前所做的肯定会导致该线程阻塞,因为没有及时提供内存用于计算。请记住,如果一个线程在 warp 中阻塞,则整个 warp 都会被阻塞。如果就绪队列中没有足够的 warp,程序将阻塞,直到任何 warp 准备就绪。
这应该起作用的原因如下:
来自 GMEM 中内存的请求大约 >~400-600 个滴答。如果线程试图对当时不可用的内存执行操作,它将阻塞。这意味着如果每个内存请求都不存在于 L1-L2 中,则每个 warp 必须等待那个时间或更长时间才能继续。
我怀疑的是 temp.x+phi_cap_s*Uj[PP].x
正在这样做。通过暂存(第 2 步)每个内存传输到一个寄存器,然后继续下一阶段,您将通过允许您在传输内存时做其他工作来隐藏延迟。
当您到达第 3 步时,内存有望可用,否则您只需等待更少的时间。
如果rtemp
不适合登记册以实现 100% 的入住率,您可能必须分批进行。
您也可以尝试制作 phi_cap_s
放入一个数组并将其放入第一个循环中,如下所示:
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
//stage memory first
PP = modulo((r_cc_points + m -K ),(cc*N));
rtemp[m] = Uj[PP]; //2
P = (K*K-(cc_points-(r_cc_points+m-K))*(cc_points-(r_cc_points+m-K)));
// 1
if(P>0.) phi_cap_s[m] = (1./pi_double)*((sinh(alfa*sqrt(P)))/sqrt(P));
else if(P<0.) phi_cap_s[m] = (1./pi_double)*((sin(alfa*sqrt(-P)))/sqrt(-P));
else phi_cap_s[m] = alfa/pi_double;
}
#pragma unroll
for(nt m=0; m<(2*K+1); m++) {
temp.x = temp.x+phi_cap_s[m]*rtemp[m].x; //3
temp.y = temp.y+phi_cap_s[m]*rtemp[m].y;
}
表达
P = (K*K-(cc_points-(r_cc_points+(double)(m-K)))*(cc_points-(r_cc_points+(double)(m-K))));
可以分解为:
const double cc_diff = cc_points-r_cc_points;
double exp = cc_diff - (double)(m-K);
exp *= exp;
P = (K*K-exp);
这可能会减少使用的指令数量。
__global__ void interpolation(cufftDoubleComplex *Uj, double *points, cufftDoubleComplex *result, int N, int M)
{
int i = threadIdx.x + blockDim.x * blockIdx.x;
int PP;
double P,tempd;
cufftDoubleComplex rtemp[(2*K+1)];
double phi_cap_s[2*K+1];
if(i<M) {
const double cc_points=cc*points[i];
cufftDoubleComplex temp = make_cuDoubleComplex(0.,0.);
const double alfa=(2.-1./cc)*pi_double-0.01;
const double r_cc_points=rint(cc_points);
const double cc_diff = cc_points-r_cc_points;
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
PP = m-k; //reuse PP
double exp = cc_diff - (double)(PP); //stage exp to be used later, will explain
PP = modulo(((int)r_cc_points + PP ),(cc*N));
rtemp[m] = Uj[PP]; //2
exp *= exp;
P = (K*K-exp);
if(P<0.) {tempd=rsqrt(-P); phi_cap_s[m] = (1./pi_double)*((sin(alfa/tempd))*tempd); }
else if(P>0.) {tempd=rsqrt(P); phi_cap_s[m] = (1./pi_double)*((sinh(alfa/tempd))*tempd); }
else phi_cap_s[m] = alfa/pi_double;
}
#pragma unroll //unroll the loop
for(int m=0; m<(2*K+1); m++) {
temp.x = temp.x+phi_cap_s[m]*rtemp[m].x;
temp.y = temp.y+phi_cap_s[m]*rtemp[m].y;
}
result[i] = temp;
}
}
我所做的是在 if 语句内的所有计算中移动,以在计算和内存获取方面释放一些资源,不知道您在第一个 if 语句上的分歧 if(i<M)
.作为m-K
代码中出现了两次,我先放在了PP
在计算 exp
时使用和 PP
.
你还可以做的是尝试对指令进行排序,这样,如果你设置了一个变量,那么在下一次使用该变量之间尽可能多地编写指令,因为它需要大约 20 次控制设置到寄存器中。因此,我将常量 cc_diff 放在顶部,但是,由于这只是一个指令,它可能不会显示任何好处。
__device__ modulo(int val, int _mod) {
int p = (val&(_mod-1));// as modulo is always the power of 2
if(val < 0) {
return _mod - p;
} else {
return p;
}
}
因为我们有_mod
始终作为 2 的整数次幂(cc = 2, N = 64, cc*N = 128
),我们可以使用此函数代替 mod 运算符。这应该“快得多”。不过请检查一下,这样我的算术就正确了。来自Optimizing Cuda - Part II Nvidia第 14 页。
关于cuda - 使用非均匀节点优化 CUDA 内核插值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13941872/
我的问题:非常具体。我正在尝试想出解析以下文本的最简单方法: ^^domain=domain_value^^version=version_value^^account_type=account_ty
好吧,这就是我的困境: 我正在为 Reddit 子版 block 开发常见问题解答机器人。我在 bool 逻辑方面遇到了麻烦,需要一双更有经验的眼睛(这是我在 Python 中的第一次冒险)。现在,该
它首先遍历所有 y 值,然后遍历所有 x 值。我需要 X 和 y 同时改变。 For x = 3 To lr + 1 For y = 2 To lr anyl.Cells(x, 1)
假设我有一个包含 2 列的 Excel 表格:单元格 A1 到 A10 中的日期和 B1 到 B10 中的值。 我想对五月日期的所有值求和。我有3种可能性: {=SUM((MONTH(A1:A10)=
如何转换 Z-score来自 Z-distribution (standard normal distribution, Gaussian distribution)到 p-value ?我还没有找到
我正在重写一些 Javascript 代码以在 Excel VBA 中工作。由于在这个网站上搜索,我已经设法翻译了几乎所有的 Javascript 代码!但是,有些代码我无法准确理解它在做什么。这是一
我遇到过包含日期格式的时间戳日期的情况。然后我想构建一个图表,显示“点击”项目的数量“每天”, //array declaration $array1 = array("Date" => 0); $a
我是scala的新手! 我的问题是,是否有包含成员的案例类 myItem:Option[String] 当我构造类时,我需要将字符串内容包装在: Option("some string") 要么 So
我正在用 PHP 创建一个登录系统。我需要用户使用他或她的用户名或电子邮件或电话号码登录然后使用密码。因为我知道在 Java 中我们会像 email==user^ username == user 这
我在 C++ 项目上使用 sqlite,但是当我在具有文本值的列上使用 WHERE 时出现问题 我创建了一个 sqlite 数据库: CREATE TABLE User( id INTEGER
当构造函数是显式时,它不用于隐式转换。在给定的代码片段中,构造函数被标记为 explicit。那为什么在 foo obj1(10.25); 情况下它可以工作,而在 foo obj2=10.25; 情况
我知道这是一个主观问题,所以如果需要关闭它,我深表歉意,但我觉得它经常出现,让我想知道是否普遍偏爱一种形式而不是另一种形式。 显然,最好的答案是“重构代码,这样你就不需要测试是否存在错误”,但有时没有
这两个 jQuery 选择器有什么区别? 以下是来自 w3schools.com 的定义: [attribute~=value] 选择器选择带有特定属性,其值包含特定字符串。 [attribute*=
为什么我们需要CSS [attribute|=value] Selector根本当 CSS3 [attribute*=value] Selector基本上完成相同的事情,浏览器兼容性几乎相似?是否存在
我正在解决 regx 问题。我已经有一个像这样的 regx [0-9]*([.][0-9]{2})。这是 amont 格式验证。现在,通过此验证,我想包括不应提供 0 金额。比如 10 是有效的,但
我正在研究计算机科学 A 考试的样题,但无法弄清楚为什么以下问题的正确答案是正确的。 考虑以下方法。 public static void mystery(List nums) { for (
好的,我正在编写一个 Perl 程序,它有一个我收集的值的哈希值(完全在一个完全独立的程序中)并提供给这个 Perl 脚本。这个散列是 (string,string) 的散列。 我想通过 3 种方式对
我有一个表数据如下,来自不同的表。仅当第三列具有值“债务”并且第一列(日期)具有最大值时,我才想从第四列中获取最大值。最终值基于 MAX(DATE) 而不是 MAX(PRICE)。所以用简单的语言来说
我有一个奇怪的情况,只有错误状态保存到数据库中。当“状态”应该为 true 时,我的查询仍然执行 false。 我有具有此功能的 Controller public function change_a
我有一个交易表(针对所需列进行了简化): id client_id value 1 1 200 2 2 150 3 1
我是一名优秀的程序员,十分优秀!