Một ngôn ngữ động như Ruby / Python có thể đạt C / C ++ như hiệu suất không?


64

Tôi tự hỏi liệu có thể xây dựng trình biên dịch cho các ngôn ngữ động như Ruby để có hiệu suất tương tự và có thể so sánh với C / C ++ không? Từ những gì tôi hiểu về trình biên dịch, lấy ví dụ về Ruby, biên dịch mã Ruby không bao giờ hiệu quả vì cách Ruby xử lý sự phản chiếu, các tính năng như chuyển đổi loại tự động từ số nguyên sang số nguyên lớn và thiếu gõ tĩnh khiến việc xây dựng trình biên dịch hiệu quả đối với Ruby vô cùng khó khăn.

Có thể xây dựng trình biên dịch có thể biên dịch Ruby hoặc bất kỳ ngôn ngữ động nào khác thành nhị phân thực hiện rất gần với C / C ++ không? Có một lý do cơ bản tại sao các trình biên dịch JIT, như PyPy / Rubinius cuối cùng sẽ hoặc không bao giờ phù hợp với C / C ++ trong hiệu suất?

Lưu ý: Tôi thực sự hiểu rằng hiệu suất của mối quan hệ có thể mơ hồ, vì vậy để làm rõ điều đó, ý tôi là, nếu bạn có thể làm X trong C / C ++ với hiệu suất Y, bạn có thể làm X trong Ruby / Python với hiệu suất gần với Y không? Trong đó X là tất cả mọi thứ, từ trình điều khiển thiết bị và mã hệ điều hành, đến các ứng dụng web.


1
Bạn có thể viết lại câu hỏi để nó khuyến khích các câu trả lời được hỗ trợ bởi bằng chứng thích hợp hơn những người khác không?
Raphael

@Raphael Tôi đã đi trước và chỉnh sửa. Tôi nghĩ rằng chỉnh sửa của tôi về cơ bản không thay đổi ý nghĩa của câu hỏi, nhưng làm cho nó ít mời hơn các ý kiến.
Gilles 'SO- ngừng trở nên xấu xa'

1
Cụ thể, tôi nghĩ bạn cần sửa một (hoặc một vài) biện pháp thực hiện cụ thể. Thời gian chạy? Sử dụng không gian? Sử dụng năng lượng? Thời gian phát triển? Lợi nhuận đầu tư? Cũng lưu ý câu hỏi này trên meta của chúng tôi liên quan đến câu hỏi này (hay đúng hơn là câu trả lời của nó).
Raphael

Câu hỏi này là một người khởi xướng điển hình của các cuộc chiến tôn giáo. Và như chúng ta có thể thấy từ các câu trả lời, chúng ta đang có một, mặc dù rất văn minh.
Andrej Bauer

Có các ngôn ngữ động cho phép chú thích loại tùy chọn (ví dụ: Clojure). Từ những gì tôi biết hiệu suất liên quan đến các hàm được chú thích kiểu tương đương với khi một ngôn ngữ sẽ được gõ tĩnh.
Pedro Morte Rolo

Câu trả lời:


68

Đối với tất cả những người đã nói rằng, vâng, tôi sẽ đưa ra một điểm phản biện rằng câu trả lời là không có, theo thiết kế . Những ngôn ngữ đó sẽ không bao giờ có thể phù hợp với hiệu suất của các ngôn ngữ được biên dịch tĩnh.

Kos đưa ra quan điểm (rất hợp lệ) rằng các ngôn ngữ động có nhiều thông tin hơn về hệ thống trong thời gian chạy có thể được sử dụng để tối ưu hóa mã.

Tuy nhiên, có một mặt khác của đồng tiền: thông tin bổ sung này cần được theo dõi. Trên các kiến ​​trúc hiện đại, đây là một kẻ giết người hiệu suất.

William Edwards cung cấp một cái nhìn tổng quan tốt đẹp của cuộc tranh luận .

Cụ thể, các tối ưu hóa được đề cập bởi Kos không thể được áp dụng vượt quá phạm vi rất hạn chế trừ khi bạn giới hạn sức mạnh biểu cảm của ngôn ngữ của bạn khá mạnh mẽ, như Devin đã đề cập. Tất nhiên đây là một sự đánh đổi khả thi nhưng vì lợi ích của cuộc thảo luận, sau đó bạn kết thúc bằng một ngôn ngữ tĩnh , không phải là một ngôn ngữ động. Những ngôn ngữ đó khác nhau cơ bản với Python hoặc Ruby vì hầu hết mọi người sẽ hiểu chúng.

William trích dẫn một số slide thú vị của IBM :

  • Mỗi biến có thể được gõ động: Cần kiểm tra loại
  • Mọi tuyên bố đều có khả năng đưa ra các ngoại lệ do loại không khớp và v.v.: Cần kiểm tra ngoại lệ
  • Mọi trường và ký hiệu có thể được thêm, xóa và thay đổi khi chạy: Cần kiểm tra truy cập
  • Kiểu của mọi đối tượng và hệ thống phân cấp lớp của nó có thể được thay đổi trong thời gian chạy: Cần kiểm tra phân cấp lớp

Một số kiểm tra có thể được loại bỏ sau khi phân tích (NB: phân tích này cũng mất thời gian - khi chạy).

Hơn nữa, Kos lập luận rằng các ngôn ngữ động thậm chí có thể vượt qua hiệu suất C ++. JIT thực sự có thể phân tích hành vi của chương trình và áp dụng các tối ưu phù hợp.

Nhưng trình biên dịch C ++ có thể làm tương tự! Các trình biên dịch hiện đại cung cấp cái gọi là tối ưu hóa hướng dẫn hồ sơ, nếu chúng được cung cấp đầu vào phù hợp, có thể mô hình hóa hành vi thời gian chạy chương trình và áp dụng các tối ưu hóa tương tự mà JIT sẽ áp dụng.

Tất nhiên, tất cả điều này đều xoay quanh sự tồn tại của dữ liệu đào tạo thực tế và hơn nữa chương trình không thể điều chỉnh các đặc điểm thời gian chạy của nó nếu mô hình sử dụng thay đổi giữa chừng. JIT về lý thuyết có thể xử lý việc này. Tôi rất muốn xem giá vé này trong thực tế như thế nào, vì, để chuyển đổi tối ưu hóa, JIT sẽ liên tục phải thu thập dữ liệu sử dụng một lần nữa làm chậm việc thực thi.

Tóm lại, tôi không tin rằng tối ưu hóa điểm nóng thời gian chạy vượt xa chi phí theo dõi thông tin thời gian chạy trong thời gian dài , so với phân tích tĩnh và tối ưu hóa.


2
@Raphael Đó là một trình biên dịch thiếu sót của người biên soạn. Cụ thể, đã javacbao giờ tối ưu hóa hướng dẫn hồ sơ? Không xa như tôi biết. Nói chung, không có ý nghĩa gì để làm cho trình biên dịch của ngôn ngữ JITted tối ưu hóa tốt vì JIT có thể xử lý nó (và ít nhất, theo cách này, nhiều ngôn ngữ sẽ thu được lợi nhuận từ nỗ lực này). Vì vậy (có thể hiểu được) không bao giờ có nhiều nỗ lực vào trình javactối ưu hóa, theo như tôi biết (đối với các ngôn ngữ .NET thì điều này hoàn toàn đúng).
Konrad Rudolph

