2012-01-20 34 views
5

Tôi đang gặp phải những gì có vẻ như sự khác biệt đáng kể về hiệu năng khi lặp qua một vùng chứa nhỏ với một trình vòng lặp tùy chỉnh. Tôi đã hy vọng một ai đó có thể giúp tôi hiểu được sự khác biệt này đến từ đâu.hiệu suất lặp tùy chỉnh

Đầu tiên một số ngữ cảnh; Tôi đang viết một số mô-đun mở rộng python bằng cách sử dụng boost :: python, một trong số đó có chứa một ràng buộc với một loại vector nổi 3d thực hiện getitem. Vì nó có khả năng lặp lại nó, tuy nhiên nó có vẻ khá chậm, nhưng nó không rõ ràng vì sao tôi quyết định chơi xung quanh với một số trình vòng lặp tùy chỉnh đơn giản trong python để có ý tưởng tốt hơn về cách mọi thứ hoạt động. Đó là nơi những vòng lặp đến từ ...

class MyIterator1(object): 
    __slots__ = ['values', 'popfn'] 

    def __init__(self): 
     self.values = ['x', 'y', 'z'] 
     self.popfn = self.values.pop 

    def __length_hint__(self): 
     return 3 

    def __iter__(self): 
     return self 

    def next(self): 
     try: 
      return self.popfn() 
     except IndexError: 
      raise StopIteration 

class MyIterator2(object): 
    __slots__ = ['values', 'itfn'] 

    def __init__(self): 
     self.values = ['x', 'y', 'z'] 
     it = iter(self.values) 
     self.itfn = it.next 

    def __length_hint__(self): 
     return 3 

    def __iter__(self): 
     return self 

    def next(self): 
     return self.itfn() 

class MyIterator3(object): 
    __slots__ = ['values', 'i'] 

    def __init__(self): 
     self.values = ['x', 'y', 'z'] 
     self.i = 0 

    def __length_hint__(self): 
     return 3 

    def __iter__(self): 
     return self 

    def next(self): 
     if self.i >= 3: 
      raise StopIteration 
     value = self.values[self.i] 
     self.i += 1 
     return value 

def MyIterator4(): 
    val = ['x', 'y', 'z'] 
    yield val[0] 
    yield val[1] 
    yield val[2] 

Tiếp theo, tôi chạy này thông qua một kịch bản với các module timeit (mà giả mã trên là trong một module gọi là testiter)

import timeit 

timer1 = timeit.Timer('r = list(testiter.MyIterator1())', 'import testiter') 
timer2 = timeit.Timer('r = list(testiter.MyIterator2())', 'import testiter') 
timer3 = timeit.Timer('r = list(testiter.MyIterator3())', 'import testiter') 
timer4 = timeit.Timer('r = list(testiter.MyIterator4())', 'import testiter') 
timer5 = timeit.Timer('r = list(iter(["x", "y", "z"]))', 'import testiter') 

print 'list(testiter.MyIterator1())' 
print timer1.timeit() 

print "\n" 

print 'list(testiter.MyIterator2())' 
print timer2.timeit() 

print "\n" 

print 'list(testiter.MyIterator3())' 
print timer3.timeit() 

print "\n" 

print 'list(testiter.MyIterator4())' 
print timer4.timeit() 

print "\n" 

print 'list(iter(["x", "y", "z"]))' 
print timer5.timeit() 

in này ra sau đây

list(testiter.MyIterator1()) 
8.5735929


list(testiter.MyIterator2()) 
5.28959393501 


list(testiter.MyIterator3()) 
6.11230111122 


list(testiter.MyIterator4()) 
2.31263613701 


list(iter(["x", "y", "z"])) 
1.26243281364 

Không có gì đáng ngạc nhiên khi trình xếp hạng python là nhanh nhất, bằng một lề. Tôi cho rằng đây là một số tối ưu hóa ma thuật trong python. Máy phát cũng nhanh hơn đáng kể so với các lớp MyIterator, một lần nữa tôi không ngạc nhiên lắm, và giả định là do tất cả các công việc đang được thực hiện trong c, tuy nhiên đó chỉ là một phỏng đoán. Bây giờ những người khác là khó hiểu/supprising. Có phải thử/ngoại trừ các câu lệnh đắt tiền như chúng có vẻ trong ngữ cảnh này hay là cái gì khác đang diễn ra?

Bất kỳ trợ giúp nào trong việc giải thích những khác biệt này sẽ được đánh giá cao! Xin lỗi vì bài viết dài.

+2

Bạn có thể thử dùng tuple thay vì ['x', 'y', 'z'] (nếu có thể): xây dựng tuple nhanh hơn một chút so với danh sách. –

Trả lời

2

Một vài ý tưởng nằm ngoài đầu tôi; xin lỗi nếu họ không đủ mãnh liệt:

  • Các iterator đầu tiên được sử dụng của pop danh sách để thực hiện next, có nghĩa là danh sách được biến đổi sau mỗi phần tử được lấy ra. Có lẽ đột biến này của cấu trúc dữ liệu tổng hợp được phân bổ động được giảm hiệu suất. Tất cả phụ thuộc vào chi tiết triển khai danh sách, vì vậy điều này có thể hoàn toàn không liên quan.
  • Trình lặp cuối cùng đang sử dụng tính năng được kết nối với ngôn ngữ (năng suất) trong ngữ cảnh đơn giản để tạo kết quả. Tại một dự đoán, tôi sẽ nói điều này cho thấy có nhiều chỗ cho tối ưu hóa hơn một lớp lặp tùy chỉnh cố gắng để đạt được kết quả tương tự.
  • Bộ tính giờ thứ 5 không sử dụng trình vòng lặp tùy chỉnh và thay vào đó sử dụng bộ tính giờ được cung cấp trực tiếp cho danh sách. Các bộ lặp cho các danh sách có lẽ được tối ưu hóa tốt, và không có một lớp của sự hướng dẫn mà các lớp tùy chỉnh đó sử dụng, một số trong đó đang sử dụng một trình lặp danh sách nội bộ như vậy.
+1

Tôi muốn thêm rằng việc tăng và bắt một ngoại lệ tương đối đắt: http://paltman.com/2008/01/18/try-except-performance-in-python-a-simple-test/. – Noah

+0

Ah, tất nhiên rồi. Hài hước như thế nào những điều thực sự rõ ràng tôi bỏ lỡ =) Tôi không bao giờ thực sự hiểu tại sao Python và Ruby nghĩ rằng ngoại lệ nên được sử dụng cho những thứ như thế ... – Louis

Các vấn đề liên quan