青蛙学 / 博客 / 本周提示 / 压扁 可可的性能分析

压扁 可可的性能分析

版本5 压扁 可可的新产品内置了Function Profiler,可支持应用程序的性能测量。像任何分析工具一样,这包括提供有关过程消耗的时间的信息。

在这里,我们介绍如何与Squish 可可合作’s分析器,使用表达式解析器示例。本文介绍了编译应用程序,缩小性能问题,确定有问题的功能,重写源代码以及比较重构后的代码。’的性能达到原始版本。让’s get started.

应用程序建设

From the code generation, enabling the profiler requires only that you compile with an additional flag to the coverage flags: --cs-function-profiler.

有两个选项:

  • --cs-function-profiler=all:分析所有功能;
  • --cs-function-profiler=skip-trivial:跳过单条语句功能(通常是getter或setter函数。)

构建后,收集性能分析数据与收集coverage数据相同。无需额外的工作即可使用 浏览器.

For our parser sample, in the instrumentation script, we simply add the switch --cs-function-profiler=all to the COVERAGESCANNER_ARGS flag, and rebuild and execute the tests using:

$ make -f gmake.mak clean parser tests

分析性能问题

识别测试用例

使用 执行力 的窗口 浏览器,我们看到一些测试导致执行时间较长:


测试包括添加新变量。我们可以看到,在此功能中花费的时间随着插入次数的增加而呈指数增长。

为了分析这种情况,我们在 浏览器 (文件–>执行比较分析。)我们选择具有1 000个可变插入的测试作为参考测试,并将其与具有10 000个插入的测试进行比较。


识别有问题的功能

移至 功能分析器 窗口允许进行更详细的分析:

使用Function Profiler窗口进行比较。


此窗口显示当前测试的执行时间和调用次数(插入次数为10000)和参考测试的插入次数为1000(标题中带有关键字(参考)的列)。

此外,还列出了另外两列,用于计算当前测试与参考的比率,公式为:

比率= test_current的持续时间/ test_reference的持续时间

Using this information, it is easy to see that for a test set which is growing by a factor of 10, the execution time of toupper(), Variablelist::add() and Variablelist::get_id are increasing by a factor of over 100. Further, reducing the call to toupper() seems to be necessary because the number of executions is increased by a factor of 99 whereas the others are less than 10.

Now, if we look at the code behind these functions, we see a classic source code example originally written in C: usage of char* and manual handling a list of elements into a table. STL containers are used but just to replace the classic C arrays. get_id() is a function which finds the position of the variable in the table by iterating over the complete table. So, with a complexity linear to the number of allocated items, it could be logarithmic in the case of a binary search. Also there is a call to toupper() after each iteration, which further slows down the algorithm.

/*
 * Add a name and value to the variable list
 */
bool Variablelist::add(const char* name, double value)
{
    VAR new_var;
    strncpy(new_var.name, name, 30);
    new_var.value = value;

    int id = get_id(name);
    if (id == -1)
    {
        // variable does not yet exist
        var.push_back(new_var);
    }
    else
    {
        // variable already exists. overwrite it
        var[id] = new_var;
    }
    return true;
}

/*
 * Returns the id of the given name in the variable list. Returns -1 if name
 * is not present in the list. 名称 is case insensitive
 */
int Variablelist::get_id(const char* name)
{
    // first make the name uppercase
    char nameU[NAME_LEN_MAX+1];
    char varU[NAME_LEN_MAX+1];
    toupper(nameU, name);

    for (unsigned int i = 0; i < var.size(); i++)
    {
        toupper(varU, var[i].name);
        if (strcmp(nameU, varU) == 0)
        {
            return i;
        }
    }
    return -1;
}


/*
 * str is copied to upper and made uppercase
 * upper is the returned string
 * str should be null-terminated
 */
void toupper(char upper[], const char str[])
{
    int i = -1;
    do
    {
        i++;
        upper[i] = std::toupper(str[i]);
    }
    while (str[i] != '\0');
}

重写源代码

的 solution here is simple: replace this array through a simple std::map. Here is the updated code:

std::map<std::string, double> var;

bool Variablelist::add(const char* name, double value)
{
    var[ toUpper( name ) ] = value ;
    return true;
}

bool Variablelist::set_value(const char* name, const double value)
{
    return add(name, value);
}

std::string toUpper(const char str[])
{
    std::string upper;
    upper.reserve(NAME_LEN_MAX);
    int i = -1;
    do
    {
        i++;
        upper += std::toupper(str[i]);
    }
    while (str[i] != '\0');
    return upper;
}

Here toupper() is rewritten for std::string and the function add() is only a simple affectation of std::map, which has a logarithmic complexity.

比较结果

我们可以使用最新版本重新执行测试,并将结果与 浏览器. Here we load the latest version of parser.csmes and compare it with the previous one (工具–> Compare with…)

通过检查 执行力 窗口中,我们看到速度问题已解决。测试的执行时间不再成倍增长。


功能分析器 窗口进一步证实了这一发现。 压扁 可可以粗体突出显示已修改的功能,在新功能下划线并删除已删除的功能。现在,通过测量执行时间的差异(1分钟和32秒),我们可以看到性能增益与以前的执行时间几乎相同。


结论

使用Squish 可可’的新配置文件扩展名,可以对应用程序进行事后分析’的表现。这种离线分析与许多其他性能分析工具不同,后者通常更专注于仅在开发人员上获取性能分析信息’s computer.

在这里,可以在执行完完整的套件后分析完整的数据集。使用 浏览器,则可以选择相关测试,进行比较,然后在代码重写或重构之后,将两个软件版本进行比较。换句话说,Coco允许我们执行一个记录了概要分析数据的套件,以后可以对其进行存档或分析。

1 评论

[…] We’关于使用Function Profiler编写了一个单独的how-to博客。其中涵盖了编译应用程序,缩小性能问题,确定有问题的功能,重写源代码以及比较新代码。’的性能达到原始版本。在这里看看。 […]

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *

复制链接