2011-01-12 29 views
7

Đây là một câu hỏi lập trình đơn giản, đến từ sự thiếu hiểu biết của tôi về cách PHP xử lý sao chép mảng và unsetting trong một vòng lặp foreach. Nó như thế này, tôi có một mảng đến từ tôi từ một nguồn bên ngoài được định dạng theo cách tôi muốn thay đổi. Một ví dụ đơn giản sẽ là:Không tắt các giá trị mảng trong khi lặp lại lưu trên bộ nhớ?

$myData = array('Key1' => array('value1', 'value2')); 

Nhưng những gì tôi muốn sẽ là một cái gì đó như:

$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2')))); 

Vì vậy, tôi lấy $myData đầu tiên và định dạng nó như thứ hai $myData. Tôi hoàn toàn ổn với thuật toán định dạng của tôi. Câu hỏi của tôi nằm trong việc tìm kiếm một cách để bảo tồn bộ nhớ vì các mảng này có thể hơi khó sử dụng. Vì vậy, trong vòng lặp foreach của tôi, tôi sao chép (các) giá trị mảng hiện tại vào định dạng mới, sau đó tôi bỏ đặt giá trị mà tôi đang làm việc với từ mảng ban đầu. Ví dụ:

$formattedData = array(); 
foreach ($myData as $key => $val) { 
    // do some formatting here, copy to $reformattedVal 

    $formattedData[] = $reformattedVal; 

    unset($myData[$key]); 
} 

Cuộc gọi đến unset() có phải là ý tưởng hay không? Tức là, nó bảo tồn bộ nhớ vì tôi đã sao chép dữ liệu và không còn cần giá trị ban đầu? Hoặc, không PHP tự động thu thập dữ liệu rác vì tôi không tham chiếu nó trong bất kỳ mã tiếp theo nào?

Mã chạy tốt, và cho đến nay các tập dữ liệu của tôi quá nhỏ không đáng kể để kiểm tra sự khác biệt về hiệu suất. Tôi chỉ không biết nếu tôi đang thiết lập bản thân mình cho một số lỗi lạ hoặc CPU truy cập sau này.

Cảm ơn mọi thông tin chi tiết.
-sR

+0

Trừ khi dữ liệu của bạn là tuyệt đối lớn (một phần lớn RAM) thì bạn không lo lắng gì cả. Nếu php chạy ra một lý thuyết nó sẽ cho bạn biết, và bạn có thể tăng nó trong php.ini. – Ian

+4

Đó là một ý tưởng ngớ ngẩn *. Bạn vừa mới giới thiệu một tác dụng phụ có thể bị lãng quên sau này đối với một số * tối ưu hóa vi *: -/Không, PHP (cũng như bất kỳ ngôn ngữ GC chuẩn nào khác mà tôi biết) có thể làm cho dữ liệu * chứa * trong một cấu trúc dữ liệu có sẵn để cải tạo trong khi tham chiếu đến * container * tồn tại (điều này loại trừ các khái niệm như tham chiếu mềm/yếu). 'Unset' có thể/sẽ làm cho PHP GC khởi động, nhưng hiệu năng thực tế đã đạt được - nếu có - do áp lực bộ nhớ phát hành không phải là tầm thường để khái quát hóa. Nếu điều này * trở thành * một vấn đề, * sau đó * giải quyết nó. –

+0

kích thước của mảng này là bao nhiêu? –

Trả lời

0

Trừ khi bạn truy cập phần tử bằng cách hủy tham chiếu sẽ không làm gì cả, vì bạn không thể thay đổi mảng trong vòng lặp trong vòng lặp.

Điều đó nói rằng, thường được coi là thực hành không tốt để sửa đổi bộ sưu tập mà bạn đang lặp lại - cách tiếp cận tốt hơn là chia mảng nguồn thành các phần nhỏ hơn (chỉ tải một phần dữ liệu nguồn tại một thời điểm) và xử lý chúng, xóa toàn bộ mảng "chunk" khi bạn đi.

+0

"unsetting sẽ không làm gì cả" - điều này không đúng, mã của anh ta sẽ không đặt biến từ mảng gốc – Andy

+0

@Andy Tôi đã tuyên bố rõ ràng nó sẽ không làm bất cứ điều gì nếu nó không được truy cập bằng tham chiếu **. Từ hướng dẫn sử dụng PHP - "Trừ khi mảng được tham chiếu, foreach hoạt động trên một bản sao của mảng được chỉ định chứ không phải bản thân mảng đó." –

+0

Đúng, nhưng bạn sẽ nhận thấy mã của anh ấy đang xóa biến khỏi mảng ban đầu, không phải là bản sao. – Andy

4

Sử dụng tham chiếu đến biến trong vòng foreach bằng toán tử &. Điều này tránh tạo bản sao của mảng trong bộ nhớ cho foreach để lặp lại.

chỉnh sửa: như được chỉ ra bởi Artefacto việc đặt biến chỉ làm giảm số lượng tham chiếu đến biến ban đầu, do đó bộ nhớ được lưu chỉ trên con trỏ thay vì giá trị của biến. Việc sử dụng một tham chiếu kỳ quặc thực sự làm tăng tổng dung lượng bộ nhớ có lẽ là giá trị được sao chép vào một vị trí bộ nhớ mới thay vì được tham chiếu.

