Hiểu hơn về interger và list trong Python và sử dụng Numpy để tiết kiệm memory hơn
Giả sử chúng ta muốn tạo ra một list chứa các số integer như sau:
arr = list(range(1000000))
Bạn đoán Python sẽ dùng bao nhiêu memory để lưu list arr? Hmm... Ở đại học ta được học integer là 4 bytes (tùy máy tính, tùy compiler), vậy tính ra với một triệu phần tử thì sẽ ~4MiB?
Trong thực tế, Python tốn ~36MiB để lưu arr và các số integer này. Sử dụng memory_profiler để profile memory usage của code.
Line # Mem usage Increment Line Contents
================================================
1 37.586 MiB 37.586 MiB @profile
2 def test_list():
3 74.184 MiB 36.598 MiB arr = list(range(1000000))
Thực chất là thế nào?
Trong Python, mọi thứ đều là object vì thế mà sẽ tốn thêm 1 lượng bytes cho overhead memory.
In [1]: number = 1; number.__dir__()
Out[1]:
['__repr__',
'__hash__',
'__getattribute__',
'__lt__',
'__le__',
'__eq__',
'__ne__',
'__gt__',
...]
Như bạn thấy, trong đoạn code trên là các attribute (đã bị lược bớt cho đỡ dài) của object number
. Hãy thử xem size của number
là bao nhiêu bytes.
In [2]: number.__sizeof__()
Out[2]: 28
Oh, 28 bytes. Quay về với ví dụ ban đầu, sẽ là ~28MiB cho 1 triệu số integer. Vậy còn ~8MiB nữa đi đâu? ~8MiB đó thuộc về object arr
. List trong python là 1 mảng các pointer (con trỏ) trỏ tới bất kỳ object nào. Với ví dụ của chúng ta sẽ là 1 triệu pointers. Trong hệ điều hành 64-bit, con trỏ chiếm 8 bytes.
In [3]: arr.__sizeof__()
Out[3]: 8000040
Sử dụng Numpy Array
Khác với kiểu list, numpy array không lưu reference tới bất kỳ Python object nào mà thay vào đó chỉ lưu các số mà thôi.
Profile đoạn code sau:
import numpy as np
arr = np.zeros((1000000,), dtype=np.uint32)
for i in range(1000000):
arr[i] = i
ta được:
Line # Mem usage Increment Line Contents
================================================
8 37.711 MiB 37.711 MiB @profile
9 def test_numpy():
10 50.414 MiB 12.703 MiB import numpy as np
11
12 50.414 MiB 0.000 MiB arr = np.zeros((1000000,), dtype=np.uint32)
13 54.586 MiB 0.000 MiB for i in range(1000000):
14 54.586 MiB 0.562 MiB arr[i] = i
Vậy là chỉ tốn 4MiB cho arr
, giống như đã được học. ?
Kết luận
Sự chênh lệch giữa 4MiB và 36MiB có thể là không lớn và ta có thể sống vui vẻ, nhưng giữa 4GiB và 36GiB lại là một chuyện rất lớn. Ngoài việc sử dụng numpy để tăng tốc độ trong tính toán, giờ đây ta đã có thêm lý do khác nữa: tiết kiệm memory
.