11.1 Global Interpreter Lock
Global Interpreter Lock (GIL) — là một cơ chế trong trình thông dịch CPython, đảm bảo rằng chỉ có một luồng thực thi byte-code Python tại một thời điểm. GIL ngăn cản thực thi song song của các luồng, điều này có thể ảnh hưởng xấu đến hiệu suất của các chương trình đa luồng, đặc biệt là trên bộ xử lý đa lõi.
Lý do tồn tại của GIL
Đơn giản hóa quản lý bộ nhớ: GIL đơn giản hóa quản lý bộ nhớ và thu gom rác, làm cho Python dễ thực hiện hơn.
An toàn luồng: GIL ngăn cản các trạng thái đua, làm cho việc thực thi mã trở nên an toàn luồng mà không cần sử dụng khóa.
Vấn đề gây ra bởi GIL
Hiệu suất hạn chế: Các chương trình đa luồng thực hiện các nhiệm vụ tính toán chuyên sâu không thể tận dụng hoàn toàn lợi ích của bộ xử lý đa lõi do hạn chế của GIL.
Sai lệch hiệu suất: Các chương trình sử dụng mạnh mẽ luồng để thực hiện nhiệm vụ có thể gặp phải sự suy giảm hiệu suất do chuyển đổi ngữ cảnh giữa các luồng.
Hiện tại có 4 cách chính để "giải quyết GIL":
11.2 Sử dụng đa xử lý (multiprocessing)
Mô-đun multiprocessing cho phép tạo các tiến trình, hoạt động song song và không bị giới hạn bởi GIL, vì mỗi tiến trình có trình thông dịch Python và bộ nhớ riêng.
Ví dụ:
import multiprocessing
def worker(num):
print(f'Worker: {num}')
def main():
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
main()
11.3 Lập trình bất đồng bộ
Lập trình bất đồng bộ sử dụng asyncio cho phép thực hiện các nhiệm vụ song song, không khóa luồng chính. Mặc dù không giải quyết GIL theo nghĩa đen, nhưng nó cho phép sử dụng hiệu quả thời gian chờ để thực hiện các nhiệm vụ khác.
Ví dụ:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 Sử dụng thư viện với quản lý luồng riêng
Một số thư viện, như NumPy và SciPy, được viết bằng C và sử dụng cơ chế quản lý luồng riêng, cho phép chúng giải quyết GIL và sử dụng hiệu quả bộ xử lý đa lõi cho tính toán.
Thực sự, đây là một trong những lý do chính sự thành công của Python, mặc dù nó có chậm đến đâu. Tất cả các tính toán phức tạp được viết lại bằng C/C++ và thực hiện trên tất cả các lõi của bộ xử lý hoặc thậm chí ngay trên các lõi của card đồ họa. Mà trên các card đồ họa hiện đại có hàng ngàn lõi đó nha.
Hóa ra, việc ngôn ngữ nhanh hay chậm không còn quan trọng nữa, nếu tất cả các tính toán cần nhiều tài nguyên đều thực hiện bằng các thư viện bên ngoài hoặc thậm chí hoàn toàn chuyển đến các trung tâm dữ liệu từ xa.
11.5 Thực hiện tính toán ngoài trình thông dịch Python
Sử dụng các phần mở rộng trên C/C++ hoặc các ngôn ngữ khác có thể thực hiện tính toán ngoài trình thông dịch Python và sau đó trả lại kết quả. Điều này cho phép tránh khóa GIL trong thời gian thực hiện tính toán chuyên sâu.
Ví dụ sử dụng Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Biên dịch và sử dụng:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION