19

就我对资源管理的了解而言,在堆上分配东西(操作符new)应该总是比在堆栈上分配(自动存储)慢,因为堆栈是基于 LIFO 的结构,因此它需要最少的簿记,并且要分配的下一个地址的指针是微不足道的。

到目前为止,一切都很好。现在看下面的代码:

/* ...includes... */

using std::cout;
using std::cin;
using std::endl;

int bar() { return 42; }

int main()
{
    auto s1 = std::chrono::steady_clock::now();
    std::packaged_task<int()> pt1(bar);
    auto e1 = std::chrono::steady_clock::now();

    auto s2 = std::chrono::steady_clock::now();
    auto sh_ptr1 = std::make_shared<std::packaged_task<int()> >(bar);
    auto e2 = std::chrono::steady_clock::now();

    auto first = std::chrono::duration_cast<std::chrono::nanoseconds>(e1-s1);
    auto second = std::chrono::duration_cast<std::chrono::nanoseconds>(e2-s2);

    cout << "Regular: " << first.count() << endl
         << "Make shared: " << second.count() << endl;

    pt1();
    (*sh_ptr1)();

    cout << "As you can see, both are working correctly: " 
         << pt1.get_future().get() << " & " 
         << sh_ptr1->get_future().get() << endl;

    return 0;
}

结果似乎与上面解释的内容相矛盾:

常规:6131

分享:843

如您所见,两者都正常工作:42 & 42

程序以退出代码结束:0

在第二次测量中,除了 operator 的调用之外,( )new的构造函数必须完成。我似乎无法理解为什么这比常规分配更快。std::shared_ptrauto sh_ptr1

对此有何解释?

4

3 回答 3

29

问题是对构造函数的第一次调用std::packaged_task负责初始化每个线程状态的负载,然后不公平地归因于pt1. 这是基准测试(尤其是微基准测试)的常见问题,可以通过预热来缓解;尝试阅读如何在 Java 中编写正确的微基准测试?

如果我复制您的代码但先运行两个部分,则结果在系统时钟分辨率的范围内是相同的。这展示了微基准测试的另一个问题,您应该多次运行小型测试以准确测量总时间。

通过预热和运行每个部分 1000 次,我得到以下(示例):

Regular: 132.986
Make shared: 211.889

差异(大约 80ns)非常符合malloc 每次调用需要 100ns的经验法则。

于 2015-07-24T15:10:14.007 回答
8

这是您的微基准测试的问题:如果您交换测量时间的顺序,您将得到相反的结果(演示)。

看起来std::packaged_task构造函数的第一次调用会造成很大的影响。添加一个不定时的

std::packaged_task<int()> ignore(bar);

在测量时间之前解决了这个问题(演示):

常规:505
共享:937

于 2015-07-24T15:11:11.763 回答
5

在 ideone 试过你的例子,得到的结果与你的相似:

Regular: 67950 
Make shared: 696

然后我颠倒了测试的顺序:

auto s2 = std::chrono::steady_clock::now();
auto sh_ptr1 = std::make_shared<std::packaged_task<int()> >(bar);
auto e2 = std::chrono::steady_clock::now();

auto s1 = std::chrono::steady_clock::now();
std::packaged_task<int()> pt1(bar);
auto e1 = std::chrono::steady_clock::now();

并发现了相反的结果:

Regular: 548
Make shared: 68065

所以这不是堆栈与堆的区别,而是第一次和第二次调用的区别。也许您需要研究std::packaged_task.

于 2015-07-24T15:11:14.497 回答