JavaScript có nhanh hơn C++ không

Tôi xem xét một số đánh đổi hiệu suất được thực hiện bằng các cách tiếp cận khác nhau, nhưng đạo đức của câu chuyện là hiệu suất phức tạp và phụ thuộc rất nhiều vào trường hợp sử dụng. Một trong những cân nhắc đánh đổi chính là CPU so với bộ nhớ, nhưng khía cạnh bộ nhớ của phương trình đó có thể trở nên rất phức tạp

Một trong những phần thú vị nhất (ít nhất là theo quan điểm của tôi) khi trở thành kiến ​​trúc sư phần mềm là tư vấn cho các nhà phát triển và giúp họ tiếp cận với các khái niệm mới và ý nghĩa lớn hơn của các quyết định kỹ thuật. Cũng rất thú vị khi thúc đẩy môi trường học tập bằng cách thỉnh thoảng để một nhà phát triển hỗn xược sa vào mặt họ, một phần nào đó của kiểu 'ăn miếng trả miếng' từ khi tôi còn là một nhà phát triển trẻ và hỗn xược.

Một ví dụ hoàn hảo là khi một nhà phát triển xanh thách thức các đề xuất của bạn (thực tế là một kiến ​​trúc sư, bạn luôn đưa ra lựa chọn sai trong mắt người khác) và tiếp tục đặt cược rằng cách tiếp cận của họ là cách tiếp cận tốt nhất. Tôi biết xa mọi thứ, nhưng tôi đã ở đây đủ lâu để thấy một kẻ khờ khạo. Làm thế nào tôi có thể cưỡng lại? . Và rồi nhiều năm sau tôi sẽ viết một bài về nó

cá cược

Thành thật mà nói, tôi không nhớ chi tiết cụ thể (đã vài năm rồi), nhưng tôi nhớ rằng tôi đã đề xuất sử dụng Node. js chủ yếu dựa trên bộ kiến ​​thức của nhóm hiện có, các thư viện có sẵn và nợ kỹ thuật khác. Một junior dev xinh xắn muốn khoe những tân cử nhân khoa học máy tính và thể hiện kỹ năng 'điên rồ' của mình. Có lẽ họ biết tôi chỉ học môn khoa học máy tính và cho rằng tôi đơn giản là không biết máy tính thực sự hoạt động như thế nào (công bằng mà nói, sau ~ 20 năm, tôi đã tin rằng chúng chỉ là phép thuật)

Tuyên bố là một cái gì đó dọc theo dòng tiêu chuẩn 'C ++ nhanh hơn JavaScript', bị phản đối bởi phản hồi (kiến trúc sư khuôn mẫu) của tôi về. nó phụ thuộc. Hoặc có lẽ cụ thể hơn, 'C ++ được tối ưu hóa sẽ hoạt động tốt hơn JavaScript được tối ưu hóa' vì có chi phí không thể tránh khỏi để chạy JavaScript (tốt, bạn có thể biên dịch nó thành một chương trình tĩnh và nhận được hiệu suất tương tự nếu bạn thực sự đã thử). Không cần phải nói, tôi thích một thử thách tốt

Và kết quả…

Điều 'ngạc nhiên' là giải pháp JavaScript nhanh hơn một chút so với chương trình C++ và (quan trọng hơn là từ quan điểm kiến ​​trúc) có lợi ích là nhóm hiện tại có thể bảo trì hoàn toàn. Hãy để dàn hợp xướng phải tặc lưỡi và gãi đầu. TBH, tôi không chắc chắn 100% rằng nó sẽ thắng, nhưng dựa trên khả năng phụ thuộc của trường hợp sử dụng cụ thể này vào các đối tượng bộ nhớ có kích thước động và sự thiếu kinh nghiệm của nhà phát triển, tôi đã đoán

Chờ đợi. Làm sao?

Nếu bạn không thể đoán tại sao, đừng lo lắng. Theo kinh nghiệm của tôi, hầu hết các nhà phát triển cũng sẽ không biết tại sao. Kết quả trái ngược với nguyên tắc chung là các ngôn ngữ 'được biên dịch' nhanh hơn các chương trình 'được thông dịch' và các chương trình 'tĩnh' nhanh hơn các chương trình 'VM'. Nhưng đây chỉ là một quy tắc ngón tay cái

'Tối ưu hóa' là từ khóa trong câu vặn lại ở trên của tôi, vì một chương trình C++ ngây thơ có thể nhanh chóng đi chệch hướng. Mặt khác, nút. js (tận dụng các thư viện V8 & libuv dựa trên C++/C) đã đạt được nhiều bước tiến trong việc tối ưu hóa JS ngu ngốc để chạy nhanh, nghĩa là có những trường hợp JS ngây thơ có thể đánh bại C++ ngây thơ. Nhưng nó rõ ràng là phức tạp hơn thế

Ồ vâng, ký ức…

Hầu hết các nhà phát triển đều quen thuộc với các ý tưởng về ngăn xếp và đống, nhưng nhiều người không hiểu sâu hơn các đặc điểm ở cấp độ bề mặt như ngăn xếp là tuyến tính và đống là một đống có con trỏ (hoặc đại loại như thế). Có lẽ họ cũng đã bỏ lỡ rằng đây chỉ là những khái niệm (và có những cách tiếp cận khác) với nhiều cách triển khai. Phần cứng cấp thấp thường không biết 'đống' là cái quái gì vì phần mềm xác định cách quản lý bộ nhớ* và các lựa chọn đưa ra có thể có tác động lớn đến các đặc tính hiệu suất của chương trình cuối cùng

*Có cả một cái hang thỏ mà bạn có thể (và có thể nên) trèo xuống. Các hạt nhân có thể trở nên phức tạp và phần cứng hiện đại không hề ngu ngốc và thường có thể bao gồm một số tối ưu hóa cho mục đích đặc biệt có thể tận dụng bố cục bộ nhớ cấp cao trong các tối ưu hóa của chúng. Điều này có nghĩa là phần mềm có thể (hoặc buộc phải) ủy quyền cho các tính năng quản lý bộ nhớ do phần cứng cung cấp. Và điều này thậm chí không bắt đầu bao gồm ảo hóa…

gian lận. Thu gom rác thải

Chắc chắn, nút. js mất nhiều thời gian hơn để bắt đầu vì nó phải tải và chạy tập lệnh thông qua trình biên dịch JIT của nó, nhưng sau khi được tải, nó có một lợi thế bí mật. Đó là rác được thu thập

Mặt khác, trong chương trình C++, ứng dụng thường xuyên tạo các đối tượng có kích thước động trong heap và sau đó xóa chúng. Điều này có nghĩa là bộ cấp phát của chương trình phải cấp phát và giải phóng bộ nhớ trong heap nhiều lần. Đây thường không phải là một thao tác nhanh và phụ thuộc nhiều vào thuật toán nào được sử dụng trong bộ cấp phát. Trong nhiều trường hợp, dealloc đặc biệt chậm và phân bổ được khử trùng cũng không phải là rẻ nhất

Đối với nút. js, gian lận xảy ra do chương trình chạy một lần và thoát. Nút. js chạy tập lệnh, phân bổ tất cả bộ nhớ cần thiết nhưng việc xóa thực tế bị hoãn lại để trình thu gom rác xử lý (tốt nhất là trong thời gian nhàn rỗi). Giờ đây, việc thu gom rác vốn dĩ không tốt hơn hay tệ hơn các chiến lược quản lý bộ nhớ khác (đánh đổi, đánh đổi, đánh đổi…), nhưng trong trường hợp của chương trình cụ thể này, nó tỏ ra có lợi vì nó chưa bao giờ thực sự chạy. Chúng tôi đã ném một loạt các đối tượng vào bộ nhớ, sau đó bỏ tất cả cùng một lúc khi chúng tôi rời đi

Điều này phải trả giá, Node. js sử dụng nhiều bộ nhớ hơn đáng kể so với chương trình C++. Đó là sự đánh đổi kinh điển của 'ít cpu hơn = nhiều bộ nhớ hơn so với ít bộ nhớ hơn = nhiều cpu hơn', nhưng đó là một sự đánh đổi tốt để thắng cược

Và đặt cược chỉ hoạt động vì nhà phát triển đã chọn một chiến lược ngây thơ và thực hiện nó một cách chính xác. Một cách nhanh chóng để giành chiến thắng là thêm rò rỉ bộ nhớ, cố tình giữ tất cả các phân bổ trong bộ nhớ. Nó có thể vẫn sử dụng ít bộ nhớ hơn Node. js và nhanh hơn đáng kể. Hoặc bạn có thể sử dụng những thứ như bộ đệm được phân bổ ngăn xếp để tăng hiệu suất hơn nữa và 'sẵn sàng sản xuất'

Điều này cũng dẫn đến một vấn đề với điểm chuẩn, họ thường sử dụng một số liệu đơn lẻ như ops/s. Câu chuyện JS vs C++ này là một ví dụ hoàn hảo về lý do tại sao biết tổng chi phí hiệu suất lại quan trọng trước khi đưa ra lựa chọn. Trong kiến ​​trúc phần mềm, bạn quan tâm đến 'tổng chi phí sở hữu' cho tất cả các lựa chọn được đưa ra

Ngày nay. Nhập Rust

Rust là một trong những ngôn ngữ của tôi ngày nay. Nó có rất nhiều tính năng hiện đại tuyệt vời, nhanh và có mô hình bộ nhớ tuyệt vời dẫn đến mã nói chung an toàn. Nó chắc chắn có nhược điểm, thời gian biên dịch vẫn là một vấn đề và nó có một số ngữ nghĩa kỳ lạ ở đây và ở đó, nhưng nói chung tôi thực sự khuyên bạn nên sử dụng nó. Bạn có thể có nhiều quyền kiểm soát đối với cách quản lý bộ nhớ trong Rust, nhưng bộ nhớ 'ngăn xếp' tuân theo mô hình sở hữu tạo ra sự an toàn cho thương hiệu của nó

Một trong những dự án mà tôi hiện đang thực hiện là máy chủ lưu trữ FaaS (Chức năng dưới dạng dịch vụ) được viết bằng Rust để thực thi các chức năng WASM (WebAssugging). Nó được thiết kế để thực thi các chức năng bị cô lập một cách an toàn một cách nhanh chóng, giảm thiểu chi phí sử dụng FaaS. Và nó khá nhanh, có thể nhận được 90 nghìn yêu cầu sạch mỗi giây trên mỗi lõi. Tốt hơn nữa, nó có thể làm điều đó với tổng dung lượng bộ nhớ tham chiếu là ~20MB

Điều này có liên quan gì đến Node. js & C++? . js làm điểm chuẩn cho hiệu suất 'hợp lý' của tôi (Go được sử dụng làm mục tiêu 'trong mơ', thật khó để so sánh với ngôn ngữ được thiết kế cho các dịch vụ web trong khi bổ sung thêm chi phí hoạt động của FaaS) và các phiên bản đầu tiên của chương trình không hứa hẹn ( . js). Mặc dù người ta thường tập trung vào việc 'làm cho thứ gì đó hoạt động' trước khi tối ưu hóa, nhưng không phải là cảm giác tuyệt vời khi thực hiện hàng tấn công việc bằng ngôn ngữ 'nhanh' chỉ để bị đánh bại bởi JavaScript mới làm quen

Tuy nhiên, nút cổ chai đã khá rõ ràng từ rất sớm. Đó là quản lý bộ nhớ. Mỗi hàm khách được phân bổ một mảng bộ nhớ, nhưng có rất nhiều chi phí hoạt động giữa việc phân bổ bên trong hàm và sao chép dữ liệu đến và từ bộ nhớ của hàm và bộ nhớ của máy chủ. Do dữ liệu động bị ném xung quanh, bộ cấp phát đã bị chặn từ mọi hướng. Giải pháp. gian lận (loại)

Tôi yêu đống, tôi sẽ lấy hai (hoặc ba)

Về cơ bản, một đống chỉ là một số bộ nhớ mà bộ cấp phát quản lý ánh xạ cho. Chương trình yêu cầu N đơn vị bộ nhớ và bộ cấp phát sẽ tìm thấy nó trong nhóm bộ nhớ khả dụng của nó (hoặc yêu cầu máy chủ cung cấp thêm bộ nhớ), lưu trữ rằng các đơn vị đang được sử dụng và sau đó trả về con trỏ vị trí của bộ nhớ đó. Khi chương trình được thực hiện với bộ nhớ đó, nó sẽ báo cho bộ cấp phát và bộ cấp phát sau đó cập nhật ánh xạ của nó để biết các đơn vị đó hiện có sẵn. Đơn giản, phải không?

Các vấn đề bắt đầu phát sinh trong khi phân bổ một loạt các đơn vị bộ nhớ có kích thước khác nhau với thời gian tồn tại khác nhau, bạn sẽ gặp phải rất nhiều phân mảnh làm tăng chi phí phân bổ bộ nhớ mới. Đây là lúc bạn bắt đầu thấy hình phạt hiệu suất đó bắt đầu vì về cơ bản, nó là chương trình riêng của nó chỉ để tìm ra nơi cất giữ mọi thứ. Rõ ràng là không có một giải pháp nào cho vấn đề này, có rất nhiều thuật toán phân bổ khác nhau từ bạn bè đến phiến cho đến khối. Mỗi cách tiếp cận đều có sự đánh đổi, nghĩa là bạn có thể chọn cách nào phù hợp nhất với trường hợp sử dụng của mình (hoặc chỉ chọn cách mặc định như hầu hết mọi người vẫn làm)

Bây giờ để gian lận, bạn không cần phải chọn chỉ một cách tiếp cận. Và đối với FaaS, bạn có thể thoải mái với dealloc mỗi lần chạy và chỉ cần xóa toàn bộ đống sau mỗi lần chạy. Và bạn có thể sử dụng các trình cấp phát khác nhau cho các phần khác nhau của vòng đời chức năng, chẳng hạn như. g. khởi tạo so với chạy. Điều này cho phép một chức năng sạch (được đặt lại về cùng trạng thái bộ nhớ mỗi lần chạy) hoặc một chức năng có trạng thái (duy trì trạng thái giữa các lần chạy) và mỗi trường hợp được tối ưu hóa bằng cách sử dụng một chiến lược bộ nhớ khác

Đối với dự án FaaS của tôi, cuối cùng chúng tôi đã xây dựng một bộ cấp phát động chọn thuật toán cấp phát dựa trên mức sử dụng và lựa chọn đó vẫn tồn tại giữa các lần chạy. Đối với các chức năng 'mức sử dụng thấp' (dường như là phần lớn các chức năng cho đến nay), một bộ cấp phát ngăn xếp ngây thơ được sử dụng để chỉ duy trì một con trỏ duy nhất tới vị trí trống tiếp theo. Khi dealloc được gọi, nếu đơn vị là đơn vị cuối cùng trong ngăn xếp, nó sẽ chỉ quay trở lại con trỏ, nếu không thì đó là noop. Khi chức năng hoàn thành, con trỏ được đặt thành 0 (như Node. js thoát trước GC). Nếu hàm đạt đến một số lượng thỏa thuận không thành công nhất định và một ngưỡng sử dụng nhất định, thì một thuật toán phân bổ khác sẽ được sử dụng cho phần còn lại của lệnh gọi. Kết quả là cấp phát bộ nhớ rất nhanh trong phần lớn các trường hợp

Ngoài ra còn có một 'đống' khác được sử dụng trong thời gian chạy, đó là máy chủ - chức năng chia sẻ bộ nhớ. Nó sử dụng cùng một chiến lược phân bổ động và cho phép ghi trực tiếp vào bộ nhớ của hàm, bỏ qua bước sao chép trong các phiên bản đầu tiên. Điều này có nghĩa là I/O được sao chép trực tiếp từ nhân sang chức năng khách, bỏ qua thời gian chạy máy chủ và cải thiện đáng kể thông lượng

Nút. js vs Rust

Sau khi tối ưu hóa, thời gian chạy Rust FaaS kết thúc nhanh hơn >70% trong khi sử dụng bộ nhớ ít hơn >90% so với Node tham chiếu của chúng tôi. triển khai js. Nhưng điều quan trọng là 'sau khi tối ưu hóa', việc triển khai ban đầu chậm hơn. Và nó đã yêu cầu đặt một số hạn chế đối với các chức năng WASM để hoạt động, mặc dù những hạn chế đó được áp dụng rõ ràng trong quá trình biên dịch với sự không tương thích hiếm gặp

Lợi ích chính của việc triển khai Rust là dung lượng bộ nhớ thấp, tất cả RAM bổ sung có thể được sử dụng cho những thứ như bộ nhớ đệm và lưu trữ trong bộ nhớ phân tán. Điều đó có nghĩa là nó có thể thậm chí còn nhanh hơn trong quá trình sản xuất bằng cách giảm chi phí I/O, đây có lẽ là một chiến thắng lớn hơn so với mức tăng hiệu suất CPU khiêm tốn

Chúng tôi dự kiến ​​có nhiều tối ưu hóa hơn, nhưng chúng chủ yếu liên quan đến các thay đổi đối với lớp máy chủ có ý nghĩa bảo mật lớn. Chúng cũng không liên quan trực tiếp đến hiệu suất quản lý bộ nhớ, nhưng chúng cung cấp nhiều nguồn lực cho trại 'Rust nhanh hơn Node'

Sự kết luận

Không thực sự chắc chắn. Tôi đoán một vài điểm

  • Quản lý bộ nhớ rất thú vị và mọi cách tiếp cận đều có sự đánh đổi. Có thể đạt được hiệu suất cao bằng cách chơi với các chiến lược khác nhau
  • Tôi vẫn sử dụng (và khuyên dùng) cả Node. js và Rust cho các mục đích khác nhau, vì vậy cũng không có chiến thắng nào. JavaScript có khả năng di động tuyệt vời và hoạt động tuyệt vời cho một loạt các tình huống gốc trên đám mây, nhưng Rust là một lựa chọn tuyệt vời khi hiệu suất thực sự quan trọng
  • Và bất cứ khi nào tôi nói JavaScript, ý tôi thực sự là TypeScript. Rốt cuộc tôi không phải là một kẻ man rợ

Vào cuối ngày, bạn phải chọn công nghệ tốt nhất cho tình huống của mình và đó hiếm khi là một câu trả lời đơn giản, nhưng việc hiểu các đặc điểm khác nhau của các ngăn xếp khác nhau chắc chắn có thể hữu ích

C nhanh hơn bao nhiêu so với js?

Điều đó có nghĩa là, nếu bạn đang sử dụng một hệ thống như vậy, thì bạn đang nói tính bằng micro giây cho C và mili giây cho JavaScript (theo tài liệu trực tuyến của JS). Vì vậy, thay vì JS nhanh hơn bốn lần, C++ thực sự nhanh hơn 250 lần

C hay JavaScript nào tốt hơn?

C thường được sử dụng cho các máy tính nhúng và các ứng dụng yêu cầu hiệu năng cao như hệ điều hành . JavaScript lần đầu tiên chỉ được nhúng trong các trang web, nhưng nó đang tìm kiếm một vai trò mới trong các ứng dụng máy chủ được phát triển thông qua Node.

Java có chạy nhanh hơn C không?

Thời gian đã trôi qua. Dựa trên những kết quả này, C là 2. Chậm hơn 34 lần so với Java và Python là 33. Chậm hơn 34 lần so với Java.

JavaScript có phải là ngôn ngữ nhanh nhất không?

js một trong những ngôn ngữ lập trình nhanh nhất hiện nay . Nút. js cũng biên dịch nhanh vì nó chạy trên công cụ JavaScript V8 của Google biên dịch mã thành các hướng dẫn máy gốc để làm cho nó chạy nhanh. Do đó, cho phép bạn xây dựng các ứng dụng của mình nhanh hơn.

Cái nào nhanh hơn C # hoặc js?

Nếu bạn là nhà phát triển C# thành thạo và nhà phát triển JavaScript mới làm quen - C# của bạn chắc chắn sẽ nhanh hơn . Nếu bạn thành thạo cả hai thì C# của bạn có thể sẽ nhanh hơn, nhưng sự khác biệt có thể không nhiều như bạn nghĩ - đây là tất cả các chương trình cụ thể.

Có ngôn ngữ nào nhanh hơn C không?

Đây là lý do tại sao Fortran thường nhanh hơn C . Đây là lý do tại sao các thư viện số vẫn được viết bằng Fortran. Tuy nhiên, nó phải trả giá bằng số học con trỏ.