Hướng dẫn ý tưởng Python

Ngôn ngữ lập trình tương đối chậm khi triển khai CPython mặc định, mặc dù có nhiều cách để làm cho ngôn ngữ này nhanh hơn, bao gồm cả các lựa chọn thay thế hướng đến hiệu suất như PyPy. Trong lịch sử, Van Rossum dường như không quan tâm đến hiệu suất của Python, ủng hộ sự đơn giản của trình biên dịch được tối ưu hóa kém hơn

Các slide từ hội nghị thượng đỉnh hiện đã được đăng [PDF] gợi ý một sự thay đổi của trái tim. "Tôi đã chán ngồi ở nhà trong khi nghỉ hưu," ông nói. "Tôi nộp đơn tại Microsoft và được tuyển dụng. Tôi được tự do chọn một dự án. Tôi đã chọn trở về cội nguồn của mình. Đây là cách Microsoft trả lại Python. "

Dự án là một "nhóm nhỏ do Microsoft tài trợ", bao gồm nhà phát triển Python Core và Kỹ sư phần mềm cao cấp của Microsoft Eric Snow, và Mark Shannon, kỹ sư nghiên cứu tại Semmle, chuyên phát triển các sản phẩm để phân tích bảo mật mã. Semmle đã là một phần của GitHub kể từ tháng 9 năm 2019, vì vậy đây là một nhóm toàn Microsoft, mặc dù Van Rossum lưu ý rằng nó "có thể phát triển"

Dự án có kho lưu trữ GitHub bao gồm một nhánh của CPython cũng như trình theo dõi vấn đề cho các ý tưởng và công cụ để phân tích hiệu suất. Theo Van Rossum, sẽ "không có nhánh/nhánh tồn tại lâu dài, không có 6.000 yêu cầu kéo dòng bất ngờ" và mọi thứ sẽ là nguồn mở.

Shannon đã làm việc về hiệu suất Python một thời gian, với các dự án trước đây HotPy và HotPy [2] cho trình biên dịch tức thời cho CPython

Anh ấy có kho lưu trữ CPython nhanh hơn của riêng mình, nơi anh ấy đã viết rằng "chúng tôi muốn tăng tốc CPython lên gấp 5 lần trong bốn bản phát hành tiếp theo. "

Ông lưu ý rằng mặc dù một số nền tảng, chẳng hạn như iOS của Apple, không cho phép "tạo mã thời gian chạy", nhưng ông tin rằng ngay cả trong những trường hợp này, tốc độ tăng gấp đôi là có thể.

Mặc dù cuối cùng Shannon cũng gửi một trình biên dịch JIT, nhưng điều này sẽ không xảy ra cho đến khi Python 3. 12 trong kế hoạch của mình. Những thay đổi cho Python 3. 11 sẽ dựa trên "rất nhiều điều chỉnh" chẳng hạn như bố cục bộ nhớ tốt hơn, cải thiện hiệu suất cho các số nguyên nhỏ, gọi và trả về nhanh hơn và xử lý ngoại lệ không cần thiết

Có một số hạn chế, như Van Rossum đã lưu ý trong bài nói chuyện của mình, bao gồm việc không phá vỡ khả năng tương thích ABI [Giao diện nhị phân ứng dụng] ổn định, giữ cho mã tương thích và không gây chậm trong các trường hợp cạnh. Anh ấy nói rằng nhiều thứ có thể được thay đổi một cách an toàn, bao gồm mã byte Python, bố cục khung ngăn xếp, trình biên dịch và trình thông dịch. Bố cục khung ngăn xếp mô tả cách dữ liệu xác định chức năng được định vị trong bộ nhớ

Các ghi chú của Van Rossum dường như rất phù hợp với những ý tưởng hiện có của Shannon. "Có thế hệ mã máy trong tương lai của chúng tôi," anh ấy nói, và giống như Shannon, đề cập đến việc tăng tốc gấp 5 lần như một mục tiêu dài hạn, mặc dù "chúng tôi sẽ phải sáng tạo. "

Trăn 3. 10, hiện đang trong giai đoạn thử nghiệm, dự kiến ​​ra mắt vào tháng 10 năm nay. Lịch phát hành là khoảng hàng năm, vì vậy chúng tôi có thể mong đợi 3. 11 tháng 10 năm 2022. ®

Đăng ký podcast này để nhận liên kết riêng để nghe trong trình phát podcast yêu thích của bạn. Tìm hiểu về RSS

SubscribeAudio phát lại không được hỗ trợ trên trình duyệt của bạn. vui lòng nâng cấp

Phần mềm theo quy mô là nơi chúng tôi thảo luận về các câu chuyện kỹ thuật đằng sau các ứng dụng phần mềm lớn

Đặt mua

chi tiết tập

bình luận

Guido van Rossum là người tạo ra ngôn ngữ lập trình Python và là Kỹ sư xuất sắc tại Microsoft.

Podcast của Apple . Spotify . Google Podcast

Đặt mua

Chia sẻ phần mềm theo quy mô

Chúng tôi thảo luận về công việc mới của Guido trong việc làm cho CPython nhanh hơn [ PEP 659 ], Các cấp của Trình thông dịch Python Thực thi và tác động cao, cải tiến hiệu suất thấp.

nổi bật

[bản tóm tắt đã chỉnh sửa]

[00. 21] Điều gì khiến bạn quan tâm đến việc cải thiện hiệu suất của Python?

chàng. Theo một nghĩa nào đó, đây có lẽ là một chủ đề khá thoải mái đối với tôi vì nó có nghĩa là làm việc với cốt lõi của Python, nơi mà tôi vẫn cảm thấy mình biết mọi thứ. Khi tôi bắt đầu làm việc tại Microsoft, tôi đã xem qua Azure nhưng nhận ra rằng tôi chưa bao giờ thích loại công việc đó tại Google hay Dropbox. Sau đó, tôi xem xét Machine Learning, nhưng sẽ mất rất nhiều thời gian để làm điều gì đó thú vị với các phần không liên quan đến Python và thậm chí cả Python

[02. 31] Điều gì khác biệt về bộ ý tưởng về hiệu suất Python của Mark Shannon đã thuyết phục bạn theo đuổi chúng?

