| 下载 IBM® XL C/C++ for AIX 编译器 | IBM® XL C/C++ for Linux 编译器 |
|---|
| 下载 IBM® XL Fortran for AIX 编译器 | IBM® XL Fortran for Linux 编译器 |
| 下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。 |
1 PDF(Profile-directed feedback)概述
Profile-directed feedback(PDF)是 IBM® Rational® Xl 系列编译器具有的一项重要功能,目前支持的语言有 C, C++ 和 Fortran,适用的操作系统有 AIX®,Linux®和 z/OS®。PDF 通过应用程序运行时的性能信息来分析原始代码分支和模块的执行频率,然后将分析结果反馈回编译器用来改进对应用程序的优化,从而提高其在运行时的性能。PDF 优化的流程见图 1:
图 1. PDF 优化的流程
PDF 功能的使用流程如图 1 所示。首先,用户用 -qpdf1 选项对源代码进行编译(优化级别必须为 -O2 或以上),然后运行生成的可执行程序以收集程序性能分析信息。如图 1 中虚线所示,用户还可以多次编译代码并运行可执行程序,从而获取程序的多次性能分析信息。程序的样本输入必须能代表该应用程序的典型使用的情况。这些性能信息将为编译器对应用程序的优化提供基础。
最后,使用 -qpdf2 的选项对代码进行重新编译,编译器会根据上一阶段中运行可执行文件所采集到的程序性能分析信息来对应用程序进行相应的优化。
例如,对一个 test.c 的源文件进行 PDF 优化,可以采用下列基本步骤:
- 编译 test.c 并指定 -qpdf1 选项和优化级别 -O4。
xlc test.c -O4 -qpdf1 - 利用典型的数据 (sample.data) 运行生成的可执行文件 a.out。
./a.out < sample.data - 重新编译 test.c 并指定 -qpdf2 选项。编译器会根据步骤 2 编译器生成的性能分析信息,生成优化的可执行文件 a.out。
xlc test.c -O4 -qpdf2
PDF 优化为程序带来的运行时性能提升是巨大的。通过 PDF, 编译器会了解到程序的实际运行状况,以及各代码块执行频率的差别,然后根据这些信息调整优化算法,并进而通过改变寄存器的分配策略和修改汇编指令等方法来提高应用程序的性能。
如果不采用 PDF 优化,编译器会直接对源文件进行编译和链接,并最终产生没有经过 PDF 优化的可执行文件。这种未经优化的可执行文件在性能方面会比较低。下面我们利用三个 SPEC CPU2006 中的例子来展示采用 PDF 优化和不采用 PDF 优化时应用程序的性能差别。
例 1:
名称:gamess
硬件环境: squadra3/Power5®
优化等级:-O3
例 2:
名称:sphinx3
硬件环境: khesahn/Power6®
优化等级:-O5
例 3:
名称:lbm
硬件环境:khesahn/Power6®
优化等级:-O5
图 2 直观地比较了和展示了经过 PDF 优化的应用程序在性能方面的提高。
图 2. 未采用 PDF 优化与采用 PDF 优化的应用程序运行时间比较
注:本文中的所有编译命令和示例代码均以 IBM XL C for AIX 和 IBM XL C/C++ for Linux 编译器为例。在 Linux 系统中,运行当前路径下的程序时,要用 ./ 指明文件路径,而 AIX 则没有这个要求。本文一律采用加 ./ 的方式,以同时满足两个系统的要求。
编译器所产生的性能分析数据文件包含了与优化相关的信息,用户可以通过以下三种方式来查看性能分析信息 (PDF 信息 ):
- 用 showpdf 命令查看其信息内容
- 用 -qpdf2 和 -qreport 选项编译文件,在生成的 .lst 列表文件中查看 PDF 信息
- 用 -qpdf2 和 -qlistfmt 选项,在生成的诊断报告里阅读 PDF 信息
通过查看 PDF 信息,用户可以了解代码中各个分支和模板的运行状况以及缓存的利用情况。
在 -qpdf1 的编译阶段,编译器会生成 PDF 映射文件,如 ._pdf_map。该文件包含了关于程序的静态信息。而运行经过 -qpdf1 编译出来的应用程序时,编译器会生成 PDF 文件(性能分析数据文件),如 ._pdf。该文件包含程序为 PDF 优化所收集的各种信息。用户可以通过 showpdf 来查看这两者的具体信息。
下面通过清单 1 中的例子来说明如何查看 PDF 文件中的性能分析信息。程序源代码 (myprogram.c) 如下:
清单 1. 程序源代码(myprogram.c)
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *env[])
{
unsigned long i = 0;
double j = 4.12301;
if (atoi(argv[1]) <= 100)
{
for (i = 0; i < 4294967295; i++)
{
j = j + 3.1415;
}
printf("The value of j is %.6f.\n", j);
}
else {
printf("Value bigger than 100!");
}
return 0;
}
|
用 xlc myprogram.c -qpdf1 -O3 对源文件进行编译并运行所生成的可执行文件,然后用 showpdf 命令来查看 PDF 信息,结果如图 3 所示:
图 3. PDF 信息
图 3 中的 Call counters 部分显示的是被调用函数在所有函数中的所占比例。例如 atoi 函数,在程序第七行被调用了一次。Block coverage 部分显示的是在本文件中执行到的代码区块在所有代码区块中的比例。 Total call coverage 显示的是在所有源文件中执行到的代码区块在所有代码区块中的比例。
进行 PDF 优化之前首先要考虑的问题是应用程序或应用程序的某些模块是否适合 PDF 优化,因为如果对不合适的程序进行 PDF 优化,可能并不会有效提高程序的性能。
进行 PDF 优化时,编译器主要是对代码路径的执行频率进行判断,然后根据不同路径的执行频率,对相应代码区域中的代码进行重新组合,进而更有效地编排指令、减少内存占用和提高缓存存取效率。
如图 4 所示,一个代码区域中如果存在的不同的代码路径,且各路径的执行频率有很大差别,则编译器会有机会进行 PDF 优化:
图 4. 代码执行路径图
假如路径 1 的执行频率远远高于其它路径,则编译器会对相关代码进行合适的优化,尤其会对路径 1 的代码进行特殊处理,从而提高应用程序的整体性能。
在一个代码区域中,如果各个路径的执行频率差别不大,则编译器不会有很大的机会对该区域的代码进行 PDF 优化。因此,适合进行 PDF 优化的应用程序,应该是那些代码路径的执行频率差别比较大的程序,即有的路径执行频率非常高而有的路径执行频率非常低的程序。
另外,如果一个应用程序包含多个源代码文件,并不一定要对所有文件进行 PDF 优化。只要选择合适的源文件进行优化,然后再链接所生成的目标文件即可。这一点本文会有单独的章节进行讨论。
确定合适的应用程序或源代码文件之后,就要利用典型数据对相应的程序进行编译训练(即利用典型数据引导编译器的优化)。这里要强调的是,选择的训练数据必须代表应用程序实际使用过程中输入的典型数据,否则, PDF 的优化效果可能会适得其反。比如在清单 2 中的函数 PDF_test 中,输入的变量 i 决定了不同代码路径的执行频率:
清单 2. 程序示例
…
int PDF_test (int i) {
int j;
for (j=0; j<1000; j++) {
if (i<50) {
// 执行某些操作
doSth();
} else {
// 执行不同的操作
doSmOthrTh();
}
}
}
…
|
在应用程序的实际使用过程中,如果在 PDF_test 每次被调用时,i 的值在 90% 的情况下都小于 50, 则可以使用一个或多个小于 50 的整数作为典型的训练数据。否则如果输入的训练数据不具有这种代表性,则编译器不会进行有效的 PDF 优化。
在确定了合适的应用程序,并使用了典型的训练数据的情况下,PDF 优化会大大提高应用程序的性能。我们曾经利用 SPEC CPU2006 对一个应用程序进行了性能测试,结果如下:
| O2 | O2 PDF | O3 PDF | O4 PDF | O5 PDF | |
|---|---|---|---|---|---|
| 运行时间(秒) | 693.94 | 618.07 | 617.08 | 539.08 | 515.88 |
| 性能比 O2 提升(%) | 10.93 | 11.07 | 22.32 | 25.66 |
从图表中可以看出,从 -O2 到 -O5,每种级别上的 PDF 优化都比没有 PDF 优化的 -O2 缩短了程序的运行时间,从而提高了程序的性能。
进行 PDF 优化时,既可以进行单遍优化分析,也可以进行多遍优化分析。利用典型数据进行 PDF 优化时,多遍优化分析有利于编译器采集全面的数据,从而在处理器指令、内存占用和缓存利用等方面做出更好的编译决策,并进而更好地提高应用程序的性能。
在利用 -qpdf1 进行优化分析时,我们可以用 level 参数指定优化分析的级别为 0, 1 或 2。在不同的级别上编译器会进行不同类型的性能分析并生成不同类别的分析数据,如下表所示:
注:表中 + 代表相应的优化分析级别包含该性能分析类型。
| 性能分析类型 | 优化分析的级别 | ||
|---|---|---|---|
| 0 | 1 | 2 | |
| 区块计数分析(Block-counter profiling) | + | + | + |
| 调用计数分析 (Call-counter profiling) | + | + | + |
| 单遍分析 (single-pass profiling) | + | + | |
| 值分析 (value profiling) | + | + | |
| 多遍分析 (multiple-pass profiling) | + | ||
|
缓存读写失败分析 (Cache-miss profiling) (仅支持 POWER5®, POWER6®, 和 POWER7®处理器) | + | ||
在级别为 0 时,编译器进行的分析类别相对较少,但生成的文件会比较小并且编译时间也会较短。在级别为 1 时,编译器所进行的分析相对多一些。如果编译时未指定级别,编译器会默认 -qpdf1=level=1。而在级别为 2 时,编译器会进行大量的性能分析,而且只有在这一级别,编译器才支持多遍性能分析。
在进行多遍 PDF 优化分析时,首先要通过指定 -qpdf1=level=0 或者 -qpdf1=level=1 来生成初始的性能分析数据,然后再利用 -qpdf1=level=2 来进行多遍性能分析,并进而获得更全面的分析数据。
例如,待优化的源代码文件为 test.c,而希望的优化选项为 -O4,则我们可以通过以下步骤进行多遍 PDF 优化训练。
- 编译 test.c 并指定 -qpdf1=level=0 选项和优化级别 -O4:
xlc test.c -O4 -qpdf1=level=0 - 利用典型数据 sample.data 运行所生成的可执行文件 a.out:
./a.out < sample.data - 多次编译 test.c 并指定 -qpdf1=level=2 选项,并多次执行生成的可执行文件:
xlc test.c -O4 -qpdf1=level=2./a.out < sample.data - 用 -qpdf2 进行最终编译:
xlc test.c -O4 -qpdf2
在运行通过 -qpdf1 编译出来的可执行文件时,会生成含有性能分析数据的性能分析数据文件 (PDF 文件 )。 PDF 文件的默认文件名为 ._pdf。当 -qpdf1=level=0 或者 -qpdf1=level=1 时,每次运行可执行文件所生成的 PDF 文件会覆盖以前的文件。而当 -qpdf1=level=2 时,编译器会利用前面 PDF 文件中的分析数据进行进一步的优化分析。运行通过 -qpdf1=level=2 编译生成的可执行文件时,新生成的 PDF 文件会以 ._pdf.1 为文件名进行命名。随着运行次数的增加,这个文件名可以累积到 ._pdf.5。如果已经累积到 ._pdf.5,则最后一次的 PDF 文件会被覆盖。
多遍优化分析可以使编译器有机会收集到更全面的分析数据,因而最终生成的可执行文件也会有比较好的性能提升。我们以 1.2 节中的代码为例,此处为清单 3,分别进行了单遍 PDF 优化分析和多遍优化分析,然后对最后生成的可执行文件的运行速度进行了比较。
清单 3. 程序示例
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *env[])
{
unsigned long i = 0;
double j = 4.12301;
if (atoi(argv[1]) <= 100)
{
for (i = 0; i < 4294967295; i++)
{
j = j + 3.1415;
}
printf("The value of j is %.6f.\n", j);
}
else {
printf("Value bigger than 100!");
}
return 0;
}
|
在单遍优化分析的过程中,输入的典型数据(即 argv[1])为 30,而在多遍优化分析的过程中,输入的典型数据分别为 12,17,33,45 和 47。
分别运行经过 PDF 优化的可执行文件并记录运行速度,结果如下:
- 经过单遍优化分析:7.296 秒
- 经过多遍优化分析:6.699 秒
这个结果就印证了多遍性能分析优化会比单遍性能优化分析更能提高应用程序的性能。
由于用户程序包含的代码量一般很大,并且并不是所有的代码都适合进行 PDF 优化,我们最好利用编译器将优化集中在合适的关键性代码上,比如占用较多运行时间的某个函数。因为 PDF 优化的单位是源文件,所以在做 PDF 优化前,需要找出这些关键性代码所在的文件 , 然后只有在编译这些文件时,才指定 PDF 选项。
比如一个用户程序包含了 3 个文件 file1.c, file2.c 和 file3.c,其中 file3.c 包含了适合 PDF 优化的关键性代码,那么可以通过以下步骤来完成有针对性的 PDF 优化:
- 在编译 file3.c 时指定 -qpdf1 选项和优化级别 -O4,其中 -c 表示源文件在编译结束后就停止,不进入链接环节。
xlc file3.c -c -O4 -qpdf1 - 编译 file1.c 和 file2.c。
xlc file1.c fil2.c -c - 将前两个步骤生成的 file1.o, file2.o 和 file3.o 链接在一起。注意在这个步骤中仍然需要指定 -qpdf1 选项和优化级别。
xlc file1.o file2.o file3.o -O4 -qpdf1 - 利用典型数据 sample.data 运行所生成的可执行文件 a.out。
./a.out<sample.data - 性能分析数据文件生成。重新编译 file3.c 文件并指定 -qpdf2 选项,编译器会根据性能分析数据来进行优化。
xlc file3.c -O4 -qpdf2 -c - 将步骤 5 和步骤 2 生成的 file1.o, file2.o 和 file3.o 链接在一起。
xlc file1.o file2.o file3.o -O4 -qpdf2
经过以上 6 步,得到的可执行文件 a.out 即为经过优化的最终应用程序。
在某些特殊情况下,用户用 IBM XL 系列编译器对代码做编译,而用其它编译器做链接。传统的 PDF 过程的优化集中在链接环节,而在这种特殊情况下,就要求 PDF 优化过程在编译阶段完成。另外,有时需要提供的补丁包不是可执行文件,而是目标文件或库文件,这也要求 PDF 能够对目标文件进行优化。传统的 PDF 过程优化效率更高,所以一般建议用户使用传统的 PDF 优化方法。
针对目标文件的优化和传统的 PDF 过程基本类似,区别在于前者需要在 -qpdf2 时指定 -qnoipa 选项来跳过 IPA 链接环节。下面用一个例子来详细说明整个 PDF 的过程。假设有两个文件 file1.c 和 file2.c 需要经过 PDF 优化,我们可以采取以下步骤:
- 编译 file1.c 和 file2.c 文件并指定 -qpdf1 选项和优化级别 -O4。
xlc file1.c file2.c -O4 -qpdf1 - 利用典型数据 sample.data 运行所生成的可执行文件 a.out。
./a.out<sample.data - 性能分析数据文件生成。重新编译 file1.c 和 file2.c 并指定 -qpdf2 和 -qnoipa 选项 , 编译器会根据性能分析数据来进行优化。
xlc file1.c file2.c -c -O4 -qpdf2 -qnoipa
这样生成的 file1.o 和 file2.o 即为优化后的目标文件。如果想要得到可执行文件,可以在步骤 3 中去除 -c 选项,或者用 ld 命令来完成链接。
在 PDF 优化过程中,如果用户在运行可执行文件时输入了多个典型数据集,那么会有多个性能分析数据文件生成。要利用所有的性能分析数据进行优化,就需要把这些文件合并起来。在多个性能分析数据文件中,同一个函数的运行次数可能差别很大,如果按照相同的优先级将这些数据文件进行合并,那么小的典型数据集运行得到的性能分析数据会完全被大的数据集运行出来的结果所淹没。
例如,假设在运行了可执行文件输入了典型数据集 1.data, 2.data 和 3.data,这三个数据集在运行时都调用了函数 func1(), func2() 和 func3(),并且三个函数的运行次数如下:
| Function | 函数执行次数 | ||
|---|---|---|---|
| 1.data | 2.data | 1.data + 2.data | |
| func1() | 19 | 1000 | 1019 |
| func2() | 5 | 200 | 205 |
| func3() | 10 | 10000 | 10010 |
在 1.data 中,func1() 执行次数最多,而在 2.data 中,func3() 执行次数最多。把在两个典型数据集中的函数运行次数按照相同的权重进行相加,2.data 中的数据占有绝对的优势。如果按照这样的统计结果来进行 PDF 优化,生成的可执行文件在输入 1.data 数据集时性能几乎没有改善。
在这种情况下,需要为各个性能分析数据文件设定权重,让它们都为 PDF 优化做出贡献。XL 系列编译器提供了 mergepdf 命令来使得多个性能分析数据文件可以根据优先级来进行合并。
下面用一个例子来描述 mergepdf 的过程。
- 假设源文件为 file.c。编译该文件时指定 -qpdf1 选项和优化级别 -O4。
xlc file.c -O4 -qpdf1 - 运行步骤 1 生成的可执行文件 a.out,并输入典型数据集 1.data。
./a.out<1.data - 将步骤 2 生成性能分析数据文件 ._pdf 改名为 ._pdf1。
mv ._pdf ._pdf1 - 再次运行可执行文件 a.out, 并输入典型数据集 2.data。
./a.out<2.data - 将步骤 4 生成性能分析数据文件 ._pdf 改名为 ._pdf2。
mv ._pdf ._pdf2 - 将两个性能分析数据文件按照权重 100 和 1 来进行合并。
mergepdf -r 100 ._pdf1 -r ._pdf2 -o ._pdf_final - 利用合并后的 ._pdf_final 文件行进行 PDF 优化。我们需要在这步指定 pdfname,否则编译器无法读取正确的性能分析数据文件。
xlc file.c -O4 -qpdf2=pdfname=._pdf_final
经过上述合并,在新的性能分析文件里,func1() 和 func3() 具有相同的执行次数量 2000。编译器在优化过程中认为它们同等重要。
除了上述章节所讨论的主要 PDF 优化技巧之外,在 PDF 优化过程中还有以下几点需要注意:
- 虽然在 PDF 过程中,用户可以设定任何一个 -O2 及其以上的优化级别,但是强烈建议将优化等级设定为 -O4 或以上。
- 在整个 PDF 过程中,必须一直使用相同的优化级别。
- 如果在 -qpdf1 步骤时设定了 -qpdf1=pdfname 选项,那么在 -qpdf2 时,也需要设定相同的 -qpdf2=pdfname 选项,否则编译器会无法找到正确的性能分析数据文件。
- 确保设定的 PDFDIR 环境变量的值为性能分析数据文件的绝对路径。否则编译器无法找到正确的性能分析数据文件。在 PDF 过程中,不能修改 PDFDIR 的值。也建议不要修改其他的编译器设置。
- 不能手动修改性能分析数据文件,否则会影响程序的优化效果。
- 在用 -qpdf1 和 -qpdf2 编译源文件的两个步骤之间,用户可以修改源文件。这种情况下,旧的性能分析数据文件仍然可以使用,但是优化效果会受到影响。如果关键性代码被修改了,用户可能需要重新执行 PDF 过程来生成新的性能分析数据文件。
- 在合并性能分析数据文件时,不能将不同版本编译器生成的数据文件合并在一起。
本节概述 PDF 功能相关的编译选项、命令以及环境变量。由于篇幅限制,无法面面俱到。具体的细节,请参阅 IBM Rational XL 系列编译器相关文档。
该编译选项利用程序运行时的统计数据来分析原始代码中分支和模块的执行频率,然后将分析结果反馈回编译器用来改进应用程序的优化,从而提高其在运行时的性能。
图 5. -qpdf1, -qpdf2 语法图
默认选项:
-qnopdf1, -qnopdf2 |
参数介绍:
- defname
- 将一个 PDF 文件改回其默认名称。
- exename
- 根据输出文件的名称命名生成的 PDF 文件。
- pdfname= file_path
- 指定 PDF 文件或者 PDF 映射文件的名称和所在路径
- Level=0 | 1 | 2
- 指定结果应用程序生成的性能分析信息的级别。
- -qpdf1=level=0 代表最基本的级别。相比 -qpdf1=level=1,这个级别生成的 PDF 文件更小,而且编译速度更快。
- -qpdf1=level=1 为默认的级别。
- -qpdf1=level=2 比 -qpdf=level=0 和 -qpdf=level=1 所包含的性能分析类别都要多。这个级别支持所有类型的性能分析。
在编译和链接阶段,与 -qpdf1 和至少 -O2 以上的优化级别共同使用该编译选项,会生成程序中所有过程的相关性能分析信息,从而允许用户利用 showpdf 工具来读取这些性能分析信息。
默认选项:-qshowpdf
图 6. -qshowpdf 语法图
clearnpdf 与 resetpdf 命令功能相同,都可以用来删除 PDF 过程中所生成的性能分析文件。
图 7. cleanpdf 和 resetpdf 语法图
参数:
- pdfdir
- 指定要删除的性能分析文件数据文件所在的路径。
- pdfname
- 需要删除的性能分析数据文件的名字。
3.2.2 mergepdf
该命令可以按照权重合并多个性能分析数据文件。
图 8. mergepdf 语法图
参数:
- -r scaling
- 指定某个性能分析数据文件的权重比例。该值必须大于 0, 可以是一个整数或者一个小数。默认值为 1.0。
- input
- 指定输入性能分析数据文件的名称或路径。
- -o output
- 指定输出性能分析数据文件的名称或路径。
- -n
- 如果用户给该参数赋值了的话,性能分析数据文件将不会被进行常态化理。否则 mergepdf 命令将会根据内部的一个比率对文件进行常态化理。
- -v
- 指定性能分析数据文件为详尽模式,并输出内部的或者是用户自定义的标度比率。
该命令显示在性能分析数据文件中的信息。
图 9. showpdf 语法图
参数:
- pdfdir
- 性能分析数据文件的路径。
- pdfname
- 性能分析数据文件的名称。
- pdfmapdir
- PDF 映射文件所在的路径。
- -xml
- PDF 信息的输出格式。如果用户指定了该参数,则以 XML 格式输出;否则以文本格式输出。
使用 -qpdf1 或者 -qpdf2 对程序进行编译时,用户可以通过 PDFDIR 环境变量来指定生成的性能分析数据文件所在的路径。
用户可以通过该环境变量将应用程序与一个特定的处理器绑定来进行缓存存取失败分析 (cache-miss profiling)。
3.3.3 PDF_PM_EVENT
用户在运行由 -qpdf1=level2 编译生成的应用程序并想收集不同级别的缓存存取失败信息时,可以相应的将该变量设置为 L1MISS, L2MISS 或者 L3MISS。
PDF 优化是 IBM XL 系列编译器的一个重要功能,它为提高应用程序的性能提供了保证。使用该功能尤其要注意的是,在运行利用 -qpdf1 编译生成的可执行文件时,所作输入的样本数据必须代表该应用程序的典型使用情况。否则,编译器所得到的性能分析数据将不能真实地反映程序的真正使用情况,并因而降低优化效果。
学习
- 参考“IBM AIX 编译器信息中心”,了解更多关于 AIX 平台上编译器的用法。
- SPC CPU 2006:http://www.spec.org/cpu2006/
- 访问 IBM developerWorks 中国网站 Rational 专区,获得关于 IBM Rational 软件交付平台(Rational Software Delivery Platform)产品的技术资源和最佳实践。
- 订阅 IBM developerWorks 时事通讯,一份关于 developerWorks 指南、文章、下载、社区活动、网络广播和技术讲座的电子周刊。
获得产品和技术
- IBM XL 编译器新特性以及试用版下载
- 获取免费的 Rational 软件工具包系列,了解最新的 IBM Rational 软件开发工具技术文档和资源。
- 下载更多免费的 IBM Rational 试用版软件,了解 IBM Rational 软件的最新特性。
- 获取更多 IBM 试用版软件,并熟练掌握来自 DB2®、Lotus®、Tivoli®,以及 WebSphere® 的开发工具和中间件产品,用这些试用版软件开发您的下一个项目。这些试用版软件可以免费直接从 developerWorks 下载。
讨论
- 加入 developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。
- 加入 IBM 软件下载与技术交流群组,参与在线交流。