我想将输出从gethrtime转换为毫秒。
这样做的显而易见的方法是除以1000000。 但是,我经常这样做,并想知道它是否会成为瓶颈。
处理1000000这样的数字时是否有优化的除法运算?
注意:任何代码都必须是可移植的。我正在使用gcc,这通常是在Sparc硬件上
使用下面的代码进行一些快速测试...希望是对的。
#include <sys/time.h>
#include <iostream>
using namespace std;
const double NANOSECONDS_TO_MILLISECONDS = 1.0 / 1000000.0;
int main()
{
hrtime_t start;
hrtime_t tmp;
hrtime_t fin;
start = gethrtime();
tmp = (hrtime_t)(start * NANOSECONDS_TO_MILLISECONDS);
fin = gethrtime();
cout << "Method 1"
cout << "Original val: " << start << endl;
cout << "Computed: " << tmp << endl;
cout << "Time:" << fin - start << endl;
start = gethrtime();
tmp = (start / 1000000);
fin = gethrtime();
cout "Method 2"
cout << "Original val: " << start << endl;
cout << "Computed: " << tmp << endl;
cout << "Time:" << fin - start << endl;
return 0;
}
示例输出:
Original val: 3048161553965997
Computed: 3048161553
Time:82082
Original val: 3048161556359586
Computed: 3048161556
Time:31230
Original val: 3048239663018915
Computed: 3048239663
Time:79381
Original val: 3048239665393873
Computed: 3048239665
Time:31321
Original val: 3048249874282285
Computed: 3048249874
Time:81812
Original val: 3048249876664084
Computed: 3048249876
Time:34830
如果这是正确的,那么在这种情况下,乘以倒数的倍数实际上更慢。这可能是由于使用浮点数学而不是定点数学。我会坚持整数除法,然后几乎不需要任何时间。
答案 0 :(得分:49)
让你的编译器弄明白!
说真的,如果你真的担心这个级别的优化(除非它出现在配置文件中,否则你不应该这样做),你应该习惯于查看编译器的汇编语言输出。你会惊讶于编译器代表你做的事情。
所有推荐数学技巧的人要么编写错误,要么低估他们的编译器。例如,尝试编译此函数:
unsigned long div1000000(unsigned long n) {
return n / 1000000UL;
}
在x86(-O3,-fomit-frame-pointer)上用gcc 4.3.3编译,我得到:
$ objdump -d div.o -M intel
test2.o: file format elf32-i386
Disassembly of section .text:
00000000 <div1000000>:
0: b8 83 de 1b 43 mov eax,0x431bde83
5: f7 64 24 04 mul DWORD PTR [esp+0x4]
9: c1 ea 12 shr edx,0x12
c: 89 d0 mov eax,edx
e: c3 ret
换句话说,编译器使用n / 1000000UL
并将其转换为(unsigned long long)(n * 0x431bde83) >> (0x12 + 32)
。为什么这样做?在我的头顶,我不知道!但是编译器认为它比发布原生鸿沟更快。
故事的道德:
答案 1 :(得分:33)
分部不是一项昂贵的操作。我非常怀疑1000000除以后的操作是否会接近应用程序的主要瓶颈。浮点处理器比任何类型的“技巧”都快,而不仅仅是单一操作。
答案 2 :(得分:15)
我很惊讶没有人得到这个......
所以,
const uint64_t numerator = (1LL<<32)/1000000;
...
millionths = ( number * numerator ) >> 32;
快速!
答案 3 :(得分:3)
乘以1 / 1,000,000。它应该更快。我的谷歌搜索说要加快分歧,乘以倒数。因此,如果有一组相对已知的可能值,我会预先计算倒数或倒数列表,然后相乘。
雅各
答案 4 :(得分:3)
但是,我经常这样做,并想知道它是否会成为瓶颈。
首先要做的事情。如果您认为这将成为瓶颈,个人资料有问题的代码并确定无误。
如果,(且仅当)这是你的瓶颈,那么就要努力改进它。
现在,改进你的改进方案:
1.
您可能不需要立即转换为毫秒。如果您只是收集数据,只需存储从gethrtime()
返回的完整64位数字并完成它。人类需要阅读的任何内容都可以在以后进行后期处理,或者在更新的频率上进行后期处理。
2.
如果您正在计算一些重复事件,您可以尝试在两次调用之间执行差异的划分,如果您是非常正在经常调用gethrtime()
以产生瓶颈:
static hrtime_t oldtime;
hrtime_t newtime = gethrtime();
int milliseconds = fastDivByOneMillion((UI32)(newtime - oldtime));
oldtime = newtime;
3.
您可以将fastDivByOneMillion()
实现为乘法,并将幂除以2:
int fastDivByOneMillion(UI32 nanoseconds)
{
return (int)((UI64)nanoseconds * 4295 >> 32);
}
注意:
>> 32
的最佳方法。大多数情况下,这只是一个或两个时钟周期
UI32
和UI64
来表示32位和64位无符号数。
答案 5 :(得分:2)
作为Joshua Haberman mentioned,您的编译器可能已经将除法乘以常数1000000转换为乘以“幻数”后跟移位(如果除法是整数运算)。您可以在Henry Warren的“Hacker's Delight”一书以及随附网站上了解更多有关正在发生的事情的详细信息:
他甚至有一个页面,其中包含一个用于幻数的Javascript计算器:
答案 6 :(得分:2)
首先,明显的免责声明:除非你每秒执行几百万次至少,否则它不会成为瓶颈,你应该放弃它。过早优化等等。
其次,您需要多大准确的结果?在二进制和十进制之间转换的一个方便的经验法则是2 ^ 10~ = 10 ^ 3.
换句话说,百万大致等于2 ^ 20。所以你可以正确地移动20.当然,编译器不会自动为你做这件事,因为它改变了结果。但是如果你愿意以微小的准确度生活,和该部门实际上是一个真正的性能问题,这将是我的建议。
答案 7 :(得分:0)
可以将整数除法转换为一系列更简单的操作。由Terje Mathisen推广的通用方法概述于第136页 Optimizing subroutines in assembly language。如果您事先知道数据类型的宽度以及您要划分的内容,那么将引导您完成如何将其转换为更简单的操作,理论上这可能比必须处理的更通用的除法操作更快任何除数。如果你担心其中一些整数的大小不同,仍然会有一些平台问题需要关注。
除非您实际上是用汇编语言对此进行编程,否则我会反对您实际上在改进SPARC除法实现过程中的任何内容。也许如果你使用的是一个非常古老的SPARC V7处理器,从分割前implemented in hardware开始,你可能会得到一些改进,但即使这样,我也会打赌内置分区更快。
无论如何,我怀疑你已经在这里进行了一些过早的优化。您应该首先分析您已经获得的应用程序,然后假设此部门对其运行时有任何重大影响,您应该同样地对该部门进行任何更改,以证明它按预期工作。你可以很容易地获得你认为会更快执行的代码,但实际上现在并不是这样,因为CPU缓存有些复杂。
答案 8 :(得分:0)
如果你可以解决这个问题,这是我的解决方案。
并说服自己毫秒应该是base2而不是base10。 ; - )
答案 9 :(得分:0)
1/1000000是0.000000000000000000 0100 0011 0001 1011 1101 1110 1000 0010 1101 0111 1011 0110 0011 01二进制 - 这是0x431BDE82 * 2 ^ -18
因此n / 1000000相当于(n * 0x431BDE82)&gt;&gt; 18
此外,n / 1000000相当于(n * 0x8637BD04)&gt;&gt; 19
请注意,这是一个“定点”计算,你应该知道精度可能会丢失。