Hoạt động theo nguyên tắc trách nhiệm duy nhất (SRP) trong Python khi các cuộc gọi đắt tiền


12

Một số điểm cơ bản:

  • Các cuộc gọi phương thức Python là "đắt" do bản chất được giải thích của nó . Về lý thuyết, nếu mã của bạn đủ đơn giản, việc phá vỡ mã Python có tác động tiêu cực bên cạnh khả năng đọc và tái sử dụng ( đó là một lợi ích lớn cho các nhà phát triển, không nhiều cho người dùng ).
  • Nguyên tắc trách nhiệm duy nhất (SRP) giữ cho mã có thể đọc được, dễ kiểm tra và bảo trì hơn.
  • Dự án có một loại nền đặc biệt nơi chúng tôi muốn mã có thể đọc, kiểm tra hiệu suất thời gian.

Ví dụ, mã như thế này gọi một số phương thức (x4) chậm hơn so với phương thức sau chỉ là một phương thức.

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

So với điều này:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

Nếu tôi song song một cái gì đó như vậy, đó là mục tiêu khá là tôi mất hiệu suất. Tâm trí chỉ là một ví dụ; dự án của tôi có một số thói quen nhỏ với toán học như vậy - Mặc dù làm việc với nó dễ dàng hơn nhiều, các trình biên dịch của chúng tôi không thích nó.


Làm thế nào và ở đâu chúng ta nắm lấy SRP mà không ảnh hưởng đến hiệu suất trong Python, vì việc triển khai vốn có của nó ảnh hưởng trực tiếp đến nó?

Có cách giải quyết nào không, giống như một số loại tiền xử lý đưa mọi thứ vào hàng để phát hành?

Hay Python chỉ đơn giản là kém trong việc xử lý sự cố mã hoàn toàn?



19
Đối với những gì nó có giá trị, hai ví dụ mã của bạn không khác nhau về số lượng trách nhiệm. SRP không phải là một bài tập đếm phương pháp.
Robert Harvey

2
@RobertHarvey Bạn nói đúng, xin lỗi vì ví dụ nghèo nàn và tôi sẽ chỉnh sửa một cái tốt hơn khi tôi có thời gian. Trong cả hai trường hợp, khả năng đọc và bảo trì đều bị ảnh hưởng và cuối cùng SRP bị phá vỡ trong cơ sở mã khi chúng tôi cắt giảm các lớp và phương thức của chúng.
lucasgcb

4
lưu ý rằng các lệnh gọi hàm đắt ở bất kỳ ngôn ngữ nào , mặc dù trình biên dịch AOT có sự sang trọng của nội tuyến
Eevee

6
Sử dụng triển khai JITted của python như PyPy. Chủ yếu nên khắc phục vấn đề này.
Bakuriu

Câu trả lời:


17

Python đơn giản là kém trong việc xử lý sự cố mã hoàn toàn?

Thật không may, Python chậm và có nhiều giai thoại về những người làm tăng đáng kể hiệu năng bằng cách nội tuyến các hàm và làm cho mã của họ trở nên xấu xí.

Có một công việc xung quanh, Cython, đây là phiên bản được biên dịch của Python và nhanh hơn nhiều.

- Chỉnh sửa Tôi chỉ muốn giải quyết một số ý kiến ​​và câu trả lời khác. Mặc dù lực đẩy của chúng có lẽ không phải là trăn cụ thể. nhưng tối ưu hóa chung hơn.

  1. Đừng tối ưu hóa cho đến khi bạn gặp vấn đề và sau đó tìm kiếm các nút thắt cổ chai

    Nói chung là lời khuyên tốt. Nhưng giả định là mã 'bình thường' thường có hiệu suất. Đây không phải là luôn luôn như vậy. Mỗi ngôn ngữ và khung riêng lẻ đều có những đặc điểm riêng. Trong trường hợp này chức năng gọi.

  2. Chỉ vài mili giây, những thứ khác sẽ chậm hơn

    Nếu bạn đang chạy mã của mình trên một máy tính để bàn mạnh mẽ, có lẽ bạn không quan tâm miễn là mã người dùng duy nhất của bạn thực thi trong vài giây.

    Nhưng mã doanh nghiệp có xu hướng chạy cho nhiều người dùng và yêu cầu nhiều hơn một máy để hỗ trợ tải. Nếu mã của bạn chạy nhanh gấp đôi, điều đó có nghĩa là bạn có thể có gấp đôi số người dùng hoặc một nửa số lượng máy.

    Nếu bạn sở hữu máy móc và trung tâm dữ liệu của mình thì bạn thường có một phần lớn chi phí sử dụng CPU. Nếu mã của bạn chạy chậm một chút, bạn có thể hấp thụ nó, ít nhất là cho đến khi bạn cần mua máy thứ hai.

    Trong những ngày này của điện toán đám mây nơi bạn chỉ sử dụng chính xác sức mạnh tính toán mà bạn yêu cầu và không còn nữa, có một chi phí trực tiếp cho mã không thực hiện.

    Cải thiện hiệu suất có thể cắt giảm đáng kể chi phí chính cho một doanh nghiệp dựa trên đám mây và hiệu suất thực sự nên ở phía trước và trung tâm.


