2013-08-15 42 views
5

Tôi có một chương trình mà tôi cần biên dịch hàng nghìn các regex lớn, tất cả đều được sử dụng nhiều lần. Vấn đề là, phải mất quá nhiều thời gian (theo cProfiler, 113 giây) đến re.compile() chúng. (BTW, thực sự tìm kiếm bằng cách sử dụng tất cả các regex này < 1,3 giây sau khi biên soạn.)Python: Biên dịch các regex song song

Nếu tôi không biên dịch trước, nó chỉ trì hoãn vấn đề khi tôi thực sự tìm kiếm, từ re.search(expr, text) biên dịch hoàn toàn expr. Trên thực tế, nó tồi tệ hơn, bởi vì re sẽ biên dịch lại toàn bộ danh sách các regex mỗi khi tôi sử dụng chúng.

Tôi đã thử sử dụng multiprocessing, nhưng điều đó thực sự làm chậm mọi thứ. Dưới đây là một thử nghiệm nhỏ để chứng minh:

## rgxparallel.py ## 
import re 
import multiprocessing as mp 

def serial_compile(strings): 
    return [re.compile(s) for s in strings] 

def parallel_compile(strings): 
    print("Using {} processors.".format(mp.cpu_count())) 
    pool = mp.Pool() 
    result = pool.map(re.compile, strings) 
    pool.close() 
    return result 

l = map(str, xrange(100000)) 

Và kịch bản thử nghiệm của tôi:

#!/bin/sh 
python -m timeit -n 1 -s "import rgxparallel as r" "r.serial_compile(r.l)" 
python -m timeit -n 1 -s "import rgxparallel as r" "r.parallel_compile(r.l)" 
# Output: 
# 1 loops, best of 3: 6.49 sec per loop 
# Using 4 processors. 
# Using 4 processors. 
# Using 4 processors. 
# 1 loops, best of 3: 9.81 sec per loop 

Tôi đoán rằng phiên bản song song là:

  1. Song song đó, biên soạn và tẩy các regexes , ~ 2 giây
  2. Nối tiếp, không tẩy, và do đó biên dịch lại tất cả, ~ 6,5 giây

Cùng với chi phí để bắt đầu và dừng quá trình, multiprocessing trên 4 bộ vi xử lý lớn hơn 25% chậm hơn hơn nối tiếp.

Tôi cũng đã cố gắng chia danh sách các regex thành 4 danh sách phụ và pool.map -ing các danh sách phụ, thay vì các biểu thức riêng lẻ. Điều này đã cho một hiệu suất nhỏ tăng, nhưng tôi vẫn không thể nhận được tốt hơn so với ~ 25% chậm hơn so với nối tiếp.

Có cách nào để biên dịch nhanh hơn nối tiếp không?

EDIT: Đã sửa thời gian chạy của quá trình biên dịch regex.

Tôi cũng đã thử sử dụng threading nhưng do GIL, chỉ có một bộ xử lý được sử dụng. Nó tốt hơn một chút so với multiprocessing (130 giây so với 136 giây), nhưng vẫn chậm hơn nối tiếp (113 giây).

CHỈNH SỬA 2: Tôi nhận thấy rằng một số regex có thể được nhân đôi, vì vậy tôi đã thêm một dict cho bộ nhớ đệm chúng. Điều này cạo ra ~ 30 giây. Tuy nhiên, tôi vẫn quan tâm đến việc song song. Máy mục tiêu có 8 bộ vi xử lý, giúp giảm thời gian biên dịch xuống ~ 15 giây.

+0

Làm thế nào bạn có quá nhiều regex lớn và chỉ tìm kiếm rất ít với chúng? thay thế chúng bằng thao tác chuỗi đồng bằng cũ, hoặc tránh chạy một số trong số chúng? – delnan

+0

Thời gian tìm kiếm chỉ dành cho một lần sử dụng toàn bộ danh sách. Điều rất quan trọng là thời gian cho một tìm kiếm danh sách duy nhất là nhỏ, bởi vì người dùng (và chủ nhân của tôi) sẽ mong đợi phản ứng gần như ngay lập tức. Tôi đã cố gắng đơn giản hóa càng nhiều càng tốt, và đây là điều tốt nhất tôi có thể nhận được mà không cần cắt bỏ các tính năng chính. (Danh sách cụm từ tìm kiếm thực tế là ~ 200.000 mục; Tôi có mã chuyển sang các hàm chuỗi đơn giản bất cứ khi nào có thể, nhưng vẫn còn ~ 5.000 regex.) –

+0

Bạn đã thử sử dụng các chuỗi thay thế chưa? 1 chủ đề cho mỗi cpu và regex của divvied lên trong số đó? regex được thực hiện trong C vì vậy bạn sẽ nhận được một mức độ khá song song mặc dù GIL. – tdelaney

Trả lời

0

Như nhiều như tôi yêu python, tôi nghĩ rằng giải pháp là, làm điều đó trong perl (see this speed comparison, for example), hoặc C, vv

Nếu bạn muốn giữ lại các chương trình chính trong python, bạn có thể sử dụng để gọi một subprocess kịch bản perl (chỉ cần đảm bảo truyền nhiều giá trị nhất có thể được trong một số ít nhất là các cuộc gọi càng tốt để tránh phải trả phí.)