Hướng dẫn. Tôi thích cách anh ấy suy nghĩ về vấn đề. Hầu hết các cách tiếp cận khác xung quanh hiệu suất Python như PyPy Cinder đều không phù hợp với mọi trường hợp sử dụng vì chúng không tương thích ngược với các mô-đun mở rộng. Mark có quan điểm và kinh nghiệm của một nhà phát triển CPython, cũng như cách tiếp cận khả thi để duy trì khả năng tương thích ngược, đây là vấn đề khó giải quyết nhất. Trình thông dịch Python Bytecode được sửa đổi thường xuyên qua các bản phát hành nhỏ [ví dụ:. 3. 8 → 3. 9] vì nhiều lý do như opcode mới, vì vậy sửa đổi đó là một cách tiếp cận tương đối an toàn.

Utsav. [09. 45] Bạn có thể hướng dẫn chúng tôi ý tưởng về các bậc thực thi của Trình thông dịch Python không?

chàng. Khi bạn thực thi một chương trình, bạn không biết liệu nó có bị lỗi sau khi chạy một phần nghìn giây hay liệu nó sẽ là một quá trình tính toán kéo dài ba tuần hay không. Bởi vì nó có thể là cùng một mã, chỉ trong trường hợp đầu tiên, nó có lỗi. Và vì vậy, nếu phải mất ba tuần để chạy chương trình, có lẽ nên dành nửa giờ trước thời hạn để tối ưu hóa tất cả mã sẽ được chạy. Nhưng rõ ràng, đặc biệt là trong các ngôn ngữ động như Python, nơi chúng tôi làm nhiều nhất có thể mà không yêu cầu người dùng cho chúng tôi biết chính xác họ cần làm như thế nào, bạn chỉ muốn bắt đầu thực thi mã nhanh nhất có thể. Vì vậy, nếu đó là một tập lệnh nhỏ hoặc một chương trình lớn bị lỗi sớm hoặc thoát sớm vì một lý do chính đáng, bạn sẽ không mất thời gian để bị phân tâm bằng cách tối ưu hóa tất cả mã đó

Vì vậy, những gì chúng tôi cố gắng làm ở đó là giữ cho trình biên dịch bytecode đơn giản để chúng tôi có thể thực thi phần đầu của mã càng sớm càng tốt. Nếu chúng ta thấy rằng một số chức năng đang được thực thi nhiều lần, thì chúng ta gọi đó là chức năng nóng và một số định nghĩa về “nóng”. Đối với một số mục đích, có thể đó là một chức năng hấp dẫn nếu nó được gọi nhiều hơn một lần hoặc hơn hai lần hoặc hơn 10 lần. Đối với các mục đích khác, bạn muốn thận trọng hơn và bạn có thể nói, “Chà, nó chỉ hấp dẫn nếu nó được gọi 1000 lần. ”

Trình biên dịch thích ứng chuyên dụng [ PEP 659 ] sau đó cố gắng thay thế các mã byte nhất định bằng mã byte nhanh hơn, nhưng chỉ hoạt động nếu các loại các đối số là các loại cụ thể. Một ví dụ giả thuyết đơn giản là toán tử cộng trong Python. Nó có thể thêm nhiều thứ như số nguyên, chuỗi, danh sách hoặc thậm chí là bộ dữ liệu. Mặt khác, bạn không thể thêm một số nguyên vào một chuỗi. Vì vậy, bước tối ưu hóa - thường được gọi là tăng tốc, nhưng thông thường trong ngữ cảnh của chúng tôi, chúng tôi gọi nó là chuyên biệt hóa - là có một mã byte số nguyên "thêm nhị phân" riêng biệt, một mã byte cấp hai được ẩn khỏi người dùng. Mã lệnh này giả định rằng cả hai đối số của nó đều là các đối tượng số nguyên thực tế của Python, truy cập trực tiếp vào các đối tượng đó để tìm giá trị, cộng các giá trị đó lại với nhau trong thanh ghi máy và đẩy kết quả trở lại ngăn xếp.

Thao tác cộng số nguyên nhị phân vẫn phải thực hiện kiểm tra kiểu trên các đối số. Vì vậy, nó không hoàn toàn miễn phí nhưng kiểm tra loại có thể được triển khai nhanh hơn nhiều so với một loại công văn hướng đối tượng hoàn toàn chung chung, giống như những gì thường xảy ra đối với hầu hết các hoạt động thêm chung chung

Cuối cùng, luôn có khả năng một hàm được gọi hàng triệu lần với các đối số nguyên, rồi đột nhiên một phần dữ liệu gọi hàm đó với một đối số dấu phẩy động hoặc điều gì đó tệ hơn. Tại thời điểm đó, trình thông dịch sẽ chỉ thực thi mã byte gốc. Đó là một phần quan trọng để bạn vẫn có đầy đủ ngữ nghĩa Python

Utsav [18. 20] Nói chung, bạn nghe nói về các kỹ thuật này trong ngữ cảnh của JIT, trình biên dịch Just-In-Time, nhưng điều đó hiện không được triển khai

Bản tổng hợp Just-In-Time có rất nhiều hành lý cảm xúc mà chúng tôi đang cố gắng tránh vào thời điểm này. Trong trường hợp của chúng tôi, không rõ chúng tôi biên dịch chính xác cái gì và khi nào. Tại một số thời điểm trước khi thực thi chương trình, chúng tôi biên dịch mã nguồn của bạn thành mã byte. Sau đó, chúng tôi dịch mã byte thành mã byte chuyên dụng. Ý tôi là, mọi thứ xảy ra tại một thời điểm nào đó trong thời gian chạy, vậy phần nào bạn sẽ gọi là Just-In-Time?

Ngoài ra, người ta thường cho rằng quá trình biên dịch Just-In-Time sẽ tự động làm cho mã của bạn tốt hơn. Thật không may, bạn thường không thể dự đoán hiệu suất mã của mình sẽ như thế nào. Và chúng ta có đủ điều đó với các CPU hiện đại và khả năng dự đoán nhánh tuyệt vời của chúng. Ví dụ: chúng tôi viết mã theo cách mà chúng tôi nghĩ rõ ràng sẽ giảm số lần truy cập bộ nhớ. Khi chúng tôi đo điểm chuẩn, chúng tôi thấy rằng nó chạy nhanh như mã cũ chưa được tối ưu hóa vì CPU đã tìm ra các mẫu truy cập mà không cần bất kỳ sự trợ giúp nào của chúng tôi. Tôi ước tôi biết điều gì đã xảy ra trong các CPU hiện đại khi nói đến dự đoán nhánh và bộ nhớ đệm nội tuyến bởi vì đó là điều kỳ diệu tuyệt đối

Bảng điểm đầy đủ

Utsav. [00. 14] Cảm ơn Guido đã tham gia cùng tôi trong một tập khác của podcast Phần mềm theo quy mô. thật tuyệt khi có bạn ở đây.

Hướng dẫn. [00. 20] Thật tuyệt khi có mặt tại đây trong chương trình.

utsav. [00. 21] Vâng. Và thật vui khi được nói chuyện với bạn lần nữa. Vì vậy, lần cuối cùng chúng ta nói chuyện là tại Dropbox rất nhiều năm trước. Và bạn đã nghỉ hưu, và sau đó bạn quyết định rằng bạn muốn làm một cái gì đó mới. Và bạn đang làm việc về hiệu suất tại Microsoft, và điều đó thật tuyệt vời. Vì vậy, để bắt đầu, tôi chỉ muốn hỏi bạn, bạn có thể chọn bất kỳ dự án nào bạn muốn, dựa trên một số slide mà tôi đã xem. Vì vậy, điều gì khiến bạn hứng thú khi làm việc với hiệu suất của Python?

Hướng dẫn. [00. 47] Theo một nghĩa nào đó, đây có lẽ là một chủ đề khá thoải mái đối với tôi vì nó có nghĩa là làm việc với cốt lõi của Python, nơi tôi vẫn cảm thấy mình biết đường đi của mình. Một số điều khác mà tôi đã xem xét ngắn gọn trong tháng đầu tiên làm việc tại Microsoft, tôi đã xem xét, “Chà, tôi có thể làm gì với Azure?”, và tôi gần như ngay lập tức nhớ ra rằng mình không phù hợp để trở thành một kỹ sư đám mây. Đó chưa bao giờ là phần thú vị trong công việc của tôi tại Dropbox. Đó cũng không phải là phần thú vị trong công việc của tôi trước đó tại Google. Và sẽ không vui chút nào nếu làm điều đó ở Microsoft. Vì vậy, tôi đã từ bỏ nó một cách nhanh chóng. Tôi tìm hiểu về machine learning, thứ mà tôi hoàn toàn không biết gì khi gia nhập Microsoft. Tôi vẫn chưa biết gì, nhưng ít nhất tôi đã tham gia một khóa học ngắn hạn và nói chuyện với một nhóm người biết nhiều về nó. Và kết luận của tôi thực sự là đó là một lĩnh vực rộng lớn. Nó chủ yếu là toán học và thống kê và có rất ít nội dung Python trong lĩnh vực này. Và tôi sẽ mất nhiều năm để làm bất cứ điều gì thú vị với phần không phải Python và thậm chí có thể với phần Python, vì mọi người chỉ viết các hàm và lớp rất đơn giản, tốt nhất là trong mã máy học của họ. Nhưng ít nhất tôi biết thêm một chút về thuật ngữ mà mọi người sử dụng. Và khi mọi người nói hạt nhân, bây giờ tôi biết ý nghĩa của họ. Hoặc ít nhất tôi không còn bối rối như trước nữa.

Utsav. [02. 31] Điều đó hợp lý. Và điều đó rất giống với trải nghiệm của tôi với máy học. Được rồi, vậy là bạn đã quyết định rằng bạn muốn làm việc với hiệu năng của Python, phải không?

Hướng dẫn. [02. 43] Rất nhiều. vâng.

utsav. [02. 44] Vâng. Vì vậy, có điều gì khác biệt về tập hợp các ý tưởng khiến bạn quyết định rằng điều này có ý nghĩa và tôi nên thực hiện một dự án để thực hiện những ý tưởng này?

Hướng dẫn. [02. 55] Ý tưởng của Mark Shannon có lẽ không phải là duy nhất, nhưng tôi biết anh ấy đã nghiên cứu trong một thời gian dài. Tôi nhớ cách đây nhiều năm, tôi đã tham dự một trong những hội nghị Python đầu tiên ở Vương quốc Anh, nơi anh ấy nói về công việc tiến sĩ của mình, cũng là về việc làm cho Python nhanh hơn. Và trong những năm qua, anh ấy chưa bao giờ ngừng nghĩ về nó. Và anh ấy có một thái độ toàn diện về nó. Rõ ràng, kết quả vẫn còn phải chờ xem, nhưng tôi thích những gì anh ấy nói về cách anh ấy nghĩ về nó. Và nếu bạn sử dụng PyPy, có vẻ như PyPy luôn là một giải pháp kỳ diệu mà chỉ một số ít người trên thế giới hiểu được cách thức hoạt động của nó. Và những người đó đã xây dựng nó và sau đó quyết định làm những thứ khác. Và sau đó, họ giao nó cho một nhóm kỹ sư giải quyết các vấn đề thực sự với PyPy, tất cả đều nằm trong lĩnh vực tương thích với các mô-đun mở rộng. Và họ chưa bao giờ thực sự giải quyết được điều đó.

[04. 09] Vì vậy, bạn có thể nhớ rằng đã có một số người sử dụng PyPy tại Dropbox vì có một quy trình nhỏ trong đó ai đó đã phát hiện ra rằng PyPy thực sự nhanh hơn rất nhiều nên đáng giá. Nhưng nó phải chạy theo quy trình nhỏ của riêng nó và không có bảo trì. Và tất nhiên, thật khó để đảm bảo rằng có sẵn một phiên bản PyPy trên mọi máy. Bởi vì đối với ứng dụng Dropbox chính, chúng tôi không bao giờ có thể chuyển sang PyPy vì điều đó phụ thuộc vào 100 mô-đun mở rộng khác nhau. Và chỉ kiểm tra tất cả mã đó sẽ mất mãi mãi.

[04. 49] Tôi nghĩ vì chúng ta đang nói về Dropbox nên Pyston cũng là một ví dụ thú vị. Họ đã thực sự trở lại; . Người Pyston thực dụng hơn nhiều và họ đã học được từ những thất bại của PyPy.

[05. 04] Nhưng họ luôn giữ thái độ này, một lần nữa, “chúng ta sẽ bắt đầu với CPython,” điều này tốt vì theo cách đó họ được đảm bảo khả năng tương thích với các mô-đun mở rộng. Tuy nhiên, họ vẫn thực hiện những thay đổi lớn này, ít nhất là một Pyston, và họ phải hủy bỏ rất nhiều thứ bởi vì, một lần nữa, về các vấn đề tương thích, trong đó tôi nghĩ một trong những thứ, họ có rất nhiều điều thú vị cải tiến bộ sưu tập rác. Tôi nghĩ rằng họ đã loại bỏ việc đếm tài liệu tham khảo, mặc dù. Và do đó, hành vi của nhiều chương trình Python trong thế giới thực đã hoàn toàn thay đổi.

[05. 53] Vậy tại sao tôi nghĩ rằng tác phẩm của Mark sẽ khác biệt hay ý tưởng của Mark? . Và vì vậy, anh ấy biết những gì chúng ta đang chống lại. Anh ấy biết chúng tôi cẩn thận như thế nào với khả năng tương thích ngược. Và anh ấy biết rằng chúng ta không thể chỉ nói loại bỏ việc đếm tham chiếu hoặc thay đổi bố cục đối tượng. Giống như có một dự án được Facebook phát hành gần đây về cơ bản đã chết, hoặc ít nhất là nó đã được tiết lộ với thế giới ở dạng đã chết, CI Python [ Cinder ], đây là cách triển khai Python nhanh hơn đáng kể, nhưng việc sử dụng nhiều loại tối ưu hóa đến từ những thay đổi trong bố cục đối tượng không tương thích với các mô-đun mở rộng. Và Mark đã sắp xếp những ý tưởng này hoạt động trên chính trình thông dịch mã byte.

[06. 58] Bây giờ, mã byte là thứ mà chúng tôi biết rằng nó sẽ không ảnh hưởng quá nhiều đến các mô-đun mở rộng của bên thứ ba nếu chúng tôi thay đổi nó, bởi vì mã byte thay đổi trong mỗi bản phát hành Python. Và nội bộ của trình thông dịch của trình thông dịch mã byte, thay đổi trong mỗi bản phát hành Python. Và vâng, thỉnh thoảng chúng tôi vẫn gặp sự cố. Mỗi lần phát hành, có một số hack bí truyền mà ai đó đang sử dụng đã phá vỡ. Và họ gửi một vấn đề trong trình theo dõi lỗi vì họ không muốn nghiên cứu hoặc họ chưa nghiên cứu chính xác nguyên nhân cốt lõi của vấn đề là gì, bởi vì tất cả những gì họ biết là người dùng của họ nói, “Chương trình của tôi đã hoạt động trong Python 3. 7 và nó đã bị hỏng trong Python 3. 8. Rõ ràng như vậy, Python 3. 8 đã phá vỡ một cái gì đó. ” Và vì nó chỉ bị hỏng khi chúng tôi đang sử dụng Thư viện X, nên có thể đó là lỗi của Thư viện X. Nhưng Thư viện X, những người bảo trì không biết chính xác chuyện gì đang xảy ra vì người dùng chỉ nói rằng nó không hoạt động hoặc cung cấp cho họ truy nguyên hàng nghìn dòng. Và họ đưa nó trở lại Python cốt lõi, và họ nói, “Python 3. 8 đã phá vỡ thư viện của chúng tôi cho tất cả người dùng của chúng tôi hoặc 10% người dùng của chúng tôi,” hoặc bất cứ điều gì.

[08. 16] Và phải mất một thời gian dài để tìm ra, “Ồ, vâng, họ chỉ đang chọc vào bên trong một trong các đối tượng tiêu chuẩn, có thể sử dụng thông tin mà họ thu thập được từ các tiêu đề nội bộ hoặc họ' đang gọi API C bắt đầu bằng dấu gạch dưới. ” Và bạn không được phép làm điều đó. Chà, bạn có thể làm điều đó nhưng sau đó bạn phải trả giá, đó là bạn phải sửa mã của mình ở mỗi lần phát hành Python tiếp theo. Và ở giữa, loại dành cho các bản phát hành sửa lỗi như nếu bạn đi từ 3. 8. 0 đến 3. 8. 1, tất cả các cách lên đến 3. 8. 9, chúng tôi đảm bảo nhiều hơn nữa - mã byte luôn ổn định. nhưng 3. 9 có thể phá vỡ tất cả các bản hack của bạn và nó thay đổi mã byte. Một điều chúng tôi đã làm tôi nghĩ trong 3. 10, tất cả các bước nhảy trong mã byte hiện được tính theo hướng dẫn thay vì byte và hướng dẫn là hai byte. Mặt khác, định dạng hướng dẫn giống nhau, nhưng tất cả các bước nhảy sẽ nhảy một khoảng cách khác nếu bạn không cập nhật mã byte của mình. Và tất nhiên, trình biên dịch Python bytecode biết về điều này. Nhưng những người tạo mã byte của riêng họ như một kiểu hack Python cuối cùng sẽ bị ảnh hưởng.

Utsav. [09. 30] Vì vậy, thách thức lớn nhất cho đến nay là khả năng tương thích ngược.

Hướng dẫn. [09. 34] Lúc nào cũng vậy. Vâng, mọi người đều muốn Python của họ nhanh hơn cho đến khi họ phát hiện ra rằng làm cho nó nhanh hơn cũng phá vỡ một số trường hợp góc trong mã của họ.

utsav. [09. 45] Vì vậy, có lẽ bạn có thể hướng dẫn chúng tôi ý tưởng về các tầng thực thi hoặc các tầng của trình thông dịch Python đã được mô tả trong một số trang trình bày đó

