Объявление «пользователей» в ServerData.h
static std::unordered_map<long long, boost::shared_ptr<user_object>> users;
В чем разница в стоимости использования
boost::shared_ptr<ServerData::user_object> user = ServerData::users[session.userid];
session.name = user->name;//and approximately 20 other calls like this
session.age = user->age;
и
session.name = ServerData::users[session.userid]->name;
session.age = ServerData::users[session.userid]->user->age;//and approximately 20 other calls like this.
Не могли бы вы дать ответы для рассмотрения двух различных сценариев, где:
1- ServerData::users.size() составляет от 100 до 10 КБ.
2- ServerData::users.size() больше 1M
Не связанный с вопросом: зачем использовать boost::shared_ptr
, если вы явно используете С++ 11 или более позднюю версию, в которой есть std::shared_ptr
?
@ user17732522 Например, boost::shared_ptr
может хранить указатели на массивы, которые стали доступны в std::shared_ptr
только с C++17.
Если вы считаете, что поиск занимает линейное время в худшем случае, должно быть довольно ясно, какой из них предпочесть. Если вас беспокоит стоимость копирования shared_ptr
, сделайте ссылку. Хотя причин для беспокойства по этому поводу нет.
Я неправильно прочитал изначально. Вас беспокоит, перевешивает ли стоимость копирования shared_ptr повторный поиск или какую стоимость вы пытаетесь здесь сравнить? Если это так, я бы сказал, что очень маловероятно, и этого можно избежать, сделав ссылку user
в первом варианте.
@user17732522 user17732522 на самом деле я тоже не уверен в этом, единственная причина, по которой я его использую, заключается в том, что так много людей предлагали это, когда я впервые начал писать свой серверный код много лет назад. Очевидно, С++ 17 очень хорош, если бы я не использовал библиотеку asio от boost, я бы не использовал ее только для shared_ptr
Я не вижу никаких причин, чтобы предпочесть второе. Он ищет на карте каждую строку заново, что является дорогостоящей операцией. Это нужно только один раз.
Это должно быть верно независимо от размера карты, так как я не вижу почти никакой экономической выгоды во втором варианте вообще. Единственная дополнительная стоимость в первом варианте — это shared_ptr
копия, которая не имеет значения для поиска по хеш-карте, и ее можно избежать, сделав user
ссылку в первом варианте.
Да, я не был уверен в стоимости копирования shared_ptr, но получилось так, как я и ожидал. Спасибо за информацию.
@osandeniz «Я не был уверен в стоимости копирования shared_ptr» — почему вы не сказали об этом в вопросе? Вместо того, чтобы заставлять читателей выяснять, что вы видите в коде, вы должны быть явными. Например, «Какова разница в стоимости между созданием копии общего указателя с использованием» и «и поиском элемента карты каждый раз». Без дополнительного текста в вопросе я сомневаюсь, что другие с тем же вопросом смогут его найти.
@JaMiT ты вообще читал заголовок? И я даже сказал в посте «разницу между использованием этих двух». Всегда не решаюсь спросить здесь из-за невыносимого количества вопросов, на которые я натыкаюсь каждый раз, когда задаю здесь вопрос. К счастью, другие были не такими, как ты.
@ozandeniz "ты вообще читал заголовок?" -- Заголовок? Я не вижу заголовка. Вы имеете в виду название вопроса? Если так, то да, я читал, но потом проигнорировал, потому что "вызов boost::shared_ptr" не имеет смысла. Я просто воздержался от этого, потому что это казалось бесполезным (и потому что у меня сложилось впечатление, что вы, возможно, не являетесь носителем английского языка). Однако, если вы действительно хотите причислить меня к «невыносимому меньшинству», я могу перейти в режим полной критики, указывая на каждую маленькую ошибку без каких-либо предложений по улучшению...
@ozandeniz «Я даже сказал« разница между использованием этих 2 »» - не совсем. Вместо «этих 2» вы дали два блока кода для чтения (и никогда не упоминали «копировать»). Именно это я и предлагаю улучшить. Вместо «Какова разница в стоимости между использованием [кода] и [кода]» вы могли бы написать «Какова разница в стоимости между созданием копии общего указателя с использованием [кода] и поиском элемента карты каждый раз с использованием [ код]." Не полагайтесь на код для общения. Да, у вас слабая грамматика, но ваш английский все же более понятен, чем интерпретация кода.
Вы можете просто измерять вещи — хотя в этом случае результаты очень предсказуемы:
Примечание:
На практике ваше приложение будет налагать ограничения. Например. повторный запрос карты может быть причиной гонки. Как и оптимизация для хранения ссылки вместо копирования общего указателя.
Например. используя Нониус:
#define NONIUS_RUNNER
#include <boost/make_shared.hpp>
#include <nonius/main.h++>
#include <random>
#include <boost/unordered_map.hpp>
#include <unordered_map>
using Id = long long;
static constexpr auto minId = 1'000ll;
static constexpr auto maxId = 2'000'000ll;
static constexpr auto numExtraFields = 18u;
static auto const fixed_random_seed = std::random_device{}();
static auto make_idgen() {
return std::bind(std::uniform_int_distribution<Id>(minId, maxId),
std::mt19937{fixed_random_seed});
}
namespace ServerData {
struct user_object {
std::string name;
unsigned age;
std::array<double, numExtraFields> more_fields;
};
} // namespace ServerData
static auto const std_users = [] {
std::unordered_map<long long, boost::shared_ptr<ServerData::user_object>> table;
for (Id i = minId; i <= maxId; ++i)
table.emplace(i, boost::make_shared<ServerData::user_object>("anonymous", 12));
return table;
}();
NONIUS_BENCHMARK("hooboy", [](nonius::chronometer cm) {
auto users = std_users;
auto pick = make_idgen();
cm.measure([&] {
auto id = pick();
users[id]->name = "name";
users[id]->age = 12 + rand() % 30;
for (size_t i = 0; i < numExtraFields; ++i)
users[id]->more_fields[i] = i;
});
});
NONIUS_BENCHMARK("reasonable", [](nonius::chronometer cm) {
auto users = std_users;
auto pick = make_idgen();
cm.measure([&] {
auto sess = users[pick()];
sess->name = "name";
sess->age = 12 + rand() % 30;
for (size_t i = 0; i < numExtraFields; ++i)
sess->more_fields[i] = i;
});
});
NONIUS_BENCHMARK("optimizing", [](nonius::chronometer cm) {
auto users = std_users;
auto pick = make_idgen();
cm.measure([&] {
auto& sess = *users[pick()];
sess.name = "name";
sess.age = 12 + rand() % 30;
for (size_t i = 0; i < numExtraFields; ++i)
sess.more_fields[i] = i;
});
});
static auto const boost_users = [] {
boost::unordered_map<long long, boost::shared_ptr<ServerData::user_object>> table;
for (Id i = minId; i <= maxId; ++i)
table.emplace(i, boost::make_shared<ServerData::user_object>("anonymous", 12));
return table;
}();
NONIUS_BENCHMARK("boost::unordered_map", [](nonius::chronometer cm) {
auto users = boost_users;
auto pick = make_idgen();
cm.measure([&] {
auto& sess = *users[pick()];
sess.name = "name";
sess.age = 12 + rand() % 30;
for (size_t i = 0; i < numExtraFields; ++i)
sess.more_fields[i] = i;
});
});
Отпечатки
clock resolution: mean is 21.1389 ns (40960002 iterations)
benchmarking hooboy
collecting 100 samples, 45 iterations each, in estimated 2.1285 ms
mean: 700.709 ns, lb 693.765 ns, ub 710.277 ns, ci 0.95
std dev: 40.9973 ns, lb 32.3424 ns, ub 52.1336 ns, ci 0.95
found 16 outliers among 100 samples (16%)
variance is severely inflated by outliers
benchmarking reasonable
collecting 100 samples, 63 iterations each, in estimated 2.1294 ms
mean: 544.189 ns, lb 538.784 ns, ub 550.853 ns, ci 0.95
std dev: 30.5399 ns, lb 25.5757 ns, ub 37.0609 ns, ci 0.95
found 4 outliers among 100 samples (4%)
variance is severely inflated by outliers
benchmarking optimizing
collecting 100 samples, 65 iterations each, in estimated 2.1385 ms
mean: 535.605 ns, lb 529.619 ns, ub 543.946 ns, ci 0.95
std dev: 35.4216 ns, lb 27.9599 ns, ub 52.1946 ns, ci 0.95
found 3 outliers among 100 samples (3%)
variance is severely inflated by outliers
benchmarking boost::unordered_map
collecting 100 samples, 75 iterations each, in estimated 2.1075 ms
mean: 495.22 ns, lb 489.909 ns, ub 502.051 ns, ci 0.95
std dev: 30.7146 ns, lb 25.1169 ns, ub 37.8619 ns, ci 0.95
found 5 outliers among 100 samples (5%)
variance is severely inflated by outliers
Или в виде графика (нажмите для интерактивности):
users[session.userid]
выполняет поиск каждый раз. Повторение этой строки — худший вид копипасты.