Tối ưu hóa Python

Tối ưu hóa liên quan đến việc chọn tùy chọn tốt nhất trong số một số tùy chọn có thể khả thi hoặc không vi phạm các ràng buộc. Python có thể được sử dụng để tối ưu hóa các tham số trong một mô hình sao cho phù hợp nhất với dữ liệu, tăng khả năng sinh lời của một thiết kế kỹ thuật tiềm năng hoặc đáp ứng một số loại mục tiêu khác có thể được mô tả bằng toán học bằng các biến và phương trình

Mathematical optimization problems may include equality constraints (e.g. =), inequality constraints (e.g. <, <=, >, >=), objective functions, algebraic equations, differential equations, continuous variables, discrete or integer variables, etc. One example of an optimization problem from a benchmark test set is the Hock Schittkowski problem #71.

$$\min x_1 x_4 \left(x_1 + x_2 + x_3\right) + x_3$$ $$\mathrm{s. t. } \quad x_1 x_2 x_3 x_4 \ge 25$$ $$x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40$$ $$1\le x_1, x_2, x_3, x_4 \le 5$$ $

Vấn đề này có một mục tiêu phi tuyến tính mà trình tối ưu hóa cố gắng giảm thiểu. Các giá trị biến tại giải pháp tối ưu phải tuân theo (s. t. ) cả ràng buộc bình đẳng (=40) và bất bình đẳng (>25). Tích của bốn biến phải lớn hơn 25 và tổng bình phương của các biến cũng phải bằng 40. Ngoài ra, tất cả các biến phải nằm trong khoảng từ 1 đến 5 và dự đoán ban đầu là x1 = 1, x2 = 5, x3 = 5 và x4 = 1

Phương pháp số 1. APM Python


Phương pháp #2. SciPy Optimize Thu nhỏ

nhập numpy dưới dạng np
từ scipy. tối ưu hóa nhập khẩu giảm thiểu

mục tiêu xác định (x)
trả lại x[0]*x[3]*(x[0]+x[1]+x[2])+x[2]

ràng buộc def1(x)
trả lại x[0]*x[1]*x[2]*x[3]-25. 0

ràng buộc def2(x)
tổng_eq = 40. 0
cho tôi trong phạm vi (4)
tổng_eq = tổng_eq - x[i]**2
trả về sum_eq

# dự đoán ban đầu
n = 4
x0 = np. số không(n)
x0[0] = 1. 0
x0[1] = 5. 0
x0[2] = 5. 0
x0[3] = 1. 0

# hiển thị mục tiêu ban đầu
print('Mục tiêu ban đầu. ' + str(mục tiêu(x0)))

# tối ưu hóa
b = (1. 0,5. 0)
bnd = (b, b, b, b)
con1 = {'loại'. 'ineq', 'vui vẻ'. ràng buộc1}
con2 = {'loại'. 'eq', 'vui vẻ'. ràng buộc2}
khuyết điểm = ([con1,con2])
giải pháp = thu nhỏ (mục tiêu, x0, phương pháp = 'SLSQP', \
giới hạn = bnd, ràng buộc = khuyết điểm)
x = giải pháp. x

# hiển thị mục tiêu cuối cùng
print('Mục tiêu cuối cùng. ' + str(mục tiêu(x)))

# giải pháp in ấn
in ('Giải pháp')
in('x1 = ' + str(x[0]))
in('x2 = ' + str(x[1]))
in('x3 = ' + str(x[2]))
in('x4 = ' + str(x[3]))


Phương pháp #3. GEKKO Solution

Tối ưu hóa Python

từ gekko nhập khẩu GEKKO
nhập numpy dưới dạng np
m = GEKKO()
x = m. mảng(m. Var,4,value=1,lb=1,ub=5)
x1,x2,x3,x4 = x
# thay đổi giá trị ban đầu
x2. giá trị = 5; . giá trị = 5
m. Phương trình(x1*x2*x3*x4>=25)
m. Phương trình(x1**2+x2**2+x3**2+x4**2==40)
m. Thu nhỏ(x1*x4*(x1+x2+x3)+x3)
m. giải quyết()
in ('x. ',x)
in ('Mục tiêu. ', tôi. tùy chọn. OBJFCNVAL)

