0% found this document useful (0 votes)
23 views18 pages

Cheap, Simple, and Safe Logging Using Expression Templates - Marc Eaddy

Uploaded by

alan88w
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
23 views18 pages

Cheap, Simple, and Safe Logging Using Expression Templates - Marc Eaddy

Uploaded by

alan88w
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

CHEAP, SIMPLE, AND SAFE

LOGGING USING C++


EXPRESSION TEMPLATES
Marc Eaddy, Intel
2 /15

A SIMPLE LOG MACRO


#define LOG(msg) \
if (s_bLoggingEnabled) \
std::cout << __FILE__ << "(" << __LINE__ << "): " << msg << std::endl

void foo() {
string file = "blah.txt";
int error = 123;
...
LOG("Read failed: " << file << " (" << error << ")");
}

// Outputs: Convenient
// test.cpp(5): Read failed: blah.txt (123) and type safe
streaming interface
Marc Eaddy, Intel 9/12/2014
3 /15

AFTER PRE-PROCESSING

void foo() {
string file = "blah.txt";
int error = 123;
...
if (s_bLoggingEnabled)
std::cout << "foo.cpp" << "(" << 53 << "): "
<< "Read failed: " << file << " (" << error << ")")
<< std::endl;
}

Marc Eaddy, Intel 9/12/2014


4 /15

ASSEMBLER
void foo() {
string file = "blah.txt"; % icc --std=c++11 -O3 -g -fcode-asm -S test.cpp
int error = 123;
...
movb g_bLogging(%rip), %al
testb %al, %al

33 instructions
je ..B2.14
movl $_ZSt4cout, %edi

10 function calls
movl $.L_2__STRING.3, %esi
call ostream& operator<<(ostream&, char const*)
movq %rax, %rdi
movl $.L_2__STRING.0, %esi
call ostream& operator<<(ostream&, char const*)
movq %rax, %rdi
movl $19, %esi
call ostream::operator<<(int)
...
movq %rax, %rdi
movl $ostream& endl(ostream&), %esi
call ostream::operator<<(ostream& (*)(ostream&))
}

Marc Eaddy, Intel 9/12/2014


5 /15

PROBLEM
• Log-related instructions...
• may prevent compiler optimizations
• may hurt icache performance

• Goal: Reduce instructions at call site but retain


• Speed
• Type safety
• Convenience

Marc Eaddy, Intel 9/12/2014


6 /15

A SOLUTION
• How to retain streaming interface without op<< function calls
at call site?
• Evaluate expressions at compile-time instead of runtime
• "Expression templates" use operator overloading to pack an
expression into a type
• Matrix D = A + B * C;
• polygons.Find(VERTICES == 4 && WIDTH >= 20);
• LOG("Read failed: " << file << " (" << error << ")");

Adapted from Vaughn Cato’s solution


http://stackoverflow.com/questions/19415845/a-better-log-macro-using-template-metaprogramming
7 /15

LOGDATA<>
#define LOG(msg) \
if (s_bLoggingEnabled) \
(Log(__FILE__, __LINE__, LogData<None>() << msg))

template<typename List>
struct LogData {
typedef List type;
List list;
};

struct None { };
Marc Eaddy, Intel 9/12/2014
8 /15

LOGDATA<>

template<typename Begin, typename Value>


LogData<std::pair<Begin&&, Value&&>> operator<<(LogData<Begin>&& begin,
Value&& v) noexcept {
return {{ std::forward<Begin>(begin.list), std::forward<Value>(v) }};
}

Marc Eaddy, Intel


9 /15

LOGDATA<>
LOG("Read failed: " << file << " (" << error << ")");
LogData<
pair<
pair<
pair<
pair<
pair<
None,
char const*>, "Read failed: "
string const&>, file
char const*>, " ("
int const&>, error
char const*> ")"
Marc Eaddy, Intel
>
10 /15

