paint-brush
Kẻ giết C ++ thực sự (Không phải bạn, Rust)từ tác giả@oleksandrkaleniuk
50,481 lượt đọc
50,481 lượt đọc

Kẻ giết C ++ thực sự (Không phải bạn, Rust)

từ tác giả Oleksandr Kaleniuk17m2023/02/14
Read on Terminal Reader

dài quá đọc không nổi

Viết mã tốt với các lập trình viên tồi là một vấn đề của thế kỷ XX. Bây giờ chúng ta cần mã thậm chí còn tốt hơn nhưng được viết bởi các lập trình viên giỏi, một nhiệm vụ mà không kẻ sát thủ C++ hiện tại nào giải quyết được. Cuộc cách mạng thực sự nằm ngoài trình biên dịch.
featured image - Kẻ giết C ++ thực sự (Không phải bạn, Rust)
Oleksandr Kaleniuk HackerNoon profile picture


Xin chào! Tôi là Oleksandr Kaleniuk và tôi là một người cuồng C++. Tôi đã viết bằng C++ được 17 năm và trong suốt 17 năm đó, tôi đã cố gắng thoát khỏi cơn nghiện tàn khốc này.


Tất cả bắt đầu vào năm 2005 với một công cụ mô phỏng không gian 3D. Công cụ này có mọi thứ mà C++ có vào năm 2005. Con trỏ ba sao, tám lớp phụ thuộc và macro kiểu C ở khắp mọi nơi. Cũng có các bit lắp ráp. Iterators kiểu Stepanov và siêu mã kiểu Alexandrescu. Mã có tất cả mọi thứ. Tất nhiên, ngoại trừ câu trả lời cho câu hỏi quan trọng nhất: tại sao?


Trong một thời gian, ngay cả câu hỏi này đã được trả lời. Không phải là “để làm gì” mà là “tại sao lại đến”. Hóa ra, động cơ này đã được viết trong khoảng 8 năm bởi 5 đội khác nhau. Và mọi nhóm đều mang mốt yêu thích của họ đến dự án gói mã cũ thành các trình bao bọc có kiểu dáng mới, chỉ thêm khoảng 10-20 microcarmacks chức năng trong khi thực hiện.


Lúc đầu, tôi cũng thật thà mò mẫm từng thứ một. Đó không phải là một trải nghiệm hài lòng, hoàn toàn không, và đến một lúc nào đó, tôi đã bỏ cuộc. Tôi vẫn đang đóng các nhiệm vụ và sửa lỗi. Không thể nói rằng tôi làm việc rất hiệu quả, nhưng đủ để không bị sa thải. Nhưng sau đó, sếp của tôi hỏi tôi: "bạn có muốn viết lại một số mã đổ bóng từ Assembly sang GLSG không?" Tôi nghĩ có chúa mới biết GLSL này trông như thế nào nhưng nó không thể tệ hơn C++ và nói có. Nó không tệ hơn.


Và loại này đã trở thành một khuôn mẫu. Tôi vẫn chủ yếu viết bằng C++ nhưng mỗi lần ai đó hỏi tôi "bạn có muốn làm thứ không phải C++ đó không?" Tôi chắc chăn!" và tôi đã làm điều đó bất kể đó là gì. Tôi đã viết bằng C89, MASM32, C#, PHP, Delphi, ActionScript, JavaScript, Erlang, Python, Haskell, D, Rust và thậm chí cả ngôn ngữ kịch bản InstallShield cực kỳ tệ đó. Tôi đã viết bằng VisualBasic, bằng bash và bằng một số ngôn ngữ độc quyền, tôi thậm chí không thể nói về mặt pháp lý. Tôi thậm chí còn tự làm một cái một cách tình cờ. Tôi đã tạo một trình thông dịch kiểu lisp đơn giản để giúp các nhà thiết kế trò chơi tự động tải tài nguyên và đi nghỉ. Khi tôi trở lại, họ đang viết toàn bộ cảnh trò chơi trong phiên dịch này nên chúng tôi phải hỗ trợ nó ít nhất cho đến khi kết thúc dự án.


Vì vậy, trong 17 năm qua, tôi đã thực sự cố gắng từ bỏ C++ nhưng mỗi lần như vậy, sau khi thử một thứ mới mẻ, tôi lại quay trở lại. Tuy nhiên, tôi nghĩ rằng viết bằng C++ là một thói quen xấu. Nó không an toàn, không hiệu quả như người ta tưởng, và nó lãng phí một lượng khủng khiếp năng lực tinh thần của lập trình viên vào những thứ không liên quan gì đến việc tạo ra phần mềm. Bạn có biết rằng trong MSVC uint16_t(50000) + uin16_t(50000) == -1794967296 ? Bạn có biết tại sao? Yeah đó là những gì tôi nghĩ.


Tôi tin rằng trách nhiệm đạo đức của các lập trình viên C++ lâu năm là không khuyến khích thế hệ trẻ biến C++ thành nghề nghiệp của họ cũng như trách nhiệm đạo đức của những người nghiện rượu không thể bỏ thuốc lá để cảnh báo giới trẻ về sự nguy hiểm.


Nhưng sao tôi không bỏ được? Có chuyện gì vậy? Vấn đề là, không có ngôn ngữ nào, đặc biệt là cái gọi là “sát thủ C++” mang lại bất kỳ lợi thế thực sự nào so với C++ trong thế giới hiện đại. Tất cả những ngôn ngữ mới đó chủ yếu tập trung vào việc trói buộc một lập trình viên vì lợi ích của chính họ. Điều này ổn, ngoại trừ việc viết mã tốt với các lập trình viên tồi là một vấn đề của thế kỷ XX khi các bóng bán dẫn tăng gấp đôi sau mỗi 18 tháng và số lượng lập trình viên tăng gấp đôi sau mỗi 5 năm.


Chúng ta đang sống ở năm 2023. Chúng ta có nhiều lập trình viên giàu kinh nghiệm trên thế giới hơn bao giờ hết trong lịch sử. Và chúng ta cần phần mềm hiệu quả hơn bao giờ hết.


Mọi thứ đơn giản hơn trong thế kỷ XX. Bạn có một ý tưởng, bạn đưa nó vào một giao diện người dùng nào đó và bán nó dưới dạng một sản phẩm dành cho máy tính để bàn. Nó có chậm không? Ai quan tâm! Trong mười tám tháng, máy tính để bàn dù sao cũng sẽ trở nên nhanh hơn gấp 2 lần. Điều quan trọng là tham gia thị trường, bắt đầu bán các tính năng và tốt nhất là không có lỗi. Trong môi trường đó, chắc chắn rồi, nếu một trình biên dịch giúp các lập trình viên không mắc lỗi - thì tốt! Bởi vì các lỗi không mang lại tiền và bạn phải trả tiền cho các lập trình viên của mình cho dù họ có thêm tính năng hay lỗi hay không.


Bây giờ mọi thứ đã khác. Bạn có một ý tưởng, bạn bọc nó trong bộ chứa Docker và chạy nó trên đám mây. Bây giờ bạn nhận được doanh thu từ những người chạy phần mềm của bạn nếu phần mềm đó làm cho vấn đề của họ biến mất. Ngay cả khi nó làm một việc nhưng làm đúng, bạn sẽ được trả tiền. Bạn không cần phải nhồi nhét vào sản phẩm của mình những tính năng đã được tạo sẵn chỉ để bán một phiên bản mới của nó. Mặt khác, người trả tiền cho sự thiếu hiệu quả của mã của bạn bây giờ là chính bạn. Mọi quy trình dưới mức tối ưu đều hiển thị trong hóa đơn AWS của bạn.


Vì vậy, trong môi trường mới, giờ đây bạn cần ít tính năng hơn nhưng hiệu suất tốt hơn cho bất cứ thứ gì bạn có.


Và đột nhiên hóa ra tất cả những “sát thủ C++”, kể cả những người mà tôi hết lòng yêu mến và kính trọng như Rust, Julia và D, đều không giải quyết được vấn đề của thế kỷ XXI. Họ vẫn bị mắc kẹt trong XX. Chúng giúp bạn viết nhiều tính năng hơn với ít lỗi hơn, nhưng chúng không giúp được gì nhiều khi bạn cần vắt kiệt phần cứng cuối cùng từ phần cứng bạn thuê.


Chúng không mang lại cho bạn lợi thế cạnh tranh so với C++. Hoặc, đối với vấn đề đó, thậm chí hơn nhau. Hầu hết trong số họ, chẳng hạn như Rust, Julia và Clan thậm chí còn chia sẻ cùng một phần phụ trợ. Bạn không thể thắng một cuộc đua ô tô nếu tất cả các bạn dùng chung một chiếc ô tô.


Vì vậy, những công nghệ nào mang lại cho bạn lợi thế cạnh tranh so với C++ hay nói chung là tất cả các trình biên dịch truyền thống đi trước thời đại? Câu hỏi hay. Rất vui vì bạn đã hỏi.


Sát thủ C++ số 1. Xoắn ốc

Nhưng trước khi chúng ta tiếp tục với chính Xoắn ốc, hãy kiểm tra xem trực giác của bạn hoạt động tốt như thế nào. Bạn nghĩ cái nào nhanh hơn: hàm sin C++ tiêu chuẩn hay mô hình đa thức 4 phần của hàm sin?


 auto y = std::sin(x); // vs. y = -0.000182690409228785*x*x*x*x*x*x*x +0.00830460224186793*x*x*x*x*x -0.166651012143690*x*x*x +x;


Câu hỏi tiếp theo. Điều gì hoạt động nhanh hơn, sử dụng các phép toán logic với đoản mạch hoặc đánh lừa trình biên dịch để tránh nó và tính toán biểu thức logic hàng loạt?


 if (xs[i] == 1 && xs[i+1] == 1 && xs[i+2] == 1 && xs[i+3] == 1) // xs are bools stored as ints // vs. inline int sq(int x) { return x*x; } if(sq(xs[i] - 1) + sq(xs[i+1] - 1) + sq(xs[i+2] - 1) + sq(xs[i+3] - 1) == 0)


Và một cái nữa. Sắp xếp bộ ba nào nhanh hơn: sắp xếp hoán đổi hay sắp xếp theo chỉ mục?


 if(s[0] > s[1]) swap(s[0], s[1]); if(s[1] > s[2]) swap(s[1], s[2]); if(s[0] > s[1]) swap(s[0], s[1]); // vs. const auto a = s[0]; const auto b = s[1]; const auto c = s[2]; s[int(a > b) + int(a > c)] = a; s[int(b >= a) + int(b > c)] = b; s[int(c >= a) + int(c >= b)] = c;


Nếu bạn trả lời tất cả các câu hỏi một cách dứt khoát và thậm chí không cần suy nghĩ hay tra cứu trên Google, thì trực giác của bạn đã làm bạn thất vọng. Bạn đã không nhìn thấy cái bẫy. Không có câu hỏi nào trong số này có câu trả lời chắc chắn mà không có ngữ cảnh.


Mã nhắm mục tiêu đến CPU hoặc GPU nào? Trình biên dịch nào được cho là để xây dựng mã? Tối ưu hóa trình biên dịch nào đang bật và tắt? Bạn chỉ có thể bắt đầu dự đoán khi bạn biết tất cả những điều đó hoặc tốt hơn là đo thời gian chạy cho từng giải pháp cụ thể.


  1. Mô hình đa thức nhanh hơn 3 lần so với mô hình sin tiêu chuẩn nếu được xây dựng bằng clang 11 với -O2 -march=native và chạy trên Intel Core i7-9700F. Nhưng nếu build bằng nvcc với --use-fast-math và trên GPU cụ thể là GeForce GTX 1050 Ti Mobile thì sine chuẩn nhanh gấp 10 lần model.


  2. Giao dịch logic ngắn mạch cho số học vector hóa cũng có ý nghĩa trên i7. Làm cho đoạn trích hoạt động nhanh gấp đôi. Nhưng trên ARMv7 với cùng tiếng kêu và -O2, logic tiêu chuẩn nhanh hơn 25% so với tối ưu hóa vi mô.


  3. Và với sắp xếp chỉ mục so với sắp xếp hoán đổi, sắp xếp chỉ mục nhanh hơn 3 lần trên Intel và sắp xếp hoán đổi nhanh hơn 3 lần trên GeForce.


Vì vậy, các tối ưu hóa vi mô thân yêu mà tất cả chúng ta đều vô cùng yêu thích có thể tăng tốc mã của chúng ta lên gấp 3 lần và làm chậm nó xuống 90%. Tất cả phụ thuộc vào bối cảnh. Thật tuyệt vời biết bao nếu một trình biên dịch có thể chọn giải pháp thay thế tốt nhất cho chúng ta, ví dụ: sắp xếp chỉ mục sẽ biến thành sắp xếp hoán đổi một cách thần kỳ khi chúng ta chuyển đổi mục tiêu xây dựng. Nhưng nó không thể.


  1. Ngay cả khi chúng tôi cho phép trình biên dịch thực hiện lại sin như một mô hình đa thức, để đánh đổi độ chính xác lấy tốc độ, thì nó vẫn không biết độ chính xác mục tiêu của chúng tôi. Trong C++, chúng ta không thể nói rằng “hàm này được phép có lỗi đó”. Tất cả những gì chúng tôi có là các cờ trình biên dịch như “--use-fast-math” và chỉ trong phạm vi của một đơn vị dịch thuật.


  2. Trong ví dụ thứ hai, trình biên dịch không biết rằng các giá trị của chúng tôi bị giới hạn ở 0 hoặc 1 và không thể đề xuất tối ưu hóa mà chúng tôi có thể. Chúng ta có thể gợi ý về nó bằng cách sử dụng một kiểu bool thích hợp nhưng đó sẽ là một vấn đề hoàn toàn khác.


  3. Và trong ví dụ thứ ba, các đoạn mã rất khác nhau để được công nhận là đồng nghĩa. Chúng tôi đã chi tiết mã quá nhiều. Nếu nó chỉ là std::sort, thì điều này đã giúp trình biên dịch tự do hơn trong việc lựa chọn thuật toán. Nhưng nó sẽ không chọn sắp xếp chỉ mục không sắp xếp hoán đổi vì cả hai đều không hiệu quả trên các mảng lớn và std::sort hoạt động với một vùng chứa có thể lặp lại chung.


Và đó là cách chúng ta đến Spiral . Đây là một dự án chung của Đại học Carnegie Mellon và Eidgenössische Technische Hochschule Zürich. TL&DR: các chuyên gia xử lý tín hiệu cảm thấy nhàm chán với việc viết lại các thuật toán yêu thích của họ cho mọi phần cứng mới bằng tay và viết một chương trình thực hiện công việc này cho họ. Chương trình lấy mô tả cấp cao của thuật toán và mô tả chi tiết về kiến trúc phần cứng, đồng thời tối ưu hóa mã cho đến khi nó thực hiện triển khai thuật toán hiệu quả nhất cho phần cứng được chỉ định.


Một sự khác biệt quan trọng giữa Fortran và những thứ tương tự, Spiral thực sự giải quyết vấn đề tối ưu hóa theo nghĩa toán học. Nó định nghĩa thời gian chạy là một hàm mục tiêu và tìm kiếm mức tối ưu toàn cầu của nó trong không gian nhân tố của các biến thể triển khai bị giới hạn bởi kiến trúc phần cứng. Đây là điều mà trình biên dịch không bao giờ thực sự làm.


Một trình biên dịch không tìm kiếm sự tối ưu thực sự. Nó tối ưu hóa mã được hướng dẫn bởi kinh nghiệm mà các lập trình viên đã dạy. Về cơ bản, một trình biên dịch không hoạt động như một cỗ máy tìm kiếm giải pháp tối ưu, nó hoạt động như một bộ lập trình hợp ngữ. Một trình biên dịch tốt hoạt động giống như một trình lập trình hợp ngữ tốt, nhưng chỉ có vậy.




Xoắn ốc là một dự án nghiên cứu. Nó bị giới hạn về phạm vi và ngân sách. Nhưng kết quả mà nó cho thấy đã rất ấn tượng. Trên phép biến đổi Fourier nhanh, giải pháp của họ vượt trội hơn hẳn cả việc triển khai MKL và FFTW. Mã của họ nhanh hơn ~ 2 lần. Ngay cả trên Intel.


Chỉ để làm nổi bật quy mô thành tích, MKL là Thư viện hạt nhân toán học của chính Intel, do đó, bởi những người biết cách sử dụng phần cứng của họ nhiều nhất. Và WWTF AKA “Biến đổi Fourier nhanh nhất ở phương Tây” là một thư viện chuyên môn cao từ những người hiểu rõ nhất về thuật toán. Cả hai đều là những nhà vô địch trong những gì họ làm và việc Spiral đánh bại họ gấp đôi là điều đáng kinh ngạc.


Khi công nghệ tối ưu hóa mà Spiral sử dụng sẽ được hoàn thiện và thương mại hóa, không chỉ C++ mà cả Rust, Julia và thậm chí cả Fortran sẽ phải đối mặt với sự cạnh tranh mà họ chưa từng đối mặt trước đây. Tại sao mọi người lại viết bằng C++ nếu viết bằng ngôn ngữ mô tả thuật toán cấp cao giúp mã của bạn nhanh hơn gấp 2 lần?


Sát thủ C++ số 2. Numba

Ngôn ngữ lập trình tốt nhất là ngôn ngữ bạn đã biết rõ. Trong nhiều thập kỷ liên tục, ngôn ngữ nổi tiếng nhất đối với hầu hết các lập trình viên là C. Nó cũng dẫn đầu chỉ số TIOBE với các ngôn ngữ thích C khác nằm trong top 10. Tuy nhiên, chỉ hai năm trước, một điều chưa từng có đã xảy ra. Chữ C nhường vị trí đầu tiên cho một thứ khác.


"Cái gì khác" dường như là Python. Một ngôn ngữ không ai coi trọng vào những năm 90 vì nó là một ngôn ngữ kịch bản khác mà chúng tôi đã có rất nhiều.



Ai đó sẽ nói: “Bah, Python chậm chạp”, và sẽ trông giống như một kẻ ngốc vì đây là thuật ngữ vô nghĩa. Cũng giống như đàn accordion hay chảo rán, ngôn ngữ đơn giản là không thể nhanh hay chậm. Giống như tốc độ của đàn accordion phụ thuộc vào người chơi, “tốc độ” của một ngôn ngữ phụ thuộc vào trình biên dịch của nó nhanh như thế nào.


“Nhưng Python không phải là một ngôn ngữ được biên dịch” ai đó có thể tiếp tục và bắn nhầm một lần nữa. Có rất nhiều trình biên dịch Python và hứa hẹn nhất trong số đó là một tập lệnh Python. Hãy để tôi giải thích.


Tôi đã từng có một dự án. Mô phỏng in 3D ban đầu được viết bằng Python và sau đó được viết lại bằng C++ “để đạt hiệu suất”, sau đó được chuyển sang GPU, tất cả những điều đó trước khi tôi tham gia. Sau đó, tôi đã dành hàng tháng trời để chuyển bản dựng sang Linux, tối ưu hóa mã GPU cho Tesla M60 vì nó rẻ nhất trong AWS vào thời điểm đó và xác thực tất cả các thay đổi trong mã C++/CU để phù hợp với mã gốc trong Python. Vì vậy, tôi đã làm mọi thứ trừ những thứ mà tôi thường chuyên môn hóa, cụ thể là nghĩ ra các thuật toán hình học.


Và khi tôi cuối cùng đã làm mọi thứ hoạt động, một sinh viên làm việc bán thời gian từ Bremen đã gọi cho tôi và hỏi: “Vậy là bạn giỏi về những thứ không đồng nhất, bạn có thể giúp tôi chạy một thuật toán trên GPU không?” Tất nhiên rồi! Tôi đã nói với anh ấy về việc xây dựng, thử nghiệm và tối ưu hóa CUDA, CMake, Linux; có lẽ đã dành một giờ để nói chuyện. Anh ấy lắng nghe tất cả những điều đó một cách rất lịch sự, nhưng cuối cùng nói: “Điều này rất thú vị, nhưng tôi có một câu hỏi rất cụ thể. Vì vậy, tôi có một hàm, tôi đã viết @cuda.jit trước định nghĩa của nó và Python nói điều gì đó về mảng và không biên dịch kernel. Anh có biết vấn đề ở đây là gì không?”


Tôi không biết. Anh ấy đã tự mình tìm ra nó trong một ngày. Rõ ràng, Numba không hoạt động với danh sách Python gốc, nó chỉ chấp nhận dữ liệu trong mảng NumPy. Vì vậy, anh ấy đã tìm ra nó và chạy thuật toán của mình trên GPU. Trong Python. Anh ấy không gặp vấn đề gì mà tôi đã dành hàng tháng trời. Bạn có muốn nó trên Linux không? Không thành vấn đề, chỉ cần chạy nó trên Linux. Bạn có muốn nó nhất quán với mã Python không? Không thành vấn đề, đó là mã Python. Bạn có muốn tối ưu hóa cho nền tảng đích không? Không phải là một vấn đề một lần nữa. Numba sẽ tối ưu hóa mã cho nền tảng mà bạn chạy mã vì nó không biên dịch trước, nó biên dịch theo yêu cầu khi đã được triển khai.


Đó không phải là tuyệt vời sao? Ồ không. Không phải cho tôi anyway. Tôi đã dành nhiều tháng với C++ để giải quyết các vấn đề không bao giờ xảy ra ở Numba, và một người làm việc bán thời gian từ Bremen đã làm được điều tương tự trong vài ngày. Có thể là vài giờ nếu đó không phải là trải nghiệm đầu tiên của anh ấy với Numba. Vậy Numba này là gì? Đó là loại ma thuật gì?


Không có phép thuật. Trình trang trí Python biến mọi đoạn mã thành cây cú pháp trừu tượng của nó cho bạn, vì vậy bạn có thể làm bất cứ điều gì bạn muốn với nó. Numba là một thư viện Python muốn biên dịch các cây cú pháp trừu tượng với bất kỳ chương trình phụ trợ nào mà nó có và cho bất kỳ nền tảng nào mà nó hỗ trợ. Nếu bạn muốn biên dịch mã Python của mình để chạy trên các lõi CPU theo kiểu song song ồ ạt – chỉ cần yêu cầu Numba biên dịch mã đó. Nếu bạn muốn chạy thứ gì đó trên GPU, một lần nữa, bạn chỉ nên hỏi .


 @cuda.jit def matmul(A, B, C): """Perform square matrix multiplication of C = A * B.""" i, j = cuda.grid(2) if i < C.shape[0] and j < C.shape[1]: tmp = 0. for k in range(A.shape[1]): tmp += A[i, k] * B[k, j] C[i, j] = tmp


Numba là một trong những trình biên dịch Python khiến C++ trở nên lỗi thời. Tuy nhiên, về lý thuyết, nó không tốt hơn C++ vì nó sử dụng cùng các chương trình phụ trợ. Nó sử dụng CUDA để lập trình GPU và LLVM cho CPU. Trên thực tế, vì không yêu cầu xây dựng lại trước thời hạn cho mọi kiến trúc mới, nên các giải pháp Numba thích ứng tốt hơn với mọi phần cứng mới và các tối ưu hóa có sẵn của nó.


Tất nhiên, sẽ tốt hơn nếu có lợi thế hiệu suất rõ ràng như với Spiral. Nhưng Spiral là một dự án nghiên cứu nhiều hơn, nó có thể giết chết C++ nhưng chỉ sau cùng, và chỉ khi nó may mắn. Numba với Python bóp nghẹt C++ ngay bây giờ, trong thời gian thực. Bởi vì nếu bạn có thể viết bằng Python và có hiệu suất của C++, tại sao bạn lại muốn viết bằng C++?


Sát thủ C++ số 3. ForwardCom

Hãy chơi một trò chơi khác. Tôi sẽ đưa cho bạn ba đoạn mã, và bạn sẽ đoán xem đoạn nào trong số chúng, hoặc có thể nhiều hơn, được viết dưới dạng hợp ngữ. Họ đây rồi:


 invoke RegisterClassEx, addr wc ; register our window class invoke CreateWindowEx,NULL, ADDR ClassName, ADDR AppName,\ WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT, CW_USEDEFAULT,\ CW_USEDEFAULT, CW_USEDEFAULT,\ NULL, NULL, hInst, NULL mov hwnd,eax invoke ShowWindow, hwnd,CmdShow ; display our window on desktop invoke UpdateWindow, hwnd ; refresh the client area .while TRUE ; Enter message loop invoke GetMessage, ADDR msg,NULL,0,0 .break .if (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endw


 (module (func $add (param $lhs i32) (param $rhs i32) (result i32) get_local $lhs get_local $rhs i32.add) (export "add" (func $add)))


 v0 = my_vector // we want the horizontal sum of this int64 r0 = get_len ( v0 ) int64 r0 = round_u2 ( r0 ) float v0 = set_len ( r0 , v0 ) while ( uint64 r0 > 4) { uint64 r0 >>= 1 float v1 = shift_reduce ( r0 , v0 ) float v0 = v1 + v0 }


Vậy cái nào, hoặc nhiều cái, đang được lắp ráp? Nếu bạn nghĩ rằng cả ba, xin chúc mừng! trực giác của bạn đã trở nên tốt hơn nhiều rồi!


Cái đầu tiên là trong MASM32. Đó là một trình biên dịch macro với “nếu” và “trong khi” mọi người viết các ứng dụng Windows gốc. Đúng vậy, không phải “được sử dụng để viết” mà là “viết” cho đến ngày nay. Microsoft nhiệt tình bảo vệ khả năng tương thích ngược của Windows với API Win32 để tất cả các chương trình MASM32 từng được viết cũng hoạt động tốt trên PC hiện đại.


Thật là mỉa mai, C đã được phát minh để làm cho việc dịch UNIX từ PDP-7 sang PDP-11 dễ dàng hơn. Nó được thiết kế như một bộ lắp ráp di động có khả năng sống sót sau vụ nổ kỷ Cambri của các kiến trúc phần cứng thập niên 70. Nhưng trong thế kỷ XXI, kiến trúc phần cứng phát triển rất chậm chạp, các chương trình mà tôi đã viết trong MASM32 20 năm trước được lắp ráp và chạy hoàn hảo cho đến ngày nay, nhưng tôi không tin rằng ứng dụng C++ mà tôi đã xây dựng năm ngoái bằng CMake 3.21 sẽ được xây dựng bằng CMake ngày nay 3,25.


Đoạn mã thứ hai là Web Assembly. Nó thậm chí không phải là một trình biên dịch macro, nó không có “nếu” và “trong khi”, nó giống một mã máy mà con người có thể đọc được hơn cho trình duyệt của bạn. Hoặc một số trình duyệt khác. Về mặt khái niệm, bất kỳ trình duyệt nào.


Mã hội Web hoàn toàn không phụ thuộc vào kiến trúc phần cứng của bạn. Cỗ máy mà nó phục vụ là trừu tượng, ảo, phổ quát, bạn muốn gọi nó là gì cũng được. Nếu bạn có thể đọc văn bản này, thì bạn đã có một văn bản trên máy vật lý của mình.


Nhưng đoạn mã thú vị nhất là đoạn mã thứ ba. Đó là ForwardCom – một nhà lắp ráp Agner Fog, một tác giả nổi tiếng về C++ và hướng dẫn tối ưu hóa lắp ráp, đề xuất. Cũng giống như với Web Assembly, đề xuất này không bao gồm nhiều trình biên dịch mã như tập hợp các hướng dẫn chung được thiết kế để cho phép không chỉ khả năng tương thích ngược mà còn chuyển tiếp. Do đó tên. Tên đầy đủ của ForwardCom là “ một kiến trúc tập lệnh tương thích với chuyển tiếp mở .” Nói cách khác, đây không phải là một đề xuất tập hợp mà là một đề xuất hiệp ước hòa bình.


Chúng tôi biết rằng tất cả các họ kiến trúc phổ biến nhất: x64, ARM và RISC-V đều có các tập lệnh khác nhau. Nhưng không ai biết một lý do chính đáng để giữ nó theo cách này. Tất cả các bộ xử lý hiện đại, ngoài bộ xử lý có thể là đơn giản nhất, không chạy mã mà bạn cung cấp cho nó mà là vi mã mà chúng dịch đầu vào của bạn thành. Vì vậy, không chỉ M1 có lớp tương thích ngược cho Intel, về cơ bản, mọi bộ xử lý đều có lớp tương thích ngược cho tất cả các phiên bản trước đó của chính nó.


Vì vậy, điều gì ngăn cản các nhà thiết kế kiến trúc đồng ý về một lớp tương tự nhưng để tương thích về phía trước? Ngoài những tham vọng mâu thuẫn của các công ty đang cạnh tranh trực tiếp, không có gì. Nhưng nếu các nhà sản xuất bộ xử lý tại một thời điểm nào đó sẽ quyết định có một tập lệnh chung thay vì triển khai một lớp tương thích mới cho mọi đối thủ cạnh tranh khác, thì ForwardCom sẽ đưa lập trình hợp ngữ trở lại xu hướng chủ đạo. Lớp tương thích chuyển tiếp này sẽ chữa lành chứng rối loạn thần kinh tồi tệ hơn của mọi lập trình viên hợp ngữ ngoài kia: “điều gì sẽ xảy ra nếu tôi viết mã một lần trong đời cho kiến trúc cụ thể này và kiến trúc cụ thể này sẽ tự lỗi thời sau một năm?”


Với lớp tương thích chuyển tiếp, nó sẽ không bao giờ lỗi thời. Đó là điểm.


Lập trình hợp ngữ cũng bị cản trở bởi một lầm tưởng rằng viết bằng hợp ngữ là khó và do đó không thực tế. Đề xuất của Fog cũng giải quyết vấn đề này. Nếu mọi người nghĩ rằng viết bằng hợp ngữ là khó, còn viết bằng C thì không, thì, chúng ta hãy làm cho trình hợp dịch trông giống như C. Không thành vấn đề. Không có lý do chính đáng nào để một ngôn ngữ lắp ráp hiện đại trông giống hệt như ông nội của nó trong những năm 50.


Bạn vừa xem ba mẫu lắp ráp cho mình. Không cái nào giống kiểu lắp ráp “truyền thống” và không cái nào nên như vậy.


Vì vậy, ForwardCom là tập hợp trong đó bạn có thể viết mã tối ưu sẽ không bao giờ lỗi thời và không bắt bạn phải học một tập hợp “truyền thống”. Đối với tất cả các cân nhắc thực tế, nó là C của tương lai. Không phải C++.

Vậy khi nào С++ cuối cùng sẽ chết?

Chúng ta đang sống trong một thế giới hậu hiện đại. Không có gì chết nữa ngoài con người. Cũng giống như tiếng Latin chưa bao giờ thực sự chết, giống như COBOL, Algol 68 và Ada, - C ++ sẽ phải chịu sự tồn tại nửa vời vĩnh cửu giữa sự sống và cái chết. C ++ sẽ không bao giờ thực sự chết, nó sẽ chỉ bị đẩy ra khỏi dòng chính bởi các công nghệ mạnh hơn mới hơn.


Chà, không phải "sẽ bị đẩy" mà là "bị đẩy". Tôi đến với công việc hiện tại là một lập trình viên C++, và hôm nay ngày làm việc của tôi bắt đầu với Python. Tôi viết các phương trình, SymPy giải chúng cho tôi và sau đó dịch lời giải sang C++. Sau đó, tôi dán mã này vào thư viện C++ thậm chí không cần định dạng nó một chút, vì dù sao thì clang-tidy cũng sẽ làm điều đó cho tôi. Máy phân tích tĩnh sẽ kiểm tra xem tôi có làm hỏng không gian tên hay không và máy phân tích động sẽ kiểm tra rò rỉ bộ nhớ. CI/CD sẽ đảm nhận việc biên dịch đa nền tảng. Trình lược tả sẽ giúp tôi hiểu mã của tôi thực sự hoạt động như thế nào và trình dịch ngược mã - tại sao.


Nếu tôi đánh đổi C++ để lấy “không phải C++”, 80% công việc của tôi sẽ không thay đổi. C ++ đơn giản là không liên quan đến hầu hết những gì tôi làm. Điều đó có nghĩa là đối với tôi, C++ đã chết 80%?



Hình ảnh đạo trình được phát triển bởi sự khuếch tán ổn định.