C++ 检查C+中数组位置是否为空的CPU高效方法+;

C++ 检查C+中数组位置是否为空的CPU高效方法+;,c++,arrays,C++,Arrays,我编写的一个程序需要检查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->particleG

我编写的一个程序需要检查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;
}
Particle*particleGrid[1920][1080];
布尔传感器::checkForParticle(整数x,整数y){
如果(x>1920 | | x<0)返回0;
如果(y>1080 | | y<0)返回0;
如果(主控制器->particleGrid[x][y]!=NULL){
返回1;
}
返回0;
}

此函数在整个应用程序中使用最多的CPU(应用程序的CPU使用率约70%是由于此函数),甚至比我实现的Bresenham线条绘制算法(示例函数在Bresenham算法生成的线条的每个点上调用)还要多。是否有一种CPU效率更高的方法来执行空检查操作?

如果在循环中调用空检查操作,则可以不检查参数而直接执行。在检查内存位置附近的数据时,行优先也会更快,这将减少缓存命中

如果它是在循环中调用的,您可以不检查参数就离开。在检查内存位置附近的数据时,行优先也会更快,这将减少缓存命中

如果与无符号文字进行比较,则可以免费获得0的检查,因为负数在转换为无符号文字时会非常大。此外,您不需要所有这些假设:

bool Sensor::checkForParticle(int x, int y)
{
    return (x < 1920u) && (y < 1080u)   // note both "u" suffixes for unsigned
        && (mainController->particleGrid[x][y] != NULL);
}
bool传感器::checkForParticle(int x,int y)
{
return(x<1920u)和&(y<1080u)//注意未签名的
&&(主控制器->particleGrid[x][y]!=NULL);
}
顺便问一下,为什么数组是按列主顺序排列的?你的外环在x还是y上?如果它们在y上,切换到row MARGY将由于缓存友好性而显著提高效率:

Particle *particleGrid[1080][1920];

bool Sensor::checkForParticle(int x, int y)
{
    return (x < 1920u) && (y < 1080u)
        && (mainController->particleGrid[y][x] != NULL);   // note switched order
}
Particle*particleGrid[1080][1920];
布尔传感器::checkForParticle(整数x,整数y)
{
返回(x<1920u)和(y<1080u)
&&(主控制器->particleGrid[y][x]!=NULL);//注意切换顺序
}

如果与无符号文字进行比较,则可以免费获得0的检查,因为负数在转换为无符号文字时会非常大。此外,您不需要所有这些假设:

bool Sensor::checkForParticle(int x, int y)
{
    return (x < 1920u) && (y < 1080u)   // note both "u" suffixes for unsigned
        && (mainController->particleGrid[x][y] != NULL);
}
bool传感器::checkForParticle(int x,int y)
{
return(x<1920u)和&(y<1080u)//注意未签名的
&&(主控制器->particleGrid[x][y]!=NULL);
}
顺便问一下,为什么数组是按列主顺序排列的?你的外环在x还是y上?如果它们在y上,切换到row MARGY将由于缓存友好性而显著提高效率:

Particle *particleGrid[1080][1920];

bool Sensor::checkForParticle(int x, int y)
{
    return (x < 1920u) && (y < 1080u)
        && (mainController->particleGrid[y][x] != NULL);   // note switched order
}
Particle*particleGrid[1080][1920];
布尔传感器::checkForParticle(整数x,整数y)
{
返回(x<1920u)和(y<1080u)
&&(主控制器->particleGrid[y][x]!=NULL);//注意切换顺序
}

如果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();
}
Particle*particleGrid[1920][1080];
//在你紧张的循环之前的某个地方
std::map createCache()
{
地图缓存;
for(无符号整数i=0;i<1920;++i)
{
用于(无符号整数j=0;j<1080;++j)
{
if(主控制器->particleGrid[i][j])
{
std::pair coord=std::make_pair(i,j);
cache[coord]=mainController->particleGrid[i][j];
}
}
}
返回缓存;
}
//这就是你的紧循环
布尔传感器::checkForParticle(无符号整数x、无符号整数y、常数std::映射和缓存)
{
std::pair coord=std::make_pair(x,y);
返回cache.find(coord)!=map.end();
}

如果它不是稀疏的,这将毫无帮助。

如果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();
}
Particle*particleGrid[1920][1080];
//在你紧张的循环之前的某个地方
std::map createCache()
{
地图缓存;
for(无符号整数i=0;i<1920;++i)
{
用于(无符号整数j=0;j<1080;++j)
{
if(主控制器->particleGrid[i][j])
{
std::pair coord=std::make_pair(i,j);
cache[coord]=mainController->particleGrid[i][j];
}
}
}
返回缓存;
}
//这就是你的紧循环
布尔传感器::checkForParticle(无符号整数x、无符号整数y、常数std::映射和缓存)
{
std::pair coord=std::make_pair(x,y);
返回cache.find(coord)!=map.end();
}

如果它不是稀疏的,这将毫无帮助。

步骤1:将一致性检查从循环中提升出来:

bool Sensor::uncheckedCheckForParticle(int x, int y) {
    return mainController->particleGrid[y][x];
}
如果您确实需要防止草率编程,可以在函数中使用
assert()
,和/或保护调用站点。我敢打赌,这将大大提高性能


步骤2:使现在微不足道的函数
内联

步骤1:将一致性检查从循环中提出来:

bool Sensor::uncheckedCheckForParticle(int x, int y) {
    return mainController->particleGrid[y][x];
}
如果您确实需要防止草率编程,可以在函数中使用
assert()
,和/或保护调用站点。我敢打赌,这将大大提高性能


步骤2:将现在微不足道的函数
内联

您可以将数组从二维展平到一维(遗憾的是,这可能需要在代码中的其他地方重构):


您可以将数组从二维展平到一维(遗憾的是,这可能需要在代码的其他地方进行重构):


(~70%的应用程序CPU使用率是由于此功能)。你怎么知道的?这个问题可能更适合代码评审。不管怎样,你的x/y签名有什么原因吗?一个无符号的比较而不是两个有符号的比较似乎更便宜。只是一个旁注:它应该是
x>=1920
y>=1080
,如果它在一个循环中,那么它就占用了那么多时间。您是否考虑过将其内联?除了答案/其他注释中的任何性能改进之外。。。应收账