+6

Python Guru Series 🐍🐍🐍 - Part 3: Global Interpreter Lock (GIL)

Xin chào các bạn, trong bài viết trước chúng ta đã cùng nhau tìm hiểu về các trình thông dịch (intepreters) phổ biến trong Python. Chúng ta cũng thấy được sự ảnh hưởng của các trình thông dịch đối với performance của chương trình Python trong một số bài toán cụ thể.

Cpython là trình thông dịch default của Python, support tất cả các bản phân phối của Python.

Cpython nổi tiếng với việc implement Golbal Intepreters Lock(GIL), thứ khiến Python trở thành ngôn ngữ single-threaded base.

Ngày hôm nay, chúng ta sẽ cùng nhau tìm hiểu về GIL - một chủ đề luôn hot khi bàn tán về Python. Về vai trò và ảnh hưởng của GIL tới ngôn ngữ Python.

Global Interpreter Lock (GIL) là gì?

Python Global Interpreter Lock hay GIL, hiểu một cách đơn giản, là một khóa mutex (hay khóa) bảo vệ quyền truy cập tới Python objects và đồng thời nó chỉ cho phép một luồng giữ quyền kiểm soát của trình thông dịch Python. Điều này có nghĩa là chỉ một luồng có thể thực thi tại bất kỳ thời điểm nào ngay cả khi chạy trên bộ xử lý đa nhân (multi-core processor).

The effect of GIL to Multi-threading programs

Tác động của GIL lên các chương trình đa luồng

Tác động của GIL không hiện rõ đối khi ta thực thi các chương trình đơn luồng. Tuy nhiên, tác động lớn nhất của GIL là nó có thể tạo ra bottleneck đáng kể cho các chương trình thực thi các tác vụ liên quan đến CPU hoặc các tác vụ sử dụng khả năng đa luồng. Hãy xem xét hai ví dụ dưới đây.

Code 1: Chương trình sử dụng CPU thực hiện đếm ngược đơn giản

Example 1: Single-thread.py

Output:
Completed in  3.0810108184814453

Code 2: Hai luồng chạy song song thực hiện đếm ngược đơn giản

Example 2: Multi-thread.py

Output:
Completed in 3.1420132384019721

Chương trình được thực thi trên máy tính PC với bộ xử lý Intel Core i3-10100, 4 nhân và 8 luồng, và RAM 32GB.
Như bạn có thể thấy từ hai ví dụ trên, chương trình đơn luồng thực thi nhanh hơn một chút so chương trình đa luồng. Điều này có nghĩa là việc áp dụng đa luồng không cải thiện thời gian thực thi của chương trình. Ngược lại, nó tăng thời gian thực thi do chi phí của việc khởi tạo luồng (fork thread) tại đầu và (join thread) kết thúc chương trình.

Tại sao GIL vẫn tồn tại?

Ngay từ đầu, cùng với sự phát triển của CPython, GIL được thiết kế để đảm bảo an toàn luồng trong Python. Nó cũng đặt nền tảng được Guido van Rossum thiết kể để cung cấp sự linh hoạt trong phát triển ứng dụng cho ngôn ngữ này.

1.Tính tương thích:

Nhiều Python packages (và trình thông dịch chính Python, CPython) sử dụng nhiều tiện ích mở rộng C, vốn không là thread-safe. Có khả năng nhiều luồng cố gắng truy cập cùng một tài nguyên, dẫn đến các tác động tiêu cực rất lớn. GIL làm cho việc tạo và sử dụng các tiện ích mở rộng của C an toàn hơn. Điều giúp các nhà phát triển trong những năm 90 bắt đầu sử dụng Python để phát triển phần mềm, thúc đẩy sự chấp nhận kiến trúc này.

2.Garbage Collection and Reference Counting

Một lý do quan trọng khác liên quan đến cách Python xử lý thu gom rác (Garbage collection). Garbage collection là một quá trình quản lý bộ nhớ tự động trong đó trình thông dịch theo dõi và thu hồi bộ nhớ do các đối tượng chiếm dụng mà không còn được tham chiếu hoặc không thể truy cập trong chương trình. Trong Python, có hai phương pháp chính để thu gom bộ nhớ nhưng phương pháp nổi bật nhất là quá trình đếm tham chiếu (reference counting).

