使用 gperf 实现高效的 C/C++ 命令行处理

本文介绍gperf工具在命令行处理中的应用,包括其工作原理、配置方式及如何生成高效散列表。通过gperf,开发者能简化命令行选项的解析过程,提升软件维护效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

命令行处理和 gperf 的作用

命令行处理一直以来都是软件开发中最容易被忽视的领域。几乎所有比较复杂的软件都具有一些可用的命令行选项。事实上,大量 if-else 语句经常被用来处理用户输入,因此维护这种遗留代码相当费时,对资深程序员亦是如此。这种情形下,很多 C 开发人员通常使用冗长(通常都嵌套使用)的 if-else 语句,以及 ANSI C 库函数,例如 strcmpstrcasecmp 和 strtok 作为补充,如清单 1 所示。

清单 1. C 语言样式的命令行处理
1
2
3
4
5
6
7
8
if (strtok(cmdstring, "+dumpdirectory"))
   {
   // code for printing help messages goes here
   }
else if (strtok(cmdstring, "+dumpfile"))
   {
   // code for printing version info goes here
   }

C++ 开发人员并没有使用基于 ANSI C 的应用程序编程接口,而是使用标准模板库(Standard Template Library,STL)中的字符串。尽管如此,仍然无法避免使用嵌套的 if-else 序列语句。很明显,随着命令行选项不断增加,这种方法缺乏可伸缩性。对于具有 N 个选项的典型程序调用,代码最终执行 0(N2)比较。为了生成运行更加快捷并易于维护的代码,使用散列表存储命令行选项并使用散列验证用户指定的输入,这种方法非常有帮助。

这就是 gperf 扮演的角色。它将从预定的有效命令行选项列表和时间复杂度为 O(1) 的查找函数中生成一个散列表。因此,对于具有 N 个选项的典型程序调用,代码只需执行 O(N) [N*O(1)] 比较 — 这是对遗留代码的巨大改进。

Gperf 使用模式

Gperf 将从用户提供的文件中(通常使用 .gperf 作为扩展名,但不做强制要求)— 例如,commandoptions.gperf — 并针对散列表、散列和查找方法生成 C/C++ 源代码。所有代码被定向到标准输出,然后必须重定向到类似下面的文件:

1
gperf  -L C++ command_line_options.gperf > perfecthash.hpp

注意:-L 选项将指示 gperf 生成 C++ 代码。

Gperf 输入文件格式

清单 2 展示了 gperf 输入文件的典型格式。

清单 2. gperf 输入文件格式
1
2
3
4
5
6
7
8
%{
/* C code that goes verbatim in output */
%}
declarations
%%
keywords
%%
functions

文件格式由若干元素组成:C 代码内容、声明、关键字和函数。

C 代码内容

C 代码内容是可选的,使用 %{ 和 %} 括起来。其中的 C 代码和注释将被全部复制到 gperf 生成的输出文件中。(注意,此处类似于 GNU flex 和 bison 实用程序)。

声明

声明部分也是可选的;如果没有使用 -t 选项调用 gperf,则完全可以忽略声明部分。但是,如果启用了这个选项,声明部分中最后一个元素的第一个字段必须是使用 char* 或 const char* 标识符调用的名称

但是,通过使用 gperf 中的 -K 选项可以改写第一个字段的名称。例如,如果希望将该字段命名为 command_option,执行以下 gperf 调用:

1
gperf -t -K command_option

清单 3 展示了 C 代码内容和声明部分。

清单 3. C 代码内容和声明部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%{
struct CommandOptionCode  {
   enum {
       HELPVERBOSE = 1,
       ..., // more option codes here
       _64BIT = 5
    };
};
typedef struct CommandOptionCode CommandOptionCode;
%}
struct CommandOption
   {
   const char* command_option;
   int OptionCode;
   };
%%

关键字

