build: remove about 30 cpu-sec of compile time by explicit instantiation

Apparently the fmt contraption has some extremely popular overloads, and
the boost stuff in there gets built approximately infinite times in
every compilation unit.

Change-Id: Ideba2db7d6bf8559e4d91974bab636f5ed106198
This commit is contained in:
Jade Lovelace 2024-08-22 21:02:52 -07:00 committed by Rebecca Turner
parent 422550fd68
commit 4d89844207
No known key found for this signature in database
5 changed files with 52 additions and 15 deletions

14
src/libutil/fmt.cc Normal file
View file

@ -0,0 +1,14 @@
#include "fmt.hh" // IWYU pragma: keep
template class boost::basic_format<char>;
namespace nix {
// Explicit instantiation saves about 30 cpu-seconds of compile time
template HintFmt::HintFmt(const std::string &, const Uncolored<std::string> &s);
template HintFmt::HintFmt(const std::string &, const std::string &s);
template HintFmt::HintFmt(const std::string &, const uint64_t &, const char * const &);
HintFmt::HintFmt(const std::string & literal) : HintFmt("%s", Uncolored(literal)) {}
}

View file

@ -3,7 +3,6 @@
#include <iostream>
#include <string>
#include <optional>
#include <boost/format.hpp>
// Darwin and FreeBSD stdenv do not define _GNU_SOURCE but do have _Unwind_Backtrace.
#if __APPLE__ || __FreeBSD__
@ -12,6 +11,9 @@
#include <boost/stacktrace.hpp>
#include "ansicolor.hh"
// Explicit instantiation in fmt.cc
extern template class boost::basic_format<char>;
namespace nix {
/**
@ -157,7 +159,9 @@ public:
* Format the given string literally, without interpolating format
* placeholders.
*/
HintFmt(const std::string & literal) : HintFmt("%s", Uncolored(literal)) {}
// Moved out of line because it was instantiating the template below in
// every file in the project.
HintFmt(const std::string & literal);
/**
* Interpolate the given arguments into the format string.
@ -193,6 +197,11 @@ public:
}
};
// Explicit instantiations in fmt.cc
extern template HintFmt::HintFmt(const std::string &, const Uncolored<std::string> &s);
extern template HintFmt::HintFmt(const std::string &, const std::string &s);
extern template HintFmt::HintFmt(const std::string &, const uint64_t &, const char * const &);
std::ostream & operator<<(std::ostream & os, const HintFmt & hf);
}

View file

@ -17,6 +17,7 @@ libutil_sources = files(
'experimental-features.cc',
'file-descriptor.cc',
'file-system.cc',
'fmt.cc',
'git.cc',
'hash.cc',
'hilite.cc',

View file

@ -9,6 +9,31 @@
namespace nix {
template<typename T>
T readNum(Source & source)
{
unsigned char buf[8];
source(charptr_cast<char *>(buf), sizeof(buf));
auto n = readLittleEndian<uint64_t>(buf);
if (n > (uint64_t) std::numeric_limits<T>::max())
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
return (T) n;
}
template bool readNum<bool>(Source & source);
template unsigned char readNum<unsigned char>(Source & source);
template unsigned int readNum<unsigned int>(Source & source);
template unsigned long readNum<unsigned long>(Source & source);
template long readNum<long>(Source & source);
template unsigned long long readNum<unsigned long long>(Source & source);
template long long readNum<long long>(Source & source);
void BufferedSink::operator () (std::string_view data)
{

View file

@ -450,19 +450,7 @@ inline Sink & operator<<(Sink & sink, const Error & ex)
MakeError(SerialisationError, Error);
template<typename T>
T readNum(Source & source)
{
unsigned char buf[8];
source(charptr_cast<char *>(buf), sizeof(buf));
auto n = readLittleEndian<uint64_t>(buf);
if (n > (uint64_t) std::numeric_limits<T>::max())
throw SerialisationError("serialised integer %d is too large for type '%s'", n, typeid(T).name());
return (T) n;
}
T readNum(Source & source);
inline unsigned int readInt(Source & source)
{