为何两个Shell函数处理CSV数据的耗时差异如此巨大?
为什么纯Awk实现比grep+Awk组合的处理速度慢?
我正在完成学校项目,学习Shell与Bash编程,需要基于CSV文件统计每位司机的行程数量。具体逻辑为判断行程步骤是否为第一步(第2列值为1),并通过Awk的关联数组对司机行程数进行累加。
我编写了两个d1Flag函数,原本预期第二个纯Awk的函数速度更快,但实际结果相反:
第一个函数处理耗时1秒:
d1Flag() { grep ";1;" data/data.csv > temp/temp.csv awk -F ';' '{ count[$6]++ } END { for (i in count) print i" " count[i] }' temp/temp.csv > temp/temp2.csv sort -k3,3 -rn temp/temp2.csv | head -n10 }
第二个函数处理耗时6秒:
d1Flag() { awk -v OFS=';' -F';|\r' '$2==1 { journey_count[$6]++ } END {for (driver in journey_count) print driver, journey_count[driver] } ' data/data.csv | sort -t';' -nrk2,2 | head -n10 > temp/temp.csv }
我想知道第二个函数耗时更长的原因,是Awk本身速度慢,还是我的实现存在问题?
核心原因:Awk的字段分隔符设置导致性能损耗
第二个函数慢的问题不在Awk本身,而是你设置的字段分隔符-F';|\r'拖了后腿。这个正则表达式会让Awk在处理每一行时都要执行复杂的字符匹配,而grep是专门做文本快速匹配的工具,在筛选行的效率上远高于Awk用正则分隔符+条件判断的组合。
具体拆解差异:
- 第一个函数中,
grep ";1;"用优化过的字符串匹配算法(比如Boyer-Moore)快速筛选出目标行,之后Awk只需要处理过滤后的小文件,IO和计算量都大幅降低,自然速度快。 - 第二个函数中,Awk要处理完整的原始文件,且每一行都要按
;或\r拆分字段。正则分隔符会迫使Awk对每一行的每个字符做正则匹配,字段拆分的开销急剧上升;再加上要对所有行执行$2==1的判断,双重消耗下整体速度被拖慢。
优化建议
如果想保留单Awk进程的方式,调整字段分隔符就能大幅提升速度:
d1Flag() { awk -F';' '{ sub(/\r$/,""); if ($2 == "1") journey_count[$6]++ } END {for (driver in journey_count) print driver, journey_count[driver] }' data/data.csv | sort -rnk2,2 | head -n10 > temp/temp.csv }
这里做了两个关键调整:
- 把字段分隔符改成简单的
;,如果文件存在\r换行符,用sub(/\r$/,"")单独处理行尾,比用正则分隔符高效得多。 - 把
$2==1改成$2=="1",避免Awk做字符串转数字的隐式转换,减少额外计算开销。
修改后单Awk版本的速度会和grep+Awk版本接近甚至更快,因为少了中间临时文件的IO损耗。
内容的提问来源于stack exchange,提问作者mmh
相关产品推荐
相关产品推荐