Hướng dẫn này cũng có thể được hoàn thành với các trình tối ưu hóa lập trình phi tuyến tính có sẵn với Bộ giải Excel và Hộp công cụ tối ưu hóa MATLAB. Nhấp vào liên kết thích hợp để biết thêm thông tin và mã nguồn

Trang này dành cho các mẹo và thủ thuật khác nhau giúp cải thiện hiệu suất của các chương trình Python của bạn. Bất cứ nơi nào thông tin đến từ người khác, tôi đã cố gắng xác định nguồn

Python đã thay đổi theo một số cách quan trọng kể từ lần đầu tiên tôi viết trang "trăn nhanh" của mình vào khoảng năm 1996, điều đó có nghĩa là một số thứ tự sẽ thay đổi. Tôi đã chuyển nó sang wiki Python với hy vọng những người khác sẽ giúp duy trì nó

Bạn phải luôn kiểm tra các mẹo này với ứng dụng của mình và phiên bản triển khai Python cụ thể mà bạn định sử dụng và không mù quáng chấp nhận rằng phương pháp này nhanh hơn phương pháp khác. Xem phần để biết thêm chi tiết

Cũng mới vì điều này được viết ban đầu là các gói như Cython, Pyrex, Psyco, Weave, Shed Skin và PyInline, có thể cải thiện đáng kể hiệu suất ứng dụng của bạn bằng cách giúp đẩy mã quan trọng về hiệu suất sang ngôn ngữ C hoặc ngôn ngữ máy dễ dàng hơn.

Các phiên bản khác

Tổng quan. Tối ưu hóa những gì cần tối ưu hóa

Bạn chỉ có thể biết điều gì làm cho chương trình của mình bị chậm sau khi lần đầu tiên chương trình cho kết quả chính xác, sau đó chạy nó để xem chương trình có bị chậm hay không. Khi được phát hiện là chậm, hồ sơ có thể hiển thị phần nào của chương trình đang chiếm phần lớn thời gian. Sau đó, một bộ thử nghiệm toàn diện nhưng chạy nhanh có thể đảm bảo rằng các tối ưu hóa trong tương lai không làm thay đổi tính chính xác của chương trình của bạn. Nói ngắn gọn

  1. Làm cho đúng
  2. Kiểm tra nó đúng
  3. Hồ sơ nếu chậm
  4. Tối ưu hóa
  5. Lặp lại từ 2

Một số tối ưu hóa nhất định tạo nên phong cách lập trình tốt và vì vậy nên học khi bạn học ngôn ngữ. Một ví dụ sẽ là chuyển phép tính các giá trị không thay đổi trong vòng lặp ra bên ngoài vòng lặp

Chọn cấu trúc dữ liệu phù hợp

TBD

Sắp xếp

Sắp xếp danh sách các đối tượng Python cơ bản nói chung là khá hiệu quả. Phương pháp sắp xếp cho danh sách lấy một hàm so sánh tùy chọn làm đối số có thể được sử dụng để thay đổi hành vi sắp xếp. Điều này khá thuận tiện, mặc dù nó có thể làm chậm đáng kể quá trình sắp xếp của bạn, vì hàm so sánh sẽ được gọi nhiều lần. Trong Python 2. 4, thay vào đó, bạn nên sử dụng đối số key cho sắp xếp tích hợp, đây sẽ là cách nhanh nhất để sắp xếp

Chỉ khi bạn đang sử dụng các phiên bản Python cũ hơn (trước 2. 4) lời khuyên sau đây của Guido van Rossum có áp dụng được không

Một cách khác để tăng tốc độ sắp xếp là xây dựng một danh sách các bộ có phần tử đầu tiên là khóa sắp xếp sẽ sắp xếp đúng cách bằng cách sử dụng phép so sánh mặc định và phần tử thứ hai của nó là phần tử danh sách ban đầu. Đây là cái gọi là Biến đổi Schwartzian, còn được gọi là DecorSortUndecorate (DSU)

Ví dụ, giả sử bạn có một danh sách các bộ dữ liệu mà bạn muốn sắp xếp theo trường thứ n của mỗi bộ dữ liệu. Các chức năng sau đây sẽ làm điều đó

def sortby(somelist, n):
    nlist = [(x[n], x) for x in somelist]
    nlist.sort()
    return [val for (key, val) in nlist]

Phù hợp với hành vi của phương pháp sắp xếp danh sách hiện tại (sắp xếp tại chỗ) cũng dễ dàng đạt được

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return

Đây là một ví dụ sử dụng

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True

Từ Tim Delaney

Từ Python 2. 3 sắp xếp được đảm bảo ổn định

(chính xác là nó ổn định trong CPython 2. 3 và được đảm bảo ổn định trong Python 2. 4)

Trăn 2. 4 thêm một tham số khóa tùy chọn giúp biến đổi dễ sử dụng hơn rất nhiều

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))

Lưu ý rằng mục ban đầu không bao giờ được sử dụng để sắp xếp, chỉ có khóa được trả về - điều này tương đương với việc thực hiện

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]

Tính chính xác của phần này bị tranh cãi đối với các phiên bản Python sau này. Trong CPython 2. 5, nối chuỗi khá nhanh, mặc dù điều này có thể không áp dụng tương tự cho các triển khai Python khác. Xem ConcatenationTestCode để thảo luận

Chuỗi trong Python là bất biến. Thực tế này thường lén lút và cắn vào mông những lập trình viên Python mới làm quen. Tính bất biến mang lại một số ưu điểm và nhược điểm. Trong cột cộng, các chuỗi có thể được sử dụng làm khóa trong từ điển và các bản sao riêng lẻ có thể được chia sẻ giữa nhiều liên kết biến. (Python tự động chia sẻ chuỗi một và hai ký tự. ) Trong cột trừ, bạn không thể nói điều gì đó như "thay đổi tất cả 'a's thành 'b's" trong bất kỳ chuỗi nào. Thay vào đó, bạn phải tạo một chuỗi mới với các thuộc tính mong muốn. Việc sao chép liên tục này có thể dẫn đến sự thiếu hiệu quả đáng kể trong các chương trình Python

Tránh điều này

s = ""
for substring in list:
    s += substring

Sử dụng s = "". thay vào đó hãy tham gia (danh sách). Cái trước là một sai lầm rất phổ biến và thảm khốc khi xây dựng các chuỗi lớn. Tương tự, nếu bạn đang tạo các bit của một chuỗi theo tuần tự thay vì

s = ""
for x in list:
    s += some_function(x)

sử dụng

slist = [some_function(elt) for elt in somelist]
s = "".join(slist)

Tránh xa

________số 8_______

Thay vào đó, sử dụng

out = "%s%s%s%s" % (head, prologue, query, tail)

Thậm chí tốt hơn, để dễ đọc (điều này không liên quan gì đến hiệu quả ngoài hiệu quả của bạn với tư cách là một lập trình viên), hãy sử dụng thay thế từ điển

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
0

Hai cái cuối cùng này sẽ nhanh hơn nhiều, đặc biệt là khi được xếp chồng lên nhau qua nhiều lần thực thi tập lệnh CGI và dễ sửa đổi hơn để khởi động. Ngoài ra, cách làm chậm trở nên chậm hơn trong Python 2. 0 với việc bổ sung các phép so sánh phong phú vào ngôn ngữ. Giờ đây, máy ảo Python mất nhiều thời gian hơn để tìm ra cách nối hai chuỗi. (Đừng quên rằng Python thực hiện tra cứu tất cả các phương thức trong thời gian chạy. )

vòng lặp

Python hỗ trợ một vài cấu trúc vòng lặp. Câu lệnh for được sử dụng phổ biến nhất. Nó lặp qua các phần tử của một chuỗi, gán từng phần tử cho biến vòng lặp. Nếu phần thân của vòng lặp của bạn đơn giản, thì chi phí thông dịch viên của chính vòng lặp for có thể là một lượng chi phí đáng kể. Đây là nơi chức năng bản đồ tiện dụng. Bạn có thể coi bản đồ là một for được chuyển vào mã C. Hạn chế duy nhất là "phần thân vòng lặp" của bản đồ phải là một lệnh gọi hàm. Bên cạnh lợi ích cú pháp của việc hiểu danh sách, chúng thường nhanh hoặc nhanh hơn so với việc sử dụng bản đồ tương đương

