производительность emplace_hint, когда подсказка неверна - PullRequest
0 голосов
/ 04 июня 2018

Я пытаюсь определить, следует ли использовать emplace_hint для вставки ключа в multimap (в отличие от обычного emplace).Я уже рассчитал диапазон ключа в более ранней операции (для того же ключа):

range = multimap.equal_range(key); 

Должен ли я использовать range.first, range.second или ничего в качестве подсказки для вставки ключа,пара значений?Что делать, если диапазон пуст?

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

Во-первых, с точки зрения производительности, это не будет иметь никакого значения, если вы используете range.first или range.second.Давайте посмотрим на возвращаемое значение equal_range:

std::equal_range - возвращаемое значение

std::pair, содержащеепара итераторов, определяющих требуемый диапазон, первый указывает на первый элемент, который не меньше значения, а второй указывает на первый элемент, который больше значения.Если нет элементов, не меньших значения, последний возвращается как первый элемент.Точно так же, если нет элементов больше значения, последний возвращается как второй элемент

Это означает, что при получении значения key оба значения range.first и range.secod равны represent positions where ключ may be correctly inserted right before. So performance wise it should not matter if you use range.first or range.last`.Сложность должна быть «амортизируемой константой», поскольку новый элемент вставляется непосредственно перед подсказкой.

Во-вторых, когда диапазон «пустой», range.first и range.second оба равны один за другим.и, следовательно, производительность и результат идентичны, фактически так же, как если бы вы использовали emplace без каких-либо подсказок.

См. следующую программу, демонстрирующую это:

int main()
{
    std::multimap<std::string, std::string> m;

    // some clutter:
    m.emplace(std::make_pair(std::string("k"), std::string("1")));
    m.emplace(std::make_pair(std::string("k"), std::string("2")));
    m.emplace(std::make_pair(std::string("z"), std::string("1")));
    m.emplace(std::make_pair(std::string("z"), std::string("2")));

    // relevant portion of demo data: order a-c-b may be preserved
    m.emplace(std::make_pair(std::string("x"), std::string("a")));
    m.emplace(std::make_pair(std::string("x"), std::string("c")));
    m.emplace(std::make_pair(std::string("x"), std::string("b")));


    auto r = m.equal_range("x");
    // will insert "x.zzzz" before "x.a":
    m.emplace_hint(r.first, std::make_pair(std::string("x"), std::string("zzzz")));

    // will insert "x.0" right after "x.b":
    m.emplace_hint(r.second, std::make_pair(std::string("x"), std::string("0")));

    auto rEmpty = m.equal_range("e");
    // "empty" range, normal lookup:
    m.emplace_hint(rEmpty.first, std::make_pair(std::string("e"), std::string("b")));
    m.emplace_hint(rEmpty.second, std::make_pair(std::string("e"), std::string("a")));

    auto rWrong = m.equal_range("k");
    m.emplace_hint(rWrong.first, std::make_pair(std::string("z"), std::string("a")));

    for (const auto &p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}

Вывод:

e => b
e => a
k => 1
k => 2
x => zzzz
x => a
x => c
x => b
x => 0
z => a
z => 1
z => 2

Короче говоря: если у вас есть действительное range для key предварительно рассчитанного, используйте его при вставке key.В любом случае это поможет.

РЕДАКТИРОВАТЬ:

Были дискуссии вокруг того, может ли "недействительный" подсказка привести к вставке в позицию, которая затем не отражает "порядок вставки" длязначения с тем же ключом.Это может быть заключено из общего многопользовательского оператора «Порядок пар ключ-значение, ключи которого сравниваются, эквивалентен порядку вставки и не изменяется. (Начиная с C ++ 11)».

Я не сделалнайти поддержку той или иной точке зрения в любом нормативном документе.Я только что нашел следующее утверждение в документации по cplusplus multimap / emplace_hint:

emplate <class... Args>
  iterator emplace_hint (const_iterator position, Args&&... args);

position Подсказка для позиции, в которую можно вставить элемент.Функция оптимизирует время вставки, если позиция указывает на элемент, который будет следовать за вставленным элементом (или до конца, если он будет последним).Обратите внимание, что это не заставляет новый элемент находиться в этой позиции в контейнере мультикарты (элементы в мультикарте всегда следуют определенному порядку).const_iterator - это тип члена, определенный как двунаправленный тип итератора, который указывает на элементы.

Я знаю, что это не нормативная ссылка, но по крайней мере мой компилятор Apple LLVM 8.0 придерживается этого определения (см.демонстрация выше): если кто-то вставляет элемент с «неправильной» подсказкой, то есть с указателем даже до позиции, в которую должна быть вставлена ​​пара, алгоритм распознает это и выбирает правильную позицию (см. вставку «z» => «a»)где подсказка указывает на элемент "x").Если мы используем диапазон для клавиши «x» и используем range.first, то позиция перед первым x интерпретируется как действительная позиция.

Итак: я думаю, что m.emplace_hint(r.first,... ведет себя так, что алгоритм немедленно выбирает допустимую позицию, и что to a position close to hint отменяет общее утверждение «Порядок пар ключ-значение, ключи которых сравниваются, равенпорядок вставки и не меняется. (начиная с C ++ 11) ".

0 голосов
/ 04 июня 2018

Должен ли я использовать range.first, range.second или ничего как подсказку для вставки пары ключ-значение?

как std::multimap::emplace_hint() состояний:

Вставляет новый элемент в контейнер как можно ближе к позиции за до подсказки.

(выделение мое), вам следуетиспользуйте second итератор из диапазона, и это должно сделать вставку более эффективной:

Сложность

Логарифмический размер контейнера в целом, но постоянная амортизации, если новый элемент вставленнепосредственно перед подсказкой.

Что касается пустого диапазона, все же можно использовать итератор second, так как он всегда должен указывать на элемент больше или позади последнего, если такой не существует.

...