- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我一直认为 foo2
下面的函数比 foo3
快,直到经过测试。
所有代码如下:
#include <iostream>
#include <boost/timer.hpp>
#include <boost/lexical_cast.hpp>
#include <stdint.h>
struct session {
bool operator==(const session& r) const;
uint8_t proto;
uint16_t sport;
uint16_t dport;
uint32_t sip;
uint32_t dip;
};
bool session::operator==(const session& r) const {
return proto == r.proto && sport == r.sport && dport == r.dport
&& sip == r.sip && dip == r.dip;
}
// my L1,L2,L3 total cache size is 16MB, so set it 32MB to overflow all 16MB caches.
static const int SIZE = 32 * 1024 * 1024 / sizeof(session);
int sum;
void foo1(session* p) {
session s = {1, 2, 3, 4, 5};
for (int i = 0; i < SIZE; i++)
if (p[i] == s)
sum++;
}
void foo2(session* p) {
session s = {1, 2, 3, 4, 5};
int n = SIZE - SIZE % 4;
int i;
for (i = 0; i < n; i += 4) {
if (p[i + 0] == s)
sum++;
if (p[i + 1] == s)
sum++;
if (p[i + 2] == s)
sum++;
if (p[i + 3] == s)
sum++;
}
/*
for (; i < SIZE; i++)
if (p[i] == s)
sum++;
*/
}
void foo3(session* p) {
session s = {1, 2, 3, 4, 5};
int n = SIZE - SIZE % 4;
int i;
for (i = 0; i < n; i += 4) {
if (p[i + 0] == s)
sum++;
else if (p[i + 1] == s)
sum++;
else if (p[i + 2] == s)
sum++;
else if (p[i + 3] == s)
sum++;
}
/*
for (; i < SIZE; i++)
if (p[i] == s)
sum++;
*/
}
int main(int argc, char* argv[]) {
if (argc < 2)
return -1;
int n = boost::lexical_cast<int>(argv[1]);
session* p = new session[SIZE];
boost::timer t;
for (int i = 0; i < n; i++)
foo1(p);
std::cout << t.elapsed() << std::endl;
t.restart();
for (int i = 0; i < n; i++)
foo2(p);
std::cout << t.elapsed() << std::endl;
t.restart();
for (int i = 0; i < n; i++)
foo3(p);
std::cout << t.elapsed() << std::endl;
delete [] p;
return 0;
}
测试1000次,./a.out 1000
输出:
4.36
3.98
3.96
我的机器:
CPU:Intel(R) Xeon(R) CPU E5-2420 0 @ 1.90GHz
缓存:
一级缓存:32K
一级缓存:32K
二级缓存:256K
三级缓存:15360K
在测试中,foo2
和foo3
性能相当。由于 foo2
可能大小写CPU 并行执行所有展开的表达式,所以 foo3
是相同的。是吗?如果是,则 else if
语法违反了 C/C++ 基本 else if
语义。
有人解释一下吗?非常感谢。
更新
我的编译器是 gcc 4.4.6 ins RedHat
g++ -Wall -O2 a.cpp
最佳答案
在某些情况下,我希望 foo3 更快,因为它可以短路(会出现一些小于或等于 4 的分支,而在 foo2 中,总是出现 4 个分支)。在 s
不等于 4 个数组元素中的任何一个的情况下(在这种情况下极有可能),foo2 和 foo3 基本上是相同的代码。在这种情况下,两个函数都会发生 4 个分支。
考虑一下 foo3 的真实情况(就分支而言):
if (p[i + 0] == s)
sum++;
else
if (p[i + 1] == s)
sum++;
else
if (p[i + 2] == s)
sum++;
else
if (p[i + 3] == s)
sum++;
这应该表明,只要 if
一直为假,子分支就会发生。这意味着在所有 if 都不为真的情况下,它将执行与 foo2 相同数量的操作(尽管功能不同)。
一种粗略的思考方式是好像每个 if
都有一个成本(不是 if 的主体,实际的 if)。也就是说,执行流程中每走到一个if
,都需要付出一定的代价。这是因为必须完成一个分支。这样想,很明显,当 foo3 的流程没有短路时(当 foo3
的所有 4 个 if
都短路时,每个函数的成本是相同的遭遇)。 (正如 KillianDS 指出的那样,如果分支预测是错误的,那么 foo3 实际上会花费更长的时间,因为错误的分支将不得不倒回并执行正确的分支。尽管总是选择正确的分支,但对您来说似乎是这样。)
这有点像下面的代码片段如何具有相同的性能:
if (short_runtime()) {}
和:
if (short_runtime() && long_runtime()) {}
如果 short_runtime
返回 true,那么调用第二个函数的函数显然会花费更长的时间。如果 short_runtime()
返回为 false,则 long_runtime()
调用将永远不会发生,因此运行时间将相同(或者至少非常 类似)。
为了检验这个理论,您可以使 p[i + 0] == s
为真。只需值初始化数组 (session* p = new session[SIZE]();
),并使用 session s = {1, 2, 3, 4, 5};
本地。
循环展开的目的/结果似乎有点困惑。这样做是为了减少必须发生的跳跃。如果必须完成 n
次操作,而不是 n
次迭代(跳跃),每次迭代执行 1 个操作,您可以进行 n/k
次迭代(跳跃)反而发生了。当一切都适合缓存时,这可以提供速度提升(如果它不能适合缓存,它实际上会降低性能!)。
这些指令不是同时发生的(如果是,sum
将需要一个围绕它的互斥量,这将非常昂贵)。它们只是以 4 组而不是 1 组的形式出现。
关于c++ - 在 "loop unrolling"中,所有展开的表达式都执行了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18249987/
我阅读了有关循环展开的文档。它解释说,如果将展开因子设置为 1,则程序将像使用 #pragma nounrolling 一样工作。 但是,该文件不包括#pragma unroll(0) 案例..由于
我正在用 C++ 编写路径跟踪器,我想尝试将资源最密集的代码实现到 CUDA 或 OpenCL 中(我不确定该选择哪个)。 我听说我的显卡的 CUDA 版本不支持递归,这是我的路径追踪器大量使用的东西
在我的一个JAVA项目中,我通常必须将巨大的数组与标量相乘。因此我想通过使用所谓的循环展开来编写一种方法。到目前为止我已经想出了这个: public static float[] arrayTimes
在最近的代码审查中,出现了关于 @Unroll 注释属于类级别还是方法级别的问题。该类的大多数方法(但不是全部)都需要 @Unroll。如果在类级别声明并且并非类的所有方法都需要它,声明 @Unrol
我有同样的问题: Expression templates: improving performance in evaluating expressions? 我的目标是展开这个表达式的循环 auto
在NVIDIA的制作精良reduction optimization documentation ,他们最终得到一个看起来像这样的 warpReduce: Template __device__ v
我是 CUDA 新手,我无法理解循环展开。我编写了一段代码来理解该技术 __global__ void kernel(float *b, int size) { int tid = block
URL 缩短器在 Twitter 等空间受限的媒体中很有用。但是这样的危险已经得到充分讨论(有限的生命周期、隐藏恶意链接、可用性等)。但是有没有一种很好的方法来预先解析来自 goo.gl 或 bit.
我试图告诉我的编译器使用#pragma unroll 为我展开一个循环。但是,迭代次数由编译时变量决定,因此循环需要展开那么多次。像这样: #define ITEMS 4 #pragma unroll
在 C++11 中是否有一种简单的方法可以做到这一点?如果可能的话,我想同时保留多重继承和循环访问包中所有静态函数的能力。 #include struct A { static void foo()
给定一个 Python 元组 t = v1, v2, v3 是否有一个实用程序可以解压这些元组以便给定: def foo(v1,v2,v3): pass 取而代之的是: foo(t[0],t[1],t
假设我有一个看起来像这样的循环: for(int i = 0; i : xor ebx,ebx 0x08048406 : push ecx 0x08048407 : xor
摘自 GCC 手册: -funroll-loops Unroll loops whose number of iterations can be determined at co
不确定如何调用它,但假设您有一个看起来像这样的类: class Person { public string Name; public IEnumerable Friends; } 然
我使用 Lisp 宏的第一步...... (defconstant width 7) (defconstant height 6) ... ; board is a 2D array of width
例如假设我有以下两个函数 function a(param1) { console.log(param1); } function b(func, params) { func(par
我一直认为 foo2 下面的函数比 foo3 快,直到经过测试。 所有代码如下: #include #include #include #include struct session {
我想知道 C++ 编译器是否会像他们目前对“正常”循环所做的那样展开基于范围的循环以最大化性能,或者在某些情况下基于范围的循环会比正常循环慢? 非常感谢。 最佳答案 基于范围的循环相当于: { a
在 CUDA 中,可以使用 #pragma 展开循环。 unroll 指令通过增加指令级并行度来提高性能。 #pragma可以选择后跟一个数字,指定循环必须展开多少次。 不幸的是,文档没有给出何时应该
我有一段代码,我希望 LLVM 按特定因子展开其中的所有循环。我正在使用以下命令: opt -mem2reg -simplifycfg -loops -lcssa -loop-simplify -lo
我是一名优秀的程序员,十分优秀!