4.19. Bộ nhớ pool¶
Một camera giữ ba khung hình đầy đủ độ phân giải trong bộ nhớ pool framebuffer, chạy bộ đệm xem trước riêng song song, và vẫn còn chỗ cho một tập lệnh Python cùng các đối tượng của nó -- điều đó đòi hỏi quản lý bộ nhớ phức tạp hơn những gì một khối RAM đơn trên MCU có thể cung cấp. MicroPython đặt vừa mọi thứ bằng cách phân bổ chúng qua nhiều loại bộ nhớ khác nhau mà MCU cung cấp, và định tuyến mỗi loại phân bổ đến đúng loại bộ nhớ mà nó thực sự cần.
4.19.1. Các loại bộ nhớ¶
Một MCU OpenMV Cam hiện đại cung cấp bốn loại bộ nhớ riêng biệt. Loại đầu tiên ẩn với ứng dụng; ba loại còn lại là các pool mà các phân bổ có thể lấy từ đó.
Bộ nhớ cache dữ liệu của CPU -- một vùng bộ nhớ nhỏ, rất nhanh nằm giữa CPU và phần còn lại của RAM. Khi CPU đọc hoặc ghi một giá trị từ bộ nhớ chính, cache tự động giữ một bản sao, vì vậy các truy cập lặp lại vào cùng dữ liệu sẽ nằm trong cache và không phải trả chi phí truy cập bộ nhớ chậm hơn. Cache không phải là pool mà các phân bổ lấy từ đó. Nó trong suốt với ứng dụng -- nó chỉ làm cho phần còn lại của RAM cảm thấy nhanh hơn trong thực tế so với độ trễ thực tế của nó, cho đến khi tập dữ liệu làm việc không còn vừa với cache nữa.
Bộ nhớ bộ xử lý tích hợp chặt -- một khối RAM nhỏ được nối trực tiếp với CPU không qua bus. Truy cập một chu kỳ, không bao giờ bị lỡ, không bao giờ phải chờ. Các phân bổ thực sự cần bộ nhớ nhanh nhất có thể -- nơi mà mọi chu kỳ độ trễ đều quan trọng -- lấy từ pool này.
Bộ nhớ nhanh trên chip -- vài trăm kilobyte đến khoảng một megabyte RAM được tích hợp vào gói MCU. Độ trễ thấp, băng thông cao, nhưng kích thước hạn chế. Heap MicroPython nằm ở đây để các truy cập đối tượng Python giữ tốc độ nhanh; các bộ đệm làm việc nhỏ hơn mà CPU truy cập nhiều cùng chia sẻ pool này.
Bộ nhớ lớn chậm hơn -- trên các board ghép MCU với chip bộ nhớ ngoài, hàng chục megabyte RAM ngoài chip truy cập qua bus ngoài. Lớn hơn nhiều, nhưng mỗi lần truy cập mất nhiều thời gian hơn bộ nhớ trên chip; cache dữ liệu che khuất phần lớn chi phí đó với các tập dữ liệu làm việc có thể chứa được, và khoảng cách thể hiện rõ ở các thao tác quét qua dữ liệu quá lớn để cache. Dùng cho các phân bổ cần kích thước lớn mà CPU có thể chịu được tốc độ chậm hơn -- quan trọng nhất là bộ nhớ pool framebuffer.
Các board trong dòng sản phẩm nằm trên một phổ: một số chỉ có RAM trên chip; một số ghép RAM trên chip với một khối ngoài lớn hơn nhiều. Mỗi trong ba loại có thể phân bổ được coi là bộ nhớ pool -- một vùng mà các phân bổ lấy từ đó -- và được gắn nhãn để mỗi yêu cầu có thể chỉ định loại bộ nhớ mà nó thực sự cần.
4.19.2. Framebuffer chính¶
Framebuffer hỗ trợ snapshot() không yêu cầu bộ nhớ nhanh. Nó yêu cầu đủ bộ nhớ -- không hơn không kém. Điều đó đặt nó vào pool lớn nhất, vì vậy trên board có cả bộ nhớ trên chip lẫn ngoài chip, framebuffer sẽ nằm ở khối ngoài.
Một framebuffer đầy đủ độ phân giải với ba bộ đệm quá lớn để vừa với pool nhanh trên chip trên hầu hết các phần; pool lớn hơn là loại duy nhất có thể chứa nó. Cache dữ liệu của CPU che khuất phần lớn chi phí mỗi lần truy cập khi ứng dụng xử lý ảnh, và bộ máy DMA nạp đầy framebuffer từ cảm biến vẫn theo kịp tốc độ dữ liệu của cảm biến trong cả hai trường hợp.
Kích thước chính xác mà framebuffer chiếm được chọn từ pixformat(), framesize(), và số lượng framebuffers() hiện tại; nó tăng hoặc giảm mỗi khi bất kỳ tham số nào trong số đó thay đổi.
4.19.3. Framebuffer cảm biến phụ¶
Một phiên bản CSI thứ hai có framebuffer riêng, được phân bổ từ cùng pool mà cảm biến chính sử dụng. Pool được chia sẻ; các bộ đệm độc lập. Dung lượng của cảm biến phụ thường nhỏ hơn nhiều so với cảm biến chính, vì cảm biến phụ hoạt động ở độ phân giải thấp hơn, vì vậy bộ nhớ thêm mà framebuffer thứ hai chiếm chỉ là một phần nhỏ của cảm biến chính.
4.19.4. Framebuffer luồng¶
Bộ đệm xem trước ảnh là ngoại lệ. Nó không được phân bổ từ bất kỳ pool nào khi chạy; nó là một vùng cố định được dành riêng tại thời điểm build, với địa chỉ đã biết và kích thước đã biết. Điều đó giữ đường dẫn xem trước tách biệt khỏi mọi phân bổ khác -- vùng tồn tại từ khi khởi động và không bao giờ di chuyển.
4.19.5. Heap MicroPython¶
Các đối tượng Python -- biến, danh sách, từ điển, các thể hiện lớp, wrapper Image mà lời gọi snapshot() trả về, mọi chuỗi và tuple mà ứng dụng tạo ra -- sống trên heap thu gom rác MicroPython, tách biệt khỏi các pool bộ nhớ của camera. Heap thu gom rác (GC) là một vùng bộ nhớ mà MicroPython tự quản lý: mã Python phân bổ từ đó ngầm định mỗi khi một đối tượng được tạo ra, và MicroPython định kỳ quét heap và thu hồi không gian chiếm bởi các đối tượng mà ứng dụng không còn tham chiếu nữa, vì vậy ứng dụng không bao giờ phải giải phóng bộ nhớ thủ công.
Một vùng chuyên dụng được dành riêng cho heap GC khi khởi động, thường được đặt trong bộ nhớ nhanh trên chip để các truy cập Python giữ tốc độ nhanh, với tùy chọn tràn sang khối ngoài lớn hơn trên các board cần thêm không gian cho các cấu trúc dữ liệu lớn.
Image được trả về bởi snapshot() là một đối tượng wrapper nhỏ trên heap GC; dữ liệu điểm ảnh bên dưới nằm trong framebuffer trong một trong các pool của camera. Hai thứ không bao giờ cạnh tranh cùng bộ nhớ.
4.19.6. Tổng hợp lại¶
Định hướng mỗi loại phân bổ đến đúng pool -- bộ đệm lớn vào pool lớn hơn nơi chúng vừa, dữ liệu nhạy cảm với độ trễ vào pool nhanh hơn, heap Python vào vùng riêng của nó, xem trước vào khe dành riêng -- là điều làm cho việc chạy đồng thời một quy trình chụp đầy đủ độ phân giải, một kênh xem trước, và một tập lệnh Python không tầm thường trên các phần chỉ có vài megabyte bộ nhớ nhanh tổng cộng trở nên khả thi.