为什么这 "optimization" 会减慢我的程序?
Why does this "optimization" slow down my program?
我正在编写一个图形引擎作为大学作业,最近试图优化我的部分代码,但是优化似乎反而减慢了它的速度。
代码的这个特定部分处理 2D Lindenmayer 系统并将它们变成 "line2D" 对象的列表,这些对象可以由程序的另一部分处理成图像。
在这样做时,它使用 sin 和 cos 来计算下一个点的坐标,并且因为 sin 和 cos 是浮点运算,我认为这些操作会很耗时,尤其是在更复杂的 lindenmayer 系统中。所以我创建了一个对象 class "cossinlist" ,它从 .txt 文件中为 0 到 359 度之间的每个整数角(转换为 rad)导入 cos 和 sin 的值到两个名为 "coslist" 和 "sinlist" 以角度为键。这样我只需要在处理包含小数部分的角度时做实际的失败。
然后我决定在一个相对密集的系统上测量优化和不优化(通过注释掉)的执行时间:引擎在 33.4016 秒内生成图像,没有它只用了 25.3686 秒。这是一个实质性的差异,但不是以预期的方式。我做了更多的测试,所有测试都给出了相似比例的差异,所以现在我想知道......是什么导致了这种差异?
函数:
img::EasyImage LSystem2D(const unsigned int size, const ini::DoubleTuple & backgroundcolor, LParser::LSystem2D & System, const ini::DoubleTuple & color)
{
CosSinList cossinlist;
std::string string;
Lines2D Lines;
double origin = 0;
Point2D currentpos(origin, origin);
Point2D newpos(origin, origin);
std::stack<Point2D> savedpositions;
double currentangle = System.get_starting_angle();
std::stack<double> savedangles;
const img::Color linecolor(color.at(0)*255,color.at(1)*255,color.at(2)*255);
const img::Color BGcolor(backgroundcolor.at(0)*255,backgroundcolor.at(1)*255,backgroundcolor.at(2)*255);
string = ReplaceLsystem(System, (System.get_initiator()), (System.get_nr_iterations()));
bool optimizedangle = false;
if(System.get_angle() == rint(System.get_angle()) && (System.get_starting_angle() == rint(System.get_starting_angle()))
{
optimizedangle = true;
}
for(char& c : string)
{
if(currentangle > 359){currentangle -= 360;}
if(currentangle < -359){currentangle += 360;}
if(System.get_alphabet().count(c) != 0)
{
/*if(optimizedangle == true)
{
if(currentangle >= 0)
{
newpos.X = currentpos.X+(cossinlist.coslist[currentangle]);
newpos.Y = currentpos.Y+(cossinlist.sinlist[currentangle]);
}
else
{
newpos.X = currentpos.X+(cossinlist.coslist[360+currentangle]);
newpos.Y = currentpos.Y+(cossinlist.sinlist[360+currentangle]);
}
}
else
{*/
newpos.X = currentpos.X+cos(currentangle*PI/180);
newpos.Y = currentpos.Y+sin(currentangle*PI/180);
//}
if(System.draw(c))
{
Lines.push_back(Line2D(currentpos,newpos,linecolor));
currentpos = newpos;
}
else
{
currentpos = newpos;
}
}
else if(c=='-')
{
currentangle -= System.get_angle();
}
else if(c=='+')
{
currentangle += System.get_angle();
}
else if(c=='[')
{
savedpositions.push(currentpos);
savedangles.push(currentangle);
}
else if(c==']')
{
currentpos = savedpositions.top();
savedpositions.pop();
currentangle = savedangles.top();
savedangles.pop();
}
}
return Drawlines2D(Lines, size, BGcolor);
}
SinCosList class:
#include <fstream>
#include <iostream>
#include <map>
#include "CosSinList.h"
using namespace std;
CosSinList::CosSinList()
{
string line;
std::fstream cosstream("coslist.txt", std::ios_base::in);
double a;
double i = 0;
while (cosstream >> a)
{
coslist[i] = a;
i += 1;
}
std::fstream sinstream("sinlist.txt", std::ios_base::in);
i = 0;
while (sinstream >> a)
{
sinlist[i] = a;
i += 1;
}
};
CosSinList::~CosSinList(){};
"optimization" 被注释掉的方式与我在速度测试期间注释掉的方式相同,只有对象的实际使用被注释掉(SinCosList 仍在初始化,布尔值检查是否可以用还在初始化中)
(我假设 coslist
和 sinlist
是普通数组或类似数组)
一些事情:
- 你真的应该打开优化
关闭优化后,您正在衡量无关紧要的东西。未优化代码的性能与打开优化后的性能相关性很差。
optimzedangle
应该是编译时常量。
如果优化器知道 optimizedangle
在该程序的整个 运行 期间没有改变,则它可能能够简化代码。使用这个特定的代码片段,它可能可以弄清楚,但如果你不需要,你不应该依赖它,而且通常 很容易 不小心在你的地方编写代码认为变量保持不变是显而易见的,但编译器比你更聪明,并且意识到你已经打开了一个可能允许变量发生变化的漏洞,因此它必须编写更慢的循环来解决这个问题。
- 分支可能不好
内部循环中的分支——尤其是 unpredictable 分支——会降低性能。尝试编写你的循环,这样就没有任何分支;例如确保 currentangle
始终为正数,或者使查找 table 720
条目变长,以便您始终可以只索引 360 + currentangle
.
- 浮点数 <--> 整数转换可能很慢
我倾向于避免这些,因此我从来都不擅长预测什么时候它真的是一个问题,但这可能才是真正要了你的命。
- 您的table正在消耗缓存
您没有 post 您的数据结构,但我想大约有 6k 字节。这是您的 L1 缓存的一个重要百分比。我不清楚这是否是此循环中的重要影响。
我正在编写一个图形引擎作为大学作业,最近试图优化我的部分代码,但是优化似乎反而减慢了它的速度。
代码的这个特定部分处理 2D Lindenmayer 系统并将它们变成 "line2D" 对象的列表,这些对象可以由程序的另一部分处理成图像。
在这样做时,它使用 sin 和 cos 来计算下一个点的坐标,并且因为 sin 和 cos 是浮点运算,我认为这些操作会很耗时,尤其是在更复杂的 lindenmayer 系统中。所以我创建了一个对象 class "cossinlist" ,它从 .txt 文件中为 0 到 359 度之间的每个整数角(转换为 rad)导入 cos 和 sin 的值到两个名为 "coslist" 和 "sinlist" 以角度为键。这样我只需要在处理包含小数部分的角度时做实际的失败。
然后我决定在一个相对密集的系统上测量优化和不优化(通过注释掉)的执行时间:引擎在 33.4016 秒内生成图像,没有它只用了 25.3686 秒。这是一个实质性的差异,但不是以预期的方式。我做了更多的测试,所有测试都给出了相似比例的差异,所以现在我想知道......是什么导致了这种差异?
函数:
img::EasyImage LSystem2D(const unsigned int size, const ini::DoubleTuple & backgroundcolor, LParser::LSystem2D & System, const ini::DoubleTuple & color)
{
CosSinList cossinlist;
std::string string;
Lines2D Lines;
double origin = 0;
Point2D currentpos(origin, origin);
Point2D newpos(origin, origin);
std::stack<Point2D> savedpositions;
double currentangle = System.get_starting_angle();
std::stack<double> savedangles;
const img::Color linecolor(color.at(0)*255,color.at(1)*255,color.at(2)*255);
const img::Color BGcolor(backgroundcolor.at(0)*255,backgroundcolor.at(1)*255,backgroundcolor.at(2)*255);
string = ReplaceLsystem(System, (System.get_initiator()), (System.get_nr_iterations()));
bool optimizedangle = false;
if(System.get_angle() == rint(System.get_angle()) && (System.get_starting_angle() == rint(System.get_starting_angle()))
{
optimizedangle = true;
}
for(char& c : string)
{
if(currentangle > 359){currentangle -= 360;}
if(currentangle < -359){currentangle += 360;}
if(System.get_alphabet().count(c) != 0)
{
/*if(optimizedangle == true)
{
if(currentangle >= 0)
{
newpos.X = currentpos.X+(cossinlist.coslist[currentangle]);
newpos.Y = currentpos.Y+(cossinlist.sinlist[currentangle]);
}
else
{
newpos.X = currentpos.X+(cossinlist.coslist[360+currentangle]);
newpos.Y = currentpos.Y+(cossinlist.sinlist[360+currentangle]);
}
}
else
{*/
newpos.X = currentpos.X+cos(currentangle*PI/180);
newpos.Y = currentpos.Y+sin(currentangle*PI/180);
//}
if(System.draw(c))
{
Lines.push_back(Line2D(currentpos,newpos,linecolor));
currentpos = newpos;
}
else
{
currentpos = newpos;
}
}
else if(c=='-')
{
currentangle -= System.get_angle();
}
else if(c=='+')
{
currentangle += System.get_angle();
}
else if(c=='[')
{
savedpositions.push(currentpos);
savedangles.push(currentangle);
}
else if(c==']')
{
currentpos = savedpositions.top();
savedpositions.pop();
currentangle = savedangles.top();
savedangles.pop();
}
}
return Drawlines2D(Lines, size, BGcolor);
}
SinCosList class:
#include <fstream>
#include <iostream>
#include <map>
#include "CosSinList.h"
using namespace std;
CosSinList::CosSinList()
{
string line;
std::fstream cosstream("coslist.txt", std::ios_base::in);
double a;
double i = 0;
while (cosstream >> a)
{
coslist[i] = a;
i += 1;
}
std::fstream sinstream("sinlist.txt", std::ios_base::in);
i = 0;
while (sinstream >> a)
{
sinlist[i] = a;
i += 1;
}
};
CosSinList::~CosSinList(){};
"optimization" 被注释掉的方式与我在速度测试期间注释掉的方式相同,只有对象的实际使用被注释掉(SinCosList 仍在初始化,布尔值检查是否可以用还在初始化中)
(我假设 coslist
和 sinlist
是普通数组或类似数组)
一些事情:
- 你真的应该打开优化
关闭优化后,您正在衡量无关紧要的东西。未优化代码的性能与打开优化后的性能相关性很差。
optimzedangle
应该是编译时常量。
如果优化器知道 optimizedangle
在该程序的整个 运行 期间没有改变,则它可能能够简化代码。使用这个特定的代码片段,它可能可以弄清楚,但如果你不需要,你不应该依赖它,而且通常 很容易 不小心在你的地方编写代码认为变量保持不变是显而易见的,但编译器比你更聪明,并且意识到你已经打开了一个可能允许变量发生变化的漏洞,因此它必须编写更慢的循环来解决这个问题。
- 分支可能不好
内部循环中的分支——尤其是 unpredictable 分支——会降低性能。尝试编写你的循环,这样就没有任何分支;例如确保 currentangle
始终为正数,或者使查找 table 720
条目变长,以便您始终可以只索引 360 + currentangle
.
- 浮点数 <--> 整数转换可能很慢
我倾向于避免这些,因此我从来都不擅长预测什么时候它真的是一个问题,但这可能才是真正要了你的命。
- 您的table正在消耗缓存
您没有 post 您的数据结构,但我想大约有 6k 字节。这是您的 L1 缓存的一个重要百分比。我不清楚这是否是此循环中的重要影响。