Page 57 - 《软件学报》2021年第12期
P. 57

张杨  等:基于下推自动机的细粒度锁自动重构方法                                                         3721


         工具是由 David A.Wheeler 开发的用于精确统计代码行数的工具,不仅适用于多个操作系统平台,而且适合于多
         种语言.
             11 个测试程序共包含 1 429 775 行代码,重构后代码行数为 1 439 779 行,增加了 10 004 行代码.由于重构前
         使用的是同步锁,重构后使用的读写锁需要显示的加锁和解锁,并且需要使用 try-finally 语句块,所以重构后会
         增加代码行数.对于 HSQLDB 测试程序,由于其包含的同步锁数目最多,重构前后改变的代码行数也最多,达到
         3 756 行;对于 Jenkins,Cassandra,SPECjbb2005 和 JGroups 测试程序,由于它们包含的同步锁数目在 100 到 300
         之间,代码改变的行数也较多,分别增加了 1 762 行、1 420 行、782 行和 1 241 行;对于 RxJava,Freedomotic,Antlr
         和 MINA 测试程序,由于包含的同步锁较少,代码行数的改变也较少,分别有 171 行、274 行、59 行和 67 行.
             这些代码行数在一定程度上体现了程序开发人员重构程序时的工作量,如果使用手动重构的方式,开发人
         员需要首先在程序中找到同步锁出现的位置,然后进行修改.例如:在手动重构 HSQLDB 测试程序时,需要在 17
         万行的代码中寻找 684 个同步锁并进行重构,最终结果会增加 3 756 行代码,这种手动重构耗时较长并且容易给
         程序引入新的错误,对于该测试程序,我们发现同步锁大部分分布在 org.hsqldb 包中,分布相对比较集中;在 FOP
         中,32 个内置监视器对象在重构后只增加 235 行代码,但 32 个监视器对象分布在 2 034 个 java 文件中,分布非常
         稀疏,遍历过程十分耗时.在使用我们开发的自动重构工具重构 HSQLDB 和 FOP 时,分别仅需要 18s 和 15s 即可
         完成,可以大大节省程序员的工作量.
         5.3.3    重构时间
             11 个测试程序重构的总耗时为 192s,每个程序平均耗时 17.5s,见表 2.HSQLDB 中监视器对象较多,有 684
         个监视器对象,重构耗时为 18s;程序 Cassandra 规模相对较大,源码行数为 431 022 行,重构耗时为 73s;JGroups
         和 Xalan 重构耗时分别为 24s 和 19s;SPECjbb2005,RxJava,Freedomotic,Antlr 和 MINA 的程序规模相对较小,重
         构耗时约为 2s~8s.通过对这些程序的重构时间进行分析,我们发现:重构工具在重构时的时间消耗主要是用于
         程序的静态分析上,程序越大,静态分析时间越长,造成总的重构时间也越长.对于 FOP 测试程序中,源码行数为
         198 555 行,虽然其与 HSQLDB 程序的代码规模相当,但其中包含同步锁个数非常少,分布相对比较稀疏,通过手
         动的方式进行重构会花费大量的时间在搜索代码上;而 FLock 可以自动地完成重构,用时也仅仅为 15s,大大减
         少重构耗时,有效提高开发效率.
         5.3.4    重构的正确性
             为了对重构后测试程序的正确性进行验证,我们主要从以下 3 个方面进行了验证,包括验证推断锁的类型
         是否正确、加锁和解锁的位置是否正确、锁结构的使用是否正确.由于目前没有相关的自动验证工具,我们主
         要采用了手工检查的方式.
             在验证推断锁类型的准确性方面,我们手动地检查每一个测试程序中每一个临界区的代码,我们发现:推断
         出来的每一个锁都符合我们的推断规则,都是正确的类型.此外,我们发现:在 Cassandra 测试程序中,部分代码在
         重构前就使用了读写锁.我们将这些读写锁重构为同步锁,然后使用 Flock再对其进行重构,结果发现:Flock推断
         的锁类型和原有的锁类型基本相同,只有一处存在不同之处.该处发生在 CompactionStrategyManager 类的
         maybeReload 方法中,该方法原本使用写锁,但是 Flock 将其推断为锁分解的方式,经过手工检查发现:使用锁分
         解的方式并未改变程序原逻辑,是正确的细粒度加锁方式.
             在判断加锁和解锁位置以及锁的使用方面,我们主要检查了:(1)  每种锁的加锁操作是否对应一个解锁操
         作;(2)  解锁操作是否被放入 finally 语句块中;(3)  细粒度锁的结构是否正确.经过检查,我们并未发现相关错误.
         我们也通过运行程序的方式检查了程序的正确性,发现这些程序在执行过程中并没有报错,都可以正确运行.
         5.3.5    重构前后性能对比
             本节选取 HSQLDB,Jenkins,Cassandra,JGroups 和 SPECjbb2005 进行性能测试.之所以选择这 5 个程序,是因
         为它们在发布的版本中都提供了相应的基准测试程序.
             HSQLDB 提供了 JDBCBench 测试程序,该程序的测试结果如图 5 所示,其中:横坐标为处理事务数,分别给
         出了事务数为 100k,200k,300k 和 400k 的情况;纵坐标为事务处理率.从图中可以看出:该测试程序在处理 100k
   52   53   54   55   56   57   58   59   60   61   62