Программа, которую я написал, должна проверять миллионы точек в 2D-массиве, чтобы узнать, не являются ли они нулевыми. Вот код, который я использую:
Particle *particleGrid[1920][1080];
bool Sensor::checkForParticle(int x, int y) {
if (x > 1920 || x < 0) return 0;
if (y > 1080 || y < 0) return 0;
if (mainController->particleGrid[x][y] != NULL) {
return 1;
}
return 0;
}
Эта функция использует большинство процессоров во всем приложении (около 70% использования ЦП приложения связано с этой функцией), даже больше, чем моя реализация алгоритма рисования линии Брешенема (функция примера вызывается в каждой точке сгенерированной строки по алгоритму Брешенема). Есть ли более эффективный для процессора способ выполнения операции проверки нулевой точки?
Если он вызывается в цикле, вы можете уйти без проверки аргументов. Это также будет быстрее, когда вы исследуете данные, закрывающиеся в памяти, что уменьшит количество попаданий в кеш.
Если вы сравните с неподписанными литералами, вы получите проверку против 0 бесплатно, потому что отрицательные числа оказываются очень большими при преобразовании в unsigned. Кроме того, вам не нужны все эти ifs:
bool Sensor::checkForParticle(int x, int y)
{
return (x < 1920u) && (y < 1080u) // note both "u" suffixes for unsigned
&& (mainController->particleGrid[x][y] != NULL);
}
Кстати, почему у вас есть массив в порядке столбцов? Являются ли ваши внешние циклы на x или y? Если они находятся на y, переход на row-major значительно улучшит эффективность из-за удобства кеширования:
Particle *particleGrid[1080][1920];
bool Sensor::checkForParticle(int x, int y)
{
return (x < 1920u) && (y < 1080u)
&& (mainController->particleGrid[y][x] != NULL); // note switched order
}
Если 2D-массив разрежен, что-то вроде этого может помочь вам ускорить замкнутый цикл:
Particle *particleGrid[1920][1080];
// somewhere before your tight loop
std::map<std::pair<unsigned int, unsigned int>, Particle*> createCache()
{
std::map<std::pair<unsigned int, unsigned int>, Particle*> cache;
for (unsigned int i = 0; i < 1920; ++i)
{
for (unsigned int j = 0; j < 1080; ++j)
{
if (mainController->particleGrid[i][j])
{
std::pair<unsigned int, unsigned int> coord = std::make_pair(i, j);
cache[coord] = mainController->particleGrid[i][j];
}
}
}
return cache;
}
// then this is called in your tight loop
bool Sensor::checkForParticle(unsigned int x, unsigned int y, const std::map<std::pair<unsigned int, unsigned int>, Particle*>& cache)
{
std::pair<unsigned int, unsigned int> coord = std::make_pair(x, y);
return cache.find(coord) != map.end();
}
Если он не разрежен, это вообще не поможет.
Шаг 1: Поднимите консистенцию из цикла:
bool Sensor::uncheckedCheckForParticle(int x, int y) {
return mainController->particleGrid[y][x];
}
Если вам действительно нужно защититься от неаккуратного программирования, вы можете assert()
в функции и/или защищать сайт вызова. Готов поспорить, что это значительно улучшит производительность.
Шаг 2: сделайте теперь тривиальную функцию inline
.
Вы можете сгладить массив от двумерного к одномерному (к сожалению, это может потребовать рефакторинга в другом месте кода):
Particle *particleGrid[1920 * 1080];
bool Sensor::checkForParticle(int x, int y) {
return (mainController->particleGrid[x * 1080 + y] != NULL)
}