空别名 shared_ptr 是空操作删除 shared_ptr 的一个很好的替代方法吗?
Is an empty aliasing shared_ptr a good alternative to a no-op deleting shared_ptr?
有时我需要 shared_ptr
具有无操作删除器的实例,因为 API 需要一个 shared_ptr
实例,它想要存储有限的时间,但我得到了一个原始指针,我不允许拥有的时间超过我 运行 的时间。
对于这种情况,我一直在使用空操作删除器,例如 [](const void *){}
,但今天我发现还有另一种替代方法,使用(或滥用?)aliasing constructor共 shared_ptr
个:
void f(ExpectedClass *ec) {
std::shared_ptr<ExpectedClass> p(std::shared_ptr<void>(), ec);
assert(p.use_count() == 0 && p.get() != nullptr);
apiCall(p);
}
我的问题是,这样做的更好方法是什么?为什么?性能期望是否相同?对于无操作删除器,我希望为删除器和引用计数的存储支付一些费用,当使用带有空 shared_ptr
.
的别名构造函数时,情况似乎并非如此。
关于性能,以下基准显示不稳定的数字:
#include <chrono>
#include <iostream>
#include <limits>
#include <memory>
template <typename... Args>
auto test(Args&&... args) {
using clock = std::chrono::high_resolution_clock;
auto best = clock::duration::max();
for (int outer = 1; outer < 10000; ++outer) {
auto now = clock::now();
for (int inner = 1; inner < 20000; ++inner)
std::shared_ptr<int> sh(std::forward<Args>(args)...);
auto time = clock::now()-now;
if (time < best) {
best = time;
outer = 1;
}
}
return best.count();
}
int main()
{
int j;
std::cout << "With aliasing ctor: " << test(std::shared_ptr<void>(), &j) << '\n'
<< "With empty deleter: " << test(&j, [] (auto) {});
}
我机器上的输出 clang++ -march=native -O2
:
With aliasing ctor: 11812
With empty deleter: 651502
具有相同选项的 GCC 给出了更大的比率,5921:465794。
-stdlib=libc++
的 Clang 产生了惊人的 12:613175.
快速板凳
#include <memory>
static void aliasConstructor(benchmark::State& state) {
for (auto _ : state) {
int j = 0;
std::shared_ptr<int> ptr(std::shared_ptr<void>(), &j);
benchmark::DoNotOptimize(ptr);
}
}
BENCHMARK(aliasConstructor);
static void NoOpDestructor(benchmark::State& state) {
for (auto _ : state) {
int j = 0;
std::shared_ptr<int> ptr(&j, [](int*){});
benchmark::DoNotOptimize(ptr);
}
}
BENCHMARK(NoOpDestructor);
给予
所以别名构造函数获胜。
有时我需要 shared_ptr
具有无操作删除器的实例,因为 API 需要一个 shared_ptr
实例,它想要存储有限的时间,但我得到了一个原始指针,我不允许拥有的时间超过我 运行 的时间。
对于这种情况,我一直在使用空操作删除器,例如 [](const void *){}
,但今天我发现还有另一种替代方法,使用(或滥用?)aliasing constructor共 shared_ptr
个:
void f(ExpectedClass *ec) {
std::shared_ptr<ExpectedClass> p(std::shared_ptr<void>(), ec);
assert(p.use_count() == 0 && p.get() != nullptr);
apiCall(p);
}
我的问题是,这样做的更好方法是什么?为什么?性能期望是否相同?对于无操作删除器,我希望为删除器和引用计数的存储支付一些费用,当使用带有空 shared_ptr
.
关于性能,以下基准显示不稳定的数字:
#include <chrono>
#include <iostream>
#include <limits>
#include <memory>
template <typename... Args>
auto test(Args&&... args) {
using clock = std::chrono::high_resolution_clock;
auto best = clock::duration::max();
for (int outer = 1; outer < 10000; ++outer) {
auto now = clock::now();
for (int inner = 1; inner < 20000; ++inner)
std::shared_ptr<int> sh(std::forward<Args>(args)...);
auto time = clock::now()-now;
if (time < best) {
best = time;
outer = 1;
}
}
return best.count();
}
int main()
{
int j;
std::cout << "With aliasing ctor: " << test(std::shared_ptr<void>(), &j) << '\n'
<< "With empty deleter: " << test(&j, [] (auto) {});
}
我机器上的输出 clang++ -march=native -O2
:
With aliasing ctor: 11812
With empty deleter: 651502
具有相同选项的 GCC 给出了更大的比率,5921:465794。
-stdlib=libc++
的 Clang 产生了惊人的 12:613175.
快速板凳
#include <memory>
static void aliasConstructor(benchmark::State& state) {
for (auto _ : state) {
int j = 0;
std::shared_ptr<int> ptr(std::shared_ptr<void>(), &j);
benchmark::DoNotOptimize(ptr);
}
}
BENCHMARK(aliasConstructor);
static void NoOpDestructor(benchmark::State& state) {
for (auto _ : state) {
int j = 0;
std::shared_ptr<int> ptr(&j, [](int*){});
benchmark::DoNotOptimize(ptr);
}
}
BENCHMARK(NoOpDestructor);
给予
所以别名构造函数获胜。