Fmt: Add `extern template` in non-header mode

Created on 4 Dec 2019  路  6Comments  路  Source: fmtlib/fmt

Compilation speed could be reduced significantly with the addition of extern template declaration in non-header mode.

Compilation times for the following snippet (with cmake release build):

  • with extern template: ~ 0.6s
  • without extern template: ~ 2.5s
#include <fmt/format.h>

namespace fmt{
extern template FMT_API format_context::iterator internal::vformat_to(
    internal::buffer<char>&, string_view, basic_format_args<format_context>);
// ... all templates from fmt/src/format.cc
}

int main(){
    fmt::memory_buffer b;
    fmt::format_to(b, "Hello, {}!", "world");
    return 0;
}

Most helpful comment

Added extern template in https://github.com/fmtlib/fmt/commit/6f435f55c88f6a143e064fa061ee32ae4152bca9 which brought the compile time from 2.3s to 0.3s with -O2 or from 0.6s to 0.3s with the default settings (-O0).

Before (-O2):

% time g++ -c test.cc -I include -std=c++17 -O2
g++ -c test.cc -I include -std=c++17 -O2  2.22s user 0.08s system 99% cpu 2.311 total

After (-O2):

% time g++ -c test.cc -I include -std=c++17 -O2
g++ -c test.cc -I include -std=c++17 -O2  0.26s user 0.04s system 98% cpu 0.303 total

Before (default):

% time g++ -c test.cc -I include -std=c++17
g++ -c test.cc -I include -std=c++17  0.53s user 0.06s system 98% cpu 0.601 total

After (default):

% time g++ -c test.cc -I include -std=c++17
g++ -c test.cc -I include -std=c++17  0.24s user 0.06s system 98% cpu 0.301 total

Thanks for the suggestion!

All 6 comments

Thanks for the suggestion but you can get better compile times by using the core API: https://fmt.dev/dev/api.html#core-api

Is there some downsides of using extern template?
Compilation time of (code using) format.h with extern templates are comprable to core.h .

I can think only of inlining, but this probably should not be the problem with string formatting library and could be resolved by lto

The main drawback is duplication but I guess it can be addressed by only having the following two instantiations extern:

extern template FMT_API std::string internal::vformat<char>(
    string_view, basic_format_args<format_context>);

extern template FMT_API format_context::iterator internal::vformat_to(
    internal::buffer<char>&, string_view, basic_format_args<format_context>);

A PR would be welcome.

I think we could avoid duplication by splitting src/format.cc into something like:

  • include/fmt/format-non-header.h
FMT_EXTERN template FMT_API std::string internal::vformat<char>(
    string_view, basic_format_args<format_context>);
...
  • src/format.cc
#define FMT_EXTERN
#include "fmt/format-non-header.h"
  • include/fmt/format.h
#define FMT_EXTERN extern
#include "fmt/format-non-header.h"

If it is ok, I'll make PR.

If that's just two declarations I mentioned earlier then duplicating is fine. I don't think the rest save much and I'd rather not introduce another header just for this.

Added extern template in https://github.com/fmtlib/fmt/commit/6f435f55c88f6a143e064fa061ee32ae4152bca9 which brought the compile time from 2.3s to 0.3s with -O2 or from 0.6s to 0.3s with the default settings (-O0).

Before (-O2):

% time g++ -c test.cc -I include -std=c++17 -O2
g++ -c test.cc -I include -std=c++17 -O2  2.22s user 0.08s system 99% cpu 2.311 total

After (-O2):

% time g++ -c test.cc -I include -std=c++17 -O2
g++ -c test.cc -I include -std=c++17 -O2  0.26s user 0.04s system 98% cpu 0.303 total

Before (default):

% time g++ -c test.cc -I include -std=c++17
g++ -c test.cc -I include -std=c++17  0.53s user 0.06s system 98% cpu 0.601 total

After (default):

% time g++ -c test.cc -I include -std=c++17
g++ -c test.cc -I include -std=c++17  0.24s user 0.06s system 98% cpu 0.301 total

Thanks for the suggestion!

Was this page helpful?
0 / 5 - 0 ratings