计算代码行数





5.00/5 (6投票s)
您的项目文件中平均有多少行代码?是的,但平均值是多少?
引言
在监控代码库的健康状况时,一个“坏味道”是文件大小。过大的文件大小可能表明,例如,违反了SOLID原则中的单一职责原则。因此,跟踪项目层级中最大文件大小是有诱惑力的。
不幸的是,这种策略必然会找出异常值。文件可能臃肿的原因是它包含样板代码,在这种情况下,跟踪此信息对开发人员没有用。文件大小小可能也值得关注。例如,90年代初学习C语言的教材展示了仅用一行巧妙的代码就能实现的奇迹。不幸的是,应该使用的“巧妙”的所有同义词都太粗鲁了,无法在此重复。
那么,如何跟踪文件中(或类等)代码行数的有意义的统计数据呢?通过使用统计数据!特别是通过报告置信区间,即变量在特定概率下预计会落在哪两个值之间的范围。例如,天气预报员可能会说,明天降雨量有90%的几率在2厘米到10厘米之间。
拟合概率分布
要计算置信区间,需要对概率分布下的面积进行积分。众所周知,文件中的代码行数(LOC)是一个非负整数,并且预计LOC的值可能被某些文件共享,并且在项目中找到的不同LOC值之间,存在没有文件具备的中间值。因此,可以推断需要一个在半无限实线上连续的概率分布。
通过检查一些数据,事情会容易得多,例如Linux内核4.6的ARM构建中的文件。它包含19912871
行(只是基本计数,未过滤空行/注释),分布在42186
个文件中,平均每文件472
行。最大的文件包含33510
行。将区间从0
到34000
分成100
个桶,并计算分配到每个桶的文件数量(第一个桶包含介于1
到340
行之间的所有文件计数)。这构建了以下直方图
由于它非常陡峭,因此很难拟合曲线,是时候重新思考了。请注意,它看起来像指数衰减,通常行数会四舍五入到最接近的10的幂。因此,从行数的基数10对数构建一个直方图,得到以下结果
通过取对数,半无限实线被映射到无限实线,[0, +inf) => (-inf, +inf)
,因此映射到不同类别的概率分布。直方图看起来非常像正态(也称为高斯或钟形曲线)分布。将原始随机变量表示为X
,将新变量表示为Y = log(X)
,然后定义均值为Y_bar = sum(Y) / sum(1)
,方差为s^2 = sum( (Y -Y_bar)^2 ) / (sum(1) - 1)
,其中求和是针对文件的。对于正态分布,90%的置信区间可以计算为Y_bar +/- 1.645 * s
,然后取指数返回到X = 10^Y
。
示例数据的具体数字是
Y_bar = 2.260263
s = 0.635845
Y_bar +/- 1.645 * s = [1.214297, 3.306228]
10^Y_bar = 182
[10^1.214297, 10^3.306228] = [16, 2024]
因此,数字表明平均文件大小为182行,并且预计文件大小将在16到2024行之间变化。实际比较表明,91%的文件在此区间内。
软件实现
该方法已在我软件架构工具 DeepEnds 的HTML报告中实现(源代码)。这允许在一些示例项目上运行以评估其有用性。查看Visual C++项目层级的一个级别(部分指的是过滤器)
SLOC | 节 | ||||
Sum | Lower | 预期效果 | Upper | 最大值 | |
5060 | 6 | 28 | 130 | 469 | FEA |
3493 | 7 | 28 | 105 | 469 | FEA\Core |
472 | 9 | 33 | 114 | 93 | FEA\Equations |
522 | 6 | 36 | 195 | 189 | FEA\FileIO |
48 | 17 | 23 | 31 | 27 | FEA\LinearSystem |
237 | 12 | 47 | 178 | 101 | FEA\Mesh |
281 | 12 | 99 | 774 | 240 | FEA\Solver |
在顶层,数字看起来不错,但是可以看到FEA\Solver的上限与最大值的比率大于3。检查该部分,可以看到它仅包含两个叶节点。
依赖 | SLOC |
FEA\Solver\Solver.cpp | 240 |
FEA\Solver\Solver.h | 41 |
因此,可以看到巨大的差异是由于比较的文件幅度预计不同所致。
切换到C#项目并使用Roslyn进行解析(部分指的是命名空间)
SLOC | 节 | ||||
Sum | Lower | 预期效果 | Upper | 最大值 | |
1751 | 5 | 25 | 118 | 189 | DeepEnds |
119 | 37 | 58 | 92 | 71 | DeepEnds.Console |
712 | 4 | 23 | 122 | 189 | DeepEnds.Core |
68 | 4 | 26 | 143 | 55 | DeepEnds.Core.Complex |
94 | 1 | 12 | 116 | 63 | DeepEnds.Core.Dependent |
71 | 6 | 19 | 64 | 44 | DeepEnds.Core.Linked |
112 | 3 | 26 | 181 | 59 | DeepEnds.Cpp |
284 | 5 | 23 | 104 | 132 | DeepEnds.CSharp |
253 | 6 | 28 | 126 | 132 | DeepEnds.CSharp.ParseTree |
61 | 61 | 61 | 61 | 61 | DeepEnds.Decompile |
65 | 65 | 65 | 65 | 65 | DeepEnds.DGML |
106 | 3 | 18 | 103 | 60 | DeepEnds.GUI |
292 | 5 | 23 | 109 | 136 | DeepEnds.VBasic |
261 | 6 | 29 | 133 | 136 | DeepEnds.VBasic.ParseTree |
可以看到,在较低级别,上限/最大值的比率要好得多,但仍然不尽人意。
结论
已证明对数正态概率分布适合对项目中文件/类之间代码行数的分布进行建模。置信区间的有用性仅在层级的较高层级适用。
历史
- 2016/10/04: 首次发布