Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: make the main dout() paths faster and more maintanable #20290

Merged
merged 9 commits into from Feb 28, 2018

Conversation

rzarzynski
Copy link
Contributor

@rzarzynski rzarzynski commented Feb 4, 2018

Summary

  • The validation of subsys parameter for dout & company becomes compile-time in almost all cases. The few exceptions are explicitly requested via ceph::dout::need_dynamic.
  • The level compile-time validation is back and doesn't depend on the VLA extension anymore. It's performed in all cases except those explicitly requested with ceph::dout::need_dynamic.
  • Memory indirection layer affecting SubsystemMap::should_gather() is gone. That is, the std::vector-typed m_subsys is no longer dereferenced on the main path.
  • SubsystemMap::should_gather<>() requires one third of comparisons than before.
  • The message crafting is put outside hot paths to help ICache/ITLB.

Interface changes:

  • debug_* configurables don't accept negative values anymore -- dout() with level -1 or 0 will be always gathered. Our documentation doesn't say a word about negatives.

Todo

  • Switch to uint8_t for dout levels.
  • Handle level -1 in compile-time if possible.
  • Switch direct ::should_gather() calls to the template variant when possible.
  • Clean up.
  • More tests as almost everything is going to be affected.
  • Resurrect the check on dout's levels where possible.

Performance impact

Environment

1 physial incerta node, 3 OSDs, all NVMe, Xeon E5-2650 v3

Results

  • random reads on FileStore, debugs set to 0/0.
Reference point: cb396a78d42f034dd61606a327cc703211e49cda

randread,  4 KiB: 167427, 164852, 163403, 162503, 165675,
randread, 64 KiB: 1940050, 1944297, 1933320, 1938220, 1934929,

wip-common-subsystemmap: 7cc9c4152c32403b353ff147b58b6314c3cd46b6

randread,  4 KiB: 168687, 170500, 170551, 171261, 171806,
randread, 64 KiB: 1954171, 1954686, 1962185, 1979382, 1982293,

// The rest. Should be as small as possible to unnecessarily not
// enlarge md_config_t and spread it other elements across cache
// lines. Access can be slow.
std::vector<ceph_subsys_item_t> m_subsys;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to carry the size as its already known. std::unique_ptr<std::array> instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me.

subs.add(1, "foosys", 20, 1);
subs.add(2, "bar", 20, 2);
subs.add(3, "baz", 10, 3);
subs.set_log_level(0, 10);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduce one method for that.

@rzarzynski
Copy link
Contributor Author

The make check bot fails because of:

/home/jenkins-build/build/workspace/ceph-pull-requests/src/log/test.cc: In member function 'virtual void Log_ReuseBad_Test::TestBody()':
/home/jenkins-build/build/workspace/ceph-pull-requests/src/log/test.cc:59:8: error: 'class ceph::logging::SubsystemMap' has no member named 'add'
   subs.add(1, "foo", 1, 1);

Apparently it rebased the PR on top of freshest master that got a new testcase -- we faced an undetected merge conflict. Not resolving it for now to not lose common reference point with PR #20177.

@@ -946,7 +939,7 @@ int md_config_t::_get_val(const std::string &key, char **buf, int len) const
string k(ConfFile::normalize_key_name(key));
// subsys?
for (size_t o = 0; o < subsys.get_num(); o++) {
std::string as_option = "debug_" + subsys.get_name(o);
std::string as_option = std::string("debug_") + subsys.get_name(o);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, possibly this could be made at compile-time.

@@ -178,13 +178,6 @@ void md_config_t::validate_schema()

void md_config_t::init_subsys()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop this.

} \
}(cct); \
\
if (should_gather) { \
Copy link
Contributor Author

@rzarzynski rzarzynski Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unlikely()? I can see that GCC arranges some dout usages in a way not friendly for static branch prediction.

@@ -52,16 +66,38 @@ class SubsystemMap {
return m_subsys[subsys].gather_level;
}

const std::string& get_name(unsigned subsys) const {
// TODO(rzarzynski): move to string_view?
const char* get_name(unsigned subsys) const {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr.

class SubsystemMap {
std::vector<Subsystem> m_subsys;
unsigned m_max_name_len;
// Access the current gathering levels must be *FAST* as they are
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIXME: Access to.

}(cct); \
\
if (unlikely(should_gather)) { \
[&]() __attribute__((noinline,cold)) { \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be controversial as we're using compiler-specific extensions here. However, the befit seems to outweigh the costs. Please take a look on a comparison of KernelDevice::read() translation before and after the change.

TODO: comment the intention behind the lambda and noinline,cold.

TODO: introduce a macro-based abstraction?

} \
}(cct); \
\
if (unlikely(should_gather)) { \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could differentiate the hinting policy basing on e.g. v and the default gathering level. Though, I'm not sure it's really necessary.

@rzarzynski rzarzynski changed the title [WIP] core: make the main dout() paths faster and more maintanable core: make the main dout() paths faster and more maintanable Feb 13, 2018
// generic macros
#define dout_prefix *_dout

#define dout_impl(cct, sub, v) \
do { \
if (cct->_conf->subsys.should_gather(sub, v)) { \
const bool should_gather = [&](const auto cctX) { \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting. I had an idea for replacing the template with a function accepting a lambda at one point but this is a very nice way of moving things to compile time.

#define SUBSYS(name, log, gather) \
ceph_subsys_##name,
#define DEFAULT_SUBSYS(log, gather)
#include "common/subsys.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to keep this Magic Macro stuff instead of just open-coding a data structure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the idea behind subsys.h and the macros is to have subsystems and their log/levels defined only once - a single source of true that is transformed to dependent structures later. Maybe it's possible to perform that via constexpr functions instead of macros but I haven't investigated that.

struct ceph_subsys_item_t {
const char* name;
uint8_t log_level;
uint8_t gather_level;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use uint_fast8_t here, letting the compiler pick another width if it's faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this might be a good measure to explicitly distinguish the hot path than the comment we have around m_gather_levels. Something targeting more the human's attention than machines. Fom x86's perspective I don't expect any difference. The open questions are: 1) the effect of fast types on ARMs and 2) the proliferation of uint_fast8_t across the codebase. This would be the first occurrence (not counting 3rd party code).

// The rest. Should be as small as possible to unnecessarily not
// enlarge md_config_t and spread it other elements across cache
// lines. Access can be slow.
std::vector<ceph_subsys_item_t> m_subsys;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me.

@rzarzynski rzarzynski force-pushed the wip-common-subsystemmap branch 4 times, most recently from 8e746c6 to 00a2614 Compare February 21, 2018 10:27
@rzarzynski
Copy link
Contributor Author

Rebased to freshest master and fixed the build failures.

Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will take a closer look early tomorrow.

@@ -43,14 +43,47 @@ class DoutPrefixProvider {
virtual ~DoutPrefixProvider() {}
};

// helpers
namespace ceph {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, we could do

namespace ceph::dout {
// ...
}

in C++17.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Thanks for pointing out this feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIXED.

/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the copyright line should be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIXED.

Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm in general.

@@ -1618,10 +1618,11 @@ bool BlueStore::OnodeSpace::map_any(std::function<bool(OnodeRef)> f)
return false;
}

void BlueStore::OnodeSpace::dump(CephContext *cct, int lvl)
template <int LogLevelV = 30>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rzarzynski could you rebase your change against master, as 8471cb4#diff-a9faffcf40600fd57aea5451cef5abe9R6344 in #19843 was merged recently. and your PR changes the function signature of BlueStore::_dump_onode().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@rzarzynski rzarzynski force-pushed the wip-common-subsystemmap branch 3 times, most recently from 676139a to 360f823 Compare February 27, 2018 10:31
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
The idea is to:
  1. Do not put the dout()'s crafting stuff on the hot, fall-through
     path. Cheapest branches are those that are forward and never taken.
  2. Move it to separated sections placed far away from the main path
     to be more friendly to ICache and ITLB. That is, dout_impl constructs
     a function now.

Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
@tchaikov tchaikov merged commit 971e3c3 into ceph:master Feb 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants