rg6*_*rg6 9 c++ optimization performance function-calls pass-by-reference
我已经为计算数量向量的算法编写了优化代码.我已经在各种尝试之前和之后定时将函数中的数据计算出来.我认为计算的具体性质或数量向量的性质是不相关的.下面是代码,时间和细节的概述.
所有代码都使用以下标志编译:
g ++ -Wall -Wextra -Werror -std = c ++ 11 -pedantic -O3
我有一个这样的课:
#ifndef C_H
#define C_H
#include <iostream>
#include <iterator>
#include <vector>
Class c {
public:
void doWork( int param1, int param2 ) const {
std::array<unsigned long,40> counts = {{0}};
// LOTS of branches and inexpensive operations:
// additions, subtractions, incrementations, and dereferences
for( /* loop 1 */ ) {
// LOTS MORE branches and inexpensive operations
counts[ /* data dependent */ ] += /* data dependent */;
for( /* loop 2 */ ) {
// YET MORE branches inexpensive operations
counts[ /* data dependent */ ] += /* data dependent */;
}
}
counts [ /* data dependent */ ] = /* data dependent */;
/* exclude for profiling
std::copy( &counts[0], &counts[40], std::ostream_iterator( std::cout, "," ) );
std::cout << "\n";
*/
}
private:
// there is private data here that is processed above
// the results get added into the array/vector as they are computed
};
#endif
Run Code Online (Sandbox Code Playgroud)
像这样的主要:
#include <iostream>
#include "c.h"
int main( int argc, char * argv ) {
Class c( //set the private data of c by passing data in );
int param1;
int param2;
while( std::cin >> param1 >> param2 ) {
c.doWork( int param1, int param2 );
}
}
Run Code Online (Sandbox Code Playgroud)
以下是有关数据的一些相关详细信息:
所有这些都需要5分48秒才能运行.当然我可以在类函数中打印数组,这就是我一直在做的,但是我将公开发布代码,并且一些用例可能包括除了打印向量之外还要做其他事情.在这种情况下,我需要更改函数签名以实际获取数据给用户.这就是出现问题的地方.我尝试过的事情:
在main中创建一个向量并通过引用传入:
std::vector<unsigned long> counts( 40 );
while( std::cin >> param1 >> param2 ) {
c.doWork( param1, param2, counts );
std::fill( counts.begin(), counts.end(), 0 );
}
Run Code Online (Sandbox Code Playgroud)
这需要7分30秒.删除对std :: fill的调用只会将此减少15秒,因此不会考虑差异.
在doWork函数中创建向量并返回它,利用移动语义.由于这需要为每个结果进行动态分配,因此我没想到这会很快.奇怪的是,它并没有那么慢.7分40秒.
按值返回当前在doWork中的std :: array.当然,这必须在返回时复制数据,因为堆栈数组不支持移动语义.7分30秒
通过引用传递std :: array.
while( std::cin >> param1 >> param2 ) {
std::array<unsigned long,40> counts = {{0}};
c.doWork( param1, param2, counts )
}
Run Code Online (Sandbox Code Playgroud)
我希望这大致相当于原版.数据放在main函数的堆栈中,并通过引用传递给doWork,doWork填充它.7分20秒.这个真的让我很难受.
我没有尝试将指针传递给doWork,因为这应该等同于通过引用传递.
一个解决方案当然有两个版本的函数:一个在本地打印,另一个返回.障碍是我必须复制所有代码,因为这里的整个问题是我无法有效地从函数中获取结果.
所以我很神秘.据我所知,这些解决方案中的任何一个都需要对doWork中的数组/向量的每次访问进行额外的解引用,但与大量其他快速操作和更麻烦的数据相关分支相比,这些额外的解引用非常简单.
我欢迎任何想法来解释这一点.我唯一的想法是编译器正在优化代码,以便在原始情况下省略一些必要的计算组件,因为编译器意识到它不是必需的.但这似乎在几个方面是禁忌的:
是否就是doWork独立于main编译和优化的情况,因此如果函数有义务以任何形式返回数据,则无法确定它是否将被使用?
我的剖析方法是不打印,这是为了避免印刷成本强调各种方法的相对差异,有缺陷吗?
我很感激你能摆脱的任何光芒.
我要做的就是暂停几次,看看它大部分时间在做什么。看看你的代码,我怀疑大部分时间都花在 a) 最内层循环,特别是索引计算,或 2) std::array
.
如果 的大小counts
始终是 40,我就会这么做
long counts[40];
memset(counts, 0, sizeof(counts));
Run Code Online (Sandbox Code Playgroud)
这在堆栈上进行分配,这不需要时间,并且memset
与您正在做的其他事情相比也不需要时间。
如果大小在运行时发生变化,那么我所做的是一些静态分配,如下所示:
void myRoutine(){
/* this does not claim to be pretty. it claims to be efficient */
static int nAlloc = 0;
static long* counts = NULL;
/* this initially allocates the array, and makes it bigger if necessary */
if (nAlloc < /* size I need */){
if (counts) delete counts;
nAlloc = /* size I need */;
counts = new long[nAlloc];
}
memset(counts, 0, sizeof(long)*nAlloc);
/* do the rest of the stuff */
}
Run Code Online (Sandbox Code Playgroud)
这样,counts
总是足够大,重点是 1) 执行new
尽可能少的次数,2) 保持索引counts
尽可能简单。
但为了确定起见,我首先会暂停一下。修复之后,我会再次执行此操作,看看接下来可以修复什么。