Я пытаюсь написать широкофазную систему сортировки и развертки, и у меня возникли некоторые проблемы с производительностью на этапе создания отчетов о перекрытии.
Мой код сообщения о паре — вот где узкое место:
Основная идея состоит в том, чтобы создать временный список пар перекрытия для каждой оси, а затем для каждой пары в X, проверить, существует ли пара в Y и Z. Некоторые дополнительные проверки находятся в генерации пары, чтобы иметь дело с кубом стека и удержанием крайние случаи.
Код генерации пар выглядит следующим образом:
//temporary pair generation for X axis
for (unsigned int i = 0; i < mXExtents.size()-1; i++)
{
if (!mXExtents[i].mMax)
{
for (unsigned int j = i + 1; j < mXExtents.size(); j++)
{
if (mXExtents[j].mOwner->getID() == mXExtents[i].mOwner->getID())
{
break;
}
else
{
tempXPairs.push_back(new Pair(mXExtents[i].mOwner, mXExtents[j].mOwner));
}
}
}
}
//temporary pair generation for Y axis
for (unsigned int i = 0; i < mYExtents.size()-1; i ++)
{
if (!mYExtents[i].mMax)
{
for (unsigned int j = i + 1; j < mYExtents.size(); j++)
{
if (mYExtents[j].mOwner->getID() == mYExtents[i].mOwner->getID())
{
break;
}
else
{
tempYPairs.push_back(new Pair(mYExtents[i].mOwner, mYExtents[j].mOwner));
}
}
}
}
//temporary pair generation for Z axis
for (unsigned int i = 0; i < mZExtents.size()-1; i ++)
{
if (!mZExtents[i].mMax)
{
for (unsigned int j = i + 1; j < mZExtents.size(); j++)
{
if (mZExtents[j].mOwner->getID() == mZExtents[i].mOwner->getID())
{
break;
}
else
{
tempZPairs.push_back(new Pair(mZExtents[i].mOwner, mZExtents[j].mOwner));
}
}
}
}
Узкое место, обнаруженное при профилировании, возникает при сравнении пар с помощью оператора ==. Я подозреваю, что это связано со многими такими проверками, а не с накладными расходами самой проверки.
Соедините код сообщения следующим образом:
bool found = false;
//now search Y & Z temp storage for matching pairs
for (unsigned int i = 0; i < tempXPairs.size(); i++)
{
if (tempXPairs[i] != nullptr)
{
//search Y first
for (unsigned int j = 0; j < tempYPairs.size(); j++)
{
if (tempYPairs[j] != nullptr)
{
//match found in Y
if (*tempXPairs[i] == *tempYPairs[j])
{
//make a quick copy and stop searching
found = true;
delete tempYPairs[j];
tempYPairs[j] = nullptr;
break;
}
}
}
//element in Y found
if (found)
{
found = false;
//search Z temp list for a match
for (unsigned int j = 0; j < tempZPairs.size(); j++)
{
if (tempZPairs[j] == nullptr)
continue;
//match in Z found
if (*tempXPairs[i] == *tempZPairs[j])
{
//if we are at this stage then we have a triple match, so an overlap on all axes.
//add the pair to the manager
mPairManager->addpair(tempXPairs[i]);
//delete extranious pairs
delete tempZPairs[j];
tempZPairs[j] = nullptr;
//clear variables
tempXPairs[i] = nullptr;
//and end search
break;
}
}
//not found so get rid of all relevant pairs and move on to next in X list
delete tempXPairs[i];
tempXPairs[i] = nullptr;
}
else
{
delete tempXPairs[i];
tempXPairs[i] = nullptr;
}
}
}
//finally clear temp storage
for (unsigned int i = 0; i < tempXPairs.size(); i++)
{
if (tempXPairs[i] != nullptr)
{
delete tempXPairs[i];
}
}
for (unsigned int i = 0; i < tempYPairs.size(); i++)
{
if (tempYPairs[i] != nullptr)
{
delete tempYPairs[i];
}
}
for (unsigned int i = 0; i < tempZPairs.size(); i++)
{
if (tempZPairs[i] != nullptr)
{
delete tempZPairs[i];
}
}
В материале, который я прочитал о сортировке и развертке / развертке и обрезке, не описан быстрый способ быстрого поиска дублирующих пар или, действительно, эффективный способ поиска по другим осям пар эквивалентных растений. Я явно что-то упускаю, поэтому буду признателен за любую помощь, которая может быть оказана.
Проблема производительности не удивляет меня здесь, учитывая очевидную квадратичную сложность этого алгоритма.
Не ясно, какой класс или POD возвращается getId()
, Для ясности, скажем, что getId()
возвращает IdType
класс, и что mxExtents
это контейнер ExtentsType
классы.
Если IdType
реализует строгий слабый порядок (что означает, что он реализует operator<
, в дополнение к operator==
), и вы считаете, что достигнете лучшей производительности, если число IdType
сравнения возвращаются, тогда я бы предложил создать
std::multimap<IdType, ExtentsType *> lookup;
Сейчас заселите lookup
сделав один проход mxExtents
, вставив каждое значение IdType
и указатель на исходный экземпляр ExtentsType
в мультикарту. Операция вставки будет иметь логарифмическую сложность, тогда после вставки всего одного прохода над контейнером с несколькими картами будет просто захватить все экземпляры ExtentsType
с тем же IdType
, Поскольку исходные операции вставки имели логарифмическую сложность, я ожидаю, что общее число сравнений на первом проходе будет намного меньше.
Конечно, второй проход будет иметь линейную сложность, но для меня это выглядит как самый легкий низко висящий фрукт, который можно попробовать первым, чтобы проверить, устраняет ли это ваше подозрение на узкое место.
Другим потенциальным вариантом будет использование std::multiset
вместо std::multimap
, с пользовательским классом компаратора. Это позволит использовать некоторые дополнительные оптимизации, основанные на внутренних отношениях между каждым ExtentsType
и его внутренняя IdType
Например, чтобы устранить многие внутренние конструкции копирования / перемещения, которые также происходят здесь. Которые наследуют квадратичную сложность исходного алгоритма (и также будут соответствующим образом уменьшены путем переключения на мультикарту и, возможно, полностью исключены с помощью пользовательского мультимножественного компаратора).
Других решений пока нет …