NGÔN NGỮ LẬP TRÌNH VÀ TRÌNH BIÊN DỊCH (3 LĨNH VỰC YÊU THÍCH CỦA TÔI – phần 2)

Trên thế giới này có bao nhiêu ngôn ngữ lập trình? Chịu, không ai biết được. Chỉ cần kể ra những cái tên nổi tiếng đã có rất nhiều: C, C++, Fortran, Pascal, JavaScript, Java, Python, Perl, Basic, C#, F#, Dart, Go… hay các ngôn ngữ chuyên dụng như SQL, VBA, Powershell script…Vậy tại sao tôi lại thích học về ngôn ngữ lập trình? Câu trả lời rất đơn giản: Vì tôi thích! Khi còn đi học, cũng như những sinh viên hay mơ mộng khác, tôi cũng có mơ ước một ngày nào đó sẽ tự phát minh ra một ngôn ngữ mới. Và thật sự tôi cũng đã từng tạo ra một vài trình biên dịch nhỏ trong quá trình học và làm việc: một trình dịch giúp hỗ trợ syntax highlighting khi khách hàng viết các SMS template (các SMS này sẽ có thể chứa các thông tin về đơn hàng và được gửi đến cho các tài xế), một trình dịch khác cũng phục vụ highlighting cho 1 Java IDE (kiểu như VS code – IDE này là một pet project).

Tôi không khuyến khích các bạn đi sâu vào nghiên cứu nó, nhưng tìm hiểu một chút sẽ mang lại rất nhiều kiến thức bổ ích, giúp việc tự học dễ dàng hơn rất nhiều, vì:

👉 Bạn sẽ biết các bước trình biên dịch xử lý mã nguồn, đây là thứ gắn bó với bạn hàng ngày, hàng giờ cho đến nhiều năm sau 🤪.

👉 Khi xảy ra một thông báo lỗi, bạn có thể mường tượng ngay tại sao, thậm chí khi đọc qua một nền tảng hay ngôn ngữ mới, bạn sẽ dự đoán được những vấn đề mình có thể gặp phải.

👉 Tại sao ngôn ngữ này là hướng đối tượng hoàn toàn, ngôn ngữ kia chỉ hỗ trợ một phần, ngôn ngữ nọ hoàn toàn không hỗ trợ? Hay tại sao ngôn ngữ này bắt buộc khai báo kiểu rõ ràng ngôn ngữ kia lại không? Nếu có thể hiểu được vấn đề từ góc độ người thiết kế ra ngôn ngữ, bạn sẽ hiểu được lý do tại sao họ chọn như vậy, hay nói cách khác, bạn sẽ hiểu được họ đã hi sinh cái gì để đánh đổi lại được lợi ích (thế mạnh) gì. Biết được thế mạnh của mỗi ngôn ngữ là điều quan trọng.

Continue reading “NGÔN NGỮ LẬP TRÌNH VÀ TRÌNH BIÊN DỊCH (3 LĨNH VỰC YÊU THÍCH CỦA TÔI – phần 2)”

3 LĨNH VỰC YÊU THÍCH CỦA TÔI – phần 1

Công nghệ thông tin là một lĩnh vực rất rộng, không ai có thể giỏi tất cả, vì vậy việc chọn cho mình một ngôn ngữ, nền tảng hay chuyên ngành riêng nào đó rất quan trọng.

Câu hỏi thường gặp nhất sẽ là: Tôi nên học gì để sau này có việc làm? Thật sự công việc trong ngành này rất nhiều, và sẽ càng ngày càng nhiều, nhất là trong bối cảnh các xu hướng làm việc từ xa, dữ liệu lớn, AI, IoT… đang trở nên phổ biến hơn bao giờ hết. Nhưng tôi chắc chắn bạn chưa bao giờ tìm được một đáp án thỏa mãn, bởi đơn giản chỉ bạn mới có thể trả lời chính xác mà thôi.

Câu trả lời của tôi luôn là: Bạn hãy học những gì bạn thích nhất!

