假设你有一个函数,并且每次函数返回一个大对象时你都会调用它。我使用返回void
的仿函数优化了问题,并将返回值存储在公共成员中:
#include <vector>
const int N = 100;
std::vector<double> fun(const std::vector<double> & v, const int n)
{
std::vector<double> output = v;
output[n] *= output[n];
return output;
}
class F
{
public:
F() : output(N) {};
std::vector<double> output;
void operator()(const std::vector<double> & v, const int n)
{
output = v;
output[n] *= n;
}
};
int main()
{
std::vector<double> start(N,10.);
std::vector<double> end(N);
double a;
// first solution
for (unsigned long int i = 0; i != 10000000; ++i)
a = fun(start, 2)[3];
// second solution
F f;
for (unsigned long int i = 0; i != 10000000; ++i)
{
f(start, 2);
a = f.output[3];
}
}
是的,我可以使用内联或以其他方式优化此问题,但在这里我想强调这个问题:使用函数我只使用函数声明和构造输出变量output
一次我每次打电话都这样做。第二种解决方案比使用g++ -O1
或g++ -O2
的第一种解决方案快两倍。你怎么看待它,这是一个丑陋的优化吗?
编辑:
澄清我的目标。我必须评估函数&gt; 10M次,但我只需要几次随机输出。输入没有改变很重要,实际上我将它声明为const引用。在这个例子中,输入总是相同的,但在现实世界中输入改变,它是函数的先前输出的函数。
答案 0 :(得分:1)
更常见的情况是在函数外部创建保留足够大的对象,并通过指针或引用将大对象传递给函数。您可以在对函数的多次调用中重用此对象。因此,您可以减少持续的内存分配。
答案 1 :(得分:1)
在这两种情况下,您都要多次分配新的矢量。
您应该做的是将输入和输出对象传递给您的类/函数:
void fun(const std::vector<double> & in, const int n, std::vector<double> & out)
{
out[n] *= in[n];
}
这样您就可以将逻辑与算法分开。你必须创建一个新的std :: vector并将它传递给函数多次。请注意,没有必要进行复制/分配。
P.S。自从我做c ++以来已经有一段时间了。它可能无法立即编译。
答案 2 :(得分:1)
这不是一个丑陋的优化。它实际上是一个相当不错的。
但是,我会隐藏输出并让operator []成员访问其成员。为什么?因为您可以通过将所有数学运算移动到该函数来执行延迟评估优化,因此仅在客户端请求该值时才执行该数学运算。在用户要求之前,为什么要这样做?
编辑:
刚检查了标准。赋值运算符的行为基于insert()。该函数的注释表明如果新大小超过当前容量,则会发生分配。当然,这似乎并没有明确地禁止实现重新分配,即使不是......我很确定你会发现没有这样做,我确信标准在其他地方说了些什么。因此,您通过删除分配调用来提高速度。
您仍应隐藏内部矢量。如果使用封装,您将有更多机会更改实现。您还可以从函数向向量返回引用(可能是const)并保留原始语法。
答案 3 :(得分:1)
我玩了一下,并提出了下面的代码。我一直在想有更好的方法来做到这一点,但现在它正在逃避我。
关键区别:
output
private
,并在其周围放置了吸气剂。void
,所以我将它作为const
引用返回,这样我们就可以保留返回值语义。...享受
#include <vector>
template<typename T, typename ConstructArg, typename FuncArg>
class ReturnT
{
public:
ReturnT(ConstructArg arg): output(arg){}
virtual ~ReturnT() {}
const T& operator()(const T& in, FuncArg arg)
{
output = in;
this->doOp(arg);
return this->getOutput();
}
const T& getOutput() const {return output;}
protected:
T& getOutput() {return output;}
private:
virtual void doOp(FuncArg arg) = 0;
T output;
};
class F : public ReturnT<std::vector<double>, std::size_t, const int>
{
public:
F(std::size_t size) : ReturnT<std::vector<double>, std::size_t, const int>(size) {}
private:
virtual void doOp(const int n)
{
this->getOutput()[n] *= n;
}
};
int main()
{
const int N = 100;
std::vector<double> start(N,10.);
double a;
// second solution
F f(N);
for (unsigned long int i = 0; i != 10000000; ++i)
{
a = f(start, 2)[3];
}
}
答案 4 :(得分:0)
看起来很奇怪(我的意思是需要进行优化) - 我认为在这种情况下,一个体面的编译器应该执行return value optimization。也许您只需要启用它。