性能下降 - 可能是设计不当的方法

Posted

技术标签:

【中文标题】性能下降 - 可能是设计不当的方法【英文标题】:Performance drop - probably badly designed method 【发布时间】:2013-02-21 09:53:40 【问题描述】:

我正在开发一个基于图块的客户端-服务器游戏。客户端以 3 维 std::vector 保存数据,并在每一帧中将其内容与服务器发送的内容进行比较(我在客户端有一个单独的 std::vector,其中填充了服务器发送的数据)。

现在客户端的地图由 15x11 个图块组成,每个图块包含有关放置在其上的 10 个对象的数据,因此我在 std::vector 中得到 15*11*10 = 1650 个元素。

我正在比较std::vectors 中的数据,如果有变化,我会根据不同之处创建新对象/删除对象/移动对象。我就是这样做的:

std::vector<IdAndPosition> clientIds; 
std::vector<IdAndPosition> serverIds;

// Fill client ids
for(unsigned int i = 0; i < m_tiles.size(); i++)

    for(unsigned int j = 0; j < m_tiles[i].size(); j++)
       
        for(unsigned int s = 0; s < m_tiles[i][j].getObjects().size(); s++)
        
            clientIds.push_back(IdAndPosition(m_tiles[i][j].getObjectAtPosition(s)->getId(), i, j, s));
        
    


// Fill server ids
for(unsigned int i = 0; i < g_gameStateData.m_gameObjects.size(); i++)

    for(unsigned int j = 0; j < g_gameStateData.m_gameObjects[i].size(); j++)
       
        for(unsigned int s = 0; s < g_gameStateData.m_gameObjects[i][j].size(); s++)
        
            serverIds.push_back(IdAndPosition(g_gameStateData.m_gameObjects[i][j][s].second, i, j, s));
        
    


for (int i = 0; i < serverIds.size(); i++)

    IdAndPosition& serverId = serverIds[i];

    bool found = false;
    for(int j = 0; j < clientIds.size(); j++)
    
        IdAndPosition& clientId = clientIds[j];

        found = serverId.id == clientId.id;
        if(found)
            break;
    
    if(!found)
    
        // If not found, create that object
                        // tileX        // tileY
        m_tiles[serverId.pos[0]][serverId.pos[1]].addObjectAtPosition(
            TGameObjectFactory::createGameObject(g_gameStateData.m_gameObjects[serverId.pos[0]][serverId.pos[1]][serverId.pos[2]].first),   // Game object
            serverId.pos[2]                                                                                                                 // Position at stack
        );

        // And set it's individual id
        m_tiles[serverId.pos[0]][serverId.pos[1]].getObjectAtPosition(serverId.pos[2])->setId(g_gameStateData.m_gameObjects[serverId.pos[0]][serverId.pos[1]][serverId.pos[2]].second);
    


for (int i = 0; i < clientIds.size(); i++)

    IdAndPosition& clientId = clientIds[i];

    bool found = false;
    for(int j = 0; j < serverIds.size(); j++)
    
        IdAndPosition& serverId = serverIds[j];

        found = serverId.id == clientId.id;
        if(found)
            break;
    
    if(!found)
    
        // If not found, create empty object at this position
                        // tileX        // tileY
        m_tiles[clientId.pos[0]][clientId.pos[1]].addObjectAtPosition(
            TGameObjectFactory::createGameObject(NO_GAME_OBJECT_ID),        // Empty game object (we're removing deprecated one)
            clientId.pos[2]                                                 // Position at stack
        );
    

问题是由于在调试模式下调用该函数(从 ~90 到 ~20 fps),我的性能大幅下降。我知道每帧都要经过大量数据,但我不知道如何设计它以使其不那么慢。

我在 Visual Studio 2012 中使用了性能分析来找出导致最大性能下降的确切原因,我得到了以下结果:

所以看起来[] operator for std::vector 是主要原因。

【问题讨论】:

[](在我的 MSVC++ 中)的调试版本中有边界检查,它在发布中被删除,这可能是它相对如此高的原因.. 建议使用迭代器而不是索引和运算符[],尤其是在迭代整个向量时。 operator[] 底部的加法通常是乘法和加法。虽然编译器可能优化 operator[],但不能保证。 【参考方案1】:

[](在我的 MSVC++ 中)的调试版本中有边界检查,在发布中已删除,这可能是它相对如此高的原因..

这是我看到的代码。

const_reference operator[](size_type _Pos) const
           // subscript nonmutable sequence
 #if _ITERATOR_DEBUG_LEVEL == 2
        if (size() <= _Pos)
               // report error
            _DEBUG_ERROR("vector subscript out of range");
            _SCL_SECURE_OUT_OF_RANGE;
            

 #elif _ITERATOR_DEBUG_LEVEL == 1
        _SCL_SECURE_VALIDATE_RANGE(_Pos < size()); 
 #endif /* _ITERATOR_DEBUG_LEVEL */

        return (*(this->_Myfirst + _Pos));
        

几乎每一行都在发行版中消失了,除了最后一个......

【讨论】:

是的,确实解决了一个问题。在发布模式下,我的速度接近 250 fps。【参考方案2】:

您正在对其进行性能分析的代码看起来像是对每个项目进行线性 O(n) 搜索,从而使整个事情成为 O(n^2)。

我不太明白你想要达到什么目的,但原因不是因为 std::vector 的运算符很慢,而是因为你这样做的次数太多了。

我建议首先将您的所有 clientIds 放在一个地图中,这会将订单减少到 O(nlogn)。可能还需要进行其他优化,例如,如果您能提供帮助,请不要查看外部地图中的每个元素。

for(int j = 0; j < serverIds.size(); j++)

    IdAndPosition& serverId = serverIds[j];

    found = serverId.id == clientId.id;
    if(found)
        break;

【讨论】:

我认为问题似乎是关于发布和调试之间的差异,而不是性能本身 不过,这绝对是他应该解决的第一件事 - 暴力搜索不是要走的路。 一个可能更快的替代地图的方法是使用排序列表并压缩它们;这避免了大多数动态内存分配。 @EamonNerbonne 我不是在争辩,服务器应该只发送某种增量,而不必检查或类似的东西。 确定;他当然不应该将他的 id 列表展平并然后搜索它们 - 他有一个很好的网格可用于已经到位的查找!

以上是关于性能下降 - 可能是设计不当的方法的主要内容,如果未能解决你的问题,请参考以下文章

ADI的良好接地指导原则

高性能队列设计

干货 | DC-DC芯片应用设计中的PCB Layout设计要点

干货 | DC-DC芯片应用设计中的PCB Layout设计要点

JS 设计模式

整车电气系统设计及性能测试