You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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倍左右

优化优先级总结

  1. 先做算法剪枝,减少无效计算,这是所有优化的基础,收益最高
  2. 再做并行计算,利用多核CPU资源,尽量用语言自带的并行工具,避免引入复杂的并发框架
  3. 最后考虑切换静态编译型语言,进一步提升计算性能

内容的提问来源于stack exchange,提问作者Probastian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 21:54:02