一个空的别名shared_ptr是一个很好的替代无操作删除shared_ptr吗?

Is an empty aliasing shared_ptr a good alternative to a no-op deleting shared_ptr?

本文关键字:shared ptr 一个 操作 删除 很好 别名      更新时间:2023-10-16

有时我需要具有无操作删除器的shared_ptr实例,因为API期望它想要存储有限时间的shared_ptr实例,但我得到了一个原始指针,我不允许拥有比我运行的时间更长的时间。

对于这种情况,我一直在使用无操作删除器,如[](const void *){},但今天我发现还有另一种选择,使用(或滥用?)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);

  • gcc 10.2的比率为1/30
  • clang 11libc++的比值为1/25

所以别名构造函数胜出