Fix wrong namespacing of benchmarking constructor helpers

This commit is contained in:
Joe Burzinski 2019-11-18 22:22:38 -06:00 committed by Martin Hořeňovský
parent a537ccae22
commit cfba9dce97
3 changed files with 66 additions and 52 deletions

View File

@ -168,7 +168,7 @@ Note that it is not possible to simply use the same instance for different runs
and resetting it between each run since that would pollute the measurements with and resetting it between each run since that would pollute the measurements with
the resetting code. the resetting code.
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
the same semantics as providing a callable to `meter.measure` with `int` argument: the same semantics as providing a callable to `meter.measure` with `int` argument:
```c++ ```c++
@ -189,19 +189,17 @@ construct and destroy objects without dynamic allocation and in a way that lets
you measure construction and destruction separately. you measure construction and destruction separately.
```c++ ```c++
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
{
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs()); std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); }); meter.measure([&](int i) { storage[i].construct("thing"); });
}) };
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter) BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
{
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs()); std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage) for(auto&& o : storage)
o.construct("thing"); o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); }); meter.measure([&](int i) { storage[i].destruct(); });
}) };
``` ```
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T` `Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`

View File

@ -14,60 +14,62 @@
#include <type_traits> #include <type_traits>
namespace Catch { namespace Catch {
namespace Detail { namespace Benchmark {
template <typename T, bool Destruct> namespace Detail {
struct ObjectStorage template <typename T, bool Destruct>
{ struct ObjectStorage
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
ObjectStorage() : data() {}
ObjectStorage(const ObjectStorage& other)
{ {
new(&data) T(other.stored_object()); using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
}
ObjectStorage(ObjectStorage&& other) ObjectStorage() : data() {}
{
new(&data) T(std::move(other.stored_object()));
}
~ObjectStorage() { destruct_on_exit<T>(); } ObjectStorage(const ObjectStorage& other)
{
new(&data) T(other.stored_object());
}
template <typename... Args> ObjectStorage(ObjectStorage&& other)
void construct(Args&&... args) {
{ new(&data) T(std::move(other.stored_object()));
new (&data) T(std::forward<Args>(args)...); }
}
template <bool AllowManualDestruction = !Destruct> ~ObjectStorage() { destruct_on_exit<T>(); }
typename std::enable_if<AllowManualDestruction>::type destruct()
{
stored_object().~T();
}
private: template <typename... Args>
// If this is a constructor benchmark, destruct the underlying object void construct(Args&&... args)
template <typename U> {
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); } new (&data) T(std::forward<Args>(args)...);
// Otherwise, don't }
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
T& stored_object() template <bool AllowManualDestruction = !Destruct>
{ typename std::enable_if<AllowManualDestruction>::type destruct()
return *static_cast<T*>(static_cast<void*>(&data)); {
} stored_object().~T();
}
TStorage data; private:
}; // If this is a constructor benchmark, destruct the underlying object
template <typename U>
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
// Otherwise, don't
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
T& stored_object()
{
return *static_cast<T*>(static_cast<void*>(&data));
}
TStorage data;
};
}
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
} }
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
} }
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED

View File

@ -126,5 +126,19 @@ TEST_CASE("Benchmark containers", "[!benchmark]") {
REQUIRE(v[i] == generated); REQUIRE(v[i] == generated);
} }
} }
SECTION("construct and destroy example") {
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); });
};
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage)
o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); });
};
}
} }
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING #endif // CATCH_CONFIG_ENABLE_BENCHMARKING