Hướng dẫn. [09. 54] Vâng, đó là một bộ mục tiêu khá tùy ý mà bạn có thể sử dụng cho hầu hết các ngôn ngữ được thông dịch.

Hướng dẫn. [10. 02] Và đó thực sự là một cách hữu ích để suy nghĩ về nó. Và đó là điều mà chúng tôi dự định thực hiện, không phải là hiện tại thực sự có những cấp độ như vậy. Tốt nhất, chúng tôi có hai tầng và chúng không khớp hoàn hảo với những gì bạn đã thấy trong tài liệu đó. Nhưng ý tưởng cơ bản là-- tôi nghĩ điều này cũng được thực hiện trong. NET lõi. Nhưng một lần nữa, tôi không biết liệu đó có phải là một thứ gì đó được ghi lại hay đây chỉ là cách trình tối ưu hóa của họ hoạt động. Vì vậy, khi bạn mới bắt đầu thực hiện một chương trình, bạn không biết liệu nó có bị sập sau khi chạy một phần nghìn giây hay liệu nó sẽ là một quá trình tính toán kéo dài ba tuần hay không. Bởi vì nó có thể là cùng một mã, chỉ trong trường hợp đầu tiên, nó có lỗi. Và vì vậy, nếu phải mất ba tuần để chạy chương trình, có lẽ nên dành nửa giờ trước thời hạn để tối ưu hóa tất cả mã sẽ được chạy. Nhưng rõ ràng, đặc biệt là trong ngôn ngữ động và thứ gì đó như Python, nơi chúng tôi làm nhiều nhất có thể mà không yêu cầu người dùng cho chúng tôi biết chính xác cách họ cần thực hiện, bạn chỉ muốn bắt đầu thực thi mã nhanh nhất có thể. Vì vậy, nếu đó là một tập lệnh nhỏ hoặc một chương trình lớn bị lỗi sớm hoặc thoát sớm vì một lý do chính đáng, bạn sẽ không mất thời gian để bị phân tâm bằng cách tối ưu hóa tất cả mã đó.

[11. 38] Và vì vậy, nếu đây là một ngôn ngữ được biên dịch tĩnh, về cơ bản, người dùng sẽ phải chỉ định điều đó, khi họ chạy trình biên dịch, họ sẽ nói, “Chà, hãy chạy một loại tối ưu hóa cho tốc độ hoặc tối ưu hóa theo thời gian hoặc O2, O3 hoặc có thể tối ưu hóa cho việc gỡ lỗi O0. ”

Trong Python, chúng tôi cố gắng không làm phiền người dùng với những quyết định đó. Vì vậy, bạn phải tạo mã byte trước khi có thể thực thi ngay cả dòng mã đầu tiên. Vì vậy, những gì chúng tôi cố gắng làm ở đó là giữ cho trình biên dịch bytecode đơn giản, giữ cho trình thông dịch bytecode đơn giản, để chúng tôi có thể thực thi phần đầu của mã càng sớm càng tốt. Nếu chúng tôi thấy rằng một số hàm nhất định đang được thực thi nhiều lần, thì chúng tôi gọi đó là hàm hấp dẫn và bạn có thể sắp xếp xác định hàm nào hấp dẫn. Đối với một số mục đích, có thể đó là một chức năng hấp dẫn nếu nó được gọi nhiều hơn một lần hoặc hơn hai lần hoặc hơn 10 lần. Đối với các mục đích khác, bạn muốn thận trọng hơn và bạn có thể nói, “Chà, nó chỉ hấp dẫn nếu nó được gọi 1000 lần. ”

[12. 48] Nhưng dù sao đi nữa, đối với chức năng nóng, bạn muốn thực hiện nhiều công việc hơn. Và do đó, trình biên dịch thích ứng chuyên dụng, tại thời điểm đó, cố gắng thay thế một số mã byte nhất định bằng mã byte nhanh hơn, nhưng chỉ hoạt động nếu các loại đối số là các loại cụ thể. Một ví dụ đơn giản nhưng khá giả thuyết là ít nhất toán tử cộng trong Python, có thể thêm rất nhiều thứ. Nó có thể thêm số nguyên, nó có thể thêm số float, nó có thể thêm chuỗi, nó có thể liệt kê hoặc bộ dữ liệu. Mặt khác, bạn không thể thêm một số nguyên vào một chuỗi, chẳng hạn. Vì vậy, những gì chúng tôi làm ở đó, bước tối ưu hóa - và nó còn được gọi là tăng tốc, nhưng thường trong ngữ cảnh của chúng tôi, chúng tôi gọi nó là chuyên biệt hóa - là chúng tôi có một mã bytecode thêm số nguyên nhị phân riêng biệt. Và đó là một loại mã byte cấp hai được ẩn khỏi người dùng. Nếu người dùng yêu cầu tháo gỡ hàm của họ, họ sẽ không bao giờ thấy số nguyên cộng nhị phân, họ cũng sẽ luôn chỉ thấy phép cộng nhị phân. Nhưng những gì trình thông dịch nhìn thấy khi chức năng đã được tăng tốc, trình thông dịch có thể thấy các số nguyên cộng nhị phân. Và số nguyên cộng nhị phân chỉ giả định rằng cả hai đối số của nó, đó là cả hai số trên ngăn xếp, là các đối tượng số nguyên Python thực tế. Nó chỉ truy cập trực tiếp vào các đối tượng đó để tìm các giá trị, cộng các giá trị đó lại với nhau trong các thanh ghi máy và đẩy kết quả trở lại ngăn xếp.

[14. 35] Hiện nay, có đủ thứ khiến điều đó trở nên khó thực hiện. Ví dụ: nếu giá trị không khớp với thanh ghi kết quả hoặc một trong các giá trị đầu vào hoặc có thể mặc dù bạn đã mong đợi nó sẽ cộng hai số nguyên, thì lần này nó sẽ cộng vào một số nguyên và một dấu phẩy động hoặc thậm chí có thể là hai chuỗi.

