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

多进程处理因418k化学品字典挂起,寻求性能瓶颈排查方案

多词化学品名称连字符替换代码的性能瓶颈排查

我写了一段代码,用来把特定多词化学品名称里的空格替换成连字符(比如把magnesium oxide替换成magnesium-oxide)。但不管化学品列表长短,用multiprocessing处理都慢得离谱——列表短的时候要跑将近一天,现在列表长度到418k,跑了一天后直接挂起了。想找出导致性能差和挂起的瓶颈。

以下是相关代码和数据示例:

import re
import time
import numpy
import pandas
import pickle
from datetime import datetime
from multiprocessing import Pool 

# 定义预处理类提升效率
class Hyphenizer:
    
   def __init__(self, chem_pattern, chem_name_hyp_dict): 
       
       # chem_pattern是带单词边界的化学品名称正则联合模式(例如 r'\bmagnesium oxide\b')
       self.chem_pattern = chem_pattern
       
       # chem_name_hyp_dict是多词化学品名称到连字符版本的映射字典,例:{'magnesium oxide': 'magnesium-oxide'}
       self.chem_name_hyp_dict = chem_name_hyp_dict
       
   def hyphenize_chemicals(self, series): 

       hyphenized_text = []

       # 将Series转为列表
       series_as_list = series.tolist()
       
       for text in series_as_list:
           
           # 检查文本中是否存在多词化学品
           if bool(re.search(self.chem_pattern, text)):
               # 将匹配到的多词化学品替换为连字符版本
               text = self.chem_pattern.sub(
                   lambda match: self.chem_name_hyp_dict[match.group(0)], text)
               
           hyphenized_text.append(text)    
        
       return hyphenized_text

if __name__ == '__main__': 
    
    # 读取清洗后的论文数据,忽略不必要的列
    articles = pandas.read_csv("../data/abstracts_clean.csv", 
                               error_bad_lines = False, warn_bad_lines = True, 
                               usecols = ['doi', 'abstract', 'doc_id', 
                                          'clean_abstract'])

    # 将数据拆分为块,用于并行处理(每块100条文本)
    chunks = numpy.array_split(articles['clean_abstract'], 71)
    
    # 加载带单词边界的多词化学品列表
    with open('../data/chem_w_bound_w_filt.pkl', 'rb') as f: 
        chem_name_list_w_boundaries = pickle.load(f)

    # 编译正则模式
    chem_pattern = re.compile("|".join(chem_name_list_w_boundaries))

    # 加载多词化学品到连字符版本的映射字典
    with open('../data/chem_name_hyp_dict_filt.pkl', 'rb') as f: 
        chem_name_hyp_dict = pickle.load(f)

    # 存储所有处理后的块
    hyphenized_abs_lil = []
    
    # 创建Hyphenizer实例
    hyphenizer = Hyphenizer(chem_pattern, chem_name_hyp_dict)
    
    # 遍历块并提交到进程池
    for i, clean_abs_chunk in enumerate(chunks):
        
        # 将块进一步拆分为更小的子块(每个进程处理25条文本)
        smaller_chunks = numpy.array_split(clean_abs_chunk, 4)
              
        # 记录当前时间
        now = datetime.now()
        now_formatted = now.strftime('%H:%M:%S')
        print('当前时间:{}'.format(now_formatted))
            
        with Pool(4) as pool:     
             
            hyphenized_abs = pool.map(
                hyphenizer.hyphenize_chemicals, smaller_chunks)
            
        hyphenized_abs_lil.append(hyphenized_abs)

补充说明

数据示例

abstracts_clean.csv包含论文摘要及相关信息,处理目标列clean_abstract示例如下:

articles.clean_abstract.head()
0    sausages are one of the oldest processed foods...
1    populations of pollinating insects are in conc...
2    it was highlighted that the original article c...
3    foodborne pathogens are a major cause of diarr...
4    a sizable proportion of the world population i...

化学品映射字典示例

chem_name_hyp_dict是多词化学品名称到连字符版本的映射字典,示例如下:

