在我的主文件(具有主要功能的文件)中,我还有另一个功能:
unsigned long generate_random_number()
{
unsigned long y;
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
// mag01[x] = x * MATRIX_A for x=0,1
if (mti >= N) // generate N words at one time
{
int kk;
if (mti == N+1) // if init_genrand() has not been called
init_genrand(5489UL); // a default initial seed is used
for (kk=0;kk<N-M;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (;kk<N-1;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
本质上是来自此网站: http://www.the-control-freak.com/Random/Random.htm
如果我将函数和定义移至头文件,将实现移至cpp文件(仍不在类中)并将.o
对象文件链接到我的主程序,则性能会受到很大影响。该功能从大约11%的开销变为大约15%的开销(来自Google perf)。关于这是为什么的任何想法?
通常,如果链接目标文件,从目标文件中调用函数是否会花费很多开销?
makefile:
CXX = clang++
CFLAGS = -std=c++17 -O3 -Wall -Iinclude/
SRC = src/
INC = include/
random.o: $(SRC)random.cpp $(INC)random.hpp
$(CXX) $(CFLAGS) -c $(SRC)random.cpp
myprog: myprog.cpp random.o
$(CXX) $(CFLAGS) -o refactor myprog.cpp random.o -lprofiler
myprog.cpp的简化版本。 case语句不是在main内部,而是在另一个函数中。该函数称为N次,平均stdev通过套接字发送。
myprog.cpp
int main()
{
switch(hurry_ind)
{
case 0: return generate_random_number() % 19;
break;
case 1: return generate_random_number() % 100;
break;
case 2: return generate_random_number() % 9;
break;
case 3: return generate_random_number() % 914;
break;
case 4: return generate_random_number() % 355;
break;
case 5: return generate_random_number() % 348;
break;
case 6: return generate_random_number() % 65;
break;
}
}
答案 0 :(得分:1)
如果函数在同一编译单元中定义,则编译器可以内联一个经常调用的函数并进行一些积极的优化。这很可能是所报告的速度下降的原因。
我使用clang和gcc测试了代码。 Clang始终提供相同的生产率(每2000000000周期12.5275 s),因此我无法重现所描述的行为,但是当我将函数标记为{{1}时,gcc显着提高了性能(每2000000000周期8.31 vs. 10.42 s) }。因此,您尝试将inline
添加到初始版本(相同的编译单元)中的函数。如果降低性能,则根本原因是内联。
我使用的测试程序:
random.hpp
__attribute__((noinline))
random.cpp
#pragma once
unsigned long generate_random_number();
myprog.cpp
#define N 17U
#define M 13U
#define MATRIX_A 0x9908B0DFUL
#define UPPER_MASK 0x80000000UL
#define LOWER_MASK 0x7FFFFFFFUL
static unsigned long mt [ N ];
static int mti = N + 1;
void init_genrand ( unsigned long ulSeed )
{
mt [ 0 ]= ulSeed & 0xFFFFFFFFUL;
for ( mti = 1; mti < int(N); mti++ )
{
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt [ mti ] = ( 1812433253UL * ( mt [ mti - 1 ] ^ ( mt [ mti - 1 ] >> 30 ) ) + mti );
mt [ mti ] &= 0xFFFFFFFFUL;
/* for >32 bit machines */
}
}
#ifdef INLINE_THE_FUNCTION
inline
#endif
unsigned long generate_random_number()
{
unsigned long y;
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
// mag01[x] = x * MATRIX_A for x=0,1
if (mti >= int(N)) // generate N words at one time
{
int kk;
if (mti == N+1) // if init_genrand() has not been called
init_genrand(5489UL); // a default initial seed is used
for (kk=0; kk<int(N-M); kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (;kk<int(N-1); kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
我使用的编译器:
#ifdef SAME_COMPILATION_UNIT
#include "random.cpp"
#else
#include "random.hpp"
#endif
#include <iostream>
#include <chrono>
unsigned long calc(int hurry_ind)
{
switch(hurry_ind)
{
case 0: return generate_random_number() % 19;
case 1: return generate_random_number() % 100;
case 2: return generate_random_number() % 9;
case 3: return generate_random_number() % 914;
case 4: return generate_random_number() % 355;
case 5: return generate_random_number() % 348;
case 6: return generate_random_number() % 65;
}
return 0;
}
int main(int argc, char** argv)
{
int n = argc > 1 ? std::atol(argv[1]) : 0;
int res = 0;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < n; ++i)
res += calc(i % 7);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << res << "(" << diff.count() << " s)\n";
}
答案 1 :(得分:0)
当编译器在同一源文件(translation unit
)中看到2个函数时,它可以创建实现以优化寄存器。
这种被调用和调用函数的知识对于这种形式的优化至关重要。