Có một nhược điểm nào trong việc phân bổ một lượng lớn ngăn xếp cho một mảng trong một hệ thống nhúng không?


12

Tôi thường không có vấn đề gì trong việc quyết định liệu một số dữ liệu phải là toàn cầu, tĩnh hay trên ngăn xếp (Không phân bổ động ở đây, vì vậy không sử dụng heap). Tôi cũng đã đọc một vài Q / A như câu hỏi này nhưng câu hỏi của tôi cụ thể hơn vì nó liên quan đến một lượng dữ liệu khổng lồ, rất lớn so với bộ nhớ hệ thống.

Tôi đang làm việc với một mã hiện có mà tôi cố gắng cải thiện (thiết kế, các vấn đề có thể xảy ra, hiệu suất, v.v.). Mã này chạy trên MCU 8 bit cũ chỉ với 4KB RAM . Trong mã này, tôi phải đối mặt với việc sử dụng một mảng gần 1KB (vâng, 1KB trên hệ thống RAM 4KB ). Mỗi byte của mảng này được sử dụng, đó không phải là câu hỏi. Vấn đề là mảng này là một mảng tĩnh trong tệp nơi nó được khai báo, vì vậy vòng đời của nó giống như chương trình (nghĩa là có thể được coi là vô hạn).

Tuy nhiên, sau khi đọc mã, tôi phát hiện ra rằng mảng này không cần vòng đời vô hạn, nó được xây dựng và xử lý theo cách thủ tục đầy đủ, vì vậy chúng ta chỉ có thể khai báo nó trong hàm mà nó được sử dụng, bằng cách này, nó sẽ nằm trên ngăn xếp và do đó chúng tôi sẽ tiết kiệm được 1KB RAM này.

Bây giờ câu hỏi: Đây sẽ là một ý tưởng tốt? Từ quan điểm thiết kế, nếu nó không cần vòng đời vô hạn / toàn cầu, thì nó thuộc về ngăn xếp. Nhưng này, đó là 1KB trong số 4KB, không có bất kỳ nhược điểm nào trong việc phân bổ 25% RAM như thế này? (có thể là 50% hoặc nhiều hơn của ngăn xếp)

Ai đó có thể chia sẻ một số kinh nghiệm với loại tình huống này, hoặc ai đó nghĩ về bất kỳ lý do hợp lệ nào để không đưa mảng này vào ngăn xếp? Tôi đang tìm kiếm những hạn chế kỹ thuật cũng như nhận xét về thiết kế.

Điều duy nhất tôi có ý thức là tôi phải đảm bảo rằng tôi thực sự có 1KB ngăn xếp miễn phí khi vào chức năng này. Có lẽ đó là tất cả những gì tôi phải quan tâm, có thể không.


4
Bạn đã viết, "và do đó tiết kiệm 1KB RAM này". Lưu nó để làm gì? 1KB đó phải có sẵn khi bạn cần nó cho mảng, vậy tại sao không thực hiện phân bổ tĩnh? Bạn có sử dụng khác cho bộ nhớ khi không cần thiết cho mảng không?
kkrambo

@kkrambo Tại một số điểm, chúng tôi cho rằng một hệ thống đã đầy khi chúng tôi không thể thêm bất cứ thứ gì vào RAM, cho dù đó là một cái gì đó tĩnh hay trên ngăn xếp. Nếu chúng ta chỉ đặt mảng này trên ngăn xếp khi chúng ta sử dụng nó, thì nó sẽ đặt một chức năng khác miễn là chúng không được sử dụng cùng một lúc. Nhưng câu hỏi là hoàn toàn chính đáng, hiện tại nếu chúng ta không thay đổi bất cứ điều gì trong SW, chúng ta sẽ không cần thêm RAM;)
Tim

1
Bạn có thể làm rõ liệu mảng này có nội dung luôn giữ nguyên hoặc nếu nó thay đổi khi hàm sử dụng nó được gọi?
Blrfl

@Blrfl Nó thay đổi mỗi khi hàm được gọi.
Tim

Câu trả lời:


8

Điều duy nhất tôi có ý thức là tôi phải đảm bảo rằng tôi thực sự có 1KB ngăn xếp miễn phí khi vào chức năng này.

Vâng, và đó là một hạn chế mạnh mẽ. Bạn sẽ chắc chắn tĩnh hơn bạn có một không gian lớn như vậy có sẵn trên ngăn xếp. Nếu mã nhỏ, nếu bạn đang sử dụng GCC gần đây để biên dịch mã của mình, hãy xem mã này .

BTW, một số bộ vi xử lý giá rẻ có thể sử dụng khung cuộc gọi "lớn" tốn kém hơn so với cuộc gọi "bình thường" (ví dụ: vì tập lệnh của chúng sẽ ưu tiên bù một byte từ con trỏ ngăn xếp). YMMV.

Ngoài ra, nếu bạn đang mã hóa bằng C và nếu bạn cảm thấy rằng mảng lớn của bạn có thể sử dụng lại không gian của nó cho các mục đích khác, bạn có thể xem xét biến nó thành thành viên công đoàn (với biến toàn cục unionloại). Vâng, đó là khá xấu xí.

Ngoài ra, bạn có thể xem xét mã hóa một số phân bổ heap nguyên thủy phù hợp với ứng dụng của bạn (và nó có thể có API khác với malloc& free....).


1
Cảm ơn bạn, tôi thực sự có một chút mong đợi loại câu trả lời này, tức là giữ cho nó được phân bổ tĩnh để chắc chắn rằng tôi không kết thúc với một chồng tràn. Thật không may, tôi không có trình biên dịch GCC gần đây và mã không nhỏ.
Tim

Bạn không thể có được (có lẽ bằng cách biên dịch GCC từ mã nguồn của nó) một trình biên dịch GCC cho nền tảng của bạn?
Basile Starynkevitch

2
Tôi đã có một cái, nó thực sự cũ. Làm việc trên một cái mới không nằm trong phạm vi của tôi, cũng không phù hợp với lịch trình của tôi. Nhưng chắc chắn, một số thứ sẽ dễ dàng hơn với các công cụ cập nhật.
Tim

3
Yêu cầu sếp của bạn cung cấp cho bạn một GCC mới hơn (bằng cách cung cấp cho bạn các công cụ nhị phân cập nhật hoặc bằng cách cho bạn thời gian để biên dịch lại GCC) là đáng giá, bởi vì có lẽ GCC mới hơn sẽ tối ưu hóa tốt hơn một chút (bạn có biết gcc -flto -Oskhông? ) và bạn có thể có được một số bộ nhớ ....
Basile Starynkevitch

2
Nó đang đi đúng hướng nhưng điều đó sẽ không đến trước một thời gian. Tôi đã sử dụng -Os trên các hệ thống khác với các bộ công cụ gần đây hơn, nhưng tôi không biết về tham số -flto này, cảm ơn vì đã chỉ ra nó. (Lưu ý rằng tôi đã thực hiện một vài thử nghiệm với tham số GCC -Os và -O1-3 vài tuần trước trên GCC 5.4.1 và RAM luôn giống nhau. Thời gian chạy FLASH và MCU khác nhau. Đó không phải là trên cùng một MCU như cái tôi đã đề cập trong Q / A này)
Tim

6

Mọi người có xu hướng thận trọng với một ngăn xếp lớn, bởi vì nó phát triển ngược về RAM và ghi đè lên các giá trị của các biến, dẫn đến hành vi không thể giải thích được. Nó thậm chí còn tệ hơn, bởi vì bạn cần biết địa chỉ con trỏ ngăn xếp thấp nhất có thể và trừ kích thước để phân bổ khi vào thói quen.

Đây là tất cả công việc để quản lý bộ nhớ phần cứng (sẽ tạo ra bẫy hoặc lỗi khi xảy ra tràn ngăn xếp) hoặc cho trình biên dịch, do nó có các tính năng cho loại phân tích này.

Nếu không, bạn có thể làm những gì bạn muốn với RAM của bạn.


4

Như các câu trả lời trước đã chỉ ra, trước tiên tôi cũng khuyên bạn nên để mảng tĩnh nếu nó phù hợp với bộ nhớ. Trong hầu hết các trường hợp, điều quan trọng hơn nhiều là phải có dấu chân bộ nhớ xác định, ngay cả khi điều đó có nghĩa là bạn "lãng phí" bộ nhớ cho các biến không được sử dụng mọi lúc. Đặt các mảng lớn vào ngăn xếp của bạn sẽ quá dễ dàng thổi bay nó và tràn các ngăn xếp có xu hướng gây ra các vấn đề khó tìm và khó tái tạo (nếu bạn không thể sử dụng MMU để bảo vệ ngăn xếp).

Đề xuất chia sẻ khối với một số dữ liệu khác với union là hợp lệ IMO, mặc dù đó cũng có thể là nguồn cho các vấn đề khó tìm, nếu bạn định vị sai các biến trong đó.

Nếu bạn sắp hết bộ nhớ và rất cần tạo các biến có thời gian sống ngắn hơn để chia sẻ nó, trước khi chuyển mảng sang stack, tôi sẽ xem xét thêm phân bổ bộ nhớ động, mặc dù nó có nhược điểm riêng. Trong trường hợp này, nó có thể không phải là một câu trả lời, vì mảng nghe có vẻ khá lớn so với bộ nhớ khả dụng.


1

Bạn có một tùy chọn khác nếu bạn có một số loại lưu trữ flash. Bạn có thể đánh đổi tốc độ truy cập của ram bằng cách lưu trữ dữ liệu của bạn trong flash và đọc và tìm kiếm ở đó. Bạn sẽ chỉ cần tải một bản ghi tại một thời điểm vào ram. Sẽ phức tạp hơn một chút nếu bạn cần cập nhật hồ sơ. Bạn sẽ cần một cơ chế phân cấp độ mòn. Tôi đã làm điều này trong quá khứ và bao gồm một chỉ mục để tăng tốc truy cập.


1
Tôi cũng đã làm điều này trong quá khứ nhưng câu hỏi của tôi là về những vấn đề tôi có thể gặp phải nếu tôi đặt nó lên ngăn xếp, không thực sự về các lựa chọn thay thế cho RAM khi chúng ta thiếu nó. Dù sao cũng cảm ơn bạn đã trả lời
Tim

1

Đặc biệt là khi làm việc với các hệ thống nhúng, bạn muốn có nhiều lỗi có thể xảy ra vào thời gian biên dịch và không có gì thất bại trong thời gian chạy (thật tuyệt nếu chúng ta có thể đạt được điều này, mặc dù ...).

Tạo các mảng lớn mà bạn có thể cần trong các trạng thái tùy ý của chương trình được phân bổ tĩnh thực hiện chính xác điều đó - Trình liên kết cuối cùng sẽ cảnh báo bạn "điều này không phù hợp với RAM", trong khi phân bổ ngăn xếp sẽ khiến chương trình của bạn gặp sự cố với ngăn xếp khó gỡ lỗi tràn ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.