Tại sao chúng ta phải đánh đổi sự trừu tượng cho tốc độ?


11

Tại sao các ngôn ngữ cấp cao dường như không bao giờ đạt được các ngôn ngữ cấp thấp hơn về tốc độ? Ví dụ về các ngôn ngữ cấp cao sẽ là Python, Haskell và Java. Các ngôn ngữ cấp thấp sẽ khó xác định hơn, nhưng giả sử C. Có thể tìm thấy các phép so sánh trên Internet và tất cả đều đồng ý rằng C nhanh hơn rất nhiều, đôi khi bằng 10 hoặc hơn.1

Điều gì gây ra sự khác biệt lớn như vậy trong hiệu suất và tại sao các ngôn ngữ cấp cao không thể bắt kịp?

Ban đầu tôi tin rằng đây là tất cả lỗi của trình biên dịch và mọi thứ sẽ được cải thiện trong tương lai, nhưng một số ngôn ngữ cấp cao phổ biến nhất đã tồn tại trong nhiều thập kỷ và chúng vẫn bị tụt lại phía sau khi nói về tốc độ. Họ có thể đơn giản biên dịch thành một cây cú pháp C tương tự và sau đó làm theo các quy trình tương tự tạo mã máy không? Hoặc có lẽ đó là một cái gì đó để làm với chính cú pháp?


1 ví dụ:


5
"Và tất cả họ đều đồng ý rằng C nhanh hơn rất nhiều" - điều đó chắc chắn sai.
Raphael

2
Dù sao, tôi nghĩ rằng bài đăng được trả lời tốt nhất bởi câu trả lời của tôi cho một câu hỏi tương tự khó hiểu ; bản sao?
Raphael

2
Xem thêm ở đâyở đây . Coi chừng những câu trả lời linh tinh.
Raphael

Có liên quan: stackoverflow.com/questions/6964392/ Từ Huyền thoại về tất cả các "ngôn ngữ cấp cao" bị chậm là khá buồn.
xji

Tôi đồng ý với những người khác rằng sự trừu tượng! = Chậm, nhưng tôi sợ có một vấn đề lớn hơn nhiều mà các giáo viên khoa học máy tính (tôi là một) không nhận thức được. Đó là đối với các chương trình thực tế (1000 dòng mã trở lên) có nhiều cách để thực hiện chúng và những cách đó có thể khác nhau về hiệu suất theo các đơn đặt hàng cường độ. Chỉ nghĩ về big-O hoàn toàn bỏ lỡ vấn đề. Kiểm tra tại đây .
Mike Dunlavey

Câu trả lời:


19

Ra mắt một số huyền thoại

  1. Không có thứ gọi là ngôn ngữ nhanh. Một ngôn ngữ thường có thể tạo mã nhanh, nhưng các ngôn ngữ khác nhau sẽ vượt trội trên các điểm chuẩn khác nhau. Chúng ta có thể xếp hạng các ngôn ngữ trên một tập hợp các điểm chuẩn thiếu sót cụ thể, nhưng không thể xếp hạng các ngôn ngữ trong chân không.

  2. Mã C có xu hướng nhanh hơn bởi vì những người cần mỗi inch hiệu suất sử dụng C. Một thống kê rằng C nhanh hơn "với hệ số 10" có thể không chính xác, bởi vì có thể những người sử dụng Python không quan tâm nhiều về tốc độ và không viết mã Python tối ưu. Chúng tôi thấy điều này đặc biệt với các ngôn ngữ như Haskell. Nếu bạn thực sự cố gắng , bạn có thể viết Haskell hoạt động ngang bằng với C. Nhưng hầu hết mọi người không cần hiệu suất đó, vì vậy chúng tôi có một loạt các so sánh thiếu sót.

  3. Đôi khi, nó không an toàn, không trừu tượng, điều đó làm cho C nhanh. Việc thiếu các giới hạn mảng và kiểm tra con trỏ null giúp tiết kiệm thời gian và là nguyên nhân của nhiều lỗ hổng bảo mật trong suốt những năm qua.

  4. Ngôn ngữ không nhanh, triển khai nhanh. Nhiều ngôn ngữ trừu tượng bắt đầu chậm, vì tốc độ không phải là mục tiêu của chúng, nhưng sẽ nhanh hơn khi ngày càng có nhiều tối ưu hóa được thêm vào.

Sự trừu tượng so với sự đánh đổi tốc độ có lẽ không chính xác. Tôi sẽ đề nghị một so sánh tốt hơn:

Đơn giản, tốc độ, trừu tượng: Chọn hai.

Nếu chúng ta đang chạy các thuật toán giống hệt nhau trong các ngôn ngữ khác nhau, câu hỏi về tốc độ sẽ thuộc về vấn đề "Chúng ta cần phải làm bao nhiêu công cụ trong thời gian chạy để thực hiện công việc này?"

Trong một ngôn ngữ trừu tượng rất đơn giản , chẳng hạn như Python hoặc JavaScript, vì chúng ta không biết về biểu diễn dữ liệu cho đến khi chạy, cuối cùng có rất nhiều tham chiếu khử con trỏ và kiểm tra động xảy ra trong thời gian chạy, rất chậm.

Tương tự như vậy, có rất nhiều kiểm tra cần được thực hiện để đảm bảo rằng bạn không phá hỏng máy tính của mình. Khi bạn gọi một hàm trong python, nó cần đảm bảo rằng đối tượng bạn gọi thực sự là một hàm, vì nếu không, bạn có thể sẽ thực thi mã ngẫu nhiên thực hiện những điều khủng khiếp.

Cuối cùng, hầu hết các ngôn ngữ trừu tượng đều có chi phí từ bộ sưu tập rác. Hầu hết các lập trình viên đã đồng ý rằng phải theo dõi vòng đời của bộ nhớ được phân bổ động là một nỗi đau, và họ muốn có một người thu gom rác làm điều đó cho họ trong thời gian chạy. Điều này cần có thời gian, rằng một chương trình C không cần phải chi cho GC.

Tóm tắt không có nghĩa là chậm

Tuy nhiên, có những ngôn ngữ vừa trừu tượng vừa nhanh chóng. Nổi trội nhất trong thời đại này là Rust. Bằng cách giới thiệu trình kiểm tra mượn và hệ thống loại tinh vi, nó cho phép mã trừu tượng và sử dụng thông tin thời gian biên dịch để giảm lượng công việc chúng ta cần thực hiện trong thời gian chạy (tức là thu gom rác).

Bất kỳ ngôn ngữ gõ tĩnh nào cũng giúp chúng tôi tiết kiệm thời gian bằng cách giảm số lần kiểm tra thời gian chạy và giới thiệu sự phức tạp bằng cách yêu cầu chúng tôi làm hài lòng một máy đánh chữ tại thời gian biên dịch. Tương tự, nếu chúng ta có một ngôn ngữ mã hóa cho dù một giá trị có thể là null vào hệ thống loại của nó hay không, chúng ta có thể tiết kiệm thời gian bằng cách kiểm tra thời gian kiểm tra con trỏ null.


2
"Mã C có xu hướng nhanh hơn bởi vì những người cần mỗi inch hiệu suất sử dụng C" - chính xác. Tôi muốn xem điểm chuẩn của mẫu "thời gian chạy mã trung bình được viết bởi sinh viên / chuyên gia X năm có Y năm kinh nghiệm trong Z". Tôi hy vọng rằng câu trả lời cho C thường là "mã không chính xác" cho X nhỏ và Y. Thật thú vị khi xem bạn cần thêm bao nhiêu kinh nghiệm / chuyên môn để tận dụng tiềm năng cho hiệu suất C hứa hẹn.
Raphael

Haskell thực sự là một ngoại lệ chứng minh quy tắc. ;) Tôi nghĩ rằng C ++ đã tìm thấy một trung đất khá hợp lý về GC bởi con trỏ thông minh của mình, miễn là bạn không tổ chia sẻ con trỏ và sẽ càng nhanh càng C.
Kaveh

0

Dưới đây là một vài ý tưởng chính về điều này.

  • một so sánh dễ dàng / nghiên cứu trường hợp để làm cho trừu tượng wrt so với tốc độ là java vs c ++. java được thiết kế để trừu tượng hóa một số khía cạnh cấp thấp hơn của c ++ như quản lý bộ nhớ. trong những ngày đầu (khoảng phát minh ngôn ngữ giữa những năm 1990), phát hiện rác java không nhanh lắm, nhưng sau vài thập kỷ nghiên cứu, những người thu gom rác cực kỳ tinh vi / nhanh chóng / tối ưu hóa, vì vậy những người thu gom rác hầu như bị loại bỏ hiệu suất cống trên java. ví dụ: xem ngay cả tiêu đề năm 1998 này: Các thử nghiệm hiệu năng cho thấy Java nhanh như C ++ / javaworld

  • ngôn ngữ lập trình và sự tiến hóa lâu dài của chúng có một "cấu trúc kim tự tháp / phân cấp" vốn có như một kiểu mẫu thiết kế siêu việt. trên đỉnh của kim tự tháp là một cái gì đó kiểm soát các phần thấp hơn khác của kim tự tháp. nói cách khác, các khối xây dựng được tạo ra từ các khối xây dựng. điều này cũng có thể được nhìn thấy trong cấu trúc API. theo nghĩa này, sự trừu tượng hóa lớn hơn luôn dẫn đến một số thành phần mới ở đỉnh kim tự tháp kiểm soát các thành phần khác. Vì vậy, theo một nghĩa nào đó, nó không đến nỗi tất cả các ngôn ngữ đều ngang hàng với nhau, nhưng các ngôn ngữ đó gọi theo thói quen trong các ngôn ngữ khác. ví dụ: rất nhiều ngôn ngữ script (python / ruby) thường gọi các thư viện C hoặc C ++, các thói quen số hoặc ma trận là một ví dụ điển hình cho việc này. do đó, có các ngôn ngữ cấp cao hơn và cấp thấp hơn và các ngôn ngữ cấp cao gọi các ngôn ngữ cấp thấp hơn để nói. trong ý nghĩa này đo tốc độ tương đối là không thực sự so sánh.

  • người ta có thể nói rằng các ngôn ngữ mới đang được phát minh mọi lúc để cố gắng tối ưu hóa sự trừu tượng / đánh đổi tốc độ, tức là mục tiêu thiết kế chính của nó. có thể nó không quá nhiều đến nỗi sự trừu tượng lớn hơn luôn hy sinh tốc độ nhưng sự cân bằng tốt hơn luôn được tìm kiếm với các thiết kế mới hơn. ví dụ: Google Go theo nhiều cách được tối ưu hóa cụ thể với ý định đánh đổi, đồng thời là cả trình độ cao và trình diễn. xem ví dụ: Google Go: Tại sao ngôn ngữ lập trình của Google có thể cạnh tranh với Java trong thế giới doanh nghiệp / công nghệ


0

Cách tôi thích nghĩ về hiệu suất là "nơi cao su gặp đường". Máy tính thực hiện các hướng dẫn, không trừu tượng.

Điều tôi muốn thấy là đây: có phải mọi hướng dẫn được thực hiện "kiếm tiền giữ lại" bằng cách đóng góp đáng kể vào kết quả cuối cùng? Như một ví dụ quá đơn giản, hãy xem xét việc tìm kiếm một mục trong bảng 1024 mục. Đó là vấn đề 10 bit vì chương trình phải "học" 10 bit trước khi biết câu trả lời. Nếu thuật toán là tìm kiếm nhị phân, mỗi lần lặp lại đóng góp 1 bit thông tin, vì nó thu nhỏ độ không đảm bảo theo hệ số 2. Vì vậy, phải mất 10 lần lặp, mỗi lần lặp cho mỗi bit.

Mặt khác, tìm kiếm tuyến tính ban đầu rất không hiệu quả vì các lần lặp đầu tiên thu nhỏ độ không đảm bảo bằng một yếu tố rất nhỏ. Vì vậy, họ không học được nhiều cho những nỗ lực đã bỏ ra.

OK, vì vậy nếu trình biên dịch có thể cho phép người dùng gói các hướng dẫn tốt theo cách được coi là "trừu tượng", thì tốt thôi.


0

Về bản chất, sự trừu tượng hóa của nó làm giảm sự phân chia thông tin, cho cả lập trình viên và các lớp thấp hơn của hệ thống (trình biên dịch, thư viện và hệ thống thời gian chạy). Có lợi cho sự trừu tượng, điều này thường cho phép các lớp thấp hơn cho rằng lập trình viên không quan tâm đến bất kỳ hành vi không xác định nào, cung cấp sự linh hoạt hơn trong việc cung cấp hành vi được chỉ định.

Một ví dụ về lợi ích tiềm năng từ khía cạnh "không quan tâm" này là trong cách bố trí dữ liệu. Trong C (độ trừu tượng thấp), trình biên dịch bị hạn chế hơn trong tối ưu hóa bố cục dữ liệu. Ngay cả khi trình biên dịch có thể nhận ra (ví dụ, thông qua thông tin hồ sơ) rằng tối ưu hóa tránh nóng / lạnh hoặc chia sẻ sai sẽ có lợi, thì thường không được áp dụng như vậy. (Có một số tự do trong việc chỉ định "như thể", nghĩa là xử lý đặc tả một cách trừu tượng hơn, nhưng việc tạo ra tất cả các tác dụng phụ tiềm ẩn sẽ tạo thêm gánh nặng cho trình biên dịch.)

Một đặc điểm kỹ thuật trừu tượng hơn cũng mạnh mẽ hơn đối với những thay đổi trong sự đánh đổi và sử dụng. Các lớp thấp hơn ít bị ràng buộc hơn trong việc mở lại chương trình cho các đặc điểm hệ thống mới hoặc sử dụng mới. Một đặc tả cụ thể hơn phải được viết lại bởi một lập trình viên hoặc nỗ lực bổ sung phải được thực hiện bởi các lớp thấp hơn để đảm bảo hành vi "như thể".

Khía cạnh cản trở hiệu suất của việc trừu tượng hóa thông tin là "không thể diễn tả", mà các lớp thấp hơn thường sẽ xử lý là "không biết". Điều này có nghĩa là các lớp thấp hơn phải phân biệt thông tin hữu ích để tối ưu hóa từ các phương tiện khác như sử dụng chung điển hình, sử dụng mục tiêu hoặc thông tin hồ sơ cụ thể.

Tác động của việc che giấu thông tin cũng theo hướng khác. Lập trình viên có thể làm việc hiệu quả hơn bằng cách không phải xem xét và chỉ định từng chi tiết, nhưng lập trình viên có thể có ít thông tin hơn về tác động của các lựa chọn thiết kế cấp cao hơn.

Mặt khác, khi mã cụ thể hơn (ít trừu tượng hơn), các lớp thấp hơn của hệ thống có thể đơn giản hơn làm những gì chúng được bảo làm như chúng được bảo làm. Nếu mã được viết tốt cho mục đích sử dụng của nó, nó sẽ phù hợp với mục đích sử dụng của nó. Một ngôn ngữ ít trừu tượng (hoặc mô hình lập trình) cho phép lập trình viên tối ưu hóa việc thực hiện bằng thiết kế chi tiết và bằng cách sử dụng thông tin không dễ dàng truyền đạt bằng ngôn ngữ nhất định cho các lớp thấp hơn.

Như đã lưu ý, các ngôn ngữ trừu tượng (hoặc kỹ thuật lập trình) ít hấp dẫn hơn khi kỹ năng và nỗ lực lập trình bổ sung có thể tạo ra kết quả đáng giá. Khi nhiều nỗ lực và kỹ năng lập trình viên được áp dụng, kết quả thường sẽ tốt hơn. Ngoài ra, một hệ thống ngôn ngữ được sử dụng ít hơn cho các ứng dụng quan trọng về hiệu năng (thay vào đó nhấn mạnh nỗ lực phát triển hoặc độ tin cậy - kiểm tra giới hạn và thu gom rác không chỉ là về năng suất của lập trình viên mà còn về tính chính xác, trừu tượng giảm tải tinh thần cho lập trình viên có thể cải thiện độ tin cậy) sẽ có ít áp lực hơn để cải thiện hiệu suất.

Tính đặc hiệu cũng hoạt động theo nguyên tắc không lặp lại chính bạn vì tối ưu hóa thường có thể bằng cách điều chỉnh mã theo một mục đích sử dụng cụ thể. Điều này có độ tin cậy rõ ràng và ý nghĩa nỗ lực lập trình.

Các tóm tắt được cung cấp bởi một ngôn ngữ cũng có thể bao gồm các công việc không mong muốn hoặc không cần thiết mà không có cách nào để chọn một trừu tượng ít nặng hơn. Mặc dù các công việc không cần thiết đôi khi có thể được phát hiện và loại bỏ bởi các lớp thấp hơn (ví dụ: kiểm tra giới hạn có thể được trích xuất từ ​​phần thân của vòng lặp và loại bỏ hoàn toàn trong một số trường hợp), xác định rằng đó là một tối ưu hóa hợp lệ đòi hỏi nhiều "kỹ năng và nỗ lực" hơn trình biên dịch.

Tuổi ngôn ngữ và mức độ phổ biến cũng là những yếu tố đáng chú ý cả về sự sẵn có của các lập trình viên lành nghề và chất lượng của các lớp thấp hơn của hệ thống (bao gồm các thư viện trưởng thành và các ví dụ mã).

Một yếu tố kết hợp khác trong các so sánh như vậy là sự khác biệt có phần trực giao giữa biên dịch trước thời hạn và biên dịch chỉ trong thời gian. Mặc dù việc biên dịch đúng lúc có thể dễ dàng khai thác thông tin hồ sơ hơn (không dựa vào lập trình viên để cung cấp các hoạt động hồ sơ) và tối ưu hóa cụ thể theo hệ thống (biên dịch trước có thể nhắm mục tiêu tương thích rộng hơn), chi phí tối ưu hóa tích cực được tính là một phần của hiệu suất thời gian chạy. Kết quả JIT có thể được lưu trong bộ nhớ cache, giảm chi phí cho mã thường được sử dụng. (Việc thay thế tái cấu trúc nhị phân có thể cung cấp một số lợi thế của quá trình biên dịch JIT, nhưng các định dạng phân phối nhị phân truyền thống làm giảm hầu hết thông tin mã nguồn có khả năng buộc hệ thống cố gắng phân biệt ý định từ việc triển khai cụ thể.)

(Các ngôn ngữ trừu tượng thấp hơn, vì chúng nhấn mạnh vào kiểm soát lập trình viên, ưu tiên sử dụng biên dịch trước thời gian. Biên dịch thời gian cài đặt có thể được chấp nhận, mặc dù lựa chọn triển khai thời gian liên kết sẽ cung cấp kiểm soát lập trình lớn hơn. )

Ngoài ra còn có vấn đề về phương pháp điểm chuẩn. Nỗ lực / kỹ năng tương đương thực sự không thể thiết lập được, nhưng thậm chí nó có thể đạt được mục tiêu ngôn ngữ sẽ làm sai lệch kết quả. Nếu cần thời gian lập trình tối đa thấp, một chương trình dành cho ngôn ngữ ít trừu tượng hơn thậm chí có thể không được viết hoàn toàn so với biểu thức thành ngữ đơn giản trong ngôn ngữ trừu tượng hơn. Nếu thời gian / nỗ lực lập trình tối đa cao được cho phép, các ngôn ngữ trừu tượng thấp hơn sẽ có lợi thế. Điểm chuẩn trình bày kết quả nỗ lực tốt nhất đương nhiên sẽ được thiên vị trong các ngôn ngữ ít trừu tượng hơn.

Đôi khi có thể lập trình theo cách ít thành ngữ hơn trong ngôn ngữ để đạt được lợi thế của các mô hình lập trình khác, nhưng ngay cả khi khả năng biểu cảm có sẵn, sự đánh đổi để làm như vậy có thể không thuận lợi.

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.