2009-11-26 34 views
18

Lý do kỹ thuật tại sao các ngôn ngữ như Python và Ruby được giải thích (ngoài hộp) thay vì biên dịch? Dường như với tôi như nó không phải là quá khó khăn cho những người hiểu biết trong lĩnh vực này để làm cho các ngôn ngữ này không được giải thích như họ đang có ngày hôm nay, và chúng ta sẽ thấy lợi ích hiệu suất đáng kể. Vì vậy, chắc chắn tôi đang thiếu một cái gì đó.Tại sao (python | ruby) được giải thích?

+2

Thực tế là (ít nhất Python) đã có nhiều hơn một dự án biên dịch diễn ra trong nhiều năm, và rằng nó vẫn chưa hoàn toàn ở đó, nên tỉnh táo kỳ vọng của bạn về 'không quá khó', tôi đoán vậy. –

+5

Thực ra, tôi nghĩ Python biên dịch thành mã máy ảo, tương tự như .NET và Java. Dù bằng cách nào, tôi nghĩ tại sao những người thực hiện ngôn ngữ đưa ra lựa chọn giải thích hoặc biên dịch là một câu hỏi hay. Bạn có thể muốn xem http://stackoverflow.com/questions/475223/what-is-the-difference-between-implementing-a-compiler-and-an-interpreter –

+2

Bạn cũng có thể quan tâm đến câu trả lời này: http : //stackoverflow.com/questions/376611/why-interpreted-langs-are-mostly-ducktyped-while-compiled-have-strong-typing/376828#376828 –

Trả lời

1

Theo thiết kế.

Tác giả muốn thứ gì đó để họ có thể viết tập lệnh.

Python được biên dịch lần đầu tiên được thực thi mặc dù

+0

tức là "Byte được biên dịch" (tới .pyc) lần đầu tiên nó được thực hiện. Điều này chỉ tăng tốc thời gian tải của các lần thực hiện trong tương lai. Tôi nghĩ người hỏi đang nói về việc được biên dịch thành mã máy gốc. – overthink

+0

Perl quá ... thở dài, không có cơ thể hỏi về Perl. – dlamblin

+1

"Các tác giả muốn một cái gì đó mà họ có thể viết kịch bản vào."? Tuyên bố này làm tôi bối rối. Sự phù hợp của mục đích cho các nhiệm vụ nhất định của một ngôn ngữ cụ thể có ít hơn nhiều so với biên soạn '/ (biên dịch, liên kết)/chạy' của nó so với sự phong phú của thư viện chuẩn, quản lý bộ nhớ và kiểu mô hình. Điều này có lẽ sẽ thu được một số hỗ trợ để viết kịch bản trong Java là tốt, nếu không có xem xét nhỏ khác về bao nhiêu chi phí cần thiết để bắt đầu máy ảo ... –

2

Vâng, không phải là một trong những điểm mạnh của những ngôn ngữ này mà chúng dễ dàng có thể viết được? Họ sẽ không được nếu họ được biên soạn. Và mặt khác, các ngôn ngữ động dễ dàng hơn để liên kết với nhau hơn là biên dịch.

6

Tôi nghĩ lý do lớn nhất cho các ngôn ngữ được hiểu là tính di động. Là một lập trình viên, bạn có thể viết mã sẽ chạy trong một thông dịch viên không phải là một hệ điều hành cụ thể. Vì vậy, các chương trình của bạn hoạt động thống nhất hơn trên các nền tảng (nhiều hơn so với các ngôn ngữ biên dịch). Một ưu điểm khác mà tôi có thể nghĩ là dễ dàng hơn để có một hệ thống kiểu động trong một ngôn ngữ thông dịch. Tôi nghĩ rằng những người sáng tạo ngôn ngữ đã nghĩ đến việc có một ngôn ngữ mà các lập trình viên có thể làm việc hiệu quả hơn nhờ quản lý bộ nhớ tự động, hệ thống kiểu động và lập trình meta thắng hơn bất kỳ tổn thất hiệu suất nào do ngôn ngữ được diễn giải. Nếu bạn lo ngại về hiệu suất, bạn luôn có thể biên dịch ngôn ngữ thành mã máy gốc bằng cách sử dụng một kỹ thuật như biên dịch JIT.

+0

Tôi không chắc chắn đối số di động là âm thanh. Nếu bạn decouple lexer của bạn/AST-builder từ backend thế hệ mã của bạn, bạn có thể thay đổi hai độc lập (đó là phần nào làm thế nào gcc là kiến ​​trúc). Vì vậy, tôi không thấy đó là lý do. Điểm của bạn về một hệ thống kiểu động được dễ dàng hơn để thực hiện trong một ngôn ngữ diễn giải là âm thanh. Trên thực tế, bất kỳ hệ thống kiểu động nào đều yêu cầu hỗ trợ thời gian chạy (hoặc thông qua một trình thông dịch hoặc thông qua một thư viện thời gian chạy) theo định nghĩa thực tế. –

+0

Các đối số di động là trong thực tế không phải ở tất cả các âm thanh. Các ngôn ngữ thông dịch không dễ dàng hơn so với các ngôn ngữ được biên dịch, nói chung. Lý do duy nhất họ có thể thậm chí * dường như * dễ dàng hơn để cổng là vì bạn có thể viết cổng ngôn ngữ thông dịch của bạn mà không hiểu máy tính cơ bản, nhưng đó chỉ là vấn đề về thẩm quyền, không khó. –

+0

các bạn đúng ... điều này đã xảy ra sai rồi! Tôi sẽ cập nhật câu trả lời của mình để nói những gì tôi đã thực sự suy nghĩ. – neesh

2

Trong một ngôn ngữ biên dịch, vòng lặp bạn nhận được vào khi thực hiện phần mềm là

  1. Thực hiện một sự thay đổi
  2. Compile thay đổi
  3. thử nghiệm thay đổi
  4. goto 1

ngôn ngữ Interpreted có xu hướng nhanh hơn để tạo nội dung trong vì bạn sẽ cắt bỏ bước thứ hai của quá trình đó (và khi bạn đang xử lý một sys lớn thời gian biên dịch có thể lên tới hai phút, bước hai có thể thêm một lượng thời gian đáng kể).

Đây không nhất thiết phải là lý do mà các nhà thiết kế python | ruby ​​nghĩ đến, nhưng hãy nhớ rằng "Máy chạy được hiệu quả như thế nào?" chỉ là một nửa vấn đề phát triển phần mềm. Nó cũng có vẻ như dễ dàng hơn để biên dịch mã bằng một ngôn ngữ được diễn giải tự nhiên hơn là thêm một thông dịch viên vào một ngôn ngữ được biên dịch theo mặc định.

32

Một số lý do:

  • nhanh hơn vòng lặp phát triển, ghi-test vs ghi-biên dịch-link-test
  • dễ dàng hơn để sắp xếp cho hành vi động (phản chiếu, lập trình meta)
  • làm cho toàn bộ hệ thống di động (chỉ cần biên dịch lại mã C cơ bản và bạn tốt để tiếp tục trên nền tảng mới)

Hãy suy nghĩ về những gì sẽ xảy ra nếu hệ thống là không phải là được giải thích. Giả sử bạn đã sử dụng bản dịch-thành-C làm cơ chế. Mã được biên dịch định kỳ sẽ phải kiểm tra xem nó đã được thay thế bằng metaprogramming chưa. Tình huống tương tự phát sinh với eval() chức năng loại. Trong những trường hợp đó, nó sẽ phải chạy trình biên dịch một lần nữa, một quá trình quá chậm, hoặc nó sẽ phải cũng có trình thông dịch xung quanh vào thời gian chạy anyway.

Cách thay thế duy nhất ở đây là trình biên dịch JIT. Các hệ thống này rất phức tạp và phức tạp và có dấu chân thời gian chạy lớn hơn tất cả các lựa chọn thay thế khác. Chúng khởi động rất chậm, khiến chúng không thực tế cho việc viết kịch bản. Bạn đã từng thấy một kịch bản Java chưa? Tôi không có.

Vì vậy, bạn có hai lựa chọn:

  • tất cả những nhược điểm của cả một trình biên dịch và thông dịch viên
  • chỉ những nhược điểm của một thông dịch viên

Nó không ngạc nhiên khi nhìn chung việc thực hiện chính chỉ cần đi với sự lựa chọn thứ hai. Có thể một ngày nào đó chúng ta có thể thấy các triển khai thứ cấp như các trình biên dịch xuất hiện. Ruby 1.9 và Python có bytecode VM's; đó là & frac12; -way đó. Trình biên dịch có thể nhắm mục tiêu chỉ mã không động hoặc có thể có các mức hỗ trợ ngôn ngữ khác nhau có thể khai báo dưới dạng tùy chọn. Nhưng kể từ khi một điều như vậy không thể được thực hiện chính, nó đại diện cho rất nhiều công việc cho một lợi ích rất cận biên. Ruby đã có 200.000 dòng C trong đó ...

Tôi cho rằng tôi nên thêm rằng luôn có thể thêm phần mở rộng C (hoặc với một số nỗ lực, bất kỳ ngôn ngữ nào khác) được biên dịch. Vì vậy, nói rằng bạn có một hoạt động số chậm. Nếu bạn thêm, giả sử Array#newOp với triển khai C thì bạn sẽ nhận được tăng tốc, chương trình vẫn nằm trong Ruby (hoặc bất kỳ thứ gì) và môi trường của bạn nhận được một phương thức thể hiện mới. Mọi người đều thắng! Vì vậy, điều này làm giảm sự cần thiết cho việc triển khai thứ hai có vấn đề.

+5

"Đã từng thấy một kịch bản Java chưa?" Ha! Vì vậy, điểm danh tiếng của bạn có thể cao hơn 30 lần so với tôi, nhưng thậm chí tôi đã nghe nói về JavaScript! – mtyaka

+4

Hehe, thật tuyệt. Trên thực tế, tôi học được rất nhiều về SO, và tôi biết tôi vẫn còn rất nhiều điều để học. Tuy nhiên, tiếng Anh-pun mặc dù, (1) bởi "kịch bản" tôi có nghĩa là "một kịch bản shell thực hiện, như trong bash hoặc perl"; (2) Tôi chưa bao giờ thấy một trong số đó trong JS, và (3) JS có khá nhiều thứ không liên quan gì đến Java. – DigitalRoss

+3

@mtyaka: Rõ ràng anh ấy có nghĩa là "tập lệnh viết bằng Java", không phải là "Javascript", một ngôn ngữ hoàn toàn khác. @ DigitalRoss: thực sự, javascript đang trở nên phổ biến như một ngôn ngữ kịch bản không phải web với sự ra đời của các phiên dịch độc lập như V8 và SquirrelFish. –

5

Hôm nay, không còn phân biệt rõ ràng giữa ngôn ngữ "được biên dịch" và "được diễn giải" nữa. Python được trong thực tế biên soạn cũng giống như Java là, sự khác biệt duy nhất là:

  • Trình biên dịch Python được nhanh hơn nhiều so với các trình biên dịch Java
  • Python tự động biên dịch mã nguồn khi nó được thực thi, không có riêng "biên dịch" bước cần
  • Python bytecode khác với JVM bytecode

Python thậm chí còn có một chức năng gọi là compile() đó là một giao diện để trình biên dịch.

Có vẻ như sự khác biệt mà bạn đang thực hiện nằm giữa ngôn ngữ "được nhập động" và "được nhập tĩnh". Trong các ngôn ngữ động như Python, bạn có thể viết mã như:

def fn(x, y): 
    return x.foo(y) 

Chú ý rằng các loại xy không được chỉ định. Khi chạy, chức năng này sẽ xem xét x để xem liệu nó có chức năng thành viên có tên là foo hay không và nếu có thì sẽ gọi nó là y. Nếu không, nó sẽ ném một lỗi thời gian chạy mà chỉ ra không có chức năng như vậy đã được tìm thấy. Kiểu tra cứu thời gian chạy này dễ dàng hơn để biểu diễn bằng cách sử dụng một biểu diễn trung gian như bytecode, trong đó máy ảo thời gian chạy tra cứu thay vì phải tạo mã máy để tự tra cứu (hoặc, gọi hàm để tra cứu. bytecode sẽ làm gì).

Python có các dự án như Psyco, PyPyUnladen Swallow có các cách tiếp cận khác nhau để biên dịch mã đối tượng Python thành mã gần hơn với mã gốc. Có một nghiên cứu tích cực trong lĩnh vực này nhưng chưa có câu trả lời đơn giản.

16

Chính xác như (thực hiện điển hình) Java hoặc C#, Python được biên dịch lần đầu thành một dạng bytecode, tùy thuộc vào việc triển khai (CPython sử dụng một dạng riêng của nó, Jython sử dụng JVM giống như một Java điển hình, IronPython sử dụng CLR giống như một C# điển hình, vv) - rằng bytecode sau đó được xử lý thêm để thực hiện bởi một máy ảo (trình thông dịch AKA), cũng có thể tạo ra mã máy "ngay trong thời gian" - được gọi là JIT - nếu và khi được bảo hành (CLR và JVM triển khai thường xuyên làm, máy ảo riêng của CPython thường không nhưng có thể được thực hiện để làm như vậy, ví dụ như với psyco hoặc Unladen Swallow).

JIT có thể tự thanh toán cho các chương trình đủ dài (nếu bộ nhớ rẻ hơn chu kỳ CPU), nhưng có thể không (do thời gian khởi động chậm hơn và dung lượng bộ nhớ lớn hơn), đặc biệt khi các loại cũng phải được suy ra hoặc chuyên biệt như một phần của quá trình tạo mã. Việc tạo mã máy mà không suy luận loại hoặc chuyên môn hóa là dễ dàng nếu đó là những gì bạn muốn, ví dụ: freeze hiện nó cho bạn, nhưng nó thực sự không trình bày những lợi thế mà "máy mã fetishists" thuộc tính cho nó. Ví dụ: bạn nhận được tệp nhị phân có thể thực thi từ 1,5 đến 2 MB thay vì "hello world" nhỏ xíu .pyc - không nhiều điểm! -). Đó là thực thi độc lập và phân phối như vậy, nhưng nó sẽ chỉ hoạt động trên một phạm vi rất hẹp cụ thể của các hệ điều hành và các kiến ​​trúc CPU, do đó, sự cân bằng là khá iffy trong hầu hết các trường hợp. Và, thời gian cần để chuẩn bị thực thi là khá dài, do đó, nó sẽ là một lựa chọn điên rồ để làm cho chế độ hoạt động mặc định.

8

Việc thay thế một thông dịch viên bằng trình biên dịch đơn giản sẽ không cung cấp cho bạn hiệu suất tăng cao như bạn có thể nghĩ cho một ngôn ngữ như Python. Khi hầu hết thời gian thực sự chi tiêu tra cứu biểu tượng của các thành viên đối tượng trong từ điển, nó không thực sự quan trọng nếu cuộc gọi đến hàm thực hiện tra cứu như vậy được diễn giải, hoặc là mã máy gốc - sự khác biệt, trong khi không hoàn toàn không đáng kể. bằng cách tra cứu trên không.

Để thực sự cải thiện hiệu suất, bạn cần tối ưu hóa trình biên dịch. Và các kỹ thuật tối ưu hóa ở đây rất khác với những gì bạn có với C++, hoặc thậm chí Java JIT - một trình biên dịch tối ưu cho một ngôn ngữ gõ/gõ động như Python cần thực hiện một số suy luận kiểu rất sáng tạo (bao gồm xác suất - tức là "90% cơ hội của nó là T "và sau đó tạo ra mã máy hiệu quả cho trường hợp đó với một kiểm tra/chi nhánh trước khi nó) và thoát khỏi phân tích. Điều này thật khó.

5

Nỗ lực cần thiết để tạo trình biên dịch tốt để tạo mã gốc cho một ngôn ngữ mới là đáng kinh ngạc.Các nhóm nghiên cứu nhỏ thường mất từ ​​5 đến 10 năm (ví dụ: SML/NJ, Haskell, Clean, Cecil, lcc, Objective Caml, MLton và nhiều nhóm khác). Và khi ngôn ngữ được đề cập yêu cầu kiểm tra kiểu và các quyết định khác được thực hiện trong thời gian chạy, người viết trình biên dịch phải làm việc chăm chỉ hơn để có được hiệu suất mã gốc tốt (ví dụ tuyệt vời, xem Craig Chambers và sau đó là Urs Hoelzle Tự). Hiệu suất đạt được bạn có thể hy vọng là khó thực hiện hơn bạn nghĩ. Hiện tượng này giải thích một phần why so many dynamically typed languages are interpreted. Như đã lưu ý, một trình thông dịch phong nha cũng ngay lập tức di động, trong khi việc chuyển các trình biên dịch sang các kiến ​​trúc máy mới mất nhiều công sức (và là một vấn đề cá nhân tôi đã làm việc trong hơn 20 năm, với một thời gian cho hành vi tốt). Vì vậy, một thông dịch viên là một cách để tiếp cận đối tượng rộng một cách nhanh chóng.

Cuối cùng, mặc dù trình biên dịch nhanh và trình thông dịch chậm tồn tại, thường dễ dàng hơn để làm cho chu trình chỉnh sửa-dịch-đi nhanh hơn bằng cách sử dụng thông dịch viên. (Đối với một số ví dụ tốt đẹp của các trình biên dịch nhanh chóng thấy nói trên lcc cũng như biên dịch go Ken Thompson. Đối với một ví dụ về một thông dịch tương đối chậm thấy GHCi.

2

REPL. Đừng gõ nó cho đến khi bạn đã thử nó. :)

+1

SML/NJ đã cung cấp REPL được biên dịch mã gốc trong hơn 20 năm ... vì có nhiều hệ thống Lisp. –

1

Biên dịch Ruby ít nhất cũng nổi tiếng là khó. Tôi đang làm việc trên một, và là một phần của tôi đã viết một bài đăng blog enumerating some of the issues here.

Cụ thể, Ruby đang gặp một ranh giới rất không rõ ràng (tức là không tồn tại) giữa giai đoạn "đọc" và "thực hiện" của chương trình khiến việc biên dịch hiệu quả trở nên khó khăn. Bạn chỉ có thể mô phỏng những gì người phiên dịch làm, nhưng sau đó bạn sẽ không nhìn thấy tốc độ tăng lên nhiều, vì vậy nó sẽ không có giá trị nỗ lực. Nếu bạn muốn biên dịch nó hiệu quả thì bạn sẽ phải đối mặt với rất nhiều biến chứng khác để xử lý mức độ năng động cực đoan trong Ruby.

Tin tốt là có các kỹ thuật để khắc phục điều này. Tự, Smalltalk và Lisp/Scheme đã xử lý khá thành công với hầu hết các vấn đề tương tự. Nhưng phải mất thời gian để sàng lọc thông qua nó và tìm ra cách làm cho nó hoạt động với Ruby. Nó cũng không giúp Ruby có ngữ pháp rất phức tạp.

1

Hiệu suất tính toán thô có lẽ không phải là mục tiêu của hầu hết các ngôn ngữ thông dịch. Các ngôn ngữ được giải thích thường quan tâm nhiều hơn đến năng suất của lập trình viên so với tốc độ thô. Trong hầu hết các trường hợp, các ngôn ngữ này đủ nhanh để thực hiện các tác vụ mà các ngôn ngữ được thiết kế để giải quyết.

Cho rằng, và đó chỉ là lợi thế duy nhất của trình biên dịch là kiểm tra kiểu (khó làm bằng ngôn ngữ động) và tốc độ, không có nhiều động cơ để viết trình biên dịch cho hầu hết các ngôn ngữ thông dịch.

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