list(chem_name_hyp_dict.items())[: 5]
[("9beta-d-ribofuranosylpurine cyclic 3',5'-phosphate",
  "9beta-d-ribofuranosylpurine-cyclic-3',5'-phosphate"),
 ('2-methoxy-17betaestradiol 3-o-(beta-d-glucuronide)',
  '2-methoxy-17betaestradiol-3-o-(beta-d-glucuronide)'),
 ('n(3)-(4-methoxyfumaroyl)-2,3-diaminopropionic acid',
  'n(3)-(4-methoxyfumaroyl)-2,3-diaminopropionic-acid'),
 ('calcium disodium ethylenediamine tetraacetic acids',
  'calcium-disodium-ethylenediamine-tetraacetic-acids'),
 ('trans-1,2-ethylenedicarboxylic acid dimethyl ester',
  'trans-1,2-ethylenedicarboxylic-acid-dimethyl-ester')]

瓶颈排查与优化方案

1. 正则表达式的性能灾难

用|拼接418k个带\b边界的正则表达式是核心问题:

  • Python正则引擎处理超长分支表达式时,会陷入指数级回溯,尤其是化学品名称有重叠前缀/后缀时,匹配速度暴跌。
  • \b边界在处理含特殊字符(如'、(、-)的化学品名称时,会出现匹配异常,同时进一步拖慢匹配速度。

2. 多进程的低效使用

  • 循环中反复创建Pool(4),每次创建销毁进程池都会带来巨大开销——进程启动、大对象(正则、字典)的内存拷贝成本极高,并行效率不升反降。
  • 拆分的chunk过小(25条文本),进程间任务调度成本远高于实际处理成本。

3. 冗余的匹配检查

先调用re.search再执行sub,等于对同一段文本做两次正则扫描,完全是重复劳动。

优化步骤

第一步:替换正则方案——用Aho-Corasick自动机替代分支正则

面对十万级关键词匹配,Aho-Corasick算法是线性时间复杂度,比正则高效几个数量级,用pyahocorasick库实现:

import ahocorasick
import pandas

# 定义预处理类提升效率
class Hyphenizer:
    def __init__(self, chem_name_hyp_dict):
        self.automaton = ahocorasick.Automaton()
        # 按名称长度倒序添加,优先匹配长名称(避免短名称截断长名称)
        for name, hyphenated in sorted(chem_name_hyp_dict.items(), key=lambda x: -len(x[0])):
            self.automaton.add_word(name, hyphenated)
        self.automaton.make_automaton()

    def hyphenize_text(self, text):
        # 从后往前替换,避免替换后影响前面的匹配位置
        offset = 0
        for end_idx, hyphenated in self.automaton.iter(text):
            original_len = len(hyphenated.replace('-', ''))
            start_idx = end_idx - original_len + 1
            text = text[:start_idx+offset] + hyphenated + text[end_idx+1+offset:]
            offset += len(hyphenated) - original_len
        return text

    def hyphenize_chemicals(self, series):
        return series.apply(self.hyphenize_text).tolist()

安装依赖:pip install pyahocorasick

第二步:重构多进程逻辑

  • 只创建一次进程池,避免反复销毁重建的开销。
  • 合理设置chunk大小,每个进程处理几百到几千条文本,平衡调度成本和并行效率。
if __name__ == '__main__':
    # 只加载需要处理的列
    articles = pandas.read_csv("../data/abstracts_clean.csv", 
                               error_bad_lines=False, warn_bad_lines=True, 
                               usecols=['clean_abstract'])
    
    with open('../data/chem_name_hyp_dict_filt.pkl', 'rb') as f: 
        chem_name_hyp_dict = pickle.load(f)

    hyphenizer = Hyphenizer(chem_name_hyp_dict)
    
    # 按CPU核心数设置进程数,每个chunk分配约1000条文本
    num_processes = 4
    chunks = numpy.array_split(articles['clean_abstract'], num_processes * 10)
    
    hyphenized_abs_lil = []
    with Pool(num_processes) as pool:
        # 一次性提交所有chunk
        results = pool.map(hyphenizer.hyphenize_chemicals, chunks)
        hyphenized_abs_lil.extend(results)
    
    # 合并所有结果
    final_hyphenized = [item for sublist in hyphenized_abs_lil for item in sublist]

第三步:其他细节优化

  • 若CSV数据量极大,用chunksize参数分块读取,避免一次性加载占满内存。
  • 移除代码中不必要的变量和打印操作,减少额外开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 21:13:07