代码之家  ›  专栏  ›  技术社区  ›  Moberg

为什么我的分析类非常慢?

  •  4
  • Moberg  · 技术社区  · 15 年前

    我从来没有做过分析。昨天我编写了一个Primuling定时器类,它带有静态时间表(map & lt;STD::string,Long Long & gt),用于时间存储。

    构造函数存储开始标记,析构函数计算经过的时间并将其添加到映射中:

    ProfilingTimer::ProfilingTimer(std::string name)
     : mLocalNameLength(name.length())
    {
     sNestedName += name;
     sNestedName += " > ";
    
     mStartTick = Platform::GetTimerTicks();
    }
    
    ProfilingTimer::~ProfilingTimer()
    {
     long long totalTicks = Platform::GetTimerTicks() - mStartTick;
    
     sTimetable[sNestedName] += totalTicks;
    
     sNestedName.erase(sNestedName.length() - mLocalNameLength - 3);
    }
    

    在我想要分析的每个函数(或块)中,我需要添加:

    ProfilingTimer _ProfilingTimer("identifier");
    

    当我从VisualC++ 2010专业构建发布版本时,这种分析工作都很好。但是当我进行调试时,我得到了一个巨大的fps下降(从63下降到20)。

    这些是我在打印时间表(调试版本)时得到的数字:

    Update() > Tower::Update > : 2551 ms (84100m%)
    Update() > Tower::Update > Tower::Update1 > : 1313 ms (43284m%)
    Update() > Tower::Update > Tower::Update1 > Tower::FindNewTarget > : 6 ms (204m%)
    Update() > Tower::Update > Tower::Update1 > Tower::HasTargetInRange > : 5 ms (184m%)
    Update() > Tower::Update > Tower::Update2 > : 659 ms (21756m%)
    Update() > Tower::Update > Tower::Update2 > Tower::HasTargetInRange > : 5 ms (187m%)
    

    update1和update2分别是update的前半部分和后半部分的两倍。为什么它们加起来不到84.1%?

    尽管如此,这84%是一个巨大的数字-在发布版本中,我得到了这个输出:

    Update() > : 770 ms (1549m%)
    Update() > Tower::Update > : 722 ms (1452m%)
    Update() > Tower::Update > Tower::FindNewTarget > : 44 ms (89m%)
    Update() > Tower::Update > Tower::HasTargetInRange > : 92 ms (187m%)
    

    1.4%而不是84.1%。这是一个巨大的不同!

    有人知道为什么吗?

    编辑:我想发布要比调试快得多,但为什么这种分析如此耗时?STD::映射时间猪,还是我做了一些极端错误的事情?

    编辑:更新代码。不需要初始化,现在存储mlocalname的长度而不是实际的字符串。

    3 回复  |  直到 15 年前
        1
  •  2
  •   sth    15 年前

    您的代码有几个性能问题。

    1. 您在构建PrimeItCalor构造函数中不必要地构造STD::我建议使用const char*作为参数,并使用自定义 Twine/Rope 结构来执行附加操作。
    2. 为什么 mLocalName 甚至存在?只是指 name 直接。
    3. 如前所述,不要在调试模式下进行配置。毫无用处。
    4. 实际上,地图在实践中相当缓慢。我建议使用哈希表。不幸的是,实现是特定于编译器的。如果你用的是微软的,我相信他们有 unordered_map 可供您使用。
    5. 而不是做 sTimetable[sNestedName] = 0; ,使用已检索到的迭代器。

      Timetable::iterator loc = sTimetable.find(sNestedName);
      if(loc == sTimetable.end())
          sTimetable[sNestedName] = 0;
      

    附录:Visual Studio上一次检查时附带了一个分析器。为什么不直接用那个?

        2
  •  2
  •   wheaties    15 年前

    微软在调试模式下为容器库添加了大量的安全检查。这是有益的。您更愿意在函数(如 vector::operator[] 而不是破译内存损坏(仍然建议调用 vector::at 但是,在插入影响代码及其性能的调试器挂钩时,还有许多其他事情要做。

        3
  •  2
  •   sth    15 年前

    使用长字符串作为索引 std::map 可能不是最快的方法。有共同开头的长字符串意味着每次比较这两个字符串时,必须查看许多字符,以确定字符串是否相等。 STD::地图 基本上是一个二叉树,在每次查找/插入时进行O(log(n))比较。使用短键或数字键应该可以加快所有的映射操作。

    此外,使用 std::unordered_map 可能(或不可能)提高速度,这取决于地图包含的元素数量和使用的键类型。

    在分析类中,构造函数应将该名称作为引用,以避免创建该字符串的不必要副本:

    ProfilingTimer::ProfilingTimer(const std::string &name)
    

    一般来说,避免不必要的拷贝是个好主意。例如,您可能并不真正需要 mLocalName 它可以足够存储字符串长度。

    分析函数可能经常被调用,因此在程序运行过程中,小的延迟会累积起来。