Câu trả lời:
Mật độ mã liên quan đến số lượng lệnh vi xử lý cần thiết để thực hiện một hành động được yêu cầu và mỗi lệnh chiếm bao nhiêu dung lượng. Nói chung, một lệnh càng chiếm ít không gian và càng nhiều công việc cho mỗi lệnh mà bộ vi xử lý có thể làm thì mã của nó càng dày đặc.
Tôi nhận thấy rằng bạn đã gắn thẻ câu hỏi của bạn bằng thẻ 'arm'; Tôi có thể minh họa mật độ mã bằng hướng dẫn ARM.
Giả sử bạn muốn sao chép một khối dữ liệu từ một nơi trong bộ nhớ sang một nơi khác. Về mặt khái niệm, mã cấp cao của bạn sẽ trông giống như thế này:
void memcpy(void *dest, void *source, int count_bytes)
{
char *s, *d;
s = source; d = dest;
while(count_bytes--) { *d++ = *s++; }
}
Bây giờ một trình biên dịch đơn giản cho một bộ vi xử lý đơn giản có thể chuyển đổi nó thành một cái gì đó như sau:
movl r0, count_bytes
movl r1, s
movl r2, d
loop: ldrb r3, [r1]
strb [r2], r3
movl r3, 1
add r1, r3
add r2, r3
sub r0, r3
cmp r0, 0
bne loop
(ARM của tôi hơi rỉ sét, nhưng bạn hiểu ý)
Bây giờ đây sẽ là một trình biên dịch rất đơn giản và một bộ vi xử lý rất đơn giản, nhưng bạn có thể thấy từ ví dụ chúng ta đang xem 8 hướng dẫn trên mỗi lần lặp của vòng lặp (7 nếu chúng ta di chuyển '1' sang một thanh ghi khác và di chuyển tải ngoài vòng lặp). Điều đó không thực sự dày đặc chút nào. Mật độ mã cũng ảnh hưởng đến hiệu suất; nếu các vòng lặp của bạn dài hơn vì mã không dày đặc, bạn có thể cần thêm bộ đệm hướng dẫn để giữ vòng lặp. Nhiều bộ đệm hơn có nghĩa là một bộ xử lý đắt tiền hơn, nhưng sau đó một lần nữa giải mã lệnh phức tạp có nghĩa là nhiều bóng bán dẫn hơn để giải mã hướng dẫn được yêu cầu, vì vậy đó là một vấn đề kỹ thuật cổ điển.
ARM khá đẹp về mặt này. Mỗi hướng dẫn có thể có điều kiện, hầu hết các hướng dẫn có thể tăng hoặc giảm giá trị của các thanh ghi và hầu hết các hướng dẫn có thể tùy chọn cập nhật các cờ xử lý. Trên ARM và với trình biên dịch hữu ích vừa phải, cùng một vòng lặp có thể trông giống như thế này:
movl r0, count_bytes
movl r1, s
movl r2, d
loop: ldrb r3, [r1++]
strb [r2++], r3
subs r0, r0, 1
bne loop
Như bạn có thể thấy, vòng lặp chính hiện có 4 hướng dẫn. Mã này dày đặc hơn vì mỗi lệnh trong vòng lặp chính làm nhiều hơn. Điều này thường có nghĩa là bạn có thể làm được nhiều hơn với một lượng bộ nhớ nhất định, vì ít sử dụng nó để mô tả cách thực hiện công việc.
Bây giờ mã ARM bản địa thường có khiếu nại rằng nó không quá dày đặc; điều này là do hai lý do chính: thứ nhất, 32 bit là một lệnh "dài", vì vậy rất nhiều bit dường như bị lãng phí cho các hướng dẫn đơn giản hơn và thứ hai, mã bị phình to do bản chất của ARM: mỗi và mỗi lệnh là 32 bit dài, không có ngoại lệ. Điều này có nghĩa là có một số lượng lớn các giá trị bằng chữ 32 bit mà bạn không thể tải vào một thanh ghi. Nếu tôi muốn tải "0x12345678" vào r0, làm cách nào để mã hóa một lệnh không chỉ có 0x12345678 trong đó mà còn mô tả "tải chữ theo r0"? Không có bit còn lại để mã hoạt động thực tế. Hướng dẫn tải bằng chữ ARM là một con thú nhỏ thú vị và trình biên dịch ARM cũng phải thông minh hơn một chút so với trình biên dịch thông thường, bởi vì nó phải "bắt"
Dù sao, để trả lời những phàn nàn này, ARM đã đưa ra chế độ Thumb. Thay vì 32 bit cho mỗi lệnh, độ dài lệnh bây giờ là 16 bit cho hầu hết tất cả các lệnh và 32 bit cho các nhánh. Có một vài sự hy sinh với chế độ Thumb, nhưng nói chung, những sự hy sinh này rất dễ thực hiện vì Thumb đã giúp bạn cải thiện mật độ mã 40% chỉ bằng cách giảm thời lượng hướng dẫn.
"Mật độ mã" của một tập lệnh là thước đo xem bạn có thể nhận được bao nhiêu thứ vào một lượng bộ nhớ chương trình nhất định hoặc bao nhiêu byte bộ nhớ chương trình bạn cần để lưu trữ một lượng chức năng nhất định.
Như Andrew Kohlsmith đã chỉ ra, ngay cả trên cùng một MCU, các trình biên dịch khác nhau có thể có mật độ mã khác nhau.
Bạn có thể thích đọc "Côn trùng của thế giới máy tính" của Miro Samek, so sánh nhiều loại MCU.