Đây là một ví dụ đơn giản. Thay vì lặp qua một danh sách các từ và chuyển đổi chúng thành chữ hoa

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
1

bạn có thể sử dụng bản đồ để đẩy vòng lặp từ trình thông dịch sang mã C đã biên dịch

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
2

Khả năng hiểu danh sách đã được thêm vào Python trong phiên bản 2. 0 cũng vậy. Chúng cung cấp một cách ngắn gọn hơn về mặt cú pháp và hiệu quả hơn để viết vòng lặp for ở trên

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
3

Biểu thức trình tạo đã được thêm vào Python trong phiên bản 2. 4. Chúng hoạt động ít nhiều như hiểu danh sách hoặc bản đồ nhưng tránh được chi phí tạo toàn bộ danh sách cùng một lúc. Thay vào đó, chúng trả về một đối tượng trình tạo có thể được lặp lại từng chút một

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
4

Phương pháp nào phù hợp sẽ phụ thuộc vào phiên bản Python bạn đang sử dụng và đặc điểm của dữ liệu bạn đang thao tác

Guido van Rossum đã viết một bài kiểm tra chi tiết hơn (và ngắn gọn) về tối ưu hóa vòng lặp rất đáng để đọc

tránh dấu chấm

Giả sử bạn không thể sử dụng bản đồ hoặc hiểu danh sách? . Ví dụ về vòng lặp for có một điểm kém hiệu quả khác. cả danh sách mới. nối thêm và từ. phía trên là các tham chiếu hàm được đánh giá lại mỗi lần thông qua vòng lặp. Vòng lặp ban đầu có thể được thay thế bằng

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
5

Kỹ thuật này nên được sử dụng một cách thận trọng. Sẽ khó bảo trì hơn nếu vòng lặp lớn. Trừ khi bạn rất quen thuộc với đoạn mã đó, bạn sẽ thấy mình đang quét lên để kiểm tra các định nghĩa của append và upper.

Biến cục bộ

Tăng tốc cuối cùng có sẵn cho chúng tôi cho phiên bản không có bản đồ của vòng lặp for là sử dụng các biến cục bộ bất cứ khi nào có thể. Nếu vòng lặp trên được truyền dưới dạng hàm, nối thêm và trên trở thành biến cục bộ. Python truy cập các biến cục bộ hiệu quả hơn nhiều so với các biến toàn cục

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
6

Vào thời điểm ban đầu tôi viết bài này, tôi đang sử dụng Pentium 100 MHz chạy BSDI. Tôi đã nhận được những lần sau khi chuyển đổi danh sách các từ trong /usr/share/dict/words (38.470 từ tại thời điểm đó) thành chữ hoa

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
7

Đang khởi tạo các phần tử từ điển

Giả sử bạn đang xây dựng một từ điển về tần suất xuất hiện của từ và bạn đã chia văn bản của mình thành một danh sách các từ. Bạn có thể thực hiện một cái gì đó như

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
8

Ngoại trừ lần đầu tiên, mỗi lần một từ được nhìn thấy, kiểm tra câu lệnh if không thành công. Nếu bạn đang đếm một số lượng lớn từ, nhiều từ có thể sẽ xuất hiện nhiều lần. Trong trường hợp việc khởi tạo một giá trị sẽ chỉ xảy ra một lần và việc tăng giá trị đó sẽ xảy ra nhiều lần thì việc sử dụng câu lệnh try sẽ rẻ hơn

def sortby_inplace(somelist, n):
    somelist[:] = [(x[n], x) for x in somelist]
    somelist.sort()
    somelist[:] = [val for (key, val) in somelist]
    return
9

Điều quan trọng là nắm bắt được ngoại lệ KeyError dự kiến ​​và không có mệnh đề ngoại trừ mặc định để tránh cố gắng phục hồi từ một ngoại lệ mà bạn thực sự không thể xử lý bằng (các) câu lệnh trong mệnh đề thử

Một giải pháp thay thế thứ ba đã có sẵn với việc phát hành Python 2. x. Từ điển hiện có phương thức get() sẽ trả về giá trị mặc định nếu khóa mong muốn không được tìm thấy trong từ điển. Điều này đơn giản hóa vòng lặp

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
0