Continue reading “3 LĨNH VỰC YÊU THÍCH CỦA TÔI – phần 1”

GIẢI THÍCH: Vấn đề nằm ở CPU cache

Đáp án cho câu hỏi trong bài: https://daohainam.com/2021/12/30/vi-sao-duyet-mang-theo-dong-lai-nhanh-hon-theo-cot/

👉 Khi nằm trong bộ nhớ, các mảng nhiều chiều sẽ được diễn dịch thành một mảng 1 chiều (vì bộ nhớ về cơ bản cũng chỉ là mảng 1 chiều). Mỗi dòng sẽ được sắp xếp liên tục theo thứ tự. Mỗi khi cần truy xuất đến 1 ô nào đó có địa chỉ m[dòng, cột], trình biên dịch sẽ biến đổi thành m[dòng * chiều rộng mảng + cột] (xem hình minh họa).

👉 Như vậy, nếu ta đi chuyển theo dòng->cột (tương ứng với calculate_sum(sum, 1) trong https://github.com/…/clanc…/blob/master/CachingTests.cpp), thứ tự truy xuất trong bộ nhớ sẽ được tăng dần, trong khi đó, nếu ta di chuyển theo cột->dòng, thứ tự truy xuất theo hình minh họa sẽ là 0, 4, 8, 1, 5…

👉 Bộ nhớ cache trong CPU được tổ chức theo từng lance (không biết dịch ra thế nào, trong tiếng Việt ta vẫn dùng từ lance để chỉ các phần đường phân cách nhau). Mỗi lance có kích thước 64 byte, mỗi khi nạp từ RAM vào cache, hay từ cache vào RAM nó sẽ luôn làm việc với từng lance như vậy. Do đó khi đọc vào 1 byte, tất cả các byte lân cận trong cùng lance sẽ nằm sẵn ngay trong cache, khi bạn đọc đến byte kế tiếp bạn chỉ cần lấy nó ra từ cache (cache hit). Tốc độ của cache lại nhanh hơn RAM rất nhiều, người ta tính toán rằng cache L1 trong CPU có tốc độ nhanh hơn vài chục tới cả trăm lần so với truy xuất từ RAM.

👉 Kết quả là việc đọc/ghi dữ liệu tuần tự sẽ cho tốc độ tốt hơn nhiều so với truy xuất ngẫu nhiên. Điều này cũng tương tự như khi bạn đọc dữ liệu từ ổ SSD, vốn không có các cơ cấu cơ học và không có thời gian di chuyển đầu đọc như HDD, tuy nhiên khi copy 1 file lớn vẫn nhanh hơn nhiều so với copy nhiều file nhỏ. Đó cũng là do khi đọc/ghi tuần tự thì xác suất cache hit sẽ lớn hơn nhiều so với cache miss.❗️Khi làm việc với các ứng dụng lớn, việc tổ chức cách lưu trữ dữ liệu rất quan trọng!

VÌ SAO DUYỆT MẢNG THEO DÒNG LẠI NHANH HƠN THEO CỘT?

Mình vừa viết một chương trình nhỏ, chỉ để tính tổng các ô trong một ma trận, tuy nhiên khi thử duyệt theo dòng thì luôn thấy nhanh hơn cột, mảng càng lớn tốc độ càng khác biệt.Các bạn có thể tải về chương trình tại ( https://github.com/namdotnet/clancetest) và chạy thử xem có đúng không, và mất bao nhiêu tick mỗi bước, laptop mình đang dùng chạy Xeon mất hơn 600 ticks cho bước 1.

Nếu nhiều người ủng hộ thì mình sẽ giải thích lý do tại sao 😉(ghi chú là trong ví dụ này mình gọi dòng trước cột sau nhé int m[ROWS][COLS]).

TỪ NGÔN NGỮ LẬP TRÌNH ĐẾN MÃ MÁY (phần 2)

Sau khi đã hoàn thành tiền xử lý, chúng ta sẽ sang bước thứ hai: Biên dịch.

Về cơ bản quá trình biên dịch nhận đầu vào là file mã nguồn, phân tích nó và xuất ra file nhị phân chứa mã thực thi. Quá trình này sẽ chia làm 3 bước chính:

👉 Phân tích từ vựng (lexical analysis)Trình biên dịch sẽ đọc file mã nguồn, từng ký tự một, sau đó phân tích các ký tự rời rạc đó thành các từ vựng trong bộ từ vựng của ngôn ngữ tương ứng.

Xét ví dụ sau:

int x = myfunc(x) * 10

Ví dụ trên sẽ được phân tích thành các từ vựng (ta có thể dùng từ tiếng Anh là token): int, x, =, myfunc, (, x, ), *, 10.Trình phân tích từ vựng sử dụng các ký tự phân tách (khoảng trắng, tab, xuống dòng, dấu chấm phẩy… tùy ngôn ngữ) để tìm ra các từ vựng, nó cũng biết rằng chữ n và t nằm sau i phải được kết hợp thành keyword int, ký tự 1 và 0 phải được kết hợp thành một nhóm là 10 – và đây là một số nguyên, tương tự cho các token còn lại.

Continue reading “TỪ NGÔN NGỮ LẬP TRÌNH ĐẾN MÃ MÁY (phần 2)”

TỪ NGÔN NGỮ LẬP TRÌNH ĐẾN MÃ MÁY (phần 1 – preprocessing)

Bạn đã biết C++ chưa? Đã học C#, Java, JavaScript, Python?Nghe nói Assembly khó lắm? Nếu Assembly khó thì mã máy thế nào?

Trong bài viết này tôi sẽ cố gắng mô tả quá trình từ một chương trình viết bằng ngôn ngữ bậc cao được dịch và thực thi như thế nào, hi vọng sau bài viết này các bạn sẽ có một cái nhìn tổng quan, hiểu được cơ bản và có thể tự mình giải quyết được các trục trặc thường gặp.Trong loạt bài này tôi sẽ chủ yếu minh họa bằng C, vì đây là ngôn ngữ có đầy đủ “đồ chơi” nhất, cũng như gần với các ngôn ngữ cấp thấp nhất.

❓Trình biên dịch làm thế nào để biến chương trình của bạn thành mã máy?

Continue reading “TỪ NGÔN NGỮ LẬP TRÌNH ĐẾN MÃ MÁY (phần 1 – preprocessing)”

Tại sao chúng ta phải vẽ ra các sơ đồ khi thiết kế ứng dụng?

Các sơ đồ (class, sequence, E-R, database…) được vẽ ra để:

– Trực quan hóa: giúp người thiết kế dễ dàng nhìn thấy các thực thể, các mối quan hệ, tìm ra các vấn đề có thể xảy ra và xây dựng bước thiết kế tiếp theo.

– Xây dựng tài liệu giúp tra cứu lại sau này, giúp các thành viên mới tham gia vào dự án có thể hiểu được.- Trao đổi thông tin giữa các thành viên trong dự án khi thiết kế.

ℹ️ Mục đích của vẽ diagram, kể cả các UML diagram là để giúp thiết kế/lập tài liệu dễ dàng hơn, không phải là một phương pháp thiết kế.

Continue reading “Tại sao chúng ta phải vẽ ra các sơ đồ khi thiết kế ứng dụng?”

MÔ HÌNH 3 LỚP

Có lẽ trong quá trình học và đi làm, bạn đã từng nghe qua chữ 3 lớp (3-layer), hoặc 3 tầng (3-tier), vậy nó là gì?

ℹ️ Mô hình 3 lớp là một dạng kiến trúc ứng dụng, trong đó các phần của chương trình được gom vào 3 lớp khác nhau (xem hình minh họa). Thực chất mô hình 3-layer chỉ là một trường hợp đặc biệt của mô hình tổng quát hơn là n-layer, với số lớp có thể từ 2 đến n, các lớp này sẽ nằm lên nhau, và mỗi lớp sẽ chỉ gọi và nhận kết quả xử lý từ lớp ngay bên dưới nó.

Continue reading “MÔ HÌNH 3 LỚP”