Относительно блока подсчета ссылок shared_ptr - PullRequest
0 голосов
/ 26 ноября 2018

У меня было 2 вопроса относительно блока управления std::shared_ptr:

(1) Относительно размера : Как мне программно найти точный размер элемента управленияблок для std::shared_ptr?

(2) Относительно логики : Кроме того, boost::shared_ptr упоминает, что они полностью свободны от изменений в блоке управления. ( Начиная с Boost версии 1.33.0, shared_ptr использует реализацию без блокировки на большинстве распространенных платформ. ) Я не думаю, что std::shared_ptr следует тому же самому - планируется ли это для любой будущей версии C ++?Не означает ли это, что boost::shared_ptr - лучшая идея для многопоточных случаев?

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

(1) Относительно размера: Как программно найти точный размер блока управления для std :: shared_ptr?

Нет способа.Он недоступен напрямую.

(2) Относительно логики: Кроме того, boost :: shared_ptr упоминает, что они полностью свободны от изменений в блоке управления (начиная с версии Boost 1.33.0, shared_ptr использует реализацию без блокировки на большинстве распространенных платформ.) Я не думаю, что std :: shared_ptr следует тому же самому - это планируется для любой будущей версии C ++?Не означает ли это, что boost :: shared_ptr - лучшая идея для многопоточных случаев?

Абсолютно нет.Реализации без блокировок не всегда лучше, чем реализации, использующие блокировки.Наличие в лучшем случае дополнительного ограничения не ухудшает реализацию, но не может улучшить реализацию.

Рассмотрим двух одинаково компетентных программистов, каждый из которых старается реализовать shared_ptr.Нужно создать реализацию без блокировки.Другой совершенно свободен в своих суждениях.Просто нет способа, чтобы тот, кто должен создать реализацию без блокировки, мог создать лучшую реализацию при прочих равных условиях.В лучшем случае реализация без блокировок является лучшей, и они обе будут ее создавать.Хуже того, на этой платформе реализация без блокировки имеет огромные недостатки, и один разработчик должен ее использовать.Тьфу.

0 голосов
/ 23 декабря 2018

(1) Конечно, лучше проверить реализацию, но вы все равно можете сделать некоторые проверки из вашей программы.

Блок управления выделяется динамически, поэтому для определения его размера вамможет перегрузить новый оператор.

Тогда вы также можете проверить, предоставляет ли std :: make_shared некоторую оптимизацию размера блока управления.В правильной реализации я ожидал бы, что это сделает два выделения (objectA и блок управления):

std::shared_ptr<A> i(new A());

Однако это сделает только одно выделение (и затем objectA инициализируется с размещением new):

auto a = std::make_shared<A>();

Рассмотрим следующий пример:

#include <iostream>
#include <memory>

void * operator new(size_t size) 
{ 
    std::cout << "Requested allocation: " << size << std::endl; 
    void * p = malloc(size); 
    return p; 
} 

class A {};

class B
{
    int a[8];
};

int main()
{
  std::cout << "Sizeof int: " << sizeof(int) << ", A(empty): " << sizeof(A) << ", B(8 ints): " << sizeof(B) << std::endl;
  {
      std::cout << "Just new:" << std::endl;
      std::cout << "- int:" << std::endl;
      std::shared_ptr<int> i(new int());
      std::cout << "- A(empty):" << std::endl;
      std::shared_ptr<A> a(new A());
      std::cout << "- B(8 ints):" << std::endl;
      std::shared_ptr<B> b(new B());
  }
  {
      std::cout << "Make shared:" << std::endl;
      std::cout << "- int:" << std::endl;
      auto i = std::make_shared<int>();
      std::cout << "- A(empty):" << std::endl;
      auto a = std::make_shared<A>();
      std::cout << "- B(8 ints):" << std::endl;
      auto b = std::make_shared<B>();
  }
}

Вывод, который я получил (конечно, это зависит от архитектуры и компилятора):

Sizeof int: 4, A(empty): 1, B(8 ints): 32
Just new:
- int:
Requested allocation: 4
Requested allocation: 24

Первое выделение для int - 4 байта,следующий для блока управления - 24 байта.

- A(empty):
Requested allocation: 1
Requested allocation: 24
- B(8 ints):
Requested allocation: 32
Requested allocation: 24

Смотрит, что блок управления (наиболее вероятно) 24 байта.

Вот почему использовать make_shared:

Make shared:
- int:
Requested allocation: 24

Только одно распределение, int + control block = 24 байта, меньше, чем раньше.

- A(empty):
Requested allocation: 24
- B(8 ints):
Requested allocation: 48

Здесь можно ожидать 56 (32 + 24), но, похоже, реализация оптимизирована.Если вы используете make_shared - указатель на фактический объект не требуется в блоке управления, а его размер составляет всего 16 байтов.

Другая возможность проверить размер блока управления:

std::cout<< sizeof(std::enable_shared_from_this<int>);

В моем случае:

16

Поэтому я бы сказал, что размер блока управления в моем случае составляет 16-24 байта, в зависимости от того, как он был создан.

0 голосов
/ 26 ноября 2018

Блок управления не выставлен.В реализациях, которые я читал, он является динамическим по размеру для непрерывного хранения удалителя (и / или, в случае make shared, самого объекта).

В общем случае он содержит как минимум 3 поля размера указателя -слабый, сильный счетчик и вызывающее средство удаления.

По крайней мере одна реализация опирается на RTTI;другие - нет.

Операции на счетчике используют атомарные операции в реализациях, которые я прочитал;обратите внимание, что C ++ не требует, чтобы все атомарные операции были свободными от блокировки (я считаю, что платформа, которая не имеет операций без блокировки размера указателя, может быть соответствующей платформой C ++).

Их состояние соответствуетдруг с другом и с самим собой, но никакой попытки привести их в соответствие с состоянием объекта не происходит.Вот почему использование необработанных общих ptrs в качестве копии при записи pImpls может быть подвержено ошибкам на некоторых платформах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...