[15. 00] Vì vậy, giai đoạn đầu tiên của chuyên môn hóa thực sự là… Tôi đang bỏ trống thuật ngữ này, nhưng có một bước trung gian trong đó chúng tôi ghi lại các loại đối số. Và trong bước trung gian đó, mã byte thực thi chậm hơn một chút so với mã byte mặc định. Nhưng điều đó chỉ xảy ra với một vài lần thực thi hàm vì sau đó nó biết chỗ này luôn được gọi với số nguyên trên ngăn xếp, chỗ này luôn được gọi với chuỗi trên ngăn xếp và có thể chỗ này, chúng ta vẫn chưa biết hoặc nó một túi hỗn hợp. Và sau đó, cái mà mỗi lần nó được gọi trong giai đoạn ghi này, nó là hai số nguyên, chúng tôi thay thế nó bằng phép toán cộng số nguyên nhị phân đó. Hàm nhị phân thêm phép toán số nguyên, sau đó, trước khi tiếp cận đối tượng, vẫn phải thực hiện kiểm tra kiểu trên các đối số. Vì vậy, nó không hoàn toàn miễn phí nhưng kiểm tra loại có thể được triển khai nhanh hơn nhiều so với một loại công văn hướng đối tượng hoàn toàn chung chung, giống như những gì thường xảy ra đối với các hoạt động thêm nhị phân chung chung nhất.

[16. 14] Vì vậy, khi chúng tôi đã ghi lại các loại, chúng tôi chuyên biệt hóa nó dựa trên các loại và sau đó trình thông dịch sẽ đặt các bảo vệ. Vì vậy, mã trình thông dịch cho lệnh chuyên biệt có các bộ phận bảo vệ kiểm tra xem tất cả các điều kiện sẽ làm cho lệnh chuyên ngành hoạt động có thực sự được đáp ứng hay không. Nếu một trong các điều kiện không được đáp ứng, nó sẽ không bị lỗi, nó sẽ thực thi mã byte gốc. Thế nên, thà rơi vào con đường chậm chạp còn hơn là thất bại. Đó là một phần quan trọng để bạn vẫn có đầy đủ ngữ nghĩa Python. Và luôn có khả năng một hàm được gọi hàng trăm hoặc hàng triệu lần với các đối số nguyên, rồi đột nhiên một phần dữ liệu gọi nó bằng một đối số dấu phẩy động hoặc điều gì đó tệ hơn. Và ngữ nghĩa vẫn nói, “Chà, vậy thì nó liên quan đến cách dấu phẩy động.

Utsav. [17. 12] Theo một nghĩa nào đó, nó phải hủy tối ưu hóa.

Hướng dẫn. [17. 14] Vâng. Và có nhiều bộ đếm khác nhau trong tất cả các cơ chế, nếu bạn gặp phải thứ gì đó làm hỏng bộ bảo vệ một lần, điều đó sẽ không giải phóng toàn bộ hướng dẫn. Nhưng nếu bạn tiếp tục gặp phải sự không phù hợp của các lính canh, thì cuối cùng, hướng dẫn chuyên biệt sẽ bị hủy tối ưu hóa và chúng tôi quay lại, “Ồ, vâng, chúng ta sẽ làm theo cách chậm vì cách chậm rõ ràng là nhanh nhất, chúng tôi có thể làm. ”

Utsav. [17. 45] Nó giống như dự đoán nhánh.

Hướng dẫn. [17. 47] Tôi ước mình biết điều gì đã xảy ra trong các CPU hiện đại khi nói đến dự đoán nhánh và bộ nhớ đệm nội tuyến vì đó là điều kỳ diệu tuyệt đối. Và đó thực sự là một trong những điều chúng tôi gặp khó khăn với dự án này, bởi vì chúng tôi viết mã theo cách mà chúng tôi nghĩ rằng rõ ràng sẽ giảm số lần truy cập bộ nhớ, chẳng hạn. Và khi chúng tôi đánh giá nó, chúng tôi thấy rằng nó chạy nhanh như mã cũ chưa được tối ưu hóa bởi vì CPU đã tìm ra nó mà không cần bất kỳ sự trợ giúp nào của chúng tôi.

utsav. [18. 20] Vâng. Ý tôi là, những kỹ thuật này, bạn thường nghe thấy chúng trong ngữ cảnh của JIT, một trình biên dịch Just-In-Time, nhưng hiện tại bạn không thực hiện điều đó

Hướng dẫn. [18. 30] JIT giống như, vâng, trong trường hợp của chúng tôi, nó sẽ là một cách gọi sai. Những gì chúng tôi mong đợi cuối cùng sẽ được thực hiện, ngoài việc chuyên môn hóa, chúng tôi có thể tạo mã máy. Điều đó có lẽ sẽ được cũng qua 3. 11, có thể quá 3. 12. Vì vậy, bản phát hành mà chúng tôi vẫn còn cho đến tháng 10 năm sau sẽ là 3. 11, và đó là nơi các phiên dịch viên chuyên nghiệp sẽ thực hiện mục đầu tiên. Tôi không nghĩ rằng chúng ta sẽ làm bất cứ điều gì với mã máy trừ khi chúng ta cực kỳ may mắn với kết quả của mình vào giữa năm. Nhưng cuối cùng, đó sẽ là một cấp độ khác. Nhưng tôi không biết, bộ sưu tập Just-In-Time có cả đống hành lý cảm xúc vào thời điểm này mà chúng tôi đang cố gắng tránh.

Utsav. [19. 25] Có phải hành lý từ các dự án khác đang thử không?

Hướng dẫn. [19. 29] Mọi người cho rằng việc biên dịch Just-In-Time sẽ tự động làm cho mã của bạn tốt hơn. Hóa ra nó không đơn giản. Trong trường hợp của chúng tôi, quá trình biên dịch giống như, "Chính xác thì chúng tôi biên dịch cái gì?" . Sau đó, chúng tôi dịch mã byte thành mã byte chuyên dụng. Ý tôi là, mọi thứ xảy ra tại một thời điểm nào đó trong thời gian chạy, vậy bạn sẽ gọi cái gì là Just-In-Time?

Hướng dẫn. [hai mươi. 04] Vì vậy, tôi không thích sử dụng thuật ngữ đó. Và nó thường khiến mọi người liên tưởng đến những kỳ công tối ưu hóa kỳ diệu đã được cộng đồng Java quảng cáo trong một thời gian dài. Và thật không may, điều kỳ diệu thường đến mức bạn không thể thực sự dự đoán hiệu suất mã của mình sẽ như thế nào. Và chúng ta có đủ điều đó, ví dụ, với các CPU hiện đại và khả năng dự đoán nhánh tuyệt vời của chúng.