Khi tôi viết phần này lần đầu, có những tình huống rõ ràng mà một trong hai cách tiếp cận đầu tiên nhanh hơn. Có vẻ như cả ba cách tiếp cận hiện đều thể hiện hiệu suất tương tự nhau (trong khoảng 10% của nhau), ít nhiều độc lập với các thuộc tính của danh sách các từ

Ngoài ra, nếu giá trị được lưu trữ trong từ điển là một đối tượng hoặc danh sách (có thể thay đổi), bạn cũng có thể sử dụng lệnh dict. phương pháp setdefault, e. g

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
1

Bạn có thể nghĩ rằng điều này tránh phải tra cứu chìa khóa hai lần. Nó thực sự không (ngay cả trong python 3. 0), nhưng ít nhất việc tra cứu kép được thực hiện trong C

Một lựa chọn khác là sử dụng lớp

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
2

Chi phí khai báo nhập khẩu

báo cáo nhập khẩu có thể được thực hiện ở bất cứ đâu. Việc đặt chúng bên trong các chức năng thường hữu ích để hạn chế khả năng hiển thị của chúng và/hoặc giảm thời gian khởi động ban đầu. Mặc dù trình thông dịch của Python được tối ưu hóa để không nhập cùng một mô-đun nhiều lần, nhưng việc thực thi lặp lại một câu lệnh nhập có thể ảnh hưởng nghiêm trọng đến hiệu suất trong một số trường hợp

Hãy xem xét hai đoạn mã sau (tôi tin là gốc của Greg McFarlane - Tôi thấy nó không được phân bổ trong một. lang thang. python danh sách python@python. org và sau đó được gán cho anh ta trong một nguồn khác)

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
3

hoặc là

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
4

doit2 sẽ chạy nhanh hơn nhiều so với doit1, mặc dù tham chiếu đến mô-đun chuỗi là toàn cục trong doit2. Đây là phiên phiên dịch Python chạy bằng Python 2. 3 và mô-đun timeit mới, cho biết mô-đun thứ hai nhanh hơn mô-đun thứ nhất bao nhiêu

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
5

Các phương thức chuỗi đã được đưa vào ngôn ngữ trong Python 2. 0. Chúng cung cấp một phiên bản tránh nhập hoàn toàn và thậm chí còn chạy nhanh hơn

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
6

Đây là bằng chứng từ timeit

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
7

Ví dụ trên rõ ràng là một chút giả tạo, nhưng nguyên tắc chung đúng

Lưu ý rằng việc nhập một chức năng có thể tăng tốc độ tải ban đầu của mô-đun, đặc biệt nếu mô-đun đã nhập có thể không được yêu cầu. Đây thường là trường hợp tối ưu hóa "lười biếng" -- tránh làm việc (nhập một mô-đun, có thể rất tốn kém) cho đến khi bạn chắc chắn rằng nó được yêu cầu

Đây chỉ là một khoản tiết kiệm đáng kể trong trường hợp mô-đun hoàn toàn không được nhập (từ bất kỳ mô-đun nào) - nếu mô-đun đã được tải (như trường hợp của nhiều mô-đun tiêu chuẩn, như chuỗi hoặc lại), tránh . Để xem những mô-đun nào được tải trong hệ thống, hãy tìm trong sys. mô-đun

Một cách tốt để thực hiện nhập khẩu lười biếng là

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
8

Bằng cách này, mô-đun email sẽ chỉ được nhập một lần, trong lần gọi đầu tiên của parse_email()

Tổng hợp dữ liệu

Chi phí gọi hàm trong Python tương đối cao, đặc biệt là so với tốc độ thực thi của hàm dựng sẵn. Điều này gợi ý mạnh mẽ rằng khi thích hợp, các chức năng nên xử lý dữ liệu tổng hợp. Đây là một ví dụ giả định được viết bằng Python

