快速 C++ 符号函数
fast c++ sign function
在我的代码中,我在一个循环中多次对 double 进行符号检查,并且该循环在执行期间通常 运行 数百万次。
我的符号检查是使用 fabs()
进行的非常基本的计算,所以我想一定有其他方法可以更快地完成它,因为“除法很慢”。我遇到了一个模板函数和 copysign()
并创建了一个简单的程序来 运行 进行速度比较。我已经用下面的代码测试了三种可能的解决方案。
// C++ program to find out execution time of of functions
#include <chrono>
#include <iostream>
#include <math.h>
using namespace std;
using namespace std::chrono;
template<typename Clock>
void printResult(const std::string name, std::chrono::time_point<Clock> start, std::chrono::time_point<Clock> stop, const int iterations)
{
// Get duration.
std::chrono::duration my_duration = duration_cast<nanoseconds>(stop - start);
my_duration /= iterations;
cout << "Time taken by "<< name <<" function: " << my_duration.count() << " ns avg. for " << iterations << " iterations." << endl << endl;
}
template <typename T> int sgn(T val)
{
return (T(0) < val) - (val < T(0));
}
int main() {
// ***************************************************************** //
int numiters = 100000000;
double vel = -0.6574;
double result = 0;
// Get starting timepoint
auto start_1 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = (vel/fabs(vel)) * 12.1;
}
// Get ending timepoint
auto stop_1 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("fabs", start_1, stop_1, numiters);
// Get starting timepoint
result = 0;
auto start_2 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = sgn(vel) * 12.1;
}
// Get ending timepoint
auto stop_2 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("sgn", start_2, stop_2, numiters);
// Get starting timepoint
result = 0;
auto start_10 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = copysign(12.1, vel);
}
// Get ending timepoint
auto stop_10 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("copysign", start_10, stop_10, numiters);
cout << endl;
}
当我 运行 程序时,我有点惊讶地发现 fabs()
解决方案和 copysign
解决方案在执行时间上几乎相同。此外,当我多次 运行 时,我发现结果可能变化很大。
我的时间正确吗?还有比我测试过的三个例子更好的方法吗?
更新
我已经在 quick-bench.com 上实施了测试,其中可以指定编译器设置,所有 3 个结果似乎在那里几乎相同。我想我可能做错了什么:
https://quick-bench.com/q/PJiAmoC2NQIJyuvbdz5ZHUALu2M
您的测试无效,因为您在计时内进行阻塞 I/O。
不过,我们可以使用quick-bench来分析:https://quick-bench.com/q/gt2KzKOFP4iV3ajmqANL_MhnMZk。这表明时间几乎完全相同。编译器生成的汇编代码呢?
double result = (vel/fabs(vel)) * 12.1;
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
double result = sgn(vel) * 12.1;
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
double result = copysign(12.1, vel);
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
优化代码时:答案始终是首先进行测量,找出程序中实际上最慢的部分,然后重写它以不执行任何该代码完全没有。
因为我穿你你的测试不测量任何东西!
来自你的quick-bench.com link click godbolt icon and see this disassembly.
请注意,您的所有版本都已转换为此汇编代码:
movabs rax, -4600370724363619533 # compile time evaluated result move outside measurement loop
.LBB0_3: # =>This Inner Loop Header: Depth=1
mov qword ptr [rsp + 8], rax
add rbx, -1 # measurement loop counter
jne .LBB0_3
所以基本上编译器能够完全删除测试代码,因为它注意到所有代码都可以在编译时进行常量计算!
因此您必须提供一些值来测试编译器在编译时无法确定的值。
这里my attempt to fix your test and its assembly看看优化了什么。我不保证这个措施是你必须自己做的正确的事情。测量如此小而活泼的代码是很难的。事实上,在如此少的 CPU 周期内执行的任何事情都无法通过软件精确可靠地测量。
在我的代码中,我在一个循环中多次对 double 进行符号检查,并且该循环在执行期间通常 运行 数百万次。
我的符号检查是使用 fabs()
进行的非常基本的计算,所以我想一定有其他方法可以更快地完成它,因为“除法很慢”。我遇到了一个模板函数和 copysign()
并创建了一个简单的程序来 运行 进行速度比较。我已经用下面的代码测试了三种可能的解决方案。
// C++ program to find out execution time of of functions
#include <chrono>
#include <iostream>
#include <math.h>
using namespace std;
using namespace std::chrono;
template<typename Clock>
void printResult(const std::string name, std::chrono::time_point<Clock> start, std::chrono::time_point<Clock> stop, const int iterations)
{
// Get duration.
std::chrono::duration my_duration = duration_cast<nanoseconds>(stop - start);
my_duration /= iterations;
cout << "Time taken by "<< name <<" function: " << my_duration.count() << " ns avg. for " << iterations << " iterations." << endl << endl;
}
template <typename T> int sgn(T val)
{
return (T(0) < val) - (val < T(0));
}
int main() {
// ***************************************************************** //
int numiters = 100000000;
double vel = -0.6574;
double result = 0;
// Get starting timepoint
auto start_1 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = (vel/fabs(vel)) * 12.1;
}
// Get ending timepoint
auto stop_1 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("fabs", start_1, stop_1, numiters);
// Get starting timepoint
result = 0;
auto start_2 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = sgn(vel) * 12.1;
}
// Get ending timepoint
auto stop_2 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("sgn", start_2, stop_2, numiters);
// Get starting timepoint
result = 0;
auto start_10 = high_resolution_clock::now();
for(int x = 0; x < numiters; x++)
{
result = copysign(12.1, vel);
}
// Get ending timepoint
auto stop_10 = high_resolution_clock::now();
cout << "Result is: " << result << endl;
printResult("copysign", start_10, stop_10, numiters);
cout << endl;
}
当我 运行 程序时,我有点惊讶地发现 fabs()
解决方案和 copysign
解决方案在执行时间上几乎相同。此外,当我多次 运行 时,我发现结果可能变化很大。
我的时间正确吗?还有比我测试过的三个例子更好的方法吗?
更新
我已经在 quick-bench.com 上实施了测试,其中可以指定编译器设置,所有 3 个结果似乎在那里几乎相同。我想我可能做错了什么: https://quick-bench.com/q/PJiAmoC2NQIJyuvbdz5ZHUALu2M
您的测试无效,因为您在计时内进行阻塞 I/O。
不过,我们可以使用quick-bench来分析:https://quick-bench.com/q/gt2KzKOFP4iV3ajmqANL_MhnMZk。这表明时间几乎完全相同。编译器生成的汇编代码呢?
double result = (vel/fabs(vel)) * 12.1;
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
double result = sgn(vel) * 12.1;
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
double result = copysign(12.1, vel);
movabs [=10=]xc028333333333333,%rax
mov %rax,0x8(%rsp)
add [=10=]xffffffffffffffff,%rbx
优化代码时:答案始终是首先进行测量,找出程序中实际上最慢的部分,然后重写它以不执行任何该代码完全没有。
因为我穿你你的测试不测量任何东西!
来自你的quick-bench.com link click godbolt icon and see this disassembly.
请注意,您的所有版本都已转换为此汇编代码:
movabs rax, -4600370724363619533 # compile time evaluated result move outside measurement loop
.LBB0_3: # =>This Inner Loop Header: Depth=1
mov qword ptr [rsp + 8], rax
add rbx, -1 # measurement loop counter
jne .LBB0_3
所以基本上编译器能够完全删除测试代码,因为它注意到所有代码都可以在编译时进行常量计算!
因此您必须提供一些值来测试编译器在编译时无法确定的值。
这里my attempt to fix your test and its assembly看看优化了什么。我不保证这个措施是你必须自己做的正确的事情。测量如此小而活泼的代码是很难的。事实上,在如此少的 CPU 周期内执行的任何事情都无法通过软件精确可靠地测量。