utsav. [hai mươi. 35] Nói về điều đó, tôi thấy rằng cũng có rất nhiều chiến thắng nhỏ mà bạn đã nói đến, mà bạn có thể sử dụng để cải thiện hiệu suất, những thứ như sửa vị trí của __dict__ trong các đối tượng và thay đổi cách biểu diễn số nguyên. Điều gì có thể chỉ là một câu chuyện thú vị ra khỏi đó?

Hướng dẫn. [hai mươi. 53] Chà, tôi muốn nói rằng việc gọi các hàm Python là thứ mà chúng tôi đang thực sự nghiên cứu. Và tôi phải nói rằng đây không chỉ là nhóm của Microsoft mà còn có những người khác trong nhóm nhà phát triển cốt lõi, những người rất hào hứng với điều này và giúp đỡ chúng tôi bằng nhiều cách. Vì vậy, ý tưởng là trong trình thông dịch Python, bao gồm cả phiên bản 3. 10, sẽ được phát hành vào tuần tới, trên thực tế, bất cứ khi nào bạn gọi một hàm Python, điều đầu tiên bạn làm là tạo một đối tượng khung. Và một đối tượng khung chứa một loạt trạng thái dành riêng cho cuộc gọi mà bạn đang thực hiện. Vì vậy, nó trỏ tới đối tượng mã đại diện cho hàm đang được gọi, nó trỏ tới toàn cục, nó có khoảng trống cho các biến cục bộ của lệnh gọi, nó có khoảng trống cho các đối số, nó có khoảng trống cho các giá trị ẩn danh . trên ngăn xếp đánh giá. Nhưng điều quan trọng là nó vẫn là một đối tượng Python. Và có một số trường hợp sử dụng mà mọi người thực sự kiểm tra các đối tượng khung Python, chẳng hạn, nếu họ muốn làm những thứ kỳ lạ với các biến cục bộ.

[22. 18] Bây giờ, nếu bạn là người sửa lỗi, bạn hoàn toàn muốn xem xét tất cả các biến cục bộ trong khung này là gì? . Đó là tất cả tuyệt vời. Nhưng để thực thi hầu hết mã, chắc chắn là hầu hết thời gian, khi bạn không sử dụng trình gỡ lỗi, không có lý do gì mà khung đó cần phải là một đối tượng Python. Bởi vì một đối tượng Python có tiêu đề, nó có số lượng tham chiếu, nó có một loại, nó được phân bổ dưới dạng phân đoạn bộ nhớ nhỏ của riêng nó trên heap. Tất cả đều khá kém hiệu quả. Ngoài ra, nếu bạn gọi một hàm, sau đó bạn tạo một vài đối tượng, rồi từ hàm đó, bạn gọi một hàm khác, tất cả các đối tượng khung đó sẽ nằm rải rác trong toàn bộ đống chương trình.

[23. 17] Những gì chúng tôi đã triển khai trong phiên bản 3 của chúng tôi. 11, hiện chỉ là nhánh chính của repo CPython, là một sơ đồ phân bổ trong đó khi chúng ta gọi một hàm, chúng ta vẫn tạo thứ gì đó chứa khung, nhưng chúng ta phân bổ thứ đó trong một mảng cấu trúc khung. Vì vậy, tôi không thể gọi chúng là các đối tượng khung vì chúng không có tiêu đề đối tượng, chúng không có số lượng hoặc loại tham chiếu, nó chỉ là một mảng cấu trúc. Điều này có nghĩa là trừ khi mảng đó hết dung lượng, các cuộc gọi có thể nhanh hơn một chút vì bạn không nhảy xung quanh đống. Và kiểu phân bổ là phân bổ khung tiếp theo, bạn so sánh hai con trỏ, sau đó bạn chạm vào một bộ đếm, và bây giờ bạn có một cấu trúc khung mới. Và do đó, việc tạo và phân bổ khung cũng nhanh hơn. Khung hình nhỏ hơn vì bạn không có tiêu đề đối tượng. Bạn cũng không có chi phí malloc hoặc chi phí thu gom rác. Và tất nhiên, nó không tương thích ngược. Vậy chúng ta làm gì bây giờ? . Và những gì chúng tôi làm là khi mọi người gọi một API trả về một đối tượng khung, chúng tôi nói, “Được rồi, chắc chắn rồi. Đây là khung trong mảng của chúng tôi. Bây giờ chúng tôi sẽ cấp phát một đối tượng và chúng tôi sẽ sao chép một số giá trị vào đối tượng khung,” và chúng tôi đưa giá trị đó vào mã Python. Vì vậy, bạn vẫn có thể xem xét nội tâm và bạn có thể nhìn người dân địa phương như thể không có gì thay đổi.

[25. 04] Nhưng hầu hết thời gian, mọi người không xem xét thêm khung. Và đây thực sự là một tối ưu hóa cũ. Tôi nhớ rằng ý tưởng tương tự đã tồn tại trong IronPython. Và họ đã làm khác đi. Tôi nghĩ đối với họ, nó giống như một lựa chọn thời gian biên dịch khi mã byte tương đương trong IronPython được tạo cho một hàm, nó sẽ tự động đưa ra lựa chọn phân bổ một đối tượng khung hay chỉ một cấu trúc khung cho lệnh gọi đó. Và lỗi lớn của họ là, có một chức năng mà bạn có thể gọi là sys dunder __getFrame__ và nó chỉ cung cấp cho bạn đối tượng khung. Vì vậy, trong trình biên dịch, họ đang tìm kiếm, bạn có đang sử dụng chính xác thứ có tên system dunder __getFrame__ không và sau đó họ sẽ nói, “Ồ, đó là getFrame, bây giờ chúng tôi sẽ biên dịch cho bạn chậm hơn một chút nên bạn sử dụng một đối tượng khung. ” Chúng tôi có lợi thế là luôn có thể phân bổ đối tượng khung một cách nhanh chóng. Nhưng chúng tôi nhận được lợi ích tương tự. Và vâng, tôi đã đề cập rằng các đối tượng khung được phân bổ trong mảng, điều gì xảy ra nếu mảng đó hết? . Vì vậy, chúng tôi vẫn có thể tạo một mảng khung mới, chẳng hạn như chúng tôi có khoảng trống cho 100 hoặc hơn, trong nhiều chương trình, như vậy là rất nhiều. Và nếu ngăn xếp cuộc gọi của bạn sâu hơn 100, chúng tôi sẽ chỉ có một điểm gián đoạn, nhưng ngữ nghĩa vẫn giống nhau và chúng tôi vẫn có hầu hết các lợi ích.

