Page 243 - 《软件学报》2026年第3期
P. 243

1206                                                       软件学报  2026  年第  37  卷第  3  期



                                                  表 3 连续域访问次数分布

                                       连续域访问次数               数量             比例 (%)
                                            1              18 529 964        93.32
                                            2              1 122 460         5.65
                                           ⩾3               204 614          1.03
                                           总计              19 857 038        100.00

                    QLStat 通过递归方式遍历所有解引用表达式, 计算连续解引用次数. 统计结果如表                      4  所示, 与连续域访问次数
                 的分布类似, 单次解引用操作占绝大多数, 为             97.21%, 连续解引用次数为     2  的占了  2.79%. 上述结果表明, 在   Go  语
                 言中使用指针分析结果时, 可以对连续解引用次数大于等于                    2  的场景采用保守建模策略. 这种方式在显著降低分
                 析开销的同时, 依然能够提供较高的精度, 满足实际需求.

                                                  表 4 连续解引用次数分布

                                       连续解引用次数               数量             比例 (%)
                                            1               305 679          97.21
                                            2                8 765           2.79
                                           总计               314 444         100.00

                    结论  1. 在基本访存操作中, 域访问操作和解引用操作占有较高的比例, 分别为                      25.44%  和  17.63%, 在编译器
                 优化和静态分析工具设计中应重点关注. 此外, 单次域访问占比超过                     93%, 单次解引用操作占比超过         97%. 因此在
                 设计域敏感分析和使用指针分析结果时, 仅需要考虑单次域访问和单次解引用即可覆盖绝大部分场景. 这种设计
                 能显著降低分析开销, 同时实现快速且高精度的分析目标.
                  3.2   接口类型的分析
                    接口类型变量在       Go  的实现中通过一个结构体表示, 如代码           4  所示, 包含两个域, 其中一个域是类型方法表指
                 针  tab, 用于描述指向类型描述符或者方法表, 另外一个域是数据指针                  data, 用于指向实际数据对象. 当将非接口类
                 型的值赋给接口类型变量时, 通常需要在堆上分配一块新的空间, 并让                      data 指向该新空间以表示新对象. 只有当
                 非接口类型是指针时, 接口变量的           data 才直接指向原始对象, 而无需额外的堆分配. 因此, 接口类型赋值可能导致
                 隐式内存分配, 从而引发潜在的内存性能问题.


                 代码  4. 接口类型实现与接口类型转换.
                 1. // underlying implementation of non-empty interface
                 2. type iface struct {
                 3.   tab *itab // type descriptor or table pointer
                 4.   data unsafe.Pointer // data pointer
                 5. }
                 6. var a int
                 7. var i interface{} = a // implicit heap allocation

                    由于接口类型转换可能导致隐式内存分配, 并对内存性能与程序分析建模产生影响, QLStat 根据 Go                            语言官
                 方规范   [33] , 在抽象语法树层面总结了     5  类可能引发新分配对象的接口操作模式, 如表               5  所示. 在这 5  类模式中,
                 assign  模式中 LHS  为接口类型, call 模式中形参类型均为接口类型, conversion      模式为显式接口转换, ret 模式中返
                 回类型为接口类型, send     模式中发送到信道的数据类型为接口类型. 除               conversion  模式为显式转换外, 其他模式均
                 存在到接口类型的隐式转换. 统计结果显示, assign            和  call 模式占据所有模式中的绝大部分, 分别为            20.16%  和
                 77.48%, 表明分析和优化与接口类型相关的代码时, 需要重点关注这两类模式. 具体来说, assign                      模式对函数内的
   238   239   240   241   242   243   244   245   246   247   248