1
Trong khi Câu trả lời của Robert giúp giải quyết một số cơ sở cho những hiểu lầm tiềm ẩn đằng sau việc thực hiện loại tối ưu hóa này (phù hợp với câu hỏi này ), tôi cảm thấy câu trả lời này trực tiếp hơn một chút và phù hợp với bối cảnh Python.
lucasgcb

2
xin lỗi nó hơi ngắn Tôi không có thời gian để viết thêm. Nhưng tôi nghĩ Robert đã sai về điều này. Lời khuyên tốt nhất với python dường như là hồ sơ như bạn mã. Đừng cho rằng nó sẽ hoạt động hiệu quả và chỉ tối ưu hóa nếu bạn gặp sự cố
Ewan

2
@Ewan: Bạn không cần phải viết toàn bộ chương trình trước để làm theo lời khuyên của tôi. Một hoặc hai phương pháp là quá đủ để có được hồ sơ đầy đủ.
Robert Harvey

1
bạn cũng có thể thử pypy, đó là một con trăn JITted
Eevee

2
@Ewan Nếu bạn thực sự lo lắng về chi phí hoạt động của các cuộc gọi chức năng, bất cứ điều gì bạn đang làm có lẽ không phù hợp với python. Nhưng sau đó tôi thực sự không thể nghĩ ra nhiều ví dụ ở đó. Phần lớn mã doanh nghiệp bị giới hạn IO và các công cụ nặng của CPU thường được xử lý bằng cách gọi ra các thư viện riêng (numpy, tenorflow, v.v.).
Voo

50

Nhiều mối quan tâm hiệu suất tiềm năng không thực sự là một vấn đề trong thực tế. Vấn đề bạn nêu ra có thể là một trong số đó. Trong tiếng bản địa, chúng tôi gọi là lo lắng về những vấn đề đó mà không có bằng chứng cho thấy chúng là vấn đề thực sự tối ưu hóa sớm.

Nếu bạn đang viết một giao diện người dùng cho một dịch vụ web, hiệu suất của bạn sẽ không bị ảnh hưởng đáng kể bởi các cuộc gọi chức năng, bởi vì chi phí gửi dữ liệu qua mạng vượt xa thời gian thực hiện cuộc gọi phương thức.

Nếu bạn đang viết một vòng lặp chặt chẽ làm mới màn hình video sáu mươi lần một giây, thì điều đó có thể quan trọng. Nhưng tại thời điểm đó, tôi khẳng định bạn có vấn đề lớn hơn nếu bạn đang cố gắng sử dụng Python để làm điều đó, một công việc mà Python có lẽ không phù hợp lắm.

Như mọi khi, cách bạn tìm ra là để đo lường. Chạy trình lược tả hiệu suất hoặc một số bộ định thời trên mã của bạn. Xem nếu đó là một vấn đề thực tế trong thực tế.


Nguyên tắc trách nhiệm duy nhất không phải là luật hay nhiệm vụ; nó là một hướng dẫn hoặc nguyên tắc Thiết kế phần mềm luôn là về sự đánh đổi; không có sự tuyệt đối Không có gì lạ khi đánh đổi khả năng đọc và / hoặc khả năng duy trì tốc độ, do đó bạn có thể phải hy sinh SRP trên bàn thờ hiệu suất. Nhưng đừng tạo ra sự đánh đổi đó trừ khi bạn biết bạn có vấn đề về hiệu suất.


3
Tôi nghĩ điều này là đúng, cho đến khi chúng tôi phát minh ra điện toán đám mây. Bây giờ một trong hai chức năng có hiệu quả chi phí gấp 4 lần so với chức năng kia
Ewan

2
@Ewan 4 lần có thể không quan trọng cho đến khi bạn đo nó đủ quan trọng. Nếu Foo mất 1 ms và Bar mất 4 ms thì không tốt. Cho đến khi bạn nhận ra rằng việc truyền dữ liệu qua mạng mất 200 ms. Tại thời điểm đó, Bar chậm hơn không quan trọng lắm. (Chỉ cần một thể ví dụ về nơi là lần X chậm hơn không làm cho một sự khác biệt đáng chú ý hoặc cực kỳ sống động không có nghĩa là nhất thiết phải siêu thực tế.)
Becuzz