Utsav. [26. 39] Vâng, và có thể là một câu hỏi tổng kết, có rất nhiều cải tiến khác đang diễn ra trong cộng đồng Python về hiệu suất, phải không? . Có bất kỳ cải tiến nào khác giống như vậy mà bạn hào hứng hay chỉ muốn theo dõi không?

Hướng dẫn. [27. 01] Chà, Mypyc rất thú vị. Nó giúp tăng hiệu suất tốt hơn nhiều, nhưng chỉ khi bạn chú thích đầy đủ mã của mình và chỉ khi bạn thực sự làm theo các chú thích một cách chính xác trong thời gian chạy. Trong Mypy, nếu bạn nói, "Hàm này nhận hai số nguyên," và nó trả về một số nguyên, sau đó nếu bạn gọi nó bằng một thứ khác, nó sẽ nổ tung ngay lập tức. Nó sẽ cung cấp cho bạn một dấu vết. Nhưng ngữ nghĩa Python tiêu chuẩn là các chú thích loại là tùy chọn và đôi khi chúng là những lời nói dối trắng trợn. Và do đó, các loại mà bạn thấy trong thời gian chạy có thể không thực sự tương thích với các loại được chỉ định trong chú thích. Và nó không ảnh hưởng đến cách chương trình của bạn thực thi. Trừ khi bạn bắt đầu xem xét nội tâm các chú thích, chương trình của bạn sẽ chạy chính xác như vậy dù có hoặc không có chú thích.

[28. 05] Ý tôi là, có một vài lỗ hổng lớn trong hệ thống loại, giống như bất kỳ. Và người kiểm tra loại sẽ nói, “Ồ, nếu bạn đặt bất kỳ thứ gì, mọi thứ sẽ ổn thôi. ” Và vì vậy, sử dụng điều đó, rất dễ xảy ra trường hợp có thứ gì đó được thông qua, đối tượng thuộc loại không hợp lệ và trình kiểm tra loại sẽ không bao giờ phàn nàn về điều đó. Và lời hứa của chúng tôi là thời gian chạy sẽ không phàn nàn về điều đó trừ khi đó thực sự là lỗi thời gian chạy. Rõ ràng, nếu bạn bằng cách nào đó thêm một số nguyên vào một chuỗi trong thời gian chạy, thì đó vẫn sẽ là một vấn đề. Nhưng nếu bạn có một hàm, chẳng hạn, tính ước chung lớn nhất của hai số, đó là vòng lặp nhỏ thực sự dễ thương này, nếu bạn định nghĩa toán tử phần trăm đúng cách, bạn có thể chuyển vào bất kỳ thứ gì. Tôi nghĩ rằng có những ví dụ mà bạn thực sự có thể chuyển nó thành chuỗi và nó sẽ trả về một chuỗi mà không bao giờ bị lỗi.

[29. 07] Và về cơ bản, Mypyc thực hiện những việc như thuộc tính thể hiện luôn được thể hiện theo cách nhỏ gọn, không có sự lộn xộn __dict__. Điều tốt nhất mà chúng tôi có thể làm, mà chúng tôi đang nghiên cứu để thiết kế cách chúng tôi thực sự sẽ làm điều đó, là làm cho nó sao cho nếu bạn không nhìn vào thuộc tính __dict__ dunder, thì chúng tôi không nhất thiết phải lưu trữ phiên bản các thuộc tính trong từ điển miễn là chúng ta bảo toàn ngữ nghĩa chính xác. Nhưng nếu bạn sử dụng dunder __dict__, thì một lần nữa, giống như các đối tượng khung, chúng ta phải cụ thể hóa một từ điển. Và Mypyc không làm điều đó. Nó siêu nhanh nếu bạn không sử dụng dunder __dict__. Nếu bạn sử dụng dunder __dict__, nó chỉ nói, “dunder __dict__ không được hỗ trợ trong trường hợp này. ”

[29. 59] Mypyc thực sự chỉ biên dịch một tập hợp con nhỏ của ngôn ngữ Python. Và thật tuyệt nếu đó là tập hợp con mà bạn quan tâm. Nhưng tôi chắc rằng bạn có thể tưởng tượng nó phức tạp như thế nào trong thực tế đối với một chương trình lớn.

Utsav. [30. 17] Nó làm tôi nhớ đến hiệu suất của JavaScript khi mọi thứ đang hoạt động nhanh và sau đó bạn sử dụng một chức năng này, chức năng mà lẽ ra bạn không nên sử dụng để xem xét nội quan một đối tượng hoặc thứ gì đó, và sau đó hiệu suất sẽ bị hỏng xuống.

Hướng dẫn. [30. 29] Vâng, điều đó sẽ xảy ra.

Utsav. [30. 31] Nhưng nó vẫn cực kỳ thú vị. Và tôi cũng vô cùng biết ơn vì Python thất bại lớn khi bạn cố gắng thêm một số vào chuỗi, không giống như JavaScript,

Hướng dẫn. [30. 41] Hoặc PHP hoặc Perl.

Utsav. [30. 44] Nhưng vâng, cảm ơn bạn rất nhiều vì đã là khách. Tôi nghĩ rằng điều này là rất nhiều niềm vui. Và tôi nghĩ rằng nó đã cải thiện hiệu suất mà tất cả các bạn đang cố gắng thực hiện theo cách dễ tiếp cận. Vì vậy, tôi nghĩ rằng nó sẽ hữu ích cho nhiều người. Vâng, cảm ơn bạn đã là khách.

Chủ Đề