Trừ khi mảng được tham chiếu, foreach hoạt động trên một bản sao của mảng được chỉ định và không phải là mảng riêng của mình. foreach có một số tác dụng phụ trên con trỏ mảng. Đừng dựa vào con trỏ mảng trong hoặc sau mà không cần đặt lại.

Sử dụng memory_get_usage() để xác định lượng bộ nhớ bạn đang sử dụng.

Có ghi tốt về cách sử dụng bộ nhớ và phân bổ here.

Đây là mã kiểm tra hữu ích để xem cấp phát bộ nhớ - hãy thử bỏ ghi chú các dòng nhận xét để xem tổng mức sử dụng bộ nhớ trong các tình huống khác nhau.

echo memory_get_usage() . PHP_EOL; 
$test = $testCopy = array(); 
$i = 0; 
while ($i++ < 100000) { 
    $test[] = $i; 
} 
echo memory_get_usage() . PHP_EOL; 
foreach ($test as $k => $v) { 
//foreach ($test as $k => &$v) { 
    $testCopy[$k] = $v; 
    //unset($test[$k]); 
} 
echo memory_get_usage() . PHP_EOL; 
+0

Cảm ơn bạn đã trả lời và thông tin hữu ích Sử dụng ví dụ mã của bạn, tôi thấy khoảng 5MB khác biệt về mức sử dụng bộ nhớ khi sử dụng 'unset()'. Ngoài ra, mức sử dụng bộ nhớ sẽ tăng lên * * khi tham chiếu mảng trong foreach (trong khi không sử dụng 'unset()') Thú vị ... mặc dù đủ thời gian dành cho nó – Soulriser

+0

Không thực sự chính xác, hãy xem câu trả lời của Artefacto dưới đây! – GTodorov

2

Nếu tại bất kỳ điểm nào trong "định dạng" bạn làm như sau:

$reformattedVal['a']['b'] = $myData[$key]; 

Sau đó làm unset($myData[$key]); là không thích hợp bộ nhớ khôn ngoan bởi vì bạn chỉ được giảm số lượng tài liệu tham khảo của biến, mà bây giờ tồn tại ở hai nơi (bên trong $myData[$key]$reformattedVal['a']['b']). Trên thực tế, bạn lưu bộ nhớ lập chỉ mục biến bên trong mảng ban đầu, nhưng gần như không có gì.

+0

Điều này không đúng - theo các biến mặc định không được thông qua bởi tham chiếu, chỉ có các đối tượng là – Andy

+1

@Andy Đầu tiên không ai vượt qua bất cứ điều gì (bạn có thấy bất kỳ hàm nào không?), thứ hai, trong phân bổ '$ a = $ b' trong các tình huống bình thường không có bộ nhớ được sao chép giữa hai biến (PHP thực hiện copy-on-write), ngay cả khi nó hoạt động như thể bộ nhớ đã được sao chép. – Artefacto

+0

Sai lầm của tôi, tôi dự định chuyển nhượng hơn là chuyển các tham số. Tôi đã thêm mã thử nghiệm vào câu trả lời của mình để chứng minh bộ nhớ được lưu bằng cách sử dụng 'unset()'. – Andy

3

Xin nhớ rằng rules of Optimization Club:

  1. Nguyên tắc đầu tiên của Tối ưu hóa Club, bạn không Tối Ưu Hóa.
  2. Quy tắc thứ hai của Câu lạc bộ tối ưu hóa là, bạn không Tối ưu hóa mà không đo lường.
  3. Nếu ứng dụng của bạn chạy nhanh hơn giao thức truyền tải cơ bản, quá trình tối ưu hóa đã hết.
  4. Một yếu tố tại một thời điểm.
  5. Không có thị trường, không có lịch trình thị trường.
  6. Thử nghiệm sẽ tiếp tục miễn là phải.
  7. Nếu đây là đêm đầu tiên của bạn tại Câu lạc bộ tối ưu hóa, bạn phải viết một trường hợp kiểm tra.

Quy tắC# 1 và # 2 đặc biệt có liên quan ở đây. Trừ khi bạn biết rằng bạn cần phải tối ưu hóa, và trừ khi bạn đã đo lường rằng cần phải tối ưu hóa, sau đó không làm điều đó. Thêm việc bỏ đặt sẽ thêm lần truy cập thời gian chạy và sẽ làm cho các lập trình viên trong tương lai tại sao bạn thực hiện nó.

Để nó một mình.

+0

# 5 có nghĩa là gì – Jason

+0

"Marketroid" có nghĩa là ai đó từ bộ phận Tiếp thị. Trong ý nghĩa lớn hơn, đừng để ai đó đưa ra các thuật ngữ phi kỹ thuật cho bạn về những gì chương trình của bạn có thể làm được. –

+0

Cảm ơn bạn đã tham khảo, Andy. Tôi biết Marketroids quá tốt. – Soulriser

2

Tôi đã hết bộ nhớ trong khi xử lý các dòng của tệp văn bản (xml) trong vòng lặp. Đối với bất kỳ ai có tình huống tương tự, điều này làm việc cho tôi:

while($data = array_pop($xml_data)){ 
    //process $data 
} 
Các vấn đề liên quan