1
@Raphael Từ khóa là: có thể. Nó cũng không hiển thị. Đó là tất cả những gì tôi muốn nói. Tôi đã đưa ra lý do (nhưng không có bằng chứng) cho giả định của tôi trong các đoạn trước.
Konrad Rudolph

1
@Ben Tôi không phủ nhận rằng nó phức tạp. Đây chỉ là một trực giác. Rốt cuộc, theo dõi tất cả thông tin đó trong thời gian chạy có chi phí. Tôi không bị thuyết phục bởi quan điểm của bạn về IO. Nếu đây là dự đoán (= trường hợp sử dụng điển hình), thì PGO có thể dự đoán nó. Nếu nó giả, tôi cũng không tin rằng JIT có thể tối ưu hóa nó. Có thể thỉnh thoảng, hết may mắn. Nhưng đáng tin cậy? Tiết
Konrad Rudolph

2
@Konrad: Trực giác của bạn là sai. Nó không phải là về sự thay đổi trong thời gian chạy, mà là về sự khó đoán trong thời gian biên dịch . Điểm hay của JIT so với tối ưu hóa tĩnh không phải là khi hành vi của chương trình thay đổi khi chạy "quá nhanh" để định hình, đó là khi hành vi của chương trình dễ dàng tối ưu hóa trong mỗi lần chạy chương trình riêng lẻ, nhưng khác nhau giữa chạy. Trình tối ưu hóa tĩnh nói chung sẽ phải tối ưu hóa chỉ cho một tập hợp các điều kiện, trong khi đó JIT tối ưu hóa từng hoạt động riêng biệt, cho các điều kiện xảy ra trong lần chạy đó.
Ben

3
Lưu ý: Chủ đề bình luận này đang phát triển thành một phòng trò chuyện nhỏ. Nếu bạn muốn tiếp tục cuộc thảo luận này, vui lòng mang nó đến trò chuyện. Bình luận nên được sử dụng để cải thiện bài gốc. Hãy ngừng cuộc trò chuyện này ở đây. Cảm ơn.
Robert Cartaino

20

nếu bạn có thể làm X trong C / C ++ với hiệu suất Y, bạn có thể làm X trong Ruby / Python với hiệu suất gần với Y không?

Đúng. Lấy một ví dụ, PyPy. Nó là một tập hợp mã Python thực hiện gần với C khi thực hiện giải thích (không phải tất cả đều gần, nhưng cũng không quá xa). Nó thực hiện điều này bằng cách thực hiện phân tích chương trình đầy đủ trên mã nguồn để gán cho mỗi biến một kiểu tĩnh (xem tài liệu Chú thíchRtyper để biết chi tiết), và sau đó, khi được trang bị thông tin cùng loại bạn cung cấp cho C, nó có thể thực hiện tương tự các loại tối ưu hóa. Ít nhất là trong lý thuyết.

Tất nhiên, sự đánh đổi là chỉ một tập hợp con mã Python được RPython chấp nhận và nói chung, ngay cả khi hạn chế đó được gỡ bỏ, chỉ một tập hợp con của mã Python có thể làm tốt: tập hợp con có thể được phân tích và đưa ra các loại tĩnh.

Nếu bạn hạn chế Python đủ, trình tối ưu hóa có thể được xây dựng có thể tận dụng tập hợp con bị hạn chế và biên dịch nó thành mã hiệu quả. Đây thực sự không phải là một lợi ích thú vị, trên thực tế, nó đã được biết đến. Nhưng toàn bộ vấn đề sử dụng Python (hoặc Ruby) ở nơi đầu tiên là chúng tôi muốn sử dụng các tính năng thú vị mà có lẽ không phân tích tốt và mang lại hiệu suất tốt! Vì vậy, câu hỏi thú vị thực sự là ...

Ngoài ra, các trình biên dịch JIT, chẳng hạn như PyPy / Rubinius có bao giờ khớp với C / C ++ về hiệu suất không?

Không

Ý tôi là: chắc chắn, có thể khi mã chạy tích lũy, bạn có thể nhận đủ thông tin gõ và đủ các điểm nóng để biên dịch tất cả mã cho đến mã máy. Và có lẽ chúng ta có thể làm điều này để thực hiện tốt hơn C đối với một số mã. Tôi không nghĩ đó là tranh cãi lớn. Nhưng nó vẫn phải "khởi động" và hiệu suất vẫn khó dự đoán hơn một chút và sẽ không tốt bằng C hoặc C ++ đối với một số tác vụ yêu cầu hiệu suất cao nhất quán và có thể dự đoán được.

Dữ liệu hiệu suất hiện có cho Java, có cả thông tin loại hơn Python hoặc Ruby và trình biên dịch JIT được phát triển tốt hơn Python hoặc Ruby, vẫn không khớp với C / C ++. Đó là, tuy nhiên, trong cùng một sân bóng.


1
"Tất nhiên là sự đánh đổi là chỉ một tập hợp con của mã Python được chấp nhận, hay đúng hơn, chỉ một tập hợp con của mã Python có thể làm tốt: tập hợp con có thể được phân tích và đưa ra các loại tĩnh." Điều này không hoàn toàn chính xác. Chỉ có thể chấp nhận tập hợp con bởi trình biên dịch RPython. Việc biên dịch thành RPython thành C hiệu quả hợp lý chỉ hoạt động chính xác vì các phần khó biên dịch của Python được đảm bảo không bao giờ xảy ra trong các chương trình RPython; Không chỉ đơn thuần là nếu chúng xảy ra, chúng sẽ không được tối ưu hóa. Đó là trình thông dịch được biên dịch xử lý tất cả Python.
Ben

1
Về JIT: Tôi đã thấy nhiều hơn một điểm chuẩn trong đó Java vượt xa một số hương vị của C (++). Chỉ có C ++ với boost dường như luôn ở phía trước một cách đáng tin cậy. Trong trường hợp đó, tôi tự hỏi về hiệu suất theo thời gian của nhà phát triển, nhưng đó là một chủ đề khác.
Raphael

@Ben: một khi bạn có RPython, việc tạo một trình biên dịch / trình thông dịch quay lại sử dụng trình thông dịch CPython là không đáng kể, do đó "chỉ một tập hợp con mã Python có thể làm tốt: ..." là hoàn toàn chính xác.
Lie Ryan

9
@Raphael Nó được chứng minh nhiều lần rằng cũng bằng văn bản mã C ++ nhanh hơn so với Java. Mặc dù đó là phần "được viết tốt" khá khó để có được trong C ++, do đó, trong nhiều băng ghế, bạn thấy kết quả mà Java vượt trội hơn C ++. Do đó, C ++ đắt hơn, nhưng khi cần kiểm soát mem chặt chẽ và grit kim loại, C / C ++ là thứ bạn hướng đến. C nói riêng chỉ là trình biên dịch ac / p.
TC1

7
So sánh hiệu suất tối đa của các ngôn ngữ khác với các ngôn ngữ như C / C ++ là một bài tập vô ích, vì bạn có thể lắp ráp nội tuyến trực tiếp như một phần của thông số ngôn ngữ. Bất cứ điều gì mà máy có thể thực hiện khi chạy một chương trình được viết bằng bất kỳ ngôn ngữ nào, bạn có thể sao chép tệ nhất bằng cách viết cụm từ một dấu vết của các lệnh được thực thi. Một số liệu thú vị hơn sẽ là, như @Raphael gợi ý trong một nhận xét trước đó, hiệu suất trên mỗi nỗ lực phát triển (giờ làm việc, dòng mã, v.v.).
Patrick87

18

Câu trả lời ngắn gọn là: chúng ta không biết , hãy hỏi lại sau 100 năm nữa. (Chúng ta có thể vẫn chưa biết sau đó; có thể chúng ta sẽ không bao giờ biết.)

Về lý thuyết, điều này là có thể. Lấy tất cả các chương trình đã từng được viết, dịch thủ công chúng sang mã máy hiệu quả nhất có thể và viết một trình thông dịch ánh xạ mã nguồn thành mã máy. Điều này là có thể vì chỉ có một số lượng chương trình hữu hạn đã được viết (và khi nhiều chương trình được viết, hãy theo kịp các bản dịch thủ công). Tất nhiên, điều này cũng hoàn toàn ngu ngốc về các điều khoản thực tế.

Sau đó, một lần nữa, theo lý thuyết, các ngôn ngữ cấp cao có thể đạt được hiệu suất của mã máy, nhưng chúng sẽ không vượt qua nó. Điều này vẫn còn rất lý thuyết, bởi vì trong điều kiện thực tế, chúng tôi rất hiếm khi sử dụng để viết mã máy. Đối số này không áp dụng để so sánh các ngôn ngữ cấp cao hơn: nó không ngụ ý rằng C phải hiệu quả hơn Python, chỉ có điều mã máy không thể làm tồi hơn Python.

Đến từ phía bên kia, về các thuật ngữ thử nghiệm thuần túy, chúng ta có thể thấy rằng hầu hết thời gian , các ngôn ngữ cấp cao được giải thích hoạt động kém hơn so với các ngôn ngữ cấp thấp được biên dịch. Chúng tôi có xu hướng viết mã không nhạy cảm với thời gian bằng các ngôn ngữ cấp cao và các vòng lặp bên trong quan trọng về thời gian trong tập hợp, với các ngôn ngữ như C và Python nằm ở giữa. Mặc dù tôi không có bất kỳ số liệu thống kê nào để sao lưu điều này, tôi nghĩ rằng đây thực sự là quyết định tốt nhất trong hầu hết các trường hợp.

Tuy nhiên, có những trường hợp không thể kiểm chứng trong đó các ngôn ngữ cấp cao đánh bại mã mà người ta sẽ viết một cách thực tế: môi trường lập trình có mục đích đặc biệt. Các chương trình như Matlab và Mathematica thường giải quyết tốt hơn các loại vấn đề toán học nhất định so với những gì người phàm có thể viết. Các chức năng của thư viện có thể đã được viết bằng C hoặc C ++ (vốn là nhiên liệu đối với các ngôn ngữ cấp thấp có hiệu quả hơn trại hè), nhưng đó không phải là việc của tôi nếu tôi viết mã Mathicala, thư viện là một hộp đen.

Về mặt lý thuyết có thể là Python sẽ tiến gần hơn, hoặc thậm chí gần hơn, với hiệu suất tối ưu hơn C? Như đã thấy ở trên, có, nhưng chúng ta rất xa ngày hôm nay. Sau đó, một lần nữa, trình biên dịch đã đạt được rất nhiều tiến bộ trong những thập kỷ qua, và tiến trình đó không bị chậm lại.

Các ngôn ngữ cấp cao có xu hướng làm cho nhiều thứ tự động hơn, do đó chúng có nhiều công việc hơn để thực hiện và do đó có xu hướng kém hiệu quả hơn. Mặt khác, chúng có xu hướng có nhiều thông tin ngữ nghĩa hơn, do đó có thể dễ dàng phát hiện tối ưu hóa hơn (nếu bạn đang viết trình biên dịch Haskell, bạn không phải lo lắng rằng một luồng khác sẽ sửa đổi một biến dưới mũi của bạn). Một trong nhiều nỗ lực để so sánh táo và cam các ngôn ngữ lập trình khác nhau là Trò chơi Điểm chuẩn Ngôn ngữ Máy tính (trước đây gọi là loạt đá luân lưu). Fortran có xu hướng tỏa sáng ở các nhiệm vụ số; nhưng khi nói đến việc thao túng dữ liệu có cấu trúc hoặc chuyển đổi luồng tốc độ cao, F # và Scala làm tốt. Đừng coi những kết quả này là phúc âm: rất nhiều điều họ đang đo lường là tác giả của chương trình kiểm tra tốt như thế nào trong mỗi ngôn ngữ.

Một lập luận ủng hộ các ngôn ngữ cấp cao là hiệu suất trên các hệ thống hiện đại không tương quan mạnh với số lượng lệnh được thực thi và ít hơn theo thời gian. Các ngôn ngữ cấp thấp là kết hợp tốt cho các máy tuần tự đơn giản. Nếu một ngôn ngữ cấp cao thực thi gấp đôi số lệnh, nhưng quản lý để sử dụng bộ đệm một cách thông minh hơn, do đó, một nửa số bộ nhớ cache bị mất, nó có thể sẽ giành chiến thắng.

Trên nền tảng máy chủ và máy tính để bàn, CPU gần như đã đạt đến một cao nguyên nơi chúng không nhận được bất kỳ tốc độ nào nhanh hơn (nền tảng di động cũng đang đến đó); điều này ủng hộ các ngôn ngữ mà sự song song dễ khai thác. Rất nhiều bộ xử lý dành phần lớn thời gian của họ để chờ phản hồi I / O; thời gian dành cho việc tính toán ít quan trọng so với lượng I / O và một ngôn ngữ cho phép lập trình viên giảm thiểu giao tiếp là một lợi thế.

Nói chung, trong khi các ngôn ngữ cấp cao bắt đầu bằng một hình phạt, chúng có nhiều chỗ hơn để cải thiện. Làm thế nào gần họ có thể nhận được? Hỏi lại sau 100 năm nữa.

Lưu ý cuối cùng: thông thường, so sánh không phải là giữa chương trình hiệu quả nhất có thể được viết bằng ngôn ngữ A và cùng ngôn ngữ B, cũng không phải giữa chương trình hiệu quả nhất từng được viết bằng mỗi ngôn ngữ, mà là giữa chương trình hiệu quả nhất có thể được viết bởi một con người trong một khoảng thời gian nhất định trong mỗi ngôn ngữ. Điều này giới thiệu một yếu tố không thể được phân tích về mặt toán học, ngay cả về nguyên tắc. Về mặt thực tế, điều này thường có nghĩa là hiệu suất tốt nhất là sự thỏa hiệp giữa mức độ mã bạn cần viết để đáp ứng mục tiêu hiệu suất và mức độ mã thấp bạn có thời gian để viết để đáp ứng ngày phát hành.


Tôi nghĩ rằng sự phân biệt giữa cấp cao và cấp thấp là sai. C ++ có thể (rất) cao cấp. Tuy nhiên, hiện đại, C ++ cấp cao không (nhất thiết) hoạt động kém hơn so với tương đương cấp thấp - hoàn toàn ngược lại. C ++ và các thư viện của nó đã được thiết kế cẩn thận để cung cấp các bản tóm tắt cấp cao mà không bị phạt hiệu năng. Tương tự với ví dụ Haskell của bạn: các bản tóm tắt cấp cao của nó thường cho phép thay vì ngăn chặn tối ưu hóa. Sự khác biệt ban đầu giữa ngôn ngữ động và ngôn ngữ tĩnh có ý nghĩa hơn trong vấn đề này.
Konrad Rudolph

@KonradRudolph Bạn nói đúng, ở cấp độ thấp / cấp cao đó là một sự phân biệt hơi độc đoán. Nhưng ngôn ngữ động so với ngôn ngữ tĩnh cũng không nắm bắt được mọi thứ; một JIT có thể loại bỏ phần lớn sự khác biệt. Về cơ bản, các câu trả lời lý thuyết đã biết cho câu hỏi này là tầm thường và vô dụng, và câu trả lời thực tế là có nghĩa là nó phụ thuộc vào.
Gilles 'SO- ngừng trở nên xấu xa'

Vậy thì, tôi nghĩ rằng câu hỏi trở thành chỉ là JITs có thể trở nên tốt như thế nào và nếu họ vượt qua quá trình biên dịch tĩnh, các ngôn ngữ được biên dịch tĩnh cũng có thể kiếm lợi từ chúng? Ít nhất đó là cách tôi hiểu câu hỏi khi chúng ta tính đến JIT. Và vâng, tôi đồng ý với đánh giá của bạn nhưng chắc chắn chúng ta có thể nhận được một số dự đoán có cơ sở vượt ra ngoài điều đó phụ thuộc vào điều đó. ;-)
Konrad Rudolph

@KonradRudolph Nếu tôi muốn đoán, tôi sẽ hỏi về Kỹ thuật phần mềm .
Gilles 'SO- ngừng trở thành ác quỷ'

1
Thật không may, vụ xả súng ngôn ngữ là một nguồn đáng ngờ cho điểm chuẩn định lượng: họ không chấp nhận tất cả các chương trình chỉ dành cho những chương trình được coi là điển hình của ngôn ngữ. Đây là một yêu cầu phức tạp và rất chủ quan; điều đó có nghĩa là bạn không thể cho rằng việc thực hiện bắn súng thực sự là tốt (và trong thực tế, một số triển khai rõ ràng có những lựa chọn thay thế vượt trội bị từ chối). Mặt trái; đây là các điểm chuẩn vi mô và một số triển khai sử dụng các kỹ thuật bất thường mà bạn không bao giờ xem xét trong một kịch bản bình thường hơn chỉ để giành được hiệu suất. Vì vậy: đó là một trò chơi, không phải là một nguồn dữ liệu rất đáng tin cậy.
Eamon Nerbonne

10

Sự khác biệt cơ bản giữa các ++ tuyên bố C x = a + bvà báo cáo kết quả Python x = a + blà một C / C ++ có thể nói từ tuyên bố này (và một ít thông tin thêm rằng nó có sẵn về các loại x, ab) một cách chính xác máy gì mã cần phải được thực thi . Trong khi để cho biết các hoạt động mà câu lệnh Python sẽ làm, bạn cần giải quyết vấn đề Dừng.

Trong C, câu lệnh đó về cơ bản sẽ biên dịch thành một trong một vài loại bổ sung máy (và trình biên dịch C biết loại nào). Trong C ++, nó có thể biên dịch theo cách đó hoặc có thể biên dịch để gọi một hàm được biết đến tĩnh hoặc (trường hợp xấu nhất) nó có thể phải biên dịch để tra cứu và gọi phương thức ảo, nhưng ngay cả điều này có một chi phí mã máy khá nhỏ. Quan trọng hơn, trình biên dịch C ++ có thể cho biết từ các loại được biết đến tĩnh có liên quan đến việc nó có thể phát ra một hoạt động bổ sung nhanh hay không hoặc có cần sử dụng một trong các tùy chọn chậm hơn không.

Trong Python, một trình biên dịch về mặt lý thuyết có thể làm gần như tốt nếu nó biết điều đó abcả hai đều intlà s. Có một số quyền anh bổ sung, nhưng nếu các kiểu được biết đến một cách tĩnh, bạn cũng có thể thoát khỏi điều đó (trong khi vẫn trình bày giao diện rằng các số nguyên là các đối tượng với các phương thức, phân cấp của các siêu lớp, v.v.). Vấn đề là trình biên dịch cho Python không thểbiết điều này, bởi vì các lớp được định nghĩa trong thời gian chạy, có thể được sửa đổi trong thời gian chạy và thậm chí các mô đun xác định và nhập được giải quyết trong thời gian chạy (và ngay cả các câu lệnh nhập được thực thi phụ thuộc vào những điều chỉ có thể biết trong thời gian chạy). Vì vậy, trình biên dịch Python sẽ phải biết mã nào đã được thực thi (tức là giải quyết vấn đề tạm dừng) để biết câu lệnh mà nó đang biên dịch sẽ làm gì.

Vì vậy, ngay cả với hầu hết các phân tích tinh vi có thể về mặt lý thuyết , bạn chỉ đơn giản là không thể nói nhiều về những gì một câu lệnh Python đã cho sẽ làm trước thời hạn. Điều này có nghĩa là ngay cả khi một trình biên dịch Python tinh vi đã được triển khai, trong hầu hết các trường hợp, nó vẫn phải phát ra mã máy theo giao thức tra cứu từ điển Python để xác định lớp của một đối tượng và tìm các phương thức (đi qua MRO của hệ thống phân cấp lớp, cái này cũng có thể thay đổi linh hoạt khi chạy và do đó rất khó để biên dịch thành bảng phương thức ảo đơn giản) và về cơ bản làm những gì các trình thông dịch (chậm) làm. Đây là lý do tại sao thực sự không có bất kỳ trình biên dịch tối ưu hóa tinh vi nào cho các ngôn ngữ động. Không chỉ khó để tạo một cái, mức thưởng tối đa có thể không phải là '

Lưu ý rằng điều này không dựa trên những gì mã đang làm, nó dựa trên những gì mã thể làm. Ngay cả mã Python là một chuỗi các phép toán số nguyên đơn giản cũng phải được biên dịch như thể nó có thể gọi các phép toán lớp tùy ý. Các ngôn ngữ tĩnh có các hạn chế lớn hơn đối với các khả năng về những gì mã có thể làm và do đó trình biên dịch của chúng có thể đưa ra nhiều giả định hơn.

Trình biên dịch JIT đạt được điều này bằng cách đợi cho đến khi thời gian chạy để biên dịch / tối ưu hóa. Điều này cho phép họ phát ra mã hoạt động cho những gì mã đang làm hơn là những gì nó có thể làm. Và bởi vì trình biên dịch JIT này có khả năng chi trả tiềm năng lớn hơn nhiều cho các ngôn ngữ động so với các ngôn ngữ tĩnh; đối với các ngôn ngữ tĩnh hơn, phần lớn những gì trình tối ưu hóa muốn biết có thể được biết trước, do đó bạn cũng có thể tối ưu hóa nó sau đó, để lại ít hơn cho trình biên dịch JIT.

Có nhiều trình biên dịch JIT khác nhau cho các ngôn ngữ động tuyên bố đạt được tốc độ thực thi tương đương với các trình biên dịch C / C ++ được biên dịch và tối ưu hóa. Thậm chí có những tối ưu hóa có thể được thực hiện bởi trình biên dịch JIT mà trình biên dịch trước không thể thực hiện được đối với bất kỳ ngôn ngữ nào, vì vậy về mặt lý thuyết, trình biên dịch JIT (đối với một số chương trình) có thể vượt trội hơn trình biên dịch tĩnh tốt nhất có thể. Nhưng như Devin đã chỉ ra một cách đúng đắn, các thuộc tính của quá trình biên dịch JIT (chỉ các "điểm nóng" là nhanh và chỉ sau một thời gian khởi động) có nghĩa là các ngôn ngữ động do JIT biên dịch không bao giờ phù hợp cho tất cả các ứng dụng có thể, ngay cả khi chúng trở thành nói chung là nhanh hoặc nhanh hơn các ngôn ngữ được biên dịch tĩnh nói chung.


1
Đó là hai phiếu bầu không có bình luận. Tôi sẽ hoan nghênh đề xuất làm thế nào để cải thiện câu trả lời này!
Ben

Tôi đã không downvote, nhưng bạn không chính xác về "cần giải quyết vấn đề tạm dừng". Trong nhiều trường hợp, người ta đã chứng minh rằng mã trong các ngôn ngữ động có thể được biên dịch thành mã mục tiêu tối ưu, trong khi theo hiểu biết của tôi, không có trình diễn nào trong số các trình diễn này bao gồm một giải pháp cho vấn đề tạm dừng :-)
mikera

@mikera Tôi xin lỗi, nhưng không, bạn không chính xác. Không ai từng thực hiện trình biên dịch (theo nghĩa là chúng tôi hiểu rằng GCC là trình biên dịch) cho Python hoàn toàn chung hoặc các ngôn ngữ động khác. Mỗi hệ thống như vậy chỉ hoạt động cho một tập hợp con của ngôn ngữ, hoặc chỉ một số chương trình nhất định, hoặc đôi khi phát ra mã về cơ bản là một trình thông dịch có chứa một chương trình được mã hóa cứng. Nếu bạn muốn, tôi sẽ viết cho bạn một chương trình Python chứa dòng foo = x + ytrong đó dự đoán hành vi của toán tử bổ sung tại thời điểm biên dịch phụ thuộc vào việc giải quyết vấn đề tạm dừng.
Ben

Tôi đúng và tôi nghĩ bạn đang thiếu điểm. Tôi nói "trong nhiều hoàn cảnh". Tôi đã không nói "trong mọi trường hợp có thể". Việc bạn có thể xây dựng một ví dụ giả định liên quan đến vấn đề tạm dừng hay không phần lớn không liên quan trong thế giới thực. FWIW, bạn cũng có thể xây dựng một ví dụ tương tự cho C ++ để bạn không phải chứng minh bất cứ điều gì. Dù sao, tôi đã không đến đây để tranh luận, chỉ để đề xuất cải tiến cho câu trả lời của bạn. Hoặc là lấy đi hoặc là bỏ lại.
mikera

@mikea Tôi nghĩ bạn có thể đang thiếu điểm. Để biên dịch x + ythành các hoạt động bổ sung máy hiệu quả, bạn cần biết tại thời điểm biên dịch xem có hay không. Tất cả thời gian , không chỉ là một số thời gian. Đối với các ngôn ngữ động, điều này gần như không bao giờ có thể xảy ra với các chương trình thực tế, mặc dù các heuristic hợp lý sẽ đoán đúng hầu hết thời gian. Biên dịch yêu cầu đảm bảo thời gian biên dịch . Vì vậy, bằng cách nói về "trong nhiều trường hợp" bạn thực sự không giải quyết câu trả lời của tôi.
Ben

9

Chỉ là một con trỏ nhanh phác thảo trường hợp xấu nhất cho các ngôn ngữ động:

Perl phân tích cú pháp không thể tính toán được

Kết quả là, Perl (đầy đủ) không bao giờ có thể được biên dịch tĩnh.


Nói chung, như mọi khi, nó phụ thuộc. Tôi tự tin rằng nếu bạn cố gắng mô phỏng các tính năng động bằng ngôn ngữ được biên dịch tĩnh, các phiên dịch được hình thành tốt hoặc các biến thể được biên dịch (một phần) có thể đến gần hoặc làm giảm hiệu suất của các ngôn ngữ được biên dịch tĩnh.

Một điểm khác cần lưu ý là các ngôn ngữ động giải quyết một vấn đề khác so với C. C hầu như không phải là cú pháp hay cho trình biên dịch trong khi các ngôn ngữ động cung cấp sự trừu tượng phong phú. Hiệu suất thời gian chạy thường không phải là mối quan tâm chính: ví dụ, thời gian tiếp thị, phụ thuộc vào các nhà phát triển của bạn có thể viết các hệ thống chất lượng cao, phức tạp trong các khung thời gian ngắn. Khả năng mở rộng mà không cần biên dịch lại, ví dụ như với các plugin, là một tính năng phổ biến khác. Ngôn ngữ nào bạn thích trong những trường hợp này?


5

Trong một nỗ lực để đưa ra một câu trả lời khoa học khách quan hơn cho câu hỏi này, tôi lập luận như sau. Một ngôn ngữ động đòi hỏi một thông dịch viên, hoặc thời gian chạy, để đưa ra quyết định trong thời gian chạy. Trình thông dịch này, hoặc thời gian chạy, là một chương trình máy tính và, như vậy, được viết bằng một số ngôn ngữ lập trình, hoặc tĩnh hoặc động.

Nếu trình thông dịch / thời gian chạy được viết bằng ngôn ngữ tĩnh, thì người ta có thể viết chương trình bằng ngôn ngữ tĩnh đó (a) thực hiện chức năng tương tự như chương trình động mà nó diễn giải và (b) cũng thực hiện ít nhất. Hy vọng rằng, điều này là hiển nhiên, vì để cung cấp một bằng chứng nghiêm ngặt về những tuyên bố này sẽ đòi hỏi nỗ lực bổ sung (có thể đáng kể).

Giả sử những tuyên bố này là đúng, cách duy nhất là yêu cầu trình thông dịch / thời gian chạy cũng được viết bằng ngôn ngữ động. Tuy nhiên, chúng tôi gặp vấn đề tương tự như trước đây: nếu trình thông dịch là động, nó yêu cầu trình thông dịch / thời gian chạy, cũng phải được viết bằng ngôn ngữ lập trình, động hoặc tĩnh.

Trừ khi bạn cho rằng một phiên bản của trình thông dịch có khả năng tự phiên dịch trong thời gian chạy (tôi hy vọng điều này là vô lý), cách duy nhất để đánh bại các ngôn ngữ tĩnh là cho mỗi phiên bản trình thông dịch được phiên dịch bởi một phiên bản trình thông dịch riêng biệt; điều này dẫn đến một hồi quy vô hạn (tôi hy vọng rằng điều này rõ ràng là vô lý) hoặc một vòng kín của các phiên dịch viên (tôi hy vọng điều này cũng hiển nhiên là vô lý).

Dường như, về mặt lý thuyết, các ngôn ngữ động có thể hoạt động không tốt hơn các ngôn ngữ tĩnh nói chung. Khi sử dụng các mô hình của máy tính thực tế, nó có vẻ hợp lý hơn; Rốt cuộc, một máy chỉ có thể thực hiện các chuỗi lệnh của máy và tất cả các chuỗi lệnh của máy có thể được biên dịch tĩnh.

Trong thực tế, việc kết hợp hiệu năng của ngôn ngữ động với ngôn ngữ tĩnh có thể yêu cầu triển khai lại trình thông dịch / thời gian chạy bằng ngôn ngữ tĩnh; tuy nhiên, bạn hoàn toàn có thể làm điều đó là mấu chốt và quan điểm của lập luận này. Đó là một câu hỏi về con gà và quả trứng và, miễn là bạn đồng ý với các giả định chưa được chứng minh (mặc dù, theo tôi, chủ yếu là hiển nhiên), chúng tôi thực sự có thể trả lời nó; chúng ta phải gật đầu với các ngôn ngữ tĩnh, không động.

Một cách khác để trả lời câu hỏi, theo ý kiến ​​của cuộc thảo luận này là: trong chương trình lưu trữ, control = mô hình dữ liệu của máy tính nằm ở trung tâm của điện toán hiện đại, sự khác biệt giữa biên dịch tĩnh và động là một sự phân đôi sai; ngôn ngữ được biên dịch tĩnh phải có phương tiện tạo và thực thi mã tùy ý trong thời gian chạy. Về cơ bản nó liên quan đến tính toán phổ quát.


Đọc lại điều này, tôi không nghĩ đó là sự thật. Biên soạn JIT phá vỡ lập luận của bạn. Ngay cả mã đơn giản nhất, ví dụ main(args) { for ( i=0; i<1000000; i++ ) { if ( args[0] == "1" ) {...} else {...} }có thể tăng tốc đáng kể một khi giá trị argsđược biết đến (giả sử nó không bao giờ thay đổi, điều mà chúng ta có thể khẳng định). Một trình biên dịch tĩnh không thể tạo mã giảm so sánh bao giờ. (Tất nhiên, trong ví dụ đó, bạn chỉ cần rút ifra khỏi vòng lặp. Nhưng điều có thể sẽ phức tạp hơn.)
Raphael

@Raphael Tôi nghĩ rằng JIT có thể đưa ra lập luận của tôi. Các chương trình thực hiện biên dịch JIT (ví dụ, JVM) thường là các chương trình được biên dịch tĩnh. Nếu một chương trình JIT được biên dịch tĩnh có thể chạy tập lệnh nhanh hơn một số chương trình tĩnh khác có thể thực hiện cùng một công việc, chỉ cần "bó" tập lệnh với trình biên dịch JIT và gọi gói đó là chương trình được biên dịch tĩnh. Điều này phải làm ít nhất cũng như JIT hoạt động trên một chương trình động riêng biệt, mâu thuẫn với bất kỳ đối số nào mà JIT phải làm tốt hơn.
Patrick87

Hừm, giống như nói gói một tập lệnh Ruby với trình thông dịch của nó cung cấp cho bạn một chương trình được biên dịch tĩnh. Tôi không đồng ý (vì nó loại bỏ tất cả sự khác biệt về ngôn ngữ trong vấn đề này), nhưng đó là vấn đề về ngữ nghĩa, không phải khái niệm. Về mặt khái niệm, việc điều chỉnh chương trình trong thời gian chạy (JIT) có thể thực hiện tối ưu hóa bạn không thể thực hiện ở thời gian biên dịch (trình biên dịch tĩnh) và đó là quan điểm của tôi.
Raphael

@Raphael Rằng không có sự phân biệt có ý nghĩa nào là điểm của câu trả lời: mọi nỗ lực phân loại cứng nhắc một số ngôn ngữ là tĩnh và do đó bị hạn chế về hiệu suất, không thành công vì lý do này: không có sự khác biệt thuyết phục giữa một gói được đóng gói sẵn (Ruby , script) gói và một chương trình C. Nếu (Ruby, script) có thể làm cho máy thực hiện đúng chuỗi hướng dẫn để giải quyết hiệu quả một vấn đề nhất định, thì chương trình C được chế tạo khéo léo.
Patrick87

Nhưng bạn có thể định nghĩa sự khác biệt. Một biến thể gửi mã trong tay tới bộ xử lý không thay đổi (C), các biến thể khác trong thời gian chạy (Ruby, Java, ...). Đầu tiên là những gì chúng tôi muốn nói là "biên dịch tĩnh" trong khi cái sau sẽ là "chỉ trong thời gian biên dịch" (cho phép tối ưu hóa phụ thuộc vào dữ liệu).
Raphael

4

Bạn có thể xây dựng trình biên dịch cho các ngôn ngữ động như Ruby để có hiệu suất tương tự và tương đương với C / C ++ không?

Tôi nghĩ rằng câu trả lời là "có" . Tôi cũng tin rằng họ thậm chí có thể vượt qua kiến trúc C / C ++ hiện tại về hiệu quả (ngay cả khi trượt dốc).

Lý do rất đơn giản: Có nhiều thông tin trong thời gian chạy hơn là thời gian biên dịch.

Các kiểu động chỉ là một trở ngại nhỏ: Nếu một hàm luôn luôn hoặc hầu như luôn luôn được thực thi với cùng loại đối số, thì trình tối ưu hóa JIT có thể tạo ra một nhánh và mã máy cho trường hợp cụ thể đó. Và còn rất nhiều điều có thể làm được.

Xem Dynamic Languages ​​Strike Back , bài phát biểu của Steve Yegge của Google (cũng có một phiên bản video ở đâu đó tôi tin). Ông đề cập đến một số kỹ thuật tối ưu hóa JIT cụ thể từ V8. Cảm hứng!

Tôi đang mong chờ những gì chúng ta sẽ có trong 5 năm tới!


2
Tôi yêu sự lạc quan.
Dave Clarke

Tôi tin rằng có một số lời chỉ trích rất cụ thể về sự không chính xác trong cuộc nói chuyện của Steve. Tôi sẽ đăng chúng khi tôi tìm thấy chúng.
Konrad Rudolph

1
@DaveClarke đó là thứ giúp tôi chạy :)
Kos

2

Những người dường như nghĩ rằng điều này là có thể về mặt lý thuyết, hoặc trong một tương lai xa, theo tôi là hoàn toàn sai lầm. Vấn đề nằm ở chỗ các ngôn ngữ động cung cấp và áp đặt một phong cách lập trình hoàn toàn khác. Trên thực tế, sự khác biệt là gấp đôi, ngay cả khi cả hai khía cạnh có liên quan đến nhau:

  • Các biểu tượng (vars, hay đúng hơn là id <-> ràng buộc chuẩn của tất cả các loại) được tháo gỡ.
  • Các cấu trúc (dữ liệu, tất cả những gì sống trong thời gian chạy) cũng được gỡ bỏ bởi các loại phần tử của chúng.

Điểm thứ hai cung cấp sự hào phóng miễn phí. Lưu ý rằng các cấu trúc ở đây là các phần tử tổng hợp, các bộ sưu tập, nhưng cũng có các kiểu chính và các thói quen thậm chí (!) Của tất cả các loại (chức năng, hành động, hoạt động) ... Chúng ta có thể nhập các cấu trúc theo loại phần tử của chúng, nhưng do điểm đầu tiên kiểm tra sẽ xảy ra trong thời gian chạy nào. Chúng ta có thể đã gõ các ký hiệu và vẫn có các cấu trúc ađược gõ theo các kiểu phần tử của chúng (một mảng sẽ được gõ dưới dạng một mảng không phải là một mảng của int), nhưng ngay cả một số này không đúng trong ngôn ngữ động ( acũng có thể chứa một chuỗi).

Theo tôi, sự hoàn hảo tốt nhất chúng ta có thể đạt được là lập trình tương đương như sau: triển khai trong C mô hình của ngôn ngữ động, hãy gọi nó là , tức là một loại máy hư cấu hoặc lib hoàn chỉnh trong thời gian chạy. Và sau đó lập trình (trong C trực tiếp) chỉ sử dụng mô hình, không có tính năng C đơn giản. Điều này có nghĩa là có:L

  • một loại hoàn toàn đa hình (C) Element, bao gồm các chú thích loại (hoặc một tham chiếu đến biểu diễn thực tế của loại 'trong mô hình)LLL
  • tất cả các biểu tượng thuộc loại đó Element, chúng có thể chứa các phần tử của bất kỳ loại nàoL
  • tất cả các cấu trúc (một lần nữa, bao gồm các thói quen mô hình) chỉ nhận được Element

Rõ ràng với tôi rằng đây chỉ là một hình phạt hoàn hảo rất lớn; và tôi thậm chí không chạm vào tất cả các hậu quả (vô số các kiểm tra thời gian chạy của tất cả các loại cần thiết để đảm bảo tính nhạy cảm của chương trình) được mô tả tốt trong các bài đăng khác.


+1 Rất thú vị. Bạn đã đọc câu trả lời của tôi chưa? Suy nghĩ của bạn và tôi có vẻ giống nhau, mặc dù câu trả lời của bạn cung cấp nhiều chi tiết hơn và một viễn cảnh thú vị.
Patrick87

Các ngôn ngữ động không phải được tháo gỡ. Việc triển khai mô hình ngôn ngữ động trong C hạn chế nghiêm ngặt các khả năng tối ưu hóa; nó làm cho trình biên dịch rất khó nhận ra các tối ưu hóa mức cao (ví dụ: dữ liệu không thay đổi) và buộc một số hoạt động cơ bản (ví dụ như các hàm gọi) đi qua C. Những gì bạn mô tả không ở xa trình thông dịch mã byte, với giải mã mã byte đánh giá trước; trình biên dịch riêng có xu hướng thực hiện tốt hơn đáng kể, tôi nghi ngờ rằng việc giải mã mã byte có thể biện minh cho sự khác biệt.
Gilles 'SO- ngừng trở nên xấu xa'

@ Patrick87: bạn nói đúng, dòng suy nghĩ của chúng tôi có vẻ rất giống nhau (chưa đọc trước đây, xin lỗi, phản xạ của tôi xuất phát từ việc hiện đang thực hiện một lang dyn trong C).
spir

@Gilles: Tôi khá đồng ý về việc "... không cần phải tháo gỡ", nếu bạn muốn nói không đượctĩnh . Nhưng đây không phải là những gì mọi người nghĩ về dyn lang nói chung, tôi đoán vậy. Tôi cá nhân xem xét tính tổng quát (theo nghĩa chung được đưa ra trong câu trả lời ở trên) một tính năng mạnh mẽ hơn nhiều và khó sống hơn rất nhiều. Chúng ta có thể dễ dàng tìm ra cách thực hiện với các loại tĩnh bằng cách mở rộng suy nghĩ về cách xác định một loại (dường như đa hình) nhất định hoặc bằng cách trực tiếp linh hoạt hơn cho các thể hiện.
spir

1

Tôi không có thời gian để đọc tất cả các câu trả lời chi tiết ... nhưng tôi đã thích thú.

Có một cuộc tranh cãi tương tự vào những năm sáu mươi và đầu những năm bảy mươi (lịch sử khoa học máy tính thường lặp lại): các ngôn ngữ cấp cao có thể được biên dịch để tạo mã hiệu quả như mã máy, giả sử, mã lắp ráp, được lập trình thủ công. Mọi người đều biết một lập trình viên thông minh hơn nhiều so với bất kỳ chương trình nào và có thể đưa ra tối ưu hóa rất thông minh (suy nghĩ thực sự chủ yếu là những gì được gọi là tối ưu hóa lổ nhìn trộm). Điều này tất nhiên là trớ trêu về phía tôi.

Thậm chí còn có một khái niệm về mở rộng mã: tỷ lệ kích thước của mã do trình biên dịch tạo ra so với kích thước mã cho cùng một chương trình được tạo bởi một lập trình viên giỏi (như thể có quá nhiều trong số này :-). Tất nhiên ý tưởng là tỷ lệ này luôn lớn hơn 1. Ngôn ngữ thời đó là Cobol và Fortran 4, hay Algol 60 cho giới trí thức. Tôi tin rằng Lisp đã không được xem xét.

Vâng, có một số tin đồn rằng ai đó đã tạo ra một trình biên dịch đôi khi có thể có tỷ lệ mở rộng là 1 ... cho đến khi nó đơn giản trở thành quy tắc mã được biên dịch tốt hơn nhiều so với mã viết tay (và cũng đáng tin cậy hơn). Mọi người đã lo lắng về kích thước mã trong những thời điểm đó (những ký ức nhỏ) nhưng điều tương tự cũng xảy ra với tốc độ hoặc mức tiêu thụ năng lượng. Tôi sẽ không đi vào lý do.

Các tính năng lạ, tính năng động của ngôn ngữ không quan trọng. Điều quan trọng là làm thế nào chúng được sử dụng, cho dù chúng được sử dụng. Hiệu suất, ở bất kỳ đơn vị nào (kích thước mã, tốc độ, năng lượng, ...) thường phụ thuộc vào các phần rất nhỏ của chương trình. Do đó, rất có thể các cơ sở cung cấp sức mạnh biểu cảm sẽ không thực sự cản trở. Với thực hành lập trình tốt, các cơ sở tiên tiến chỉ được sử dụng một cách có kỷ luật, để tưởng tượng các cấu trúc mới (đó là bài học không thể thiếu).

Việc một ngôn ngữ không có kiểu gõ tĩnh chưa bao giờ có nghĩa là các chương trình được viết bằng ngôn ngữ đó không được gõ tĩnh. Mặt khác, có thể hệ thống loại mà chương trình sử dụng chưa được chính thức hóa đủ để trình kiểm tra loại tồn tại.

Trong cuộc thảo luận, đã có một số tài liệu tham khảo về phân tích trường hợp xấu nhất ("vấn đề tạm dừng", phân tích PERL). Nhưng phân tích trường hợp xấu nhất chủ yếu là không liên quan. Điều quan trọng là những gì xảy ra trong hầu hết các trường hợp hoặc trong các trường hợp hữu ích ... tuy nhiên được xác định hoặc hiểu hoặc có kinh nghiệm. Ở đây có một câu chuyện khác, liên quan trực tiếp đến tối ưu hóa chương trình. Nó đã diễn ra một thời gian dài trước đây tại một trường đại học lớn ở Texas, giữa một nghiên cứu sinh và cố vấn của ông (người sau đó được bầu vào một trong những học viện quốc gia). Khi tôi nhớ lại, sinh viên đã khăng khăng nghiên cứu một vấn đề phân tích / tối ưu hóa mà cố vấn đã cho thấy là không thể giải quyết được. Chẳng mấy chốc họ không còn nói về các điều khoản. Nhưng sinh viên đã đúng: vấn đề đã đủ dễ xử lý trong hầu hết các trường hợp thực tế để luận án mà ông tạo ra trở thành công việc tham khảo.

Và để bình luận thêm về tuyên bố rằng Perl parsing is not computable, bất kể ý nghĩa của câu đó là gì, có một vấn đề tương tự với ML, đó là một ngôn ngữ được chính thức hóa đáng chú ý. Type checking complexity in ML is a double exponential in the lenght of the program.Đó là một kết quả rất chính xác và chính thức trong trường hợp phức tạp tồi tệ nhất ... điều đó không quan trọng. Người dùng Afaik, ML vẫn đang chờ đợi một chương trình thực tế sẽ phát nổ trình kiểm tra loại.

Trong nhiều trường hợp, như trước đây, thời gian và năng lực của con người khan hiếm hơn sức mạnh tính toán.

Vấn đề thực sự của tương lai sẽ là phát triển ngôn ngữ của chúng tôi để tích hợp kiến ​​thức mới, các hình thức lập trình mới, mà không phải viết lại tất cả các phần mềm cũ vẫn còn được sử dụng.

Nếu bạn nhìn vào toán học, nó là một khối kiến ​​thức rất lớn. Các ngôn ngữ được sử dụng để diễn đạt nó, các ký hiệu và khái niệm đã phát triển qua nhiều thế kỷ. Thật dễ dàng để viết các định lý cũ với các khái niệm mới. Chúng tôi điều chỉnh các bằng chứng chính, nhưng không bận tâm về nhiều kết quả.

Nhưng trong trường hợp lập trình, chúng ta có thể phải viết lại tất cả các bằng chứng từ đầu (các chương trình là bằng chứng). Nó có thể là những gì chúng ta thực sự cần là ngôn ngữ lập trình rất cao và có thể phát triển. Các nhà thiết kế tối ưu hóa sẽ rất vui khi làm theo.


0

Một vài lưu ý:

  • Không phải tất cả các ngôn ngữ cấp cao là năng động. Haskell là cấp độ rất cao, nhưng được gõ hoàn toàn tĩnh. Ngay cả các ngôn ngữ lập trình hệ thống như Rust, Nim và D cũng có thể diễn đạt trừu tượng mức độ cao một cách ngắn gọn và hiệu quả. Trong thực tế, chúng có thể ngắn gọn như ngôn ngữ động.

  • Tối ưu hóa cao các trình biên dịch trước thời hạn cho các ngôn ngữ động tồn tại. Việc triển khai Lisp tốt đạt tới một nửa tốc độ tương đương C.

  • Biên soạn JIT có thể là một chiến thắng lớn ở đây. Tường lửa ứng dụng web của CloudFlare tạo mã Lua được thực thi bởi LuaJIT. LuaJIT tối ưu hóa rất nhiều các đường dẫn thực thi đã thực hiện (thông thường, các đường dẫn không tấn công), với kết quả là mã chạy nhanh hơn nhiều so với mã do trình biên dịch tĩnh tạo ra trên khối lượng công việc thực tế. Không giống như một trình biên dịch tĩnh với tối ưu hóa theo hướng dẫn hồ sơ, LuaJIT thích nghi với những thay đổi trong đường dẫn thực thi khi chạy.

  • Deoptimization cũng rất quan trọng. Thay vì mã do JIT biên dịch cần kiểm tra xem một lớp có bị khớp không, hành động bắt chước sẽ kích hoạt một hook trong hệ thống thời gian chạy loại bỏ mã máy phụ thuộc vào định nghĩa cũ.


Làm thế nào đây là một câu trả lời? Vâng, đạn ba có thể, nếu bạn thêm tài liệu tham khảo.
Raphael

Tôi rất nghi ngờ về tuyên bố rằng PGO không thể phù hợp với hiệu suất của LuaJIT cho mã ứng dụng web theo khối lượng công việc điển hình.
Konrad Rudolph

@KonradRudolph Ưu điểm chính của JIT là JIT điều chỉnh mã khi các đường dẫn khác nhau trở nên nóng.
Demi

@Demetri Tôi biết điều này. Nhưng thật khó để định lượng liệu đây có phải là một lợi thế hay không - hãy xem câu trả lời của tôi và thảo luận bình luận ở đó. Tóm lại: trong khi JIT có thể thích ứng với thay đổi trong cách sử dụng, thì nó cũng cần theo dõi nội dung trong thời gian chạy, phát sinh chi phí. Sự hòa vốn cho điều này chỉ bằng trực giác khi những thay đổi thường xuyên trong hành vi xảy ra. Đối với các ứng dụng web, có lẽ chỉ có một mô hình sử dụng duy nhất (hoặc rất ít) được tối ưu hóa, do đó hiệu suất tăng tối thiểu do khả năng thích ứng không bù đắp được chi phí liên tục.
Konrad Rudolph
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.