máy tính tương tự

Những máy tính tương tự – analog computer đang dần dần trở lại, ít nhất trong một vài lĩnh vực hẹp! Thấy các bạn thanh thiếu niên ở Anh, châu Âu toàn bắt đầu học lập trình với các mạch Arduino, Raspberry pi, etc… theo tôi đây là cách học rất hay, học một điều gì thì nên bắt đầu từ cái đơn giản, cụ thể, đừng vội biến nó thành vấn đề trừu tượng, to tát, mang tính học thuật! Nhưng như thế nào là một máy tính tương tự, ngay điều này cũng không phải lúc nào cũng dễ hình dung! Giả sử ta làm một mạch điện đơn giản chỉ có biến trở R, được áp một điện thế V! Dòng điện đi qua được hiển thị bằng đồng hồ đo là I. Vì I tương quan với V và R theo công thức: I=V/R, nên khi thay đổi V và R (xoay các núm điều khiển), ta sẽ có được kết quả I hiển thị trên đồng hồ đo, như là kết quả của phép chia V cho R.

Như thế, phép chia không được biểu diễn bằng các bít nhị phân, mà được biểu diễn bằng “vật chất”, tức là dòng điện! Các nhà giáo dục phải bỏ công suy nghĩ về những cách trình bày, chứ nếu chỉ như câu số 6, đề Toán tuyển sinh lớp 10 tp.HCM thì vẫn còn đơn giản lắm! Bắt đầu bằng những cái cụ thể, đơn giản, giữ liên hệ chặt chẽ với thế giới xung quanh, làm cho việc học trở thành sự ham thích lâu dài! Nếu không chúng ta chỉ biến học sinh trở thành những cỗ máy tụng bài chạy theo các số đo hư ảo, những vọng động nào đó trong “tâm” của những người “đóng vai” phụ huynh và thầy cô mà thôi! Muốn tụi nhỏ thực á, trước hết chúng ta phải tự biết cái gì là thực và phải có bản lĩnh để kiên định cái thực đã! Vì không làm được nên quay sang ép con trẻ chạy theo những cái “bánh” do mình “vẽ” ra!

telecentric

Mỗi ngày biết thêm một tí, trong video là một loại ống kính rất đặc biệt – telecentric lens, vật thể nằm trong trường nhìn của nó có kích thước giống nhau dù ở xa hay gần! Tức nó không tạo ra một phép chiếu phối cảnh (perspective projection) mà kỳ lạ thay, có thể tạo ra một phép chiếu đẳng cấu (isometric projection)! Điều này có vẻ khá là khó tin nhưng phép chiếu đẳng cấu thực ra không xa lạ như mọi người nghĩ! Thậm chí tôi còn cho rằng, bản năng đầu tiên của chúng ta khi diễn họa cấu trúc một vật thể là đẳng cấu chứ không phải phối cảnh. Xem thêm về một bức tranh vẽ trong không gian đẳng cấu ở đây!

Nhưng cụ thể, ống kính này được dùng để làm gì? Nó được dùng trong các ứng dụng đo lường, kiểm định, các ứng dụng computer vision, pattern recognition, etc.. sẽ tránh được nhiều lỗi sai về kích thước, vì dù có chụp gần hay chụp xa thì kích thước vẫn y như thế, xem như đã chuẩn hóa đầu vào! Một ví dụ khác là in vi mạch bằng phương pháp quang khắc đều dùng loại ống kính này để tránh sai số! Một chút suy luận là sẽ thấy ngay, ống kính không thể nhìn thấy được vật thể to hơn nó (lớn hơn đường kính ống), nên quên chuyện dùng để chụp phong cảnh đi, chỉ dùng để chụp những vật thể nhỏ và macro mà thôi!

overclock

Thuê con server của Amazon EC2 cấu hình 32 vCPU, 64 GB RAM, so với con Aquarium-PC của mình: 36 vCPU, 64 GB RAM, con của Amazon chạy nhanh hơn một chút, vì dù sao 2 con CPU Xeon của mình là đời cũ lắm rồi. Nhưng khi ép xung – overclock từ 2.3 lên 3.8 GHz thì con Aquarium-PC cho máy của Amazon “ngửi khói” lập tức! Nhưng ép xung cũng có nghĩa là tăng nhiệt, từ 60°C nhảy lên 75°C thường trực, và cũng phải tăng tốc độ quạt, tăng tiếng ồn. Đây là điều đành phải chấp nhận thôi, hoặc là phải có giải pháp tản nhiệt tốt hơn!