8
@Ewan Nếu việc giảm hóa đơn giúp bạn tiết kiệm 15 đô la / tháng nhưng sẽ mất một nhà thầu 125 đô la / giờ để sửa chữa và kiểm tra nó, tôi có thể dễ dàng biện minh rằng việc bạn không xứng đáng với thời gian của doanh nghiệp (hoặc ít nhất là không làm đúng bây giờ nếu thời gian để thị trường là rất quan trọng, vv). Luôn có sự đánh đổi. Và những gì có ý nghĩa trong một hoàn cảnh có thể không phải trong một hoàn cảnh khác.
Becuzz

3
hóa đơn AWS của bạn thực sự rất thấp
Ewan

6
@Ewan AWS làm tròn lên trần nhà theo từng đợt (tiêu chuẩn là 100ms). Điều đó có nghĩa là loại tối ưu hóa này chỉ giúp bạn tiết kiệm mọi thứ nếu nó luôn tránh đẩy bạn đến đoạn tiếp theo.
Delioth

2

Đầu tiên, một số làm rõ: Python là một ngôn ngữ. Có một số trình thông dịch khác nhau có thể thực thi mã được viết bằng ngôn ngữ Python. Việc triển khai tham chiếu (CPython) thường là những gì đang được tham chiếu khi ai đó nói về "Python" như thể đó là một triển khai, nhưng điều quan trọng là phải chính xác khi nói về các đặc điểm hiệu suất, vì chúng có thể khác nhau giữa các triển khai.

Làm thế nào và ở đâu chúng ta nắm lấy SRP mà không ảnh hưởng đến hiệu suất trong Python, vì việc triển khai vốn có của nó ảnh hưởng trực tiếp đến nó?

Trường hợp 1.) Nếu bạn có mã Python thuần (<= Ngôn ngữ Python phiên bản 3.5, 3.6 có "hỗ trợ mức beta") chỉ dựa trên các mô-đun Python thuần túy, bạn có thể sử dụng SRP ở mọi nơi và sử dụng PyPy để chạy nó. PyPy ( https://morepypy.blogspot.com/2019/03/pypy-v71-release-now-uses-utf-8.html ) là trình thông dịch Python có Trình biên dịch đúng thời gian (JIT) và có thể xóa chức năng gọi qua đầu miễn là nó có đủ thời gian để "làm nóng" bằng cách truy tìm mã được thực thi (một vài giây IIRC). **

Nếu bạn bị hạn chế sử dụng trình thông dịch CPython, bạn có thể trích xuất các hàm chậm thành các phần mở rộng được viết bằng C, sẽ được biên dịch trước và không phải chịu bất kỳ chi phí nào của trình thông dịch. Bạn vẫn có thể sử dụng SRP ở mọi nơi, nhưng mã của bạn sẽ được phân chia giữa Python và C. Việc này tốt hơn hay xấu hơn cho việc duy trì hơn là từ bỏ có chọn lọc SRP nhưng việc chỉ sử dụng mã Python phụ thuộc vào nhóm của bạn, nhưng nếu bạn có các phần quan trọng về hiệu suất của bạn mã, chắc chắn nó sẽ nhanh hơn cả mã Python thuần được tối ưu hóa nhất được CPython diễn giải. Nhiều thư viện toán học nhanh nhất của Python sử dụng phương pháp này (IIRC numpy và scipy). Đó là một sự khác biệt tốt đẹp trong Trường hợp 2 ...

Trường hợp 2.) Nếu bạn có mã Python sử dụng tiện ích mở rộng C (hoặc dựa vào thư viện sử dụng tiện ích mở rộng C), PyPy có thể hoặc không hữu ích tùy thuộc vào cách chúng được viết. Xem http://doc.pypy.org/en/latest/extending.html để biết chi tiết, nhưng tóm tắt là CFFI có chi phí tối thiểu trong khi CTypes chậm hơn (sử dụng với PyPy thậm chí có thể chậm hơn CPython)

Cython ( https://cython.org/ ) là một tùy chọn khác mà tôi không có nhiều kinh nghiệm. Tôi đề cập đến nó vì mục đích hoàn chỉnh để câu trả lời của tôi có thể "tự đứng vững", nhưng không yêu cầu bất kỳ chuyên môn nào. Từ việc sử dụng hạn chế của tôi, tôi cảm thấy mình phải làm việc chăm chỉ hơn để có được những cải tiến tốc độ tương tự mà tôi có thể nhận được "miễn phí" với PyPy, và nếu tôi cần thứ gì đó tốt hơn PyPy, thì cũng dễ dàng viết phần mở rộng C của riêng tôi ( có lợi ích nếu tôi sử dụng lại mã ở nơi khác hoặc trích xuất một phần của nó vào thư viện, tất cả mã của tôi vẫn có thể chạy dưới bất kỳ Trình thông dịch Python nào và không bắt buộc phải chạy bởi Cython).

Tôi sợ bị "nhốt" vào Cython, trong khi bất kỳ mã nào được viết cho PyPy cũng có thể chạy theo CPython.

** Một số ghi chú thêm về PyPy trong sản xuất

Hãy cẩn thận về việc đưa ra bất kỳ lựa chọn nào có tác dụng thực tế là "nhốt bạn" vào PyPy trong một cơ sở mã lớn. Bởi vì một số thư viện bên thứ ba (rất phổ biến và hữu ích) không hoạt động tốt vì những lý do được đề cập trước đó, nó có thể gây ra những quyết định rất khó khăn sau này nếu bạn nhận ra rằng bạn cần một trong những thư viện đó. Kinh nghiệm của tôi chủ yếu là sử dụng PyPy để tăng tốc một số (nhưng không phải tất cả) các dịch vụ siêu nhỏ nhạy cảm với hiệu suất trong môi trường công ty, nơi nó tăng thêm độ phức tạp không đáng kể cho môi trường sản xuất của chúng tôi (chúng tôi đã triển khai nhiều ngôn ngữ, một số ngôn ngữ với các phiên bản chính khác nhau như 2.7 vs. 3.5 chạy dù sao).

Tôi đã phát hiện ra việc sử dụng cả PyPy và CPython thường xuyên buộc tôi phải viết mã chỉ dựa trên các bảo đảm được thực hiện bởi chính đặc tả ngôn ngữ và không dựa trên các chi tiết triển khai có thể thay đổi bất cứ lúc nào. Bạn có thể thấy suy nghĩ về những chi tiết như vậy là một gánh nặng thêm, nhưng tôi thấy nó có giá trị trong sự phát triển chuyên nghiệp của tôi và tôi nghĩ rằng nó là "lành mạnh" cho toàn bộ hệ sinh thái Python.


Vâng! Tôi đã xem xét tập trung vào các phần mở rộng C cho trường hợp này thay vì từ bỏ nguyên tắc và viết mã tự nhiên, các câu trả lời khác cho tôi ấn tượng rằng nó sẽ chậm bất kể trừ khi tôi đổi từ trình thông dịch tham chiếu - Để xóa nó, OOP vẫn sẽ là một cách tiếp cận hợp lý trong quan điểm của bạn?
lucasgcb

1
với trường hợp 1 (đoạn 2), bạn không nhận được cùng một chức năng khi gọi các hàm, ngay cả khi chính các hàm đó được tuân thủ?
Ewan

CPython là thông dịch viên duy nhất thường được thực hiện nghiêm túc. PyPy rất thú vị , nhưng chắc chắn nó không thấy bất kỳ hình thức áp dụng rộng rãi nào. Hơn nữa, hành vi của nó khác với CPython và nó không hoạt động với một số gói quan trọng, ví dụ như scipy. Rất ít nhà phát triển lành mạnh muốn giới thiệu PyPy cho sản xuất. Như vậy, sự khác biệt giữa ngôn ngữ và việc thực hiện là không quan trọng trong thực tế.
jpmc26

Tôi nghĩ rằng bạn nhấn móng tay trên đầu mặc dù. Không có lý do gì bạn không thể có một trình thông dịch hay trình biên dịch tốt hơn. Nó không phải là bản chất của python như một ngôn ngữ. Bạn đang bị mắc kẹt với thực tế thực tế
Ewan

@ jpmc26 Tôi đã sử dụng PyPy trong sản xuất và khuyên bạn nên xem xét việc đó cho các nhà phát triển có kinh nghiệm khác. Thật tuyệt vời cho microservice sử dụng falconframework.org cho các API nghỉ ngơi nhẹ (làm một ví dụ). Hành vi khác nhau vì các nhà phát triển dựa vào chi tiết triển khai KHÔNG đảm bảo ngôn ngữ không phải là lý do để không sử dụng PyPy. Đó là một lý do để viết lại mã của bạn. Mã tương tự có thể bị phá vỡ nếu CPython thực hiện các thay đổi đối với việc triển khai của nó (điều này hoàn toàn miễn phí nếu nó vẫn tuân thủ thông số ngôn ngữ).
Steven Jackson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.