Đếm tham chiếu trong Python là một cách hiệu quả để quản lý bộ nhớ và đảm bảo rằng tài nguyên được giải phóng khi chúng không còn được sử dụng, giúp ngăn chặn rò rỉ bộ nhớ trong các chương trình Python. Đếm tham chiếu hoạt động như sau:

  • Mỗi đối tượng theo dõi số lượng tham chiếu trỏ đến nó.
  • Khi số lượng tham chiếu của một đối tượng giảm xuống không, điều đó có nghĩa là không còn tham chiếu nào đến đối tượng đó trong chương trình.
  • Điều này cho thấy rằng đối tượng không còn cần thiết.
  • Hệ thống quản lý bộ nhớ của Python tự động thu hồi bộ nhớ do đối tượng chiếm dụng, xóa nó một cách hiệu quả.
  • Nếu không có GIL, nhiều luồng chạy đồng thời có thể thao tác số lượng tham chiếu của các đối tượng cùng một lúc, dẫn đến tranh chấp (racing condition) và rò rỉ bộ nhớ (memory leak). GIL hoạt động như một biện pháp bảo vệ, chỉ cho phép một luồng thực thi mã byte của Python tại một thời điểm, ngăn chặn các vấn đề tiềm ẩn này.

Một ví dụ về đếm tham chiếu trong Python Reference Counting in Python

Tới đây, bạn có thể thắc mắc tại sao GIL vẫn tồn tại mặc dù có những hạn chế của nó.

Sự thật là, đã có nhiều nỗ lực trong quá khứ để loại bỏ GIL khỏi CPython. Tuy nhiên, nhiệm vụ này được coi là rất thách thức và đòi hỏi nhiều nỗ lực, có thể dẫn đến việc tái cấu trúc hoàn toàn hệ sinh thái của Python.

Một số trình thông dịch như PyPy, phát triển từ một tập con hạn chế của Python, vẫn có GIL nhưng tập trung vào cải thiện tốc độ thực thi của mã Python thông qua biên dịch Just-In-Time (JIT) tại thời gian chạy. Các trình thông dịch khác như Jython và IronPython không có GIL trong thiết kế của chúng vì chúng tận dụng khả năng thời gian chạy của môi trường Java và .NET tương ứng. Tuy nhiên, hạn chế của việc sử dụng các trình thông dịch này là nhiều thư viện trong Python, viết bằng C, không được hỗ trợ như trong CPython. Các trình thông dịch này chỉ hỗ trợ các phiên bản Python cụ thể để giải quyết các bài toán hẹp.

Nỗ lực cải thiện hiệu suất của Python vẫn đang tiếp tục. Kể từ Python 3.4, với sự giới thiệu của Asyncio, hiệu suất đã được cải thiện đáng kể bằng cách cho phép thực thi đồng thời mà không cần nhiều luồng. Ngoài ra, nhiều nỗ lực đã được thực hiện để liên tục nâng cao khả năng của ngôn ngữ.

Tổng kết

Mọi thứ tồn tại đều có hai mặt, và GIL cũng không ngoại lệ. Một mặt, nó giúp quản lý bộ nhớ và làm cho các nhà phát triển dễ dàng viết code hơn. Mặt khác, nó gây ra các vấn đề về hiệu suất với đa luồng trong bối cảnh máy tính hiện đại đa nhân, đa luồng.

Sự tồn tại của GIL sẽ luôn là một chủ đề nóng khi thảo luận về các vấn đề của Python.

Tuy nhiên, bất chấp những vấn đề vốn có của nó, Python vẫn tỏa sáng trong các lĩnh vực như Big Data, AI và phát triển Web, với nhiều ứng dụng thành công. Những tín hiệu mới nhất từ các bản cập nhật Python cho thấy tương lai của Python sẽ có nhiều phát triển đầy hứa hẹn.

Trong bài viết tiếp theo, mình sẽ thảo luận về các cách vượt qua những hạn chế của GIL để gia tăng performance cho các chương trình Python của bạn. Cùng đón chờ nhé!

Một lần nữa, mình là Phan, một nhà phát triển tò mò và tận tâm.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí