Optimize copy / assignment and size of json iterator#3452
Optimize copy / assignment and size of json iterator#3452sjanel wants to merge 0 commit intonlohmann:developfrom
Conversation
|
Thanks! It seems @falbrechtskirchinger is working in #3446 on similar issues - can you please check whether his adjustments go in the same direction? |
Some of this is probably redundant due to my pending PR. Will look at the code in a second.
MSVC still can't handle it: But I agree that pessimizing this for everyone else is bad practice.
This project uses Artistic Style to format code which is executed by |
Thanks for your comment. I checked the PR and it's not exactly the same thing, it's more about preparing for C++20 support with ranges. This PR is mainly an optimization of the |
Thanks for taking a look at my PR! I am currently working on a cleaner |
20ce876 to
a9b7161
Compare
| : std::alignment_of<ObjectIterator>::value); | ||
|
|
||
| protected: | ||
| typename std::aligned_storage<kMaxIteratorSize, kMaxIteratorAlignment>::type m_data; |
There was a problem hiding this comment.
Please use alignas(kMaxIteratorAlignment) std::uint8_t m_data[kMaxIteratorSize];
std::aligned_storage has been deprecated.
There was a problem hiding this comment.
I am working on this PR on my fork. Besides Windows weird issues, this change triggers some warnings (as error):
/__w/json/json/include/nlohmann/detail/iterators/internal_iterator.hpp:67:48: error: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,-warnings-as-errors]
Looks like a bit too much to use std::array here IMO.
There was a problem hiding this comment.
You can annotate the line with
// NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) to suppress the error.
NOLINTNEXTLINE works too, if you place it above the statement and not on the same line (probably better here).
There was a problem hiding this comment.
PR is ready for review (Microsoft issues fixed ;)), although I still have different formatter output with make amalgamate (my astyle version is Artistic Style Version 3.1). Is it an issue, and if so, why do I have all these differences ? (see include/nlohmann/json.hpp for instance)
a9b7161 to
9878d78
Compare
9878d78 to
32100a0
Compare
falbrechtskirchinger
left a comment
There was a problem hiding this comment.
Some initial thoughts.
The formatting of json.hpp is very distracting. I scrolled through the file very quickly and probably missed most of your changes.
| internal_iterator_impl(const internal_iterator_impl& o) noexcept | ||
| : m_it_type(o.m_it_type) | ||
| { | ||
| switch (m_it_type) |
There was a problem hiding this comment.
Any particular reason you're not using an enum class[ : std::uint8_t] here?
| ::new (&this->primitive_iterator()) primitive_iterator_t(o.primitive_iterator()); | ||
| break; | ||
| default: | ||
| break; |
There was a problem hiding this comment.
This should at least be:
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINEPossibly even:
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE| ::new (&this->primitive_iterator()) primitive_iterator_t(std::move(o.primitive_iterator())); | ||
| break; | ||
| default: | ||
| break; |
| this->primitive_iterator() = o.primitive_iterator(); | ||
| break; | ||
| default: | ||
| break; |
There was a problem hiding this comment.
And here. You get the idea...
| #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 10000) < 50000 | ||
| #undef JSON_HAS_TRIVIALLY_COPYABLE | ||
| #endif | ||
| #endif |
There was a problem hiding this comment.
a) For it to be overridable, you should define it to 0 or 1, just like the other JSON_HAS_* macros.
b) Use the Hedley version check macro: JSON_HEDLEY_GNUC_VERSION_CHECK(5, 0, 0).
c) (For later:) Remember to document it in docs/mkdocs/docs/api/macros/index.md + docs/mkdocs/docs/api/macros/json_has_trivially_copyable.md.
include/nlohmann/json.hpp
Outdated
| #include <numeric> // accumulate | ||
| #include <string> // string, stoi, to_string | ||
| #include <utility> // declval, forward, move, pair, swap | ||
| #include <vector> // vector |
There was a problem hiding this comment.
About the formatting: I assume you reformatted with clang-format and then used astyle?
astyle does not produce consistent output for equal source input. It keeps some of the input formatting, especially some white space as you can see here.
I think you'll have to find a way to undo that formatting. Otherwise reviewing and resolving merge conflicts isn't going to be much fun.
There was a problem hiding this comment.
"astyle does not produce consistent output for equal source input." Now I understood the issue, yes I have clang-format on save by default in my IDE. I did not know it would harm astyle format afterwards. I will fix all the formatting issues manually, sorry for the inconvenience.
include/nlohmann/json.hpp
Outdated
|
|
||
| // This could have been written as: | ||
| // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); | ||
| // result.m_it.set_array_iterator(m_value.array->insert(pos.m_it.array_iterator(), cnt, val)); |
There was a problem hiding this comment.
I've come across a related Codacy issue today ... both things could be solved with a helper. Doesn't have to be here though.
| : sizeof(object_iterator_t))]; | ||
| #endif | ||
| } m_data {}; | ||
| }; |
There was a problem hiding this comment.
Horrible, indeed. I think I have a better solution. Will get back to you when I have more time.
There was a problem hiding this comment.
This should work:
https://godbolt.org/z/MGhhEsd3G
namespace detail
{
template<std::size_t... Is>
struct max;
template<>
struct max<> {
using type = std::integral_constant<std::size_t, 0>;
};
template<std::size_t I, std::size_t... Rest>
struct max<I, Rest...> {
using rest_type = typename max<Rest...>::type;
using type = typename std::conditional<
(I > rest_type::value),
std::integral_constant<std::size_t, I>,
rest_type>::type;
};
template<typename... Ts>
using max_alignof = typename max<alignof(Ts)...>::type;
template<typename... Ts>
using max_sizeof = typename max<sizeof(Ts)...>::type;
}
constexpr std::size_t data_align =
detail::max_alignof<int, double, char>::value;
constexpr std::size_t data_size =
detail::max_sizeof<int, double, char>::value;
alignas(data_align) std::uint8_t data[data_size];Maybe add a new file meta/algorithm.hpp or similar.
Edit: On second thought, type_traits.hpp should be fine.
fa038d1 to
09849a6
Compare
|
Please update to the latest develop branch (we renamed some folders in #3462). |
09849a6 to
49badfc
Compare
Ok, done |
| namespace detail | ||
| { | ||
|
|
||
| template<class ObjectIterator, class ArrayIterator> |
There was a problem hiding this comment.
Please add some documentation on the idea behind the class.
| { | ||
| internal_data_t() noexcept = default; | ||
|
|
||
| #ifdef JSON_HAS_CPP_14 |
There was a problem hiding this comment.
Please document the idea behind this piece of code.
There was a problem hiding this comment.
And a note about the std::aligned_storage deprecation in C++23.
falbrechtskirchinger
left a comment
There was a problem hiding this comment.
I'd still prefer something like my proposed max_alignof and max_sizeof traits over nested tertiary operators and separate code paths for C++11 and C++14 and up.
| */ | ||
| template<class ObjectIterator, class ArrayIterator> | ||
| using internal_iterator = internal_iterator_impl < ObjectIterator, ArrayIterator, | ||
| #if defined(__clang__) || !defined(JSON_HEDLEY_GNUC_VERSION) || JSON_HEDLEY_GNUC_VERSION_CHECK(5, 0, 0) |
There was a problem hiding this comment.
This condition isn't needed anymore, right? It's handled in your type trait implementation.
| return value; | ||
| } | ||
|
|
||
| #if defined(__clang__) || !defined(JSON_HEDLEY_GNUC_VERSION) || JSON_HEDLEY_GNUC_VERSION_CHECK(5, 0, 0) |
There was a problem hiding this comment.
This can be simplified to:
#if !defined(JSON_HEDLEY_GNUC_VERSION) || JSON_HEDLEY_GNUC_VERSION_CHECK(5, 0, 0)
nlohmann
left a comment
There was a problem hiding this comment.
Please address @falbrechtskirchinger 's comments.
49badfc to
a3e6e26
Compare
Hello,
I stambled accross this clang-tidy warning in my project and investigated further,
itbeing ajson::const_iterator:It looks like
json::iteratoris actually non trivially copyable (and quite big), which is unexpected for an iterator and the reason why clang-tidy is complaining. In templated code it is common practice to pass iterators by value and not by reference. After investigation and reading of the code I realized that it was made like that at the beginning as a workaround for a MSVC bug (see #1608). It is sad to impact all other compilation modes and compilers just for a MSVC bug.I redesigned
internal_iterator_twith two distinct implementations, one optimized in the caseprimitive_iterator_t,object_t::iteratorandarray_t::iteratorare all trivially copyable (standard case, mainly for non-MSVC compilers), one for the general case (mainly used by MSVC, and other compilers with exotic vector and/or map iterator implementations).This is the fix that I propose, in the hope that since then, the MSVC issue is not a concern anymore. After this PR, all operations on iterators are expected to be faster and iterators can be passed by value without any performance impact like standard operators.