关键字部分包含关键字— 在本例中指预定义的命令行参数。在该部分中,如果每行第一列以数字标志 (#) 开头,那么该行属于注释行。关键字应该是每一个非注释行的第一个字段;通常与 char* 相关联的字符串引号是可选内容。此外,字段可以放在前面的关键字之后,但是必须使用逗号隔开并截止到行末。这些字段直接对应于声明部分中最后一部分结构,如清单 4 所示。

清单 4. 关键字部分
1
2
3
4
%%
+helpverbose, CommandOptionCode::HELPVERBOSE
+append_log, CommandOptionCode::APPEND_LOG
+compile, CommandOptionCode::COMPILE

第一个条目指 CommandOption 结构的 const char* command_option 字段,如 清单 3 所示;第二个条目指同一个结构中的 int OptionCode 字段。那么这里究竟有什么含义呢?事实上,这就是 gperf 初始化散列表的方式,其中存储了命令行选项及其相关属性。

函数

函数也是可选的部分。函数部分中所有以 %% 开头并延伸到文件末尾的文本将全部复制到生成的文件中。和声明部分一样,用户需要为函数部分提供有效的 C/C++ 代码。

Gperf 输出

Gperf 混编了一组预定义的关键字,然后对这些关键字执行快速查找。与此相似,gperf 输出两个函数:hash() 和in_word_set()。前者是一个散列例程,而后者用于执行查找。Gperf 输出可以是 C 语言,也可以是 C++ 语言 — 您可以指定为其中一种。如果将输出指定为 C 语言,将生成两个具有上述名称的 C 函数。如果指定为 C++ 语言,gperf 将生成名为Perfect_Hash 的类,该类包含两种方法。

注意:可以使用 -Z 选项修改生成的类名。

散列函数的原型为:

1
unsigned int hash (const char *str, unsigned int len);

其中 str 表示命令行选项,而 len 表示其长度。例如,如果命令行参数为 +helpverbose,则 str 为 +helpverboselen 为12

在 gperf 生成的散列内,in_word_set() 为查找函数。该例程的原型取决于用户指定的 -t 选项。如果还没有指定该选项,那么仅处理特定于用户的命令字符串(作为数据存储在 gperf 生成的散列中),而不是与命令字符串相关的结构。

例如,在 清单 3 中,将 CommandOption 结构与用户命令参数关联起来,该参数将由 in_word_set() 例程返回。您可以使用 -N选项改变这个例程的名称。该例程的参数类似于前面解释的 hash() 函数:

1
const struct CommandOption* in_word_set (const char *str, unsigned int len);

常见 gperf 选项

Gperf 是可以接受不同选项的高度可定制工具。gperf 在线手册(参阅 参考资料小节 中的链接)说明了 gperf 中所有可用的选项,包括:

  • -L language-name指示 gperf 使用指定的语言生成输出。目前支持以下几个选项:
    • KR-C这种老式的 K&R C 可以得到新旧 C 编译器的支持,但是新的符合 ANSI C 标准的编译器可能会生成警告,或者,某些情况下甚至会生成标志错误。
    • C:该选项将生成 C 代码,但是如果不对已有源代码进行调整,则可能无法使用某些旧的 C 编译器进行编译。
    • ANSI-C该选项生成符合 ANSI C 标准的代码,只能使用符合 ANSI C 标准的编译器或 C++ 编译器进行编译。
    • C++:该选项生成 C++ 代码。
  • -N该选项允许用户修改查找函数的名称。默认名为 in_word_set()
  • -H该选项允许用户修改散列例程的名称。默认名为 hash()
  • -Z该选项在提供了 -L C++ 选项时使用。它允许用户指定所生成的 C++ 类的名称,该类包含 in_word_set() 和 hash()函数。默认名为 Perfect_Hash
  • -G该选项将生成查找表并将其作为静态全局变量,而不是在查找函数内生成以隐藏该表(默认行为)。
  • -C前面讨论了 Gperf 将生成查找表。-C 选项将创建使用 const 关键字声明的查找表。所有生成的查找表中的内容都是常量 — 即只读形式。很多编译器通过将表放入只读内存中可以生成更高效的代码。
  • -D该选项将处理散列为重复值的关键字。
  • -t该选项允许包含关键字结构。
  • -K该选项允许用户选择关键字结构中的关键字组件的名称。
  • -p该选项可以与较早版本的 gperf 兼容。在早期版本中,它将生成的函数 in_word_set() 返回的默认布尔值(即 0 或 1 )修改为 pointer to wordlist array 类型。这个选项非常有用,尤其是在使用 -t(允许使用用户定义的 structs)选项时。在最新版的 gperf 中并不要求使用该选项并且可以将其删除。

Gperf 原理概述

静态搜索集 是一种抽象数据类型,包含的操作包括 initializeinsert 和 retrieve。完美散列函数是一种在时间和空间方面都十分高效的静态搜索集实现。Gperf 是一种完美散列函数生成器,它使用用户提供的关键字列表构建完美散列函数。Gperf 将 n 个用户提供的关键字元素列表转换为包含 k 个元素查找表和两个函数的源代码:

  • hash该例程将关键字惟一地映射到范围 0 .. k - 1 中,其中 k = n。如果 k = n,hash() 被认为是最小完美 hash() 函数。这种 hash() 函数具有两个属性:
    • perfect property查找时间复杂度为 O(1) 的表条目 — 就是说,至多需要一个字符串比较执行静态搜索集中的关键字识别。
    • minimal property为存储关键字而分配的最小内存。
  • in_word_set该例程使用 hash() 确定某个字符串是否属于用户提供的列表,大多数情况下只使用一个字符串比较。

Gperf 的内部实现以两个内部数据结构为核心: 关键字签名(keyword signatures)列表(Key_List)和 关联值(associated values)数组(asso_values)。所有用户指定的关键字及其属性将从用户指定的文件中读取,并存储为链接列表中的一个节点(称为 Key_List)。在搜索完美 hash() 函数时,gperf 只将每个关键字字符中的一部分作为搜索键。这部分字符被称为关键字签名 或 keysig

关联值数组在 hash() 函数内部生成,并使用 keysig 字符进行索引。Gperf 反复搜索某种关联值配置,该配置将所有 nkeysig映射到非重复的散列值。当 gperf 找到某种配置,并且该配置将每个 keysig 分配到生成的查找表中惟一位置时,将生成一个完美 hash() 函数。产生的完美 hash() 函数返回一个无符号的 int 值,范围为 0..(k-1),其中 k 值为最大关键字散列值加 1。

当 k = n 时,将生成最小完美 hash() 函数。关键字散列值通常这样计算:将关键字的 keysig 关联值和关键字长度结合。默认情况下,hash() 函数将关键字的第一个索引位置的关联值和最后一个索引位置的关联值添加到长度中;例如:

1
hash_value = length + asso_values[(unsigned char)keyword[1]];

示例项目

下面使用一个简单项目解释目前为止所讨论的概念。考虑如清单 5 所示的 gperf 文件。

清单 5. command_options.gperf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%{
#include "command_options.h"
typedef struct CommandOptionCode CommandOptionCode;
%}
struct CommandOption
   {
   const char *Option;
   int OptionCode;
   };
%%
+helpverbose, CommandOptionCode::HELPVERBOSE
+password, CommandOptionCode::PASSWORD
+nocopyright, CommandOptionCode::NOCOPYRIGHT
+nolog, CommandOptionCode::NOLOG
+_64bit, CommandOptionCode::_64BIT

清单 6 展示了包含在 gperf 文件中的 command_options.h 头文件。

清单 6. command_options.h 头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef __COMMANDOPTIONS_H
#define __COMMANDOPTIONS_H
struct CommandOptionCode
   {
   enum
     {
     HELPVERBOSE = 1,
     PASSWORD = 2,
     NOCOPYRIGHT = 3,
     NOLOG = 4,
     _64BIT = 5
     };
   };
#endif

gperf 命令行如下所示:

1
2
gperf -CGD -N IsValidCommandLineOption -K Option -L C++ -t
     command_line_options.gperf > perfecthash.hpp

散列表作为 perfecthash.hpp 文件一部分生成。由于命令行中指定了 -G 选项,将在全局范围内生成散列表。因为使用 -C 选项进行 gperf 调用,将使用 const 属性定义散列表。清单 7 展示了所生成的源代码的详细内容。

清单 7. 生成的 perfecthash.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* C++ code produced by gperf version 3.0.3 */
/* Command-line: 'C:\\gperf\\gperf.exe' -CGD -N IsValidCommandLineOption -K Option
-L C++ -t command_line_options.gperf  */
/* Computed positions: -k'2' */
 
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
       && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
       && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
       && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
       && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
       && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
       && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
       && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
       && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
       && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
       && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
       && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
       && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
       && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
       && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
       && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
       && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
       && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
       && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
       && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
       && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
       && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
       && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646.  */
#error "gperf generated tables don't work with this execution character set. \
Please report a bug to < bug-gnu-gperf @gnu.org>."
#endif
 
#line 1 "command_line_options.gperf"
 
#include "command_options.h"
 
typedef struct CommandOptionCode CommandOptionCode;
#line 6 "command_line_options.gperf"
struct CommandOption
   {
   const char *Option;
   int OptionCode;
   };
 
#define TOTAL_KEYWORDS 5
#define MIN_WORD_LENGTH 6
#define MAX_WORD_LENGTH 12
#define MIN_HASH_VALUE 6
#define MAX_HASH_VALUE 17
/* maximum key range = 12, duplicates = 0 */
 
class Perfect_Hash
{
private:
   static inline unsigned int hash (const char *str, unsigned int len);
public:
   static const struct CommandOption *IsValidCommandLineOption (const char *str,
                                                                unsigned int len);
};
 
inline unsigned int
Perfect_Hash::hash (register const char *str, register unsigned int len)
{
   static const unsigned char asso_values[] =
     {
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18,  0, 18, 18, 18, 18,
       18, 18, 18, 18,  5, 18, 18, 18, 18, 18,
        0, 18,  0, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
       18, 18, 18, 18, 18, 18
     };
   return len + asso_values[(unsigned char)str[1]];
}
 
static const struct CommandOption wordlist[] =
   {
#line 15 "command_line_options.gperf"
     {"+nolog", CommandOptionCode::NOLOG},
#line 16 "command_line_options.gperf"
     {"+_64bit", CommandOptionCode::_64BIT},
#line 13 "command_line_options.gperf"
     {"+password", CommandOptionCode::PASSWORD},
#line 14 "command_line_options.gperf"
     {"+nocopyright", CommandOptionCode::NOCOPYRIGHT},
#line 12 "command_line_options.gperf"
     {"+helpverbose", CommandOptionCode::HELPVERBOSE}
   };
 
static const signed char lookup[] =
   {
     -1, -1, -1, -1, -1, -1,  0,  1, -1,  2, -1, -1,  3, -1,
     -1, -1, -1,  4
   };
 
const struct CommandOption *
Perfect_Hash::IsValidCommandLineOption (register const char *str,
                                         register unsigned int len)
{
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
     {
       register int key = hash (str, len);
 
       if (key <= MAX_HASH_VALUE && key >= 0)
         {
           register int index = lookup[key];
 
           if (index >= 0)
             {
               register const char *s = wordlist[index].Option;
 
               if (*str == *s && !strcmp (str + 1, s + 1))
                 return &wordlist[index];
             }
         }
     }
   return 0;
}

最后,清单 8 展示了主要的源代码清单。

注意:清单 8 演示了用户可以在常量时间内从给定的命令行选项关键字中查找命令行选项,并随后使用相应的步骤处理该选项。IsValidCommandLineOption 的查找时间复杂度为 O(1)。

清单 8. 定义应用程序入口点的 gperf.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "command_options.h"
#include "perfecthash.hpp"
#include < iostream >
#include < string >
using namespace std;
 
int main(int argc, char* argv[])
   {
   string cmdLineOption = argv[1]; // First command line argument
   const CommandOption* option =
     Perfect_Hash::IsValidCommandLineOption(cmdLineOption.c_str(),
        cmdLineOption.length());
   switch (option->OptionCode)
     {
     case CommandOptionCode::HELPVERBOSE :
       cout << "Application specific detailed help goes here"; break;
     default: break;
     }
   return 0;
   }

注意:本文中的所有示例都使用 gperf 版本 3.0.3 进行了测试。如果您使用的是早期的版本,则可能需要在命令行调用中使用-p 选项。

结束语

gperf 实用程序可以为中小型数据库快速生成完美散列。但是,gperf 还可用于其他目的。事实上,可以在 GUN 编译器中使用它维护语言关键字的完美散列,其最新的功能使您能够操作更大的数据库。因此,可以考虑在您的下一个开发项目中使用 gperf。

google-perftools 简介 google-perftools 是一款针对 C/C++ 程序的性能分析工具,它是一个遵守 BSD 协议的开源项目。使用该工具可以对 CPU 时间片、内存等系统资源的分配和使用进行分析,本文将重点介绍如何进行 CPU 时间片的剖析。 google-perftools 对一个程序的 CPU 性能剖析包括以下几个步骤。 1. 编译目标程序,加入对 google-perftools 库的依赖。 2. 运行目标程序,并用某种方式启动 / 终止剖析函数并产生剖析结果。 3. 运行剖结果转换工具,将不可读的结果数据转化成某种格式的文档(例如 pdf,txt,gv 等)。 安装 您可以在 google-perftools 的网站 (http://code.google.com/p/google-perftools/downloads/list) 上下载最新版的安装包。为完成步骤 3 的工作,您还需要一个将剖析结果转化为程序员可读文档的工具,例如 gv(http://www.gnu.org/software/gv/)。 编译与运行 您需要在原有的编译选项中加入对 libprofiler.so 的引用,这样在目标程序运行时会加载工具的动态库。例如本例中作者的系统中,libprofiler.so 安装在"/usr/lib"目录下,所以需要在 makefile 文件中的编译选项加入“-L/usr/lib -lprofiler”。 google-perftools 需要在目标代码的开始和结尾点分别调用剖析模块的启动和终止函数,这样在目标程序运行时就可以对这段时间内程序实际占用的 CPU 时间片进行统计和分析。工具的启动和终止可以采用以下两种方式。 a. 使用调试工具 gdb 在程序中手动运行性能工具的启动 / 终止函数。 gdb 是 Linux 上广泛使用的调试工具,它提供了强大的命令行功能,使我们可以在程序运行时插入断点并在断点处执行其他函数。具体的文档请参照 http://www.gnu.org/software/gdb/,本文中将只对用到的几个基本功能进行简单介绍。使用以下几个功能就可以满足我们性能调试的基本需求,具体使用请参见下文示例。 命令 功能 ctrl+c 暂停程序的运行 c 继续程序的运行 b 添加函数断点(参数可以是源代码中的行号或者一个函数名) p 打印某个量的值或者执行一个函数调用 b. 在目标代码中直接加入性能工具函数的调用,该方法就是在程序代码中直接加入调试函数的调用。 两种方式都需要对目标程序重新编译,加入对性能工具的库依赖。对于前者,他的好处是使用比较灵活,但工具的启动和终止依赖于程序员的手动操作,常常需要一些暂停函数(比如休眠 sleep)的支持才能达到控制程序的目的,因此精度可能受到影响。对于后者,它需要对目标代码的进行修改,需要处理函数声明等问题,但得到的结果精度较高,缺点是每次重新设置启动点都需要重新编译,灵活度不高,读者可以根据自己的实际需求采用有效的方式。 示例详解 该程序是一个简单的例子,文中有两处耗时的无用操作,并且二者间有一定的调用关系。 清单 1. 示例程序 void consumeSomeCPUTime1(int input){ int i = 0; input++; while(i++ < 10000){ i--; i++; i--; i++; } }; void consumeSomeCPUTime2(int input){ input++; consumeSomeCPUTime1(input); int i = 0; while(i++ < 10000){ i--; i++; i--; i++; } }; int stupidComputing(int a, int b){ int i = 0; while( i++ < 10000){ consumeSomeCPUTime1(i); } int j = 0; while(j++ < 5000){ consumeSomeCPUTime2(j); } return a+b; }; int smartComputing(int a, int b){ return a+b; }; void main(){ int i = 0; printf("reached the start point of performance bottle neck\n"); sleep(5); //ProfilerStart("CPUProfile"); while( i++ MyProfile.pdf 转换后产生的结果文档如下图。图中的数字和框体的大小代表了的某个函数的运行时间占整个剖析时间的比例。由代码的逻辑可知,stupidComputing,stupidComputing2 都是费时操作并且它们和 consumeSomeCPUTime 存在着一定的调用关系。 图 1. 剖析结果 结束语 本文介绍了一个 Linux 平台上的性能剖析工具 google-perftools,并结合实例向读者展示了如何使用该工具配置、使用及分析性能瓶颈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值