Tôi biết đó là một câu hỏi cũ, nhưng có một số điều mà mọi người dường như còn thiếu.
Đầu tiên, đây là phép nhân với 2: size << 1. Đây là phép nhân với bất kỳ thứ gì từ 1 đến 2: int (float (size) * x), trong đó x là số, * là dấu phẩy động toán học và bộ xử lý có để chạy các hướng dẫn bổ sung để truyền giữa float và int. Nói cách khác, ở cấp độ máy, việc nhân đôi chỉ cần một hướng dẫn duy nhất, rất nhanh để tìm ra kích thước mới. Nhân với một cái gì đó từ 1 đến 2 yêu cầu ít nhấtmột lệnh để truyền kích thước thành float, một lệnh để nhân (là phép nhân float, vì vậy có thể mất ít nhất gấp đôi số chu kỳ, nếu không phải là 4 hoặc thậm chí gấp 8 lần) và một lệnh để truyền trở lại int, và điều đó giả định rằng nền tảng của bạn có thể thực hiện phép toán float trên các thanh ghi mục đích chung, thay vì yêu cầu sử dụng các thanh ghi đặc biệt. Tóm lại, bạn nên kỳ vọng phép toán cho mỗi lần phân bổ mất ít nhất 10 lần thời gian dịch chuyển trái đơn giản. Tuy nhiên, nếu bạn đang sao chép nhiều dữ liệu trong quá trình phân bổ lại, điều này có thể không tạo ra nhiều khác biệt.
Thứ hai, và có lẽ là yếu tố thúc đẩy lớn: Mọi người dường như cho rằng bộ nhớ đang được giải phóng vừa tiếp giáp với chính nó, vừa tiếp giáp với bộ nhớ mới được cấp phát. Trừ khi bạn đang tự mình phân bổ trước tất cả bộ nhớ và sau đó sử dụng nó như một nhóm, thì điều này gần như chắc chắn không xảy ra. Hệ điều hành đôi khi có thểkết thúc việc này, nhưng hầu hết thời gian, sẽ có đủ sự phân mảnh không gian trống để bất kỳ hệ thống quản lý bộ nhớ tốt nào có thể tìm thấy một lỗ nhỏ nơi bộ nhớ của bạn sẽ vừa khít. Khi bạn thực sự nhận được các phần nhỏ, bạn có nhiều khả năng kết thúc với các phần liền kề, nhưng khi đó, phân bổ của bạn đủ lớn để bạn không thực hiện chúng thường xuyên, đủ để nó trở nên quan trọng nữa. Tóm lại, thật thú vị khi tưởng tượng rằng việc sử dụng một số lý tưởng sẽ cho phép sử dụng hiệu quả nhất dung lượng bộ nhớ trống, nhưng trên thực tế, điều đó sẽ không xảy ra trừ khi chương trình của bạn đang chạy trên kim loại trần (như trong, không có hệ điều hành bên dưới nó đưa ra tất cả các quyết định).
Câu trả lời của tôi cho câu hỏi? Không, không có con số lý tưởng. Nó là ứng dụng cụ thể đến nỗi không ai thực sự thử. Nếu mục tiêu của bạn là sử dụng bộ nhớ lý tưởng, thì bạn đã gặp khá nhiều may mắn. Đối với hiệu suất, phân bổ ít thường xuyên hơn sẽ tốt hơn, nhưng nếu chúng ta chỉ làm như vậy, chúng ta có thể nhân với 4 hoặc thậm chí 8! Tất nhiên, khi Firefox chuyển từ sử dụng 1GB lên 8GB chỉ trong một lần, mọi người sẽ phàn nàn, vì vậy điều đó thậm chí không có ý nghĩa. Dưới đây là một số quy tắc ngón tay cái mà tôi sẽ tuân theo:
Nếu bạn không thể tối ưu hóa việc sử dụng bộ nhớ, ít nhất đừng lãng phí các chu kỳ của bộ xử lý. Nhân với 2 nhanh hơn ít nhất là một bậc lớn hơn làm toán dấu phẩy động. Nó có thể không tạo ra sự khác biệt lớn, nhưng nó sẽ tạo ra sự khác biệt ít nhất (đặc biệt là ở giai đoạn đầu, trong thời gian phân bổ thường xuyên hơn và nhỏ hơn).
Đừng nghĩ quá nhiều. Nếu bạn chỉ dành 4 giờ để cố gắng tìm ra cách thực hiện một điều gì đó đã được thực hiện, bạn chỉ lãng phí thời gian của mình. Thành thật mà nói, nếu có một lựa chọn tốt hơn * 2, nó sẽ được thực hiện trong lớp vectơ C ++ (và nhiều nơi khác) nhiều thập kỷ trước.
Cuối cùng, nếu bạn thực sự muốn tối ưu hóa, đừng đổ mồ hôi cho những thứ nhỏ nhặt. Ngày nay, không ai quan tâm đến việc bộ nhớ 4KB bị lãng phí, trừ khi họ đang làm việc trên các hệ thống nhúng. Khi bạn nhận được 1GB đối tượng có dung lượng từ 1MB đến 10MB mỗi đối tượng, việc tăng gấp đôi có lẽ là quá nhiều (ý tôi là, tức là từ 100 đến 1.000 đối tượng). Nếu bạn có thể ước tính tỷ lệ mở rộng dự kiến, bạn có thể san bằng tỷ lệ tăng trưởng tuyến tính tại một thời điểm nhất định. Nếu bạn mong đợi khoảng 10 đối tượng mỗi phút, thì việc phát triển ở 5 đến 10 kích thước đối tượng mỗi bước (30 giây đến một phút một lần) có lẽ là tốt.
Tất cả những gì cần giải quyết là, đừng suy nghĩ quá nhiều, hãy tối ưu hóa những gì bạn có thể và tùy chỉnh cho ứng dụng (và nền tảng) của bạn nếu bạn phải.