Giải pháp thì có, nhưng… kỳ công, phải học thêm nhiều kỹ năng nữa, nhất là cơ khí và CNC, mà tôi hiện tại chỉ có mấy kỹ năng làm mộc quèn mà thôi! Nói chung, xác nhận là tản nhiệt dầu vẫn có thể chạy tốt với máy ép xung được, nhưng thiết kế khâu tản nhiệt phải thật sự thông minh, thật sự hiệu quả! Viết một cái script nhỏ để bật / tắt việc ép xung cho Linux, chạy script là overclock + overvolt, tăng điện thế cấp cho các thành phần máy, chạy lần nữa là tắt tính năng này đi! Mới có mấy hôm mà kỹ năng viết shell-script của mình tăng lên thấy rõ!

vtables

Trong post trước có nói về v-table, v-table là gì!? Với lập trình procedural truyền thống, gọi hàm thực chất là một lệnh hợp ngữ (Assembly), chỉ cần một lệnh JMP – Jump là nhảy ngay đến vùng nhớ chứa đoạn mã – hàm cần thực hiện! Nên gọi hàm trong ngôn ngữ C có tốc độ nhanh, nhanh đến mức không tưởng. Nhưng sự việc không đơn giản như vậy với các ngôn ngữ hướng đối tượng, như C++. Lấy ví dụ như: a->some_function(), trong trường hợp đơn giản nhất, ta cần nhảy 2 lần, lần đầu đến vùng nhớ chứa đối tượng (object) ‘a’, sau đó mới tìm trong vùng nhớ đó con trỏ đến hàm ‘some_function’ và nhảy thêm một lần nữa. Nhưng sự việc không đơn giản như vậy vì OOP có tính kế thừa, có nhiều phiên bản khác nhau của cùng một hàm ảo, những phiên bản này được chứa trong một cái bảng gọi là v-table, và việc gọi hàm lúc này bao gồm: 1. nhảy đến vùng nhớ bắt đầu đối tượng ‘a’, 2. tìm xem trong v-table của ‘a’ địa chỉ của hàm cần gọi, 3. nhảy đến vùng nhớ chính xác của hàm đó. Và cái bảng v-table này càng ngày càng lớn nếu kế thừa nhiều cấp, 20 ~ 30 cấp cũng là việc đã từng thấy! Sự việc bắt đầu trở nên phức tạp hơn nữa với những ngôn ngữ hỗ trợ đa kế thừa, đối tượng ‘a’ có thể kế thừa từ 1, 2 hay nhiều lớp khác nhau!

Lúc đó, compiler sẽ phải tạo ra nhiều v-table, và việc gọi hàm là một công cuộc tìm kiếm, tra bảng dài lê thê, thay vì chỉ một lệnh hợp ngữ JMP đơn giản! Thời gian tiêu tốn cho việc gọi hàm có thể dao động đâu đó trong khoảng 5 ~ 50% toàn bộ thời gian chạy chương trình. Đến tận ngày hôm nay, mấy chục năm sau khi các ngôn ngữ hướng đối tượng ra đời, bản chất của việc gọi hàm vẫn không đổi, và vẫn chưa có cải tiến nào mang tính cách mạng xảy ra! OOP đúng là một mô hình rõ ràng, dễ hiểu đối với lập trình viên, nhưng cực kỳ đau khổ cho những người làm compiler! OOP sinh ra, đầu tiên và trên hết là cho lập trình UI, mỗi cửa sổ, mỗi nút bấm trên UI là một đối tượng – object, nguyên thủy là vậy, về sau người ta mới mở rộng OOP từ UI sang những lĩnh vực khác! Nên những người làm ứng dụng lớn, cần lớp lang, cấu trúc rõ ràng, những người đó sẽ cảm thấy khó hiểu khi những coder ở mức thấp bên dưới chỉ thích procedural! OOP đúng là rõ ràng và tiện lợi, nhưng không phải là cách tiếp cận duy nhất đối với các vấn đề lập trình, mọi việc có thể sẽ thay đổi! Và nhiều coder không hiểu vì sao một số người cố sống cố chết bám vào một số phong cách xưa cũ, vì… bản năng mách bảo rằng làm như thế mới là đúng!

rust

Những cái thuộc về ngôn ngữ, 10 năm là ít, 30 năm chưa phải là nhiều, thời gian để cho một ngôn ngữ trưởng thành, trở nên chính chắn, cẩn trọng từng câu, từng lời, bảo đảm mọi điều nói ra phải có nghĩa chính xác. Nhiều người bảo, chỉ là một tập cú pháp – syntax thôi mà, nói sao không được!? Không phải vậy, đã từng có vô số cú pháp “nhảm” bị đào thải sau vài năm, đơn cử như là C#, C# đã từng có vô số cú pháp nhảm, nhảm đến mức thiểu năng, ngu xuẩn! Và cũng đã có một số cú pháp kiểu “chữa lành – chảnh lừa” như async – await vẫn tiếp tục lừa người thêm độ chục năm, cho đến khi người ta nhận ra chẳng có lợi ích gì ngoài những câu chữ oang oang, trơn tuột! Và cũng đã có những ngôn ngữ đã dùng trên hai mươi năm nhưng rồi cuối cùng thì người ta quyết định: thôi, tốt hơn là… bỏ, không đi tiếp nữa, ví dụ như Obj-C, Flash, thậm chí có thể cả Java! Nhưng chừng đó năm cũng đủ cho người ta mường tượng ra được một ngôn ngữ “tốt” tương lai nó sẽ trông như thế nào!

Đầu tiên là nó phải giống ngôn ngữ C, điều này… đơn giản như chân lý vậy, cứ phải giống C thì mới tốt! Thứ hai, ngôn ngữ phải strong-type, có kiểu rõ ràng và kiểm tra kiểu khi biên dịch, không đợi đến khi chạy. Thứ ba, dù gọi tên gì: reference, optional, thì cũng phải làm cho người ta hiểu rằng đây là con trỏ – pointer, ở điểm này thì C thẳng thắn đến mức trần trụi! Thứ tư là quản lý bộ nhớ bằng reference counting và lần nữa, phải làm từ lúc biên dịch (compile time), đừng đợi đến đến lúc run-time, quên garbage collector và những thứ khác đi! Thứ năm là làm sao để lập trình concurrent, thread, process dễ hiểu hơn! Và cuối cùng, rất quan trọng, dù thời đại đã đi tới mức zettabyte, nhưng một ngôn ngữ vẫn phải thật sự hiệu quả, hiểu theo nghĩa phải đếm từng bits khi cần. Xét những tiêu chí đó thì có lẽ Rust sẽ là ngôn ngữ phổ thông kế tiếp, dần thay thế C++! Đã nói rồi, phải cải tiến trước mới dùng, đừng dùng xong mới cải tiến, gọi là ++C có phải đã tốt rồi không!? :D

Đương nhiên, đây là ngôn ngữ phổ thông (general purpose) ở mức thấp (low level), những lĩnh vực đặc thù vẫn sẽ có những ngôn ngữ riêng! Nhưng với một ngôn ngữ phổ thông cấp thấp, ưu tiên hàng đầu là performance, những thứ khác vẫn chỉ là phụ. Rust thậm chí còn chưa phải là một ngôn ngữ OOP – hướng đối tượng đúng nghĩa, theo nghĩa thường hiểu trong C++ hay Java. Nói cho đúng hơn là mô hình OOP của Rust được thiết kế ưu tiên cho performance, chứ không phải cho sự tiện lợi của người viết code! Thậm chí ta còn có thể đặt câu hỏi rằng, có thực sự cần OOP hay không, ví dụ như glibc chỉ dùng “struct” của C để biểu diễn “class” đó thôi! Nhưng qua đó cho thấy rằng, đã rất nhiều thế hệ khác nhau của C++ rồi, mà vẫn không giải quyết được bài toán vtable – gọi hàm hướng đối tượng làm sao cho hiệu quả. Nên Rust đành phải đổi một cách tiếp cận khác, mang tính chất lai lai, một nửa là OOP và nửa còn lại vẫn là functional theo kiểu ngôn ngữ C truyền thống!

Quá trình hình thành một ngôn ngữ thực chất phản ánh muôn mặt của cái cộng đồng làm ra và sử dụng nó. Đầu tiên là từ góc độ tương đối hàn lâm, ngôn ngữ phải thể hiện được tính đúng đắn và hiệu quả tính toán! Cái yếu tố “hiệu quả – performance” này là yếu tố quyết định, đôi khi nó phủ quyết (veto) tất cả những yếu tố khác. Tiếp nữa mới đến chuyện cú pháp rõ ràng, tiện lợi, thân thiện với lập trình viên. Kế đến nữa mới là chuyện tổ chức, lớp lang, các hệ thống thư viện phụ trợ để dễ dàng phát triển phần mềm! Phần lớn lập trình viên chỉ tranh luận phía trên bề mặt, cú pháp như thế này, lớp lang như thế kia, họ không hiểu rằng yếu tố tiên quyết của một ngôn ngữ là vấn đề hiệu suất (performance). Những ngôn ngữ bỏ lơ vấn đề này… đều có kết cục thê thảm!!! Ví dụ như Objective-C, người ta bỏ vì nó đã trở thành một con quái vật, phức tạp đến mức vô lý, hay như một số code React – TypeScript, mới viết có cái app Hello – World là đã chiếm mất hơn 3GB đĩa cứng.

nhiệt

Đến lúc này có thể khẳng định chắc chắn máy có thể chạy heavy – duty rất tốt, đã chạy 24 tiếng liên tục, lúc nào cũng 100% công suất, chương trình xử lý dữ liệu tạo ra 72 processes, dùng đến hơn 40GB RAM, mà nhiệt độ 2 con CPU ổn định ở mức 60~62°C, nhiệt độ dầu ổn định ở mức 41~42°C, còn nhiệt độ GPU và SSD còn thấp hơn nữa, chỉ loanh quanh 37~38°C! Tuy vậy nhìn lại, vẫn có chút tiếc nuối, vẫn có chút không hoàn hảo, vì vẫn có thể làm tốt hơn, vẫn có những giải pháp tản nhiệt tốt hơn nữa! Haiza, nhiệt, nhiệt độ, chính là vấn nạn của cả loài người!

Cứ mỗi năm mỗi nóng hơn thế này, năm nào cũng thiết lập kỷ lục mới! Cái quá trình không đảo ngược được, con người và xã hội đã bị đẩy vô guồng, bị gài vô một cái thế cứ phải hơn, hơn nữa! Ai cũng muốn có những cái máy chạy nhanh hơn nữa, kết quả là cái quả cầu màu xanh xanh kia nó sẽ cứ nóng đỏ dần lên thôi. Đây là chuyện mà không bất kỳ một loại khoa học, kỹ thuật nào, không bất kỳ một hệ tư tưởng nào có thể giải quyết được, càng không nói đến cái đám cứ xoen xoét green & clean kia! Có lẽ phải nghĩ đến chuyện đào hầm tránh nóng là vừa!

nông trại

Tốt nghiệp xong, về làm việc tại trung tâm máy tính, ĐH Khoa học tự nhiên, rồi trung tâm máy tính ĐH Quốc gia tp. HCM. Cái trung tâm mới xây, đúng là thiên đường cho những ai đam mê, chuỗi (cluster) 64 máy con hàng xịn, cộng với những con server hàng khủng, và nhiều loại thiết bị mà tôi còn chưa nhìn thấy bao giờ, tất cả được đặt trong phòng riêng, cùng với nhiều cục lưu điện – UPS to hơn cái tủ lạnh 200 lít. Nếu cúp điện thì những cục UPS này vẫn đủ khả năng giữ cho toàn hệ thống chạy suốt hơn nửa ngày, và khi lượng điện trong UPS xuống dưới một mức nào đó, thì do đã được lập trình sẵn, hệ thống sẽ tự khởi động cái máy phát điện đặt ở phòng kế bên, cấp điện cho toàn hệ thống, tất cả chạy hoàn toàn tự động!

Và đồng thời, hệ thống sẽ gởi tin nhắn về tình trạng hiện tại của các máy móc đến quản trị viên, thường là đang đi uống cafe ở đâu đó để còn biết chạy về xử lý nếu cần! Vâng, giai đoạn khá là ngu ngơ, nhiệt huyết và lý tưởng, từ chối những “lời mời gọi tình yêu” của một số “devushki – em gái”, về đó nghiên cứu grid – computing, mà bây giờ người ta hay gọi bằng cái tên computing – farm – nông trại tính toán! Vâng, đi vòng quanh, vòng quanh cái nông trang tập thể, năng lực tính toán thì có thừa, chỉ thiếu… bài toán cho nó giải quyết mà thôi! Hơn 25 năm sau, chuyện “nông trang tính toán” bây giờ thật quá dễ dàng, và cũng không thiếu bài toán cho nó giải quyết… Ekh, yedem my, yedem, Yedem, a krugom kolkhozy…

turbo button

Đã nói về nút Reset, nay nói về nút Turbo, chắc không mấy người còn nhớ, một thời phổ biến trên các dòng x86 nhưng dần biến mất kể từ Pentium 1, 2 trở đi! Cái nút Turbo này thực chất nó làm gì? Chính là overclock – ép xung để cho CPU chạy nhanh hơn! Đang có ý định làm lại cái nút Turbo này cho con Aquarium-PC! Chỉ cần nhấn nút một cái là CPU chuyển từ xung nhịp cơ sở 2.3 GHz lên xung nhịp tối đa 3.6 GHz ngay!

Trên một số dòng mainboard, CPU, thực hiện “Turbo” rất dễ, ép xung / hạ xung và tăng thế, hạ thế (undervolt) đối với các chip hoàn toàn làm được từ phần mềm một cách dễ dàng, có hiệu quả ngay lập tức, mà không cần bất kỳ can thiệp gì đến phần cứng! Tuy nhiên, và cũng có khi, cách tốt nhất để tăng tốc, gia tốc, turbo một cái máy tính, đó chính là… loại gia tốc có giá trị 9.8 mét trên giây bình phương ấy!

đệ quy

Ví dụ về đệ quy, như thế nào là đệ quy – recursive!? Đầu tiên từ Terminal, gõ lệnh VIM để sửa file. Từ trong VIM, gõ lệnh term để mở một cái Terminal mới bên trong VIM, cái này thường cần cho lập trình viên để vừa sửa code một bên, vừa chạy kiểm tra bên kia xem code có đúng hay không, mà không cần phải thoát ra ngoài!

Nhưng vì ta cắc cớ, nên bên trong cái Terminal chạy trong VIM chạy bên trong Terminal này, gõ thêm một lệnh VIM nữa, kết quả là VIM nằm trong Terminal, nằm trong VIM, nằm trong Terminal, nằm trong VIM, nằm trong Terminal, nằm trong VIM, nằm trong Terminal… và trên nguyên tắc có thể kéo dài… vô hạn cấp!

performance

Để làm con Aquarium-PC, tôi mua cái mainboard Huananzhi X99, đây được xem là loại mainboard chạy 2 CPU… rẻ nhất trên thị trường, một sản phẩm đáng ngạc nhiên của các “pháp sư” Trung Hoa, còn CPU là 2 con Xeon Haswell E5-V3, cũng là loại cũ, và khá rẻ. Đơn giản vì đây là lần đầu mạo hiểm, mua rẻ rẻ thôi, lỡ có thất bại, cháy nổ hay hỏng hóc ở đâu đó thì cũng không quá xót tiền! Hệ thống ráp xong chấm được 23K điểm Passmark, một điểm số không hề tệ, ngang ngửa với con MacMini đời 2022! Thế nhưng đến khi chạy thực tế, các bài toán thực, thì Aquarium-PC luôn thua MacMini một chút trong mọi vấn đề, mặc dù thua không nhiều! Đúng là bách nhục, một bên là hệ thống lớn, 2 CPU to cồng kềnh, lại dùng tản nhiệt dầu, còn bên kia người ta bé có chút xíu! Nhưng nghĩ kỹ thì cũng thấy hợp lý, Xeon Haswell là công nghệ của gần mười năm trước, không thể nào so sánh công bằng được!

Nhưng cũng như tôi dự đoán, có một số lớp bài toán mà Aquarium-PC hơn MacMini, nhìn vào 2 cái ảnh dưới đây bạn sẽ hiểu vì sao! Chạy chương trình phân tích dữ liệu tự viết bằng Python, nó tạo ra đến hơn một chục process và dùng hơn 30GB bộ nhớ SWAP. RAM là điểm yếu của MacMini, chương trình chạy đến một lúc lượng SWAP quá lớn, máy nó lì ra không chịu chạy nữa, thậm chí… tự khởi động lại do quá nhiệt! Nhưng con Aquarium-PC thì chạy được, tuy năng lực xử lý thua chút xíu, nhưng có đến 64GB RAM, và chạy vô cùng mát! Không chỉ về RAM, Aquarium-PC còn hơn về thời gian xử lý với những lập trình nhiều processes! Ngày xưa để xử lý dữ liệu lớn/phức tạp, người ta chia thành nhiều bước, lưu kết quả trung gian ra file/database và quay lại xử lý nhiều lần! Ngày nay, với một số lớp bài toán, cứ tận dụng tài nguyên, bỏ hết lên RAM chạy một lần, code vừa nhanh, vừa đơn giản!