LOG()
template<typename TLogData>
void Log(const char* file, int line, TLogData&& data) noexcept __attribute__((__noinline__)) {
std::cout << file << "(" << line << "): ";
Log_Recursive(std::cout, std::forward<typename TLogData::type>(data.list));
cout << endl;
}

template<typename TLogDataPair>
void Log_Recursive(std::ostream& os, TLogDataPair&& data) noexcept {
Log_Recursive(os, std::forward<typename TLogDataPair::first_type>(data.first));
os << std::forward<typename TLogDataPair::second_type>(data.second);
}

inline void Log_Recursive(std::ostream& os, None) noexcept


{ }
Marc Eaddy, Intel 9/12/2014
11 /15

HANDLE STREAM MANIPULATORS


(EG ENDL)
typedef std::ostream& (*PfnManipulator)(std::ostream&);

template<typename Begin>
LogData<pair<Begin&&, PfnManipulator>> operator<<(LogData<Begin>&& begin,
PfnManipulator pfn)
noexcept {
return {{ std::forward<Begin>(begin.list), pfn }};
}

Marc Eaddy, Intel 9/12/2014


12 /15

STRING LITERAL OPTIMIZATION


template<typename Begin, size_t n>
LogData<pair<Begin&&, const char*>> operator<<(LogData<Begin>&& begin,
const char (&sz)[n])
noexcept {
return {{ std::forward<Begin>(begin.list), sz }};
}
Specialization
handles all string
literals

Marc Eaddy, Intel 9/12/2014


13 /15

ASSEMBLER
movb g_bLogging(%rip), %al
testb %al, %al
je ..B6.7
movb $0, (%rsp)
movl $.L_2__STRING.4, %ecx
movl $.L_2__STRING.3, %edi 9 instructions
movl $40, %esi 1 pimp’d function call
lea 128(%rsp), %r9
call void Log<pair<pair<pair<pair<pair<None, char const*>,
string const&>, char const*>, int const&>, char const*> >(char
const*, int, LogData<pair<pair<pair<pair<pair<None, char
const*>, string const&>, char const*>, int const&>, char const*>
> const&)

Marc Eaddy, Intel 9/12/2014


14 /15

SUMMARY
• Expression templates solution
• Reduced instructions at call site by 73% (33  9)
• Mo' args, mo' savings

Marc Eaddy, Intel 9/12/2014


15 /15

THANK YOU!

Marc Eaddy, Intel 9/12/2014


16 /15

VARIADIC TEMPLATE SOLUTION


#define LOG(...) Log(__FILE__, __LINE__, __VA_ARGS__)

template<typename... Args>
void Log_Variadic(const char* file, int line, const Args&... args) {
std::cout << file << "(" << line << "): ";
Log_Recursive(file, line, std::cout, args...);
std::cout << std::endl;
}

template<typename T, typename... Args>


void Log_Recursive(const char* file, int line, std::ostream& os, T first, const Args&... rest) {
os << first;
Log_Recursive(file, line, os, rest...);
}

inline void Log_Recursive(const char* file, int line, std::ostream& os) { /* Empty */ }
Marc Eaddy, Intel 9/12/2014
17 /15

VARIADIC TEMPLATE ASSEMBLER


movb g_bLogging(%rip), %al
testb %al, %al
je ..B1.7
addq $-16, %rsp
movl $.L_2__STRING.3, %edi 12 instructions
movl $26, %esi
1 funky function call
movl $.L_2__STRING.4, %edx
movl $.L_2__STRING.5, %r8d
lea 24(%rsp), %rcx
lea 32(%rsp), %r9
movq $.L_2__STRING.6, (%rsp)
call void Log_Variadic<char [14], string, char [3], int, char [2]>(
char const*, int, char const (&) [14], string const&,
char const (&) [3], int const&,
Marc Eaddy, Intel char const (&) [2])
9/12/2014
18 /15

VARIADIC TEMPLATE SOLUTION


• Con: lose streaming convenience

LOG("Read failed: " << file << " (" << error << ")");

LOG("Read failed: ", file, " (", error, ")");

Marc Eaddy, Intel 9/12/2014

You might also like