Advertisement

Is it smelly code to have empty cpp file to take advantage of Precompiled headers?

Started by February 27, 2021 11:40 PM
7 comments, last by Shaarigan 3 years, 8 months ago

So I've been working with Precompiled headers for awhile now.
I only include headers that won't change, like mostly standard libraries (iostream, string, vector etc…).

But sometimes, I only need a struct to hold such data (string, vector and so on) and I would
have a class that uses the struct. Of course, it's also used by other classes, so I cannot
exclude it somehow.

An example would be this:

// Data.h
struct Data
{
	std::string mName;
 	std::string mOtherName;
	std::vector<int> mNumbers;
}

// Later in Foo.cpp
void Foo::DoSomething()
{
	Data someData;
	...Play with someData...
}

// And of course in Boo.cpp
void Boo::DoSomething()
{
	Data someData;
	...Play with someData...
}

Simply speaking, I'm in a situation where I cannot really put the Data struct into somewhere more excluded,
so I wanted to ask, is it smelly that Data has an Empty cpp file just to take advantage of Precompiled headers?

Well, the only way to make the functions inlined is to put them in the header file. So, no, it's not evil, per se.

Advertisement

taby said:

Well, the only way to make the functions inlined is to put them in the header file. So, no, it's not evil, per se.

Is that strictly true? I always assumed that if you put an inline function in a CPP file and then call it in the same file, it will in fact inline it. Maybe I need to look at the ASM code now.

Zansin said:
is it smelly that Data has an Empty cpp file just to take advantage of Precompiled headers?

no and don't forget to add the following directive at the top of the header file:

#pragma once

this directive can be used whether or not you've enabled Precompiled headers;

enjoy ?

Okay, thanks guys! Appreciate the help :D

Here is a small code to test the timing of the function calls:

inline.h

#ifndef INLINE_H
#define INLINE_H


#include <iostream>
#include <iomanip>
#include <chrono>
#include <vector>
#include <cmath>
using namespace std;


inline long unsigned int f(const long unsigned int x)
{
	return x * 10;
}


#endif

inline.cpp

#include "inline.h"


inline long unsigned int g(const long unsigned int x)
{
	return x * 10;
}

long unsigned int h(const long unsigned int x)
{
	return x * 10;
}


#define CLOCK_RES std::micro


float standard_deviation(const vector<chrono::duration<float, CLOCK_RES>>& src)
{
	float mean = 0;
	float size = static_cast<float>(src.size());

	for (size_t i = 0; i < src.size(); i++)
		mean += src[i].count();

	mean /= size;

	float sq_diff = 0;

	for (size_t i = 0; i < src.size(); i++)
	{
		float diff = src[i].count() - mean;
		sq_diff += diff * diff;
	}

	sq_diff /= size;

	return sqrtf(sq_diff);
}



int main(void)
{
	vector<chrono::duration<float, CLOCK_RES>> f_durations;
	vector<chrono::duration<float, CLOCK_RES>> g_durations;
	vector<chrono::duration<float, CLOCK_RES>> h_durations;

	for (long unsigned int reps = 0; reps < 1000000; reps++)
	{
		if(reps % 100 == 0)
			cout << reps << endl;

		chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();

		for (long unsigned int i = 0; i < 10000000; i++)
		{
			long unsigned int j = f(i);
		}

		chrono::high_resolution_clock::time_point end_time = std::chrono::high_resolution_clock::now();
		chrono::duration<float, CLOCK_RES> elapsed = end_time - start_time;

		f_durations.push_back(elapsed);

		start_time = std::chrono::high_resolution_clock::now();

		for (long unsigned int i = 0; i < 10000000; i++)
		{
			long unsigned int j = g(i);
		}

		end_time = std::chrono::high_resolution_clock::now();
		elapsed = end_time - start_time;

		g_durations.push_back(elapsed);

		start_time = std::chrono::high_resolution_clock::now();

		for (long unsigned int i = 0; i < 10000000; i++)
		{
			long unsigned int j = h(i);
		}

		end_time = std::chrono::high_resolution_clock::now();
		elapsed = end_time - start_time;

		h_durations.push_back(elapsed);
	}

	float f_total = 0;

	for (size_t i = 0; i < f_durations.size(); i++)
		f_total += f_durations[i].count();

	float g_total = 0;

	for (size_t i = 0; i < g_durations.size(); i++)
		g_total += g_durations[i].count();

	float h_total = 0;

	for (size_t i = 0; i < h_durations.size(); i++)
		h_total += h_durations[i].count();

	cout << fixed << setprecision(10);

	cout << f_total / f_durations.size() << " +/- " << standard_deviation(f_durations) << " microseconds" << endl;
	cout << g_total / g_durations.size() << " +/- " << standard_deviation(g_durations) << " microseconds" << endl;
	cout << h_total / h_durations.size() << " +/- " << standard_deviation(h_durations) << " microseconds" << endl;

	return 0;
}

Advertisement

P.S. This document is great for those using MSVC++:

https://docs.microsoft.com/en-us/cpp/cpp/inline-functions-cpp?view=msvc-160

Gnollrunner said:
I always assumed that if you put an inline function in a CPP file and then call it in the same file, it will in fact inline it.

It is for sure up to the compiler to inline it or not. There are compiler dependant hints that forces it to inline something (or at least it is more likely that it does) but the ususal inline keyword doesn't fulfill that.

Anyways, a function can only be inlined in a single compiler unit so including such a function into an overall empty .cpp file doesn't inline it for anything except this compiler unit. Inline means “don't create a function address for the code and put the instructions directly in place instead” so technically, an inlined function is just the instructions without the overhead of a context switch. I doubt precompiling also precompiles an inline function to anything except a generic function call as if it won't have the inline keyword

This topic is closed to new replies.

Advertisement