>>> somelist = [(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> somelist.sort()
>>> somelist
[(1, 2, 'def'), (2, -4, 'ghi'), (3, 6, 'abc')]
>>> nlist = sortby(somelist, 2)
>>> sortby_inplace(somelist, 2)
>>> nlist == somelist
True
>>> nlist = sortby(somelist, 1)
>>> sortby_inplace(somelist, 1)
>>> nlist == somelist
True
9

so với

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
0

Đây là bằng chứng trong bánh pudding sử dụng phiên tương tác

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
1

Ngay cả khi được viết bằng Python, ví dụ thứ hai chạy nhanh hơn khoảng bốn lần so với ví dụ đầu tiên. Nếu doit được viết bằng C thì sự khác biệt có thể còn lớn hơn (trao đổi vòng lặp Python for thành vòng lặp C for cũng như loại bỏ hầu hết các lệnh gọi hàm)

Làm công việc ít thường xuyên hơn

Trình thông dịch Python thực hiện một số kiểm tra định kỳ. Cụ thể, nó quyết định có cho phép luồng khác chạy hay không và có thực hiện cuộc gọi đang chờ xử lý hay không (thường là cuộc gọi được thiết lập bởi bộ xử lý tín hiệu). Hầu hết thời gian không có gì để làm, vì vậy việc thực hiện các kiểm tra này mỗi lần vượt qua vòng lặp thông dịch viên có thể làm mọi thứ chậm lại. Có một chức năng trong mô-đun sys, setcheckinterval, bạn có thể gọi chức năng này để báo cho trình thông dịch biết tần suất thực hiện các kiểm tra định kỳ này. Trước khi phát hành Python 2. 3 nó mặc định là 10. Trong 2. 3 điều này đã được nâng lên 100. Nếu bạn không chạy với các luồng và bạn không mong muốn bắt được nhiều tín hiệu, thì việc đặt giá trị này thành giá trị lớn hơn có thể cải thiện hiệu suất của trình thông dịch, đôi khi là đáng kể

Python không phải là C

Nó cũng không phải là Perl, Java, C++ hay Haskell. Hãy cẩn thận khi chuyển kiến ​​thức của bạn về cách các ngôn ngữ khác hoạt động sang Python. Một ví dụ đơn giản phục vụ để chứng minh

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
2

Bây giờ hãy xem xét các chương trình C tương tự (chỉ hiển thị phiên bản bổ sung)

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
3

và thời gian thực hiện

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
4

Lưu ý rằng có một lợi thế đáng kể trong Python khi thêm một số vào chính nó thay vì nhân nó với hai hoặc dịch chuyển nó sang trái một bit. Trong C trên tất cả các kiến ​​trúc máy tính hiện đại, mỗi phép toán trong số ba phép toán số học được dịch thành một lệnh máy duy nhất thực thi trong một chu kỳ, do đó, việc bạn chọn phép toán nào không thực sự quan trọng

Một "bài kiểm tra" phổ biến mà các lập trình viên Python mới thường thực hiện là dịch thành ngữ Perl phổ biến

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
5

vào mã Python trông giống như

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
6

và sử dụng nó để kết luận rằng Python phải chậm hơn nhiều so với Perl. Như những người khác đã chỉ ra nhiều lần, Python chậm hơn Perl đối với một số thứ và nhanh hơn đối với những thứ khác. Hiệu suất tương đối cũng thường phụ thuộc vào trải nghiệm của bạn với hai ngôn ngữ

Sử dụng xrange thay vì phạm vi

Phần này không còn áp dụng nếu bạn đang sử dụng Python 3, trong đó phạm vi hiện cung cấp trình vòng lặp trên các phạm vi có kích thước tùy ý và khi xrange không còn tồn tại

Python có hai cách để lấy dãy số. phạm vi và xrange. Hầu hết mọi người biết về phạm vi, vì tên rõ ràng của nó. xrange, nằm gần cuối bảng chữ cái, ít được biết đến hơn nhiều

xrange là một đối tượng trình tạo, về cơ bản tương đương với Python 2 sau. 3 mã

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
7

Ngoại trừ việc nó được thực hiện trong C thuần túy

xrange có những hạn chế. Cụ thể, nó chỉ hoạt động với ints;

Tuy nhiên, nó tiết kiệm rất nhiều bộ nhớ và trừ khi bạn lưu trữ các đối tượng được tạo ra ở đâu đó, chỉ có một đối tượng được tạo ra sẽ tồn tại tại một thời điểm. Sự khác biệt là do đó. Khi bạn gọi phạm vi, nó sẽ tạo một danh sách chứa rất nhiều đối tượng số (int, long hoặc float). Tất cả những đối tượng đó được tạo ra cùng một lúc và tất cả chúng tồn tại cùng một lúc. Đây có thể là một nỗi đau khi số lượng lớn

mặt khác, xrange không tạo số ngay lập tức - chỉ chính đối tượng phạm vi. Các đối tượng số chỉ được tạo khi bạn bật trình tạo, e. g. bằng cách lặp qua nó. Ví dụ

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
8

Và vì lý do này, mã chạy ngay lập tức. Nếu bạn thay thế phạm vi ở đó, Python sẽ khóa; . maxint số đối tượng (khoảng 2. 1 tỷ trên PC thông thường) để làm bất cứ điều gì khác. Cuối cùng, nó sẽ hết bộ nhớ và thoát

Trong các phiên bản Python trước 2. 2, các đối tượng xrange cũng hỗ trợ tối ưu hóa, chẳng hạn như thử nghiệm thành viên nhanh (i in xrange(n)). Các tính năng này đã bị xóa trong 2. 2 do ít sử dụng

Nói rằng bạn có một chức năng

# E.g. n = 1
n = 1
import operator
nlist.sort(key=operator.itemgetter(n))
# use sorted() if you don't want to sort in-place:
# sortedlist = sorted(nlist, key=operator.itemgetter(n))
9

Và giả sử chức năng này được gọi từ một nơi khác nhiều lần

Chà, séc của bạn sẽ có câu lệnh if làm bạn chậm lại mọi lúc trừ lần đầu tiên, vì vậy bạn có thể làm điều này

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
0

Chà, ví dụ này khá thiếu sót, nhưng nếu câu lệnh 'nếu' là một biểu thức khá phức tạp (hoặc một cái gì đó có nhiều dấu chấm), bạn có thể tự mình đánh giá nó, nếu bạn biết rằng nó sẽ chỉ đúng ở lần đầu tiên

Mã hồ sơ

Bước đầu tiên để tăng tốc chương trình của bạn là tìm hiểu xem nút cổ chai nằm ở đâu. Hầu như không có ý nghĩa gì khi tối ưu hóa mã không bao giờ được thực thi hoặc đã chạy nhanh. Tôi sử dụng hai mô-đun để giúp định vị các điểm nóng trong mã, hồ sơ và theo dõi của mình. Trong các ví dụ sau, tôi cũng sử dụng mô-đun timeit, mô-đun mới trong Python 2. 3

Lời khuyên trong phần này đã lỗi thời. Xem tài liệu hồ sơ riêng biệt để biết các lựa chọn thay thế cho các phương pháp được đưa ra dưới đây

lập hồ sơ

Có một số mô-đun định hình được bao gồm trong bản phân phối Python. Sử dụng một trong số này để lập hồ sơ việc thực thi một tập hợp các chức năng là khá dễ dàng. Giả sử chức năng chính của bạn được gọi là chính, không có đối số và bạn muốn thực thi nó dưới sự kiểm soát của mô-đun hồ sơ. Ở dạng đơn giản nhất, bạn chỉ cần thực hiện

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
1

Khi main() trả về, mô-đun hồ sơ sẽ in một bảng các lệnh gọi hàm và thời gian thực hiện. Đầu ra có thể được điều chỉnh bằng cách sử dụng lớp Stats đi kèm với mô-đun. Từ Python 2. 4, cấu hình đã cho phép thời gian sử dụng bởi các nội trang Python và các hàm trong các mô-đun mở rộng cũng được cấu hình

Có thể tìm thấy mô tả dài hơn một chút về việc lập hồ sơ bằng cách sử dụng các mô-đun hồ sơ và pstats tại đây (phiên bản lưu trữ)

http. // trang web. lưu trữ. org/web/20060506162444/http. // bộ cánh. com/doc/howtos/performance-profiling-python-code

Mô-đun cProfile

Mô-đun `cProfile` là một giải pháp thay thế cho cấu hình được viết bằng C thường chạy nhanh hơn nhiều. Nó sử dụng cùng một giao diện

Mô-đun dấu vết

Mô-đun theo dõi là một phần phụ của mô-đun hồ sơ mà tôi đã viết ban đầu để thực hiện một số phạm vi kiểm tra cấp câu lệnh thô. Nó đã được sửa đổi rất nhiều bởi một số người khác kể từ khi tôi phát hành nỗ lực thô sơ ban đầu của mình. Kể từ Python 2. 0 bạn nên tìm dấu vết. py trong thư mục Tools/scripts của bản phân phối Python. Bắt đầu với Python 2. 3 nó nằm trong thư viện chuẩn (thư mục Lib). Bạn có thể sao chép nó vào thư mục bin cục bộ của mình và đặt quyền thực thi, sau đó thực hiện trực tiếp. Thật dễ dàng để chạy từ dòng lệnh để theo dõi việc thực thi toàn bộ tập lệnh

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
2

Trong Python 2. 4 thậm chí còn dễ chạy hơn. Chỉ cần thực thi python -m trace

Không có tài liệu riêng, nhưng bạn có thể thực hiện "theo dõi pydoc" để xem tài liệu nội tuyến

Trực quan hóa kết quả hồ sơ

RunSnakeRun là một công cụ GUI của Mike Fletcher giúp trực quan hóa các kết xuất hồ sơ từ cProfile bằng bản đồ vuông. Các cuộc gọi chức năng/phương thức có thể được sắp xếp theo các tiêu chí khác nhau và mã nguồn có thể được hiển thị cùng với số liệu thống kê cuộc gọi và trực quan hóa. Hiện tại (tháng 4 năm 2016) RunSnakeRun hỗ trợ Python 2. x - do đó, nó không thể tải dữ liệu hồ sơ được tạo bởi các chương trình Python 3

Một ví dụ sử dụng

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
3

Gprof2Dot là một công cụ dựa trên python có thể chuyển đổi đầu ra kết quả định hình thành biểu đồ có thể được chuyển đổi thành hình ảnh PNG hoặc SVG

Một phiên định hình điển hình với python 2. 5 trông như thế này (trên các nền tảng cũ hơn, bạn sẽ cần sử dụng tập lệnh thực tế thay vì tùy chọn -m)

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
4

PyCallGraph pycallgraph là một mô-đun Python tạo biểu đồ cuộc gọi cho các chương trình Python. Nó tạo một tệp PNG hiển thị các lệnh gọi chức năng của mô-đun và liên kết của chúng với các lệnh gọi chức năng khác, số lần một chức năng được gọi và thời gian dành cho chức năng đó

sử dụng điển hình

# E.g. n = 1
n = 1
nlist = [(x[n], i, x) for (i, x) in enumerate(nlist)]
nlist.sort()
nlist = [val for (key, index, val) in nlist]
5

PyProf2CallTree là một tập lệnh giúp trực quan hóa dữ liệu định hình được thu thập bằng mô-đun python cProfile với trình phân tích cây gọi đồ họa kcachegrind

Tối ưu hóa trong Python là gì?

Tối ưu hóa liên quan đến việc chọn tùy chọn tốt nhất trong số nhiều lựa chọn khả thi hoặc không vi phạm các ràng buộc .

Python có tốt cho việc tối ưu hóa không?

Tối ưu hóa mã Python là một cách giúp chương trình của bạn thực hiện bất kỳ tác vụ nào hiệu quả hơn và nhanh hơn với ít dòng mã hơn, ít bộ nhớ hơn hoặc các tài nguyên khác có liên quan, đồng thời tạo ra kết quả phù hợp. Nó rất quan trọng khi xử lý một số lượng lớn các thao tác hoặc dữ liệu trong khi thực hiện một tác vụ

Thư viện Python nào là tốt nhất để tối ưu hóa?

Thư viện SciPy là thư viện cơ bản cho điện toán khoa học bằng Python. Nó cung cấp nhiều giao diện hiệu quả và thân thiện với người dùng cho các tác vụ như tích hợp số, tối ưu hóa, xử lý tín hiệu, đại số tuyến tính, v.v.

Điều gì thay thế cho tối ưu hóa SciPy?

NumPy, R Language, scikit-learning, Anaconda và MATLAB là những lựa chọn thay thế phổ biến nhất và là đối thủ cạnh tranh của SciPy.