我是CUDA的新手,我无法理解循环展开.我写了一段代码来理解这项技术
__global__ void kernel(float *b, int size)
{
int tid = blockDim.x * blockIdx.x + threadIdx.x;
#pragma unroll
for(int i=0;i<size;i++)
b[i]=i;
}
Run Code Online (Sandbox Code Playgroud)
以上是我的核心功能.在main我称之为下面
int main()
{
float * a; //host array
float * b; //device array
int size=100;
a=(float*)malloc(size*sizeof(float));
cudaMalloc((float**)&b,size);
cudaMemcpy(b, a, size, cudaMemcpyHostToDevice);
kernel<<<1,size>>>(b,size); //size=100
cudaMemcpy(a, b, size, cudaMemcpyDeviceToHost);
for(int i=0;i<size;i++)
cout<<a[i]<<"\t";
_getch();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是否意味着我有size*size= 10000个线程运行来执行程序?在展开循环时是否创建了100个?
在g ++ 4.9.2和5.3.1上,此代码需要几秒钟的时间来编译并生成52,776字节的可执行文件:
#include <array>
#include <iostream>
int main()
{
constexpr std::size_t size = 4096;
struct S
{
float f;
S() : f(0.0f) {}
};
std::array<S, size> a = {}; // <-- note aggregate initialization
for (auto& e : a)
std::cerr << e.f;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
增加size似乎线性增加编译时间和可执行文件大小.我无法使用clang 3.5或Visual C++ 2015重现此行为.使用-Os没有区别.
$ time g++ -O2 -std=c++11 test.cpp
real 0m4.178s
user 0m4.060s
sys 0m0.068s
Run Code Online (Sandbox Code Playgroud)
检查汇编代码显示初始化a已展开,生成4096 movl条指令:
main:
.LFB1313:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset …Run Code Online (Sandbox Code Playgroud) 考虑这段代码:
#include <iostream>
typedef long xint;
template<int N>
struct foz {
template<int i=0>
static void foo(xint t) {
for (int j=0; j<10; ++j) {
foo<i+1> (t+j);
}
}
template<>
static void foo<N>(xint t) {
std::cout << t;
}
};
int main() {
foz<8>::foo<0>(0);
}
Run Code Online (Sandbox Code Playgroud)
在编译时clang++ -O0,它会在几秒钟内编译然后运行 4 秒。
然而,使用clang++ -O2,编译需要很长的时间和大量的内存。在Compiler Explorer上可以看到,更改8为较小的值后,它完全展开了循环。
我并不是让它完全没有优化,而是让它不递归,就像嵌套循环应该表现的那样。有什么我应该做的吗?
c++ clang compiler-optimization template-meta-programming loop-unrolling
我有很多if,else if语句,我知道必须有更好的方法来做到这一点,但即使在搜索stackoverflow之后,我也不确定如何在我的特定情况下这样做.
我正在解析文本文件(账单)并根据账单上是否出现某些字符串将服务提供商的名称分配给变量(txtvar.Provider).
这是我正在做的一小部分样本(不要笑,我知道它很乱).总而言之,如果是,那么大约有300个.
if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
txtvar.Provider = "City of Austin";
}
// And so forth for many different strings
Run Code Online (Sandbox Code Playgroud)
我想使用类似switch语句的东西更高效和可读,但我不确定如何比较BillText.我正在寻找这样的东西,但无法弄清楚如何使它工作.
switch (txtvar.BillText)
{
case txtvar.BillText.IndexOf("Southwest Gas") > -1:
txtvar.Provider = "Southwest Gas";
break;
case txtvar.BillText.IndexOf("TexasGas.com") > -1:
txtvar.Provider = "Texas Gas";
break;
case txtvar.BillText.IndexOf("Southern") > -1:
txtvar.Provider = "Southern Power & Gas"; …Run Code Online (Sandbox Code Playgroud) 我目前正在开展一个项目,每个周期都很重要.在分析我的应用程序时,我发现一些内部循环的开销非常高,因为它们只包含一些机器指令.另外,这些循环中的迭代次数在编译时是已知的.
所以我认为不是手动展开带有复制和粘贴的循环,而是可以使用宏在编译时展开循环,以便以后可以轻松修改它.
我的形象是这样的:
#define LOOP_N_TIMES(N, CODE) <insert magic here>
Run Code Online (Sandbox Code Playgroud)
所以我可以替换for (int i = 0; i < N, ++i) { do_stuff(); }为:
#define INNER_LOOP_COUNT 4
LOOP_N_TIMES(INNER_LOOP_COUNT, do_stuff();)
Run Code Online (Sandbox Code Playgroud)
它将自己展开:
do_stuff(); do_stuff(); do_stuff(); do_stuff();
Run Code Online (Sandbox Code Playgroud)
由于C预处理器在大多数时间对我来说仍然是一个谜,我不知道如何实现这一点,但我知道它必须是可能的,因为Boost似乎有一个BOOST_PP_REPEAT宏.不幸的是我不能在这个项目中使用Boost.
我在搜索时找到的介绍性链接:
正如你所看到的,大多数是C语言,但我认为它们也适用于C++.这是我的代码:
template<typename T>
//__attribute__((optimize("unroll-loops")))
//__attribute__ ((pure))
void foo(std::vector<T> &p1, size_t start,
size_t end, const std::vector<T> &p2) {
typename std::vector<T>::const_iterator it2 = p2.begin();
//#pragma simd
//#pragma omp parallel for
//#pragma GCC ivdep Unroll Vector
for (size_t i = start; i < end; ++i, ++it2) {
p1[i] = p1[i] - *it2;
p1[i] += 1;
}
}
int main()
{
size_t n;
double x,y;
n = 12800000;
vector<double> v,u;
for(size_t i=0; i<n; ++i) {
x …Run Code Online (Sandbox Code Playgroud) 当编译器执行循环展开优化时,它如何确定展开循环的因素或是否展开整个循环?由于这是空间性能权衡,平均而言,这种优化技术在使程序运行得更好方面效率如何?此外,在什么条件下建议使用这种技术(即某些操作或计算)?
这不必特定于某个编译器.它可以是任何解释,概述这种技术背后的想法以及在实践中观察到的内容.
请考虑以下代码
vector<double> v;
// fill v
const vector<double>::iterator end =v.end();
for(vector<double>::iterator i = v.bgin(); i != end; ++i) {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
像g ++,clang ++,icc这样的编译器是否能够像这样展开循环.不幸的是,我不知道汇编能够从输出验证循环是否展开.(我只能访问g ++.)
对我来说,这似乎需要代表编译器比平常更聪明,首先推断迭代器是一个随机访问迭代器,然后计算循环执行的次数.编译器可以在启用优化时执行此操作吗?
感谢您的回复,在您开始讲授过早优化之前,这是一个好奇心的练习.
我如何说服GCC展开一个已知迭代次数但又很大的循环?
我正在编译-O3.
当然,有问题的真实代码更复杂,但这是一个具有相同行为的简化示例:
int const constants[] = { 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144 };
int get_sum_1()
{
int total = 0;
for (int i = 0; i < CONSTANT_COUNT; ++i)
{
total += constants[i];
}
return total;
}
Run Code Online (Sandbox Code Playgroud)
...如果CONSTANT_COUNT被定义为8(或更少),那么GCC将展开循环,传播常量,并将整个函数简化为简单return <value>;.另一方面,如果CONSTANT_COUNT是9(或更高),那么循环不会展开,并且GCC会生成一个二进制循环,读取常量,并在运行时添加它们 - 即使理论上,该函数仍然可以被优化到只返回一个常数.(是的,我看过反编译的二进制文件.)
如果我手动展开循环,如下所示:
int get_sum_2()
{
int total = 0;
total += constants[0];
total += constants[1];
total += constants[2];
total += constants[3];
total …Run Code Online (Sandbox Code Playgroud) loop-unrolling ×10
c++ ×6
c ×4
optimization ×3
g++ ×2
gcc ×2
performance ×2
boost ×1
c# ×1
clang ×1
cuda ×1
dictionary ×1
if-statement ×1
macros ×1
nvidia ×1
pragma ×1
stdarray ×1
stl ×1