Groovy多线程实现CSV字符串莱文斯坦距离计算的性能优化咨询
性能优化方案
一、优先做算法层面优化(收益远高于直接做多线程)
你的原有逻辑时间复杂度为O(n²),1.3万条记录对应约1.7亿次比对运算,大部分是无效计算,先做剪枝就能降低80%以上的计算量:
- 利用莱文斯坦距离特性提前过滤:如果两个字符串长度差超过10,距离必然大于10,直接跳过比对,无需计算
- 使用带阈值的计算接口:Apache Commons的
StringUtils.getLevenshteinDistance支持传入最大阈值,超过阈值会直接返回-1,无需计算完整个编辑距离矩阵
二、Groovy多线程改造方案(无需复杂GPars,用JDK自带并行流即可)
你的逻辑中每个新记录比对时,仅读取已生成的outputList数据,无写入操作,不存在线程安全问题,直接把内层比对逻辑改成并行即可,代码改动量极小:
import java.util.Objects // 预计算所有字符串的长度,提前过滤无效比对 def strLengths = records.collect { it[2].toString().length() } long startTime = System.currentTimeMillis() def outputList = [] // 并行线程数默认用CPU核心数即可 int parallelThreshold = 100 // 数据量小时用串行避免并行调度开销 for (int i = 0; i < records.size(); i++) { long currTime = System.currentTimeMillis() String s1 = records[i][2].toString() int len1 = strLengths[i] int matchIndex = -1 int matchDistance = -1 if (i % 50 == 0) { double progress = i / (records.size() - 1) * 100 println("Status: ${String.format("%.1f", progress)} % done. [${i}/${records.size() - 1}] (Elapsed: ${String.format("%.1f", (System.currentTimeMillis() - startTime)/1000)}s) (outputList.size(): ${outputList.size()})") } if (outputList.size() > 0) { if (outputList.size() < parallelThreshold) { // 小数据量串行计算 for (int j = 0; j < outputList.size(); j++) { if (Math.abs(len1 - strLengths[j]) > 10) continue int dist = StringUtils.getLevenshteinDistance(s1, outputList[j][2].toString(), 10) if (dist > matchDistance) { matchIndex = j matchDistance = dist } } } else { // 大数据量并行计算 def bestMatch = (0..<outputList.size()).parallelStream() .filter { j -> Math.abs(len1 - strLengths[j]) <= 10 } .map { j -> int dist = StringUtils.getLevenshteinDistance(s1, outputList[j][2].toString(), 10) dist > matchDistance ? [j, dist] : null } .filter(Objects::nonNull) .max { a, b -> a[1] - b[1] } .orElse(null) if (bestMatch) { matchIndex = bestMatch[0] matchDistance = bestMatch[1] } } } outputList.add(records[i] + [matchIndex, matchDistance]) }
改造原理说明
- 并行流会自动把内层比对任务拆分到多个CPU核心执行,无需手动管理线程池,代码复杂度极低
- 整个并行过程仅读取
outputList和预计算的长度数组,无共享变量修改,不需要加锁,不会出现数据错乱 - 增加小数据量串行判断,避免并行任务调度开销超过计算收益的情况
三、更高性能的技术选型推荐
如果上述优化后性能仍不满足需求,可以换更适合计算密集型场景的语言:
- 首选Rust:使用
strsim库的莱文斯坦距离实现,性能是Groovy的3~10倍,同样支持并行计算,1.3万条记录全量处理仅需数秒 - 次选Go:原生协程调度开销极低,字符串处理性能比Groovy高2~5倍,代码实现简单易维护
- 不想换语言的话可以把核心计算逻辑用Java实现编译后给Groovy调用,性能也比纯Groovy高2倍左右
优化优先级总结
- 先做算法剪枝,减少无效计算,这是所有优化的基础,收益最高
- 再做并行计算,利用多核CPU资源,尽量用语言自带的并行工具,避免引入复杂的并发框架
- 最后考虑切换静态编译型语言,进一步提升计算性能
内容的提问来源于stack exchange,提问作者Probastian
相关产品推荐
相关产品推荐

