sapling/eden/fs/fuse/InodeNumber.h
Victor Zverovich 3285a8f909 Replace Folly Format with fmt in logger to reduce binary size
Summary:
Now that fmt is available in Folly builds (D14813810), use it to reduce binary code size in Folly Logger. This is done by moving most of the formatting logic behind the type-erased `vformat` API. Previously it was instantiated for all combinations of formatting argument types used in calls to `FB_LOGF` and `XLOGF` in a program.

The effect of this change can be illustrated by looking at symbol sizes as given by `nm -S -td` for the following test function:

```
void test_log() {
  FB_LOGF(logger, WARN, "num events: {:06d}, duration: {:6.3f}", 1234, 5.6789);
}
```
compiled in `opt` mode.

`nm` before:

```
0000000004236736 0000000000000231 T test_log()
0000000004236992 0000000000001002 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > folly::LogStreamProcessor::formatLogString<int, double>(folly::Range<char const*>, int const&, double const&)
```

`nm` after:

```
0000000004237536 0000000000000231 T test_log()
0000000004237792 0000000000000251 W std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > folly::LogStreamProcessor::formatLogString<int, double>(folly::Range<char const*>, int const&, double const&)
0000000004238048 0000000000000740 W folly::LogStreamProcessor::vformatLogString[abi:cxx11](folly::Range<char const*>, fmt::v5::format_args, bool&)
```

Before we had one 1002 byte instantiation of `formatLogString<int, double>`. With this change it was reduced 4x to 251 bytes and non-template function `vformatLogString` was added which is shared among all logging calls. The size of `test_log` remained unchanged. There are even bigger savings from Folly Formatter instantiations which are no longer needed, e.g.

```
0000000004238032 0000000000001363 W _ZNK5folly13BaseFormatterINS_9FormatterILb0EJRKiRKdEEELb0EJS3_S5_EEclIZNKS7_8appendToINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEENSt9enable_ifIXsr12IsSomeStringIT_EE5valueEvE4typeERSH_EUlNS_5RangeIPKcEEE_EEvSK_
```

So in total this change results in ~5x per-call/instantiation binary size. It is possible to reduce binary size even further but it is not done in the current diff to keep it manageable.

In addition to binary size improvements, switching to fmt will potentially

* allow catching errors in format strings at compile time,
* simplify future migration to C++20 [`std::format`](http://eel.is/c++draft/format).

Reviewed By: simpkins

Differential Revision: D15485589

fbshipit-source-id: 06db4436839f11c2c3dbed7b36658e2193343411
2019-11-18 05:53:08 -08:00

157 lines
4.1 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <assert.h>
#include <fmt/format.h>
#include <folly/Format.h>
#include <glog/logging.h>
#include <stdint.h>
namespace facebook {
namespace eden {
/**
* Represents ino_t behind a slightly safer API. In general, it is a bug if
* Eden produces inode numbers with the value 0, so this class makes it harder
* to do that on accident.
*/
struct InodeNumber {
/// Default-initializes the inode number to 0.
constexpr InodeNumber() noexcept = default;
/**
* Initializes with a given nonzero number. Will assert in debug builds if
* initialized to zero.
*/
constexpr explicit InodeNumber(uint64_t ino) noexcept : rawValue_{ino} {
// This is intentionally an assert() rather than DCHECK_NE() since
// DCHECK_NE is not allowed in constexpr methods.
assert(0 != rawValue_);
}
/**
* Thrift does not support unsigned numbers, so it's common to instantiate
* InodeNumber from int64_t.
*/
static InodeNumber fromThrift(int64_t ino) {
return InodeNumber{static_cast<uint64_t>(ino)};
}
/**
* Returns a nonzero inode number. Asserts in debug builds if zero.
*
* Use this accessor when handing inode numbers to FUSE.
*/
uint64_t get() const {
DCHECK_NE(0, rawValue_);
return rawValue_;
}
/**
* Returns true if initialized with a nonzero inode number.
*/
constexpr bool hasValue() const {
return rawValue_ != 0;
}
/**
* Returns true if underlying value is zero.
*/
constexpr bool empty() const {
return rawValue_ == 0;
}
/**
* Returns the underlying value whether or not it's zero. Use this accessor
* when debugging or in tests.
*/
constexpr uint64_t getRawValue() const {
return rawValue_;
}
private:
uint64_t rawValue_{0};
};
inline bool operator==(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() == rhs.getRawValue();
}
inline bool operator!=(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() != rhs.getRawValue();
}
inline bool operator<(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() < rhs.getRawValue();
}
inline bool operator<=(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() <= rhs.getRawValue();
}
inline bool operator>(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() > rhs.getRawValue();
}
inline bool operator>=(InodeNumber lhs, InodeNumber rhs) {
return lhs.getRawValue() >= rhs.getRawValue();
}
std::ostream& operator<<(std::ostream& os, InodeNumber ino);
// Define toAppend() so folly::to<string>(ino) will work.
void toAppend(InodeNumber ino, std::string* result);
// Define toAppend() so folly::to<fbstring>(ino) will work.
void toAppend(InodeNumber ino, folly::fbstring* result);
constexpr InodeNumber operator""_ino(unsigned long long ino) {
return InodeNumber{ino};
}
/// The inode number of the mount's root directory.
constexpr InodeNumber kRootNodeId = 1_ino;
} // namespace eden
} // namespace facebook
namespace std {
template <>
struct hash<facebook::eden::InodeNumber> {
size_t operator()(facebook::eden::InodeNumber n) const {
// TODO: It may be worth using a different hash function. The default
// std::hash for integers is the identity function. But since we allocate
// inode numbers monotonically, this should be okay.
return std::hash<uint64_t>{}(n.getRawValue());
}
};
} // namespace std
namespace folly {
template <>
class FormatValue<facebook::eden::InodeNumber> {
public:
explicit FormatValue(facebook::eden::InodeNumber ino) : ino_(ino) {}
template <class FormatCallback>
void format(FormatArg& arg, FormatCallback& cb) const {
FormatValue<uint64_t>{ino_.getRawValue()}.format(arg, cb);
}
private:
const facebook::eden::InodeNumber ino_;
};
} // namespace folly
namespace fmt {
template <>
struct formatter<facebook::eden::InodeNumber> : formatter<uint64_t> {
auto format(facebook::eden::InodeNumber n, format_context& ctx) {
return formatter<uint64_t>::format(n.getRawValue(), ctx);
}
};
} // namespace fmt