放置新的唯一指针

placement new unique pointer

这是我的第一篇文章。我希望它包含正确的、最少的信息。如果我遗漏了什么,请告诉我。

我正在尝试使用 placement new 来提高以下代码块的效率(我想减少内存分配调用的次数,因为 number_of_actions 变量很大(> 500k))。

首先,我用的是两个类,它们的关系可以概括为:

class txn {
public:
  int i;
  txn(): i(0) {};
};

class ActionClass {
private:
  txn* t;
public:
  ActionClass(txn* t): t(t) {};
  ~ActionClass() { delete t; }
};

我最初用来创建对象指针数组的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res;
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action
    std::unique_ptr<IBatchAction> act = std::make_unique<ActionClass>(new TestTxn());

    res.push_back(std::move(act));
  }
  return res;
}

更改为使用新放置后的代码:

std::vector<std::unique_ptr<IBatchAction>> allocate_actions(unsigned int number_of_actions) {
  std::vector<std::unique_ptr<IBatchAction>> res(number_of_actions);

  // allocate all of the memory for actions up front to amortize the cost.
  ActionClass* actions = 
    reinterpret_cast<ActionClass*>(new char[number_of_actions * sizeof(ActionClass)]);
  txn* txns = reinterpret_cast<txn*>(new char[number_of_actions * sizeof(TestTxn)]);

  // use placement new to initialize actions and assign them to unique_ptrs
  for (unsigned int i = 0; i < number_of_actions; i++) {
    // construct the action using placement new from the memory allocated above.
    res[i].reset(new(&(actions[i])) ActionClass(new(&(txns[i])) TestTxn()));
  }
  return res;
}

在 main.cpp 中,我只是多次调用上述函数,对其计时并 return 0。从上述函数中编辑的向量 return 在循环迭代之间被破坏。结果,我在段错误之前得到了以下堆栈跟踪:

#0  std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >::~unique_ptr (
    this=<optimized out>, __in_chrg=<optimized out>) at /usr/include/c++/5/bits/unique_ptr.h:236
#1  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (
    __pointer=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:93
#2  std::_Destroy_aux<false>::__destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (__last=<optimized out>, __first=0x7ffff7f06018)
    at /usr/include/c++/5/bits/stl_construct.h:103
#3  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*> (
    __last=<optimized out>, __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:126
#4  std::_Destroy<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >*, std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > (__last=0x7ffff7fc9510, 
    __first=<optimized out>) at /usr/include/c++/5/bits/stl_construct.h:151
#5  std::vector<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> >, std::allocator<std::unique_ptr<IBatchAction, std::default_delete<IBatchAction> > > >::~vector (this=0x7fffffffd910, 
    __in_chrg=<optimized out>) at /usr/include/c++/5/bits/stl_vector.h:424
#6  time_workload_creation (exp_conf=...) at start_batch/main.cc:18
#7  0x000000000040320c in main (argc=<optimized out>, argv=<optimized out>)
    at start_batch/main.cc:44

我对展示位置的概念还很陌生。以下是我用来写上面代码的:

  1. Can I use placement new to reset an object within a shared_ptr? -- 使用 reset 分配使用 placement new
  2. 创建的新对象
  3. placement new on shared_ptr make seg fault when delete——一个非常相似的问题。这个问题的答案对我完全有帮助:(

我可能做了一些明显错误的事情,但我想不通。帮助?如果您需要来自 main(简化版)的实际代码,就在这里(actions.h 包含我已经讨论过的函数)。

#include "actions.h"

#include <chrono>
#include <vector>

void time_workload_creation(unsigned int act_num) {
  std::chrono::system_clock::time_point time_start, time_end;
  std::vector<double> results;
  for (unsigned int i = 0; i < 10; i++) {
    time_start = std::chrono::system_clock::now();
    auto workload = allocate_actions(act_num);
    time_end = std::chrono::system_clock::now();
    results.push_back(
        std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start).count());
  }

  for (unsigned int i = 0; i < 10; i++) {
    std::cout << i << "\t\t" <<  
      act_num << "\t\t" <<  
      results[i] << "\t\t" <<  
      act_num / results[i] << std::endl;
  }
};

int main(int argc, char** argv) {
  time_workload_creation(1000000);
  return 0;
}

编译使用:gcc 版本 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

Code after changing to use placement new:
[...]

此代码具有未定义的行为。您使用 new[] 创建一个 char 的数组,然后重置 N 个 unique_ptr 个对象,这将尝试删除 N 个不同的 ActionClass 个对象。你希望它如何工作?您需要使用 delete[] 来释放 char 数组,而不是对 char 数组的块使用 N delete 操作。

同样,每个ActionClass都会尝试delete它的txn对象,但这也是完全未定义的,因为你没有分配N个txn对象,你分配了另一个 char.

数组

这不是内存分配的工作方式。您不能先分配一大块,然后再释放其中的一部分。 (好吧,你可以,但只能通过编写你自己的自定义(取消)分配函数或分配器)。