2013-05-21 55 views
25

Tôi có một số tệp văn bản rất lớn mà tôi cần xử lý, lớn nhất là khoảng 60 GB.Xử lý dòng tệp văn bản rất lớn (> 20GB) theo dòng

Mỗi dòng có 54 ký tự trong bảy trường và tôi muốn xóa ba ký tự cuối cùng khỏi mỗi trường trong ba trường đầu tiên - điều này sẽ giảm kích thước tệp xuống khoảng 20%.

Tôi là người mới sử dụng Python và có mã sẽ làm những gì tôi muốn làm vào khoảng 3,4 GB mỗi giờ, nhưng để trở thành một bài tập đáng giá, tôi thực sự cần phải nhận được ít nhất 10 GB/giờ - bất kỳ cách nào để tăng tốc độ này lên? Mã này không đến gần với thách thức bộ vi xử lý của tôi, vì vậy tôi đang thực hiện một dự đoán không được giáo dục rằng nó bị giới hạn bởi tốc độ đọc và ghi vào ổ cứng bên trong?

ProcessLargeTextFile(): 
    r = open("filepath", "r") 
    w = open("filepath", "w") 
    l = r.readline() 
    while l: 
     x = l.split(' ')[0] 
     y = l.split(' ')[1] 
     z = l.split(' ')[2] 
     w.write(l.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3])) 
     l = r.readline() 
r.close() 
w.close() 

Mọi trợ giúp sẽ thực sự được đánh giá cao. Tôi đang sử dụng IDLE Python GUI trên Windows 7 và có bộ nhớ 16GB - có lẽ một hệ điều hành khác sẽ hiệu quả hơn ?.

Chỉnh sửa: Đây là trích xuất tệp cần xử lý.

70700.642014 31207.277115 -0.054123 -1585 255 255 255 
70512.301468 31227.990799 -0.255600 -1655 155 158 158 
70515.727097 31223.828659 -0.066727 -1734 191 187 180 
70566.756699 31217.065598 -0.205673 -1727 254 255 255 
70566.695938 31218.030807 -0.047928 -1689 249 251 249 
70536.117874 31227.837662 -0.033096 -1548 251 252 252 
70536.773270 31212.970322 -0.115891 -1434 155 158 163 
70533.530777 31215.270828 -0.154770 -1550 148 152 156 
70533.555923 31215.341599 -0.138809 -1480 150 154 158 
+0

Nếu bạn đang viết bằng Python 2.7, bạn có thể thử chạy trên [PyPy] (http://pypy.org/). Trình biên dịch vừa mới có thể cung cấp cho bạn tốc độ hiệu suất trên lĩnh vực xáo trộn của bạn, mặc dù tôi không chắc nó sẽ giúp ích bao nhiêu nếu hệ thống tập tin là nút cổ chai. – pcurry

+0

bạn có thể cho chúng tôi một đoạn nhỏ của tệp không? – root

Trả lời

0

Bạn có thể cố gắng lưu kết quả phân tách trước tiên bạn thực hiện và không thực hiện nó mỗi lần bạn cần một trường. Có thể điều này sẽ tăng tốc.

bạn cũng có thể thử không chạy nó trong gui. Chạy nó trong cmd.

1

Đọc tệp bằng cách sử dụng for l in r: để hưởng lợi từ việc lưu vào bộ đệm.

20

Đó là thành ngữ hơn để viết mã của bạn như thế này

def ProcessLargeTextFile(): 
    with open("filepath", "r") as r, open("outfilepath", "w") as w: 
     for line in r: 
      x, y, z = line.split(' ')[:3] 
      w.write(line.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3])) 

Việc tiết kiệm chính ở đây là chỉ cần làm split một lần, nhưng nếu CPU không được đánh thuế, điều này có thể sẽ làm cho rất ít sự khác biệt

có thể giúp tiết kiệm một vài nghìn dòng tại một thời điểm và viết chúng trong một lần truy cập để giảm sự đập của đĩa cứng của bạn. Một triệu dòng là chỉ 54MB RAM!

def ProcessLargeTextFile(): 
    bunchsize = 1000000  # Experiment with different sizes 
    bunch = [] 
    with open("filepath", "r") as r, open("outfilepath", "w") as w: 
     for line in r: 
      x, y, z = line.split(' ')[:3] 
      bunch.append(line.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3])) 
      if len(bunch) == bunchsize: 
       w.writelines(bunch) 
       bunch = [] 
     w.writelines(bunch) 

đề xuất bởi @Janne, một cách khác để tạo ra các dòng

def ProcessLargeTextFile(): 
    bunchsize = 1000000  # Experiment with different sizes 
    bunch = [] 
    with open("filepath", "r") as r, open("outfilepath", "w") as w: 
     for line in r: 
      x, y, z, rest = line.split(' ', 3) 
      bunch.append(' '.join((x[:-3], y[:-3], z[:-3], rest))) 
      if len(bunch) == bunchsize: 
       w.writelines(bunch) 
       bunch = [] 
     w.writelines(bunch) 
+0

nếu các dòng có kích thước không đổi, bạn có thể thử đọc/ghi tệp theo khối lớn hơn ... – root

+0

@root Không phải công cụ 'for' làm đệm trong (và) trường hợp khác? – glglgl

+0

@glglgl - tốt, nó có thể làm cho nó có thể làm các hoạt động thay thế trên hàng ngàn dòng tại thời điểm đó ... (không chắc chắn cách nào sẽ là nhanh nhất - có thể là một regex?) – root

3

Những vẻ như tập tin rất lớn ... Tại sao chúng lại lớn? Bạn đang xử lý gì trên mỗi dòng? Tại sao không sử dụng cơ sở dữ liệu với một số bản đồ giảm các cuộc gọi (nếu thích hợp) hoặc các thao tác đơn giản của dữ liệu? Điểm của cơ sở dữ liệu là trừu tượng hóa việc xử lý và quản lý một lượng lớn dữ liệu mà không thể nào phù hợp trong bộ nhớ.

Bạn có thể bắt đầu chơi với ý tưởng với sqlite3 chỉ sử dụng tệp phẳng làm cơ sở dữ liệu. Nếu bạn thấy ý tưởng hữu ích sau đó nâng cấp lên một thứ gì đó mạnh mẽ hơn và linh hoạt hơn như postgresql.

Tạo một cơ sở dữ liệu

conn = sqlite3.connect('pts.db') 
c = conn.cursor() 

Tạo một bảng

c.execute('''CREATE TABLE ptsdata (filename, line, x, y, z''') 

Sau đó sử dụng một trong những thuật toán trên để chèn tất cả các dòng và các điểm trong cơ sở dữ liệu bằng cách gọi

c.execute("INSERT INTO ptsdata VALUES (filename, lineNumber, x, y, z)") 

Bây giờ làm thế nào bạn sử dụng nó phụ thuộc vào những gì bạn muốn làm. Ví dụ để làm việc với tất cả các điểm trong một tập tin bằng cách thực hiện một truy vấn

c.execute("SELECT lineNumber, x, y, z FROM ptsdata WHERE filename=file.txt ORDER BY lineNumber ASC") 

Và nhận n dòng cùng một lúc từ truy vấn này với

c.fetchmany(size=n) 

tôi chắc chắn rằng có một wrapper tốt hơn cho các câu lệnh sql ở đâu đó, nhưng bạn có ý tưởng.

+0

Cảm ơn Chris, các tệp này là tệp .PTS cho thông tin đám mây điểm. Mỗi hàng đại diện cho một điểm khác nhau trong không gian trong tọa độ Descartes và đây là định dạng chúng tôi lấy dữ liệu từ nhà cung cấp và phần mềm của chúng tôi yêu cầu. –

+0

Trong không gian 3D? Thứ tự của dữ liệu có quan trọng không? Và phần mềm của bạn sử dụng dữ liệu như thế nào? – craastad

+2

@ChrisRaastad: Tom_b có yêu cầu giúp tái cấu trúc hệ thống đang được sử dụng hoặc cải thiện mã đã được cung cấp không? –

3

Mã của bạn khá không thành ngữ và thực hiện nhiều cuộc gọi chức năng hơn mức cần thiết. Một phiên bản đơn giản hơn là:

ProcessLargeTextFile(): 
    with open("filepath") as r, open("output") as w: 
     for line in r: 
      fields = line.split(' ') 
      fields[0:2] = [fields[0][:-3], 
          fields[1][:-3], 
          fields[2][:-3]] 
      w.write(' '.join(fields)) 

và tôi không biết một hệ thống tập tin hiện đại đó là chậm hơn so với Windows. Vì có vẻ như bạn đang sử dụng những tệp dữ liệu khổng lồ này làm cơ sở dữ liệu, bạn đã xem xét sử dụng một cơ sở dữ liệu thực chưa?

Cuối cùng, nếu bạn chỉ quan tâm đến việc giảm kích thước tệp, bạn có xem xét việc nén/nén các tệp không?

3
ProcessLargeTextFile(): 
    r = open("filepath", "r") 
    w = open("filepath", "w") 
    l = r.readline() 
    while l: 

Như đã đề xuất, bạn có thể muốn sử dụng vòng lặp for để làm cho điều này tối ưu hơn.

x = l.split(' ')[0] 
    y = l.split(' ')[1] 
    z = l.split(' ')[2] 

Bạn đang thực hiện thao tác chia 3 lần tại đây, tùy thuộc vào kích thước của mỗi dòng, điều này sẽ có ảnh hưởng tiêu cực đến hiệu suất. Bạn nên chia một lần và gán x, y, z cho các mục trong mảng quay lại.

w.write(l.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3])) 

Mỗi dòng bạn đang đọc, bạn đang viết ngay lập tức vào tệp, rất chuyên sâu về I/O. Bạn nên xem xét lưu lượng đệm của bạn vào bộ nhớ và đẩy đĩa định kỳ. Một cái gì đó như thế này:

BUFFER_SIZE_LINES = 1024 # Maximum number of lines to buffer in memory 

def ProcessLargeTextFile(): 
    r = open("filepath", "r") 
    w = open("filepath", "w") 
    buf = "" 
    bufLines = 0 
    for lineIn in r: 

     x, y, z = lineIn.split(' ')[:3] 
     lineOut = lineIn.replace(x,x[:-3]).replace(y,y[:-3]).replace(z,z[:-3]) 
     bufLines+=1 

     if bufLines >= BUFFER_SIZE: 
      # Flush buffer to disk 
      w.write(buf) 
      buf = "" 
      bufLines=1 

     buf += lineOut + "\n" 

    # Flush remaining buffer to disk 
    w.write(buf) 
    buf.close() 
    r.close() 
    w.close() 

Bạn có thể tinh chỉnh BUFFER_SIZE để xác định số dư tối ưu giữa mức sử dụng bộ nhớ và tốc độ.

12

Đo lường! Bạn có một số gợi ý hữu ích làm thế nào để cải thiện mã python của bạn và tôi đồng ý với họ. Nhưng trước tiên bạn nên tìm ra, vấn đề thực sự của bạn là gì. Các bước đầu tiên của tôi để tìm nút cổ chai của bạn là:

  • Xóa mọi quá trình xử lý khỏi mã của bạn. Chỉ cần đọc và ghi dữ liệu và đo tốc độ. Nếu chỉ đọc và ghi các tập tin là quá chậm, nó không phải là một vấn đề của mã của bạn.
  • Nếu chỉ đọc và viết đã chậm, hãy thử sử dụng nhiều đĩa. Bạn đang đọc và viết cùng một lúc. Trên cùng một đĩa? Nếu có, hãy thử sử dụng các đĩa khác nhau và thử lại.
  • Một số thư viện async io (Xoắn?) Cũng có thể hữu ích.

Nếu bạn đã tìm ra vấn đề chính xác, hãy hỏi lại để tối ưu hóa vấn đề đó.

5

Vì bạn dường như không bị giới hạn bởi CPU, mà là bởi I/O, bạn đã thử với một số biến thể trên tham số thứ ba của open chưa?

Thật vậy, tham số thứ ba này có thể được sử dụng để cung cấp kích thước bộ đệm được sử dụng cho các thao tác tệp!

Chỉ cần viết open("filepath", "r", 16777216) sẽ sử dụng bộ đệm 16 MB khi đọc từ tệp. Nó phải giúp đỡ.

Sử dụng tương tự cho tệp đầu ra và đo/so sánh với tệp giống hệt nhau cho phần còn lại.

Lưu ý: Đây là cùng một loại tối ưu hóa được đề xuất bởi người khác, nhưng bạn có thể lấy nó ở đây miễn phí, mà không thay đổi mã của bạn, mà không cần phải tự đệm.

2

Vì bạn chỉ đề cập đến tiết kiệm không gian làm lợi ích, có một số lý do bạn không thể chỉ lưu trữ các tệp được nén không? Điều đó sẽ tiết kiệm 70% và lên trên dữ liệu này. Hoặc xem xét việc nhận NTFS để nén các tệp nếu truy cập ngẫu nhiên vẫn quan trọng. Bạn sẽ nhận được tiết kiệm nhiều hơn đáng kể vào thời gian I/O sau một trong hai.

Quan trọng hơn, dữ liệu của bạn ở đâu bạn chỉ nhận được 3,4 GB/giờ? Đó là xuống xung quanh tốc độ USBv1.

5

Tôi sẽ thêm câu trả lời này để giải thích lý do tại sao đệm có ý nghĩa và cũng cung cấp thêm một giải pháp

Bạn đang nhận được hiệu suất tuyệt đẹp, hấp xấu. Bài viết này Is it possible to speed-up python IO? cho thấy rằng 10 gb đọc nên thực hiện trong vùng lân cận 3 phút. Viết tuần tự là cùng tốc độ. Vì vậy, bạn đang thiếu một yếu tố là 30 và mục tiêu hiệu suất của bạn vẫn còn chậm hơn 10 lần so với những gì nên có thể.

Gần như chắc chắn loại chênh lệch này nằm ở số số lượng tìm kiếm đầu đĩa đang hoạt động. Tìm kiếm đầu mất một phần nghìn giây. Một lần tìm kiếm tương ứng với vài megabyte đọc-ghi tuần tự. Rất tốn kém. Thao tác sao chép trên cùng một đĩa yêu cầu tìm kiếm giữa đầu vào và đầu ra. Như đã nói, một cách để giảm thiểu tìm kiếm là để đệm theo cách mà nhiều megabyte được đọc trước khi ghi vào đĩa và ngược lại. Nếu bạn có thể thuyết phục hệ thống io python để làm điều này, tuyệt vời. Nếu không, bạn có thể đọc và xử lý các dòng vào một mảng chuỗi và sau đó viết sau khi có lẽ 50 mb đầu ra đã sẵn sàng. Kích thước này có nghĩa là tìm kiếm sẽ tạo ra một hit hiệu suất10% đối với việc truyền dữ liệu. Một cách rất đơn giản khác để loại bỏ tìm kiếm giữa các tập tin đầu vào và đầu ra hoàn toàn là sử dụng một máy có hai đĩa vật lý và các kênh io hoàn toàn riêng biệt cho mỗi. Nhập từ một. Đầu ra khác. Nếu bạn đang thực hiện rất nhiều biến đổi tệp lớn, tốt nhất là nên có một máy tính năng này.