Khóa học
Gần đây, tôi có cơ hội được truy cập sớm vào Polars GPU engine, được hỗ trợ bởi NVIDIA RAPIDS cuDF, trước khi ra mắt bản open beta. Tính năng tiên tiến này có tiềm năng thay đổi quy trình dữ liệu bằng cách tăng tốc các thao tác Polars lên đến 13 lần với GPU của NVIDIA. Nếu bạn làm việc với các bộ dữ liệu quy mô lớn trong Python, đây là một bước ngoặt bạn không muốn bỏ lỡ.
Trong bài viết này, tôi sẽ giải thích mọi điều bạn cần biết về Polars GPU engine mới và cung cấp hướng dẫn từng bước để bạn bắt đầu!

Polars: Thư viện DataFrame hiệu năng cao
Trung tâm của hầu hết quy trình khoa học dữ liệu là DataFrame, một cấu trúc dữ liệu dạng bảng vừa linh hoạt vừa trực quan để xử lý dữ liệu có cấu trúc. Nói đơn giản, ai làm khoa học dữ liệu cũng đã từng làm việc với DataFrame.
DataFrame cho phép thao tác, khám phá và phân tích dữ liệu dễ dàng, cung cấp giao diện quen thuộc và nhất quán cho việc làm sạch, lọc, nhóm và biến đổi dữ liệu.
Tuy nhiên, trong bối cảnh dữ liệu lớn, các thư viện DataFrame truyền thống có thể thiếu hiệu năng và khả năng mở rộng, và đó là lúc Polars xuất hiện.
Polars là một thư viện DataFrame nhanh và hiệu quả, nhanh chóng trở thành lựa chọn hàng đầu cho xử lý dữ liệu hiệu năng cao. Được viết từ đầu bằng Rust, Polars được thiết kế để hoạt động sát phần cứng, tối ưu tốc độ và sử dụng tài nguyên mà không phụ thuộc vào các thành phần bên ngoài.
Bài viết Giới thiệu về Polars là nguồn tài liệu tuyệt vời để bắt đầu với thư viện này trong Python.

So sánh hiệu năng của các thư viện DataFrame phổ biến nhất. Nguồn ảnh: Polars
Kết quả benchmark do Polars công bố, như trong hình trên, cho thấy Polars liên tục vượt trội hơn các thư viện khác như Pandas, Modin, PySpark và Dask trên nhiều truy vấn khác nhau. Những kết quả này nhấn mạnh sức mạnh của Polars như lựa chọn nhanh nhất cho xử lý dữ liệu hiệu năng cao.
Nhưng làm sao điều đó có thể? Dưới đây là một số đặc điểm giúp Polars cực kỳ nhanh:
- Viết bằng Rust: Sử dụng lập trình cấp thấp để thực thi các thao tác, hoạt động sát phần cứng.
- Linh hoạt về I/O: Hỗ trợ hạng nhất cho mọi lớp lưu trữ dữ liệu phổ biến, dù là cục bộ, lưu trữ đám mây hay kết nối cơ sở dữ liệu.
- API trực quan: API của Polars cho phép bạn viết truy vấn một cách trực quan. Bộ tối ưu truy vấn của Polars sẽ nội bộ xác định kế hoạch thực thi hiệu quả nhất, giảm thiểu thời gian và bộ nhớ sử dụng.
- Xử lý out-of-core: API streaming của Polars cho phép xử lý các bộ dữ liệu quá lớn để vừa trong bộ nhớ.
- Thực thi song song: Polars tự động tận dụng tất cả lõi CPU sẵn có, chia nhỏ khối lượng công việc mà không cần cấu hình thêm, tối đa hóa sức mạnh tính toán của máy bạn.
- Động cơ truy vấn vector hóa: Polars xử lý truy vấn theo kiểu vector hóa bằng Apache Arrow, định dạng dữ liệu cột, và SIMD (Single Instruction, Multiple Data) để tối ưu hơn nữa việc sử dụng CPU.
Và với thông báo open beta gần đây của Polars GPU engine, nó còn sắp trở nên nhanh hơn nữa!
Tìm hiểu tích hợp NVIDIA RAPIDS cuDF
Nhu cầu xử lý và phân tích dữ liệu nhanh hơn đã tăng theo cấp số nhân trong những năm gần đây khi các bộ dữ liệu mở rộng đến hàng trăm triệu thậm chí hàng tỷ dòng. Để giải quyết thách thức này, lĩnh vực khoa học dữ liệu đang chứng kiến sự chuyển dịch ngày càng tăng từ xử lý dựa trên CPU truyền thống sang tính toán tăng tốc bằng GPU. Đây là lúc NVIDIA RAPIDS cuDF phát huy tác dụng.
NVIDIA RAPIDS là một bộ thư viện mã nguồn mở cho phép khoa học dữ liệu và phân tích được tăng tốc bằng GPU. Cốt lõi của RAPIDS được thiết kế để tinh gọn quy trình dữ liệu bằng cách tận dụng khả năng song song khổng lồ của GPU NVIDIA để tăng tốc các tác vụ thường bị ràng buộc bởi CPU.
Trong RAPIDS, cuDF là thư viện phụ trách các thao tác DataFrame. cuDF mở rộng lớp trừu tượng DataFrame quen thuộc vào bộ nhớ GPU, cho phép tích hợp vào quy trình dữ liệu mà không cần thay đổi nhiều mã. Thư viện hỗ trợ mọi thao tác chính mà nhà khoa học dữ liệu thực hiện trên DataFrame: lọc, tổng hợp, gộp và sắp xếp, tất cả đều được tăng tốc bởi GPU để đạt tốc độ cao.
NVIDIA và Polars vừa công bố bản open beta của Polars GPU engine cho Python, được hỗ trợ bởi RAPIDS cuDF. Tính năng mới này đánh dấu bước tiến lớn cho xử lý dữ liệu hiệu năng cao, mang lại quy trình nhanh hơn đến 13 lần cho người dùng Polars sở hữu GPU NVIDIA!

Thiết kế cấp cao của API Polars, bao gồm một động cơ GPU. Nguồn ảnh: Polars
Với việc bổ sung GPU engine, người dùng Polars có thể quyết định chạy khối lượng công việc dữ liệu của mình trên động cơ nào. Bộ tối ưu của Polars hỗ trợ tất cả động cơ, tự động xác định truy vấn nào có thể chạy trên GPU hoặc CPU.
Polars GPU Engine: Sự thay đổi cuộc chơi
Giờ bạn đã có bối cảnh về Polars và thư viện NVIDIA RAPIDS cuDF cũng như cách cả hai tích hợp và được cung cấp qua Python Polars API. Vậy điều này có ý nghĩa gì với nhà phát triển và nhà khoa học dữ liệu?
Dưới đây là tóm tắt những gì Polars GPU engine mang lại:
- Tích hợp với Python Polars Lazy API: Người dùng chỉ cần truyền
engine="gpu"vào thao táccollect()trong Lazy API của Polars, kích hoạt xử lý bằng GPU mà không cần thay đổi mã đáng kể. - Xử lý tương tác các bộ dữ liệu lớn: GPU engine được thiết kế để việc xử lý hàng trăm triệu dòng dữ liệu trở nên mang tính tương tác. Chỉ với một GPU, bạn có thể xử lý khối lượng dữ liệu khổng lồ vốn đòi hỏi các giải pháp phức tạp và chậm hơn.
- Tối ưu cho hiệu quả: Polars GPU engine tận dụng tối đa bộ tối ưu của Polars để đảm bảo thực thi hiệu quả nhất có thể, giảm thiểu thời gian xử lý và sử dụng bộ nhớ.
- Chuyển về CPU mượt mà: Trong trường hợp một số truy vấn không được hỗ trợ trên GPU, engine sẽ chuyển về CPU. Điều này đảm bảo quy trình không bị gián đoạn, tự động quay lại xử lý bằng CPU khi cần.
Vậy kết quả cho đến nay thế nào? Cùng xem nhé.

Polars GPU Engine tăng tốc xử lý dữ liệu lên đến 13 lần. Nguồn ảnh: Polars
Bài benchmark trên cho thấy khả năng ấn tượng của Polars GPU engine khi tăng tốc các truy vấn lên đến 13 lần.
Tuy nhiên, cần lưu ý rằng dùng Polars trên GPU không phải lúc nào cũng nhanh hơn CPU, đặc biệt với các truy vấn đơn giản không đòi hỏi nhiều tính toán. Trong những trường hợp này, hiệu năng thường bị giới hạn bởi tốc độ đọc dữ liệu từ đĩa, nghĩa là tăng tốc GPU sẽ ít lợi thế khi các thao tác nhập/xuất (I/O) là nút thắt chính.
Dẫu vậy, GPU engine mang lại cải thiện đáng kể cho các thao tác dữ liệu phức tạp, khiến nó trở thành công cụ giá trị cho các nhóm xử lý dữ liệu lớn hoặc truy vấn có join, group-by và xử lý chuỗi.
Sự tương tác giữa CPU và GPU
Một trong những điểm hấp dẫn nhất của tích hợp mới này là tính linh hoạt. Như đã đề cập, bộ tối ưu có thể chuyển đổi giữa thực thi trên CPU và GPU dựa trên độ phức tạp của truy vấn. Nhưng quá trình chuyển đổi này diễn ra như thế nào?
Polars không có tăng tốc GPU
Khi chạy Polars trên CPU, engine tuân theo một pipeline có cấu trúc và được tối ưu cao để thực thi các thao tác dữ liệu. Quá trình này bao gồm đại khái các bước sau:
- DSL (ngôn ngữ miền chuyên biệt): Engine của Polars đầu tiên tạo một phác thảo truy vấn có cấu trúc từ mã của bạn, tương tự cây cú pháp của ngôn ngữ lập trình. Bản phác thảo này xác định trình tự các thao tác bạn muốn thực hiện, như lọc, nhóm và tổng hợp.
- IR (biểu diễn trung gian): Tiếp theo, Polars chuyển phác thảo truy vấn thành kế hoạch thực thi chi tiết, đảm bảo mọi thao tác là đúng và các schema dữ liệu khớp nhau. Bước này thiết lập nền tảng cho cách Polars xử lý dữ liệu.
- Bộ tối ưu: Bộ tối ưu cải thiện kế hoạch thực thi bằng cách sắp xếp lại các thao tác và loại bỏ bước không cần thiết. Ví dụ, thao tác lọc có thể được đưa lên trước join hoặc tổng hợp để giảm kích thước bộ dữ liệu sớm hơn trong quá trình. Việc tối ưu này đảm bảo kế hoạch hiệu quả nhất trước khi thực thi.
- IR đã tối ưu: Sau tối ưu, kế hoạch thực thi được hoàn thiện và sẵn sàng xử lý bởi engine của Polars. Đây là bản thiết kế cho cách Polars sẽ thực hiện từng thao tác trên dữ liệu.
- Engine trong bộ nhớ của Polars (thực thi trên CPU): Cuối cùng, engine trong bộ nhớ của Polars thực thi kế hoạch đã tối ưu hoàn toàn trên CPU. Dù Polars đã tối ưu hiệu năng cao, bước này có thể tốn thời gian đáng kể nếu bạn làm việc với bộ dữ liệu lớn, vì CPU xử lý dữ liệu tuần tự hoặc với khả năng song song hạn chế.
Polars có tăng tốc GPU
Khi bạn bật tăng tốc GPU bằng cách truyền engine="gpu" vào phương thức .collect(), quá trình về cơ bản vẫn giống trên bề mặt nhưng có thêm các kiểm tra và tối ưu phía sau để xác định khối lượng công việc nào có thể chuyển sang GPU thông qua cuDF:
- DSL (ngôn ngữ miền chuyên biệt): Như ở chế độ CPU, Polars bắt đầu bằng việc tạo phác thảo truy vấn có cấu trúc từ mã. Phác thảo này giống nhau dù bạn chạy trên CPU hay GPU, ghi nhận các thao tác bạn muốn thực hiện.
- IR (biểu diễn trung gian): Polars chuyển phác thảo thành kế hoạch thực thi, kiểm tra tính đúng đắn và đảm bảo schema dữ liệu hợp lệ. Ở giai đoạn này, IR vẫn trung lập vì engine chưa quyết định CPU hay GPU sẽ xử lý các thao tác.
- Bộ tối ưu: Bộ tối ưu hoạt động như khi thực thi trên CPU, sắp xếp lại các thao tác để đạt hiệu quả tối đa và loại bỏ bước dư thừa. Lúc này, kế hoạch vẫn được tối ưu cho thực thi tổng quát.
- IR đã tối ưu: Kế hoạch tối ưu được hoàn thiện như khi chạy CPU. Tuy nhiên, khi bật tăng tốc GPU, engine giờ sẽ kiểm tra xem các phần nào của kế hoạch có thể chuyển sang GPU.
- Lời gọi lại cuDF (kiểm tra GPU): Đây là lúc điều kỳ diệu diễn ra! Lời gọi lại cuDF xác định liệu GPU có thể xử lý kế hoạch thực thi hay không. Một số thao tác vốn phù hợp với tăng tốc GPU, trong khi số khác thì không. Callback sẽ điều chỉnh kế hoạch để dùng GPU cho các thao tác được hỗ trợ.
- Engine trong bộ nhớ của Polars (thực thi GPU và CPU): Cuối cùng, engine trong bộ nhớ của Polars thực thi kế hoạch. GPU xử lý các truy vấn có thể tăng tốc bằng GPU, trong khi các truy vấn GPU không xử lý được sẽ chuyển về CPU.
Mô hình thực thi kết hợp này—nơi GPU và CPU được dùng song song—giúp Polars cực kỳ linh hoạt. Bạn hưởng lợi từ tăng tốc GPU mà không phải hy sinh khả năng tương thích cho các truy vấn không được GPU hỗ trợ. Bài đăng phát hành của Polars giải thích chi tiết kỹ thuật sâu hơn.
Bắt đầu với Polars GPU Engine
Và giờ là phần thú vị nhất! Trong mục này, tôi sẽ hướng dẫn các bước cần thiết để thiết lập môi trường và bắt đầu tận dụng xử lý dữ liệu tăng tốc bằng GPU.
Cách đơn giản nhất để bắt đầu là làm theo notebook Google Colab này.
Colab cung cấp mức sử dụng GPU miễn phí có giới hạn, đặc biệt hữu ích nếu bạn chưa có sẵn GPU. Nếu bạn dùng notebook kèm theo, chúng ta sẽ bắt đầu từ bước 2 bên dưới, vì vậy có thể bỏ qua các bước trước đó.
Điều kiện tiên quyết
Trước khi cài đặt Polars GPU engine, hãy đảm bảo hệ thống của bạn đáp ứng yêu cầu cho NVIDIA RAPIDS cuDF. Bạn có thể xem các thông số hệ thống cần thiết, bao gồm khả năng tương thích GPU và yêu cầu driver, trên trang tài liệu RAPIDS.
1. Tạo môi trường ảo (khuyến nghị)
Để bắt đầu với Polars GPU engine, tốt nhất bạn nên tạo môi trường ảo để tách biệt các phụ thuộc của dự án.
Tôi dùng conda cho ví dụ này nhưng bạn có thể dùng trình quản lý gói ưa thích.
Ngoài Python, bạn có thể thêm JupyterLab vào môi trường để khám phá dữ liệu tương tác và phát triển, đặc biệt hữu ích khi chạy các đoạn mã nhỏ và phân tích dữ liệu tức thì.
conda create -n polars-gpu -c conda-forge python=3.11 jupyterlab
Sau khi tạo môi trường ảo, kích hoạt nó bằng:
conda activate polars-gpu
2. Cài đặt Polars GPU engine
Giờ hãy bật tăng tốc GPU bằng cách cài đặt Polars và GPU engine. Lệnh này cũng sẽ thiết lập cuDF và các phụ thuộc khác cần thiết để dùng GPU NVIDIA:
pip install -U polars[gpu] --extra-index-url=https://pypi.nvidia.com
Sau khi cài đặt Polars GPU engine, dùng Polars trên GPU sẽ giống với làm việc trên CPU nhưng cho kết quả nhanh hơn nhiều với các quy trình phức tạp hoặc nặng dữ liệu.
Như đã giải thích, engine của Polars tự động tối ưu kế hoạch thực thi, tận dụng tối đa GPU NVIDIA để tăng tốc thao tác và giảm thiểu sử dụng bộ nhớ. Với các truy vấn không hỗ trợ tăng tốc GPU, Polars GPU engine có cơ chế chuyển về CPU mượt mà, đảm bảo quy trình tiếp tục không gián đoạn.
Hãy đưa engine vào thực chiến với vài ví dụ đơn giản!
Chạy một số truy vấn với Polars GPU Engine
Trong phần minh họa này, chúng ta sẽ dùng bộ dữ liệu 22GB các giao dịch tài chính mô phỏng từ Kaggle. NVIDIA lưu trữ bộ dữ liệu này trên bucket Google Cloud Storage (GCS), đảm bảo tốc độ tải xuống nhanh.
Đầu tiên, hãy tải bộ dữ liệu:
!wget https://storage.googleapis.com/rapidsai/polars-demo/transactions.parquet -O transactions.parquet
Vì bộ dữ liệu này có nguồn từ Kaggle, nó tuân theo giấy phép và điều khoản sử dụng dành riêng cho bộ dữ liệu Kaggle.
Hãy nạp bộ dữ liệu bằng Polars và kiểm tra schema:
import polars as pl
from polars.testing import assert_frame_equal
transactions = pl.scan_parquet("transactions.parquet")
transactions.collect_schema()
Đầu ra như sau:
Schema([('CUST_ID', String),
('START_DATE', Date),
('END_DATE', Date),
('TRANS_ID', String),
('DATE', Date),
('YEAR', Int64),
('MONTH', Int64),
('DAY', Int64),
('EXP_TYPE', String),
('AMOUNT', Float64)])
Giờ hãy tính tổng khối lượng giao dịch bằng cách cộng cột AMOUNT. Trước tiên, thử không dùng tăng tốc GPU:
transactions.select(pl.col("AMOUNT").sum()).collect()
Đầu ra:
AMOUNT
f64
3.6183e9
Tổng khối lượng giao dịch khá lớn! Hãy chạy cùng truy vấn trên GPU:
transactions.select(pl.col("AMOUNT").sum()).collect(engine="gpu")
Đầu ra:
AMOUNT
f64
3.6183e9
Với các thao tác đơn giản như thế này, CPU và GPU cho cùng kết quả với tốc độ tương tự, vì truy vấn không đủ nặng về tính toán để được hưởng lợi nhiều từ tăng tốc GPU. Tuy nhiên, GPU sẽ phát huy khi xử lý truy vấn phức tạp hơn.
Bây giờ, chuyển sang truy vấn phức tạp hơn. Trong truy vấn này, chúng ta nhóm giao dịch theo mã khách hàng (CUST_ID), cộng tổng số tiền giao dịch rồi sắp xếp kết quả theo người chi tiêu cao nhất.
Trước tiên, chạy trên CPU:
%%time
res_cpu = (
transactions
.group_by("CUST_ID")
.agg(pl.col("AMOUNT").sum())
.sort(by="AMOUNT", descending=True)
.head()
.collect()
)
res_cpu
Đầu ra như sau:
CPU times: user 4.63 s, sys: 3.75 s, total: 8.38 s
Wall time: 6.04 s
CUST_ID
AMOUNT
str
f64
"CA9UYOQ5DA"
2.0290e6
"CJUK2MTM5Q"
1.8115e6
"CYXX1NBIKL"
1.8082e6
"C6ILEYAYQ9"
1.7961e6
"CCNBC305GI"
1.7274e6
Giờ chạy cùng truy vấn trên GPU:
%%time
res_gpu = (
transactions
.group_by("CUST_ID")
.agg(pl.col("AMOUNT").sum())
.sort(by="AMOUNT", descending=True)
.head()
.collect(engine=”gpu”)
)
res_gpu
Đầu ra:
CPU times: user 347 ms, sys: 0 ns, total: 347 ms
Wall time: 353 ms
shape: (5, 2)
CUST_ID
AMOUNT
str
f64
"CA9UYOQ5DA"
2.0290e6
"CJUK2MTM5Q"
1.8115e6
"CYXX1NBIKL"
1.8082e6
"C6ILEYAYQ9"
1.7961e6
"CCNBC305GI"
1.7274e6
Như ta thấy, dùng GPU cho truy vấn phức tạp mang lại lợi ích hiệu năng rõ rệt, giảm thời gian thực thi từ 6,04 giây trên CPU xuống chỉ còn 353 mili giây trên GPU!
Ví dụ này minh họa cú hích hiệu năng mạnh mẽ mà Polars GPU engine mang lại cho các thao tác dữ liệu quy mô lớn.
Bạn có thể tìm thêm ví dụ nâng cao trong notebook Colab đi kèm.
Kết luận
Polars GPU Engine, được hỗ trợ bởi NVIDIA RAPIDS cuDF, mang lại cải thiện tốc độ ấn tượng, với xử lý dữ liệu nhanh hơn đến 13 lần cho các thao tác phức tạp. Với bộ dữ liệu quy mô lớn, Polars tạo lợi thế rõ rệt so với các thư viện DataFrame truyền thống.
Dù không phải mọi truy vấn đều được hưởng lợi như nhau từ tăng tốc GPU, Polars vẫn là công cụ mạnh mẽ cho bất kỳ ai làm việc với dữ liệu lớn. Sự dễ dàng tích hợp, kết hợp với mô hình thực thi CPU-GPU lai, khiến Polars trở thành ứng viên nặng ký cho các quy trình dữ liệu hiện đại.
Nếu bạn quan tâm phát triển kỹ năng thao tác dữ liệu, đặc biệt với Pandas, tôi rất khuyến nghị các khóa học sau:
- Data Manipulation with Pandas
- Streamlined Data Ingestion with Pandas
- Writing Efficient Code with Pandas
Những tài nguyên này sẽ giúp bạn có nền tảng vững chắc, bổ trợ cho sức mạnh của các công cụ như Polars!
FAQs
Những loại truy vấn nào hưởng lợi nhiều nhất từ tăng tốc GPU trong Polars?
Tăng tốc GPU trong Polars mang lại mức tăng tốc đáng kể nhất cho các truy vấn bị giới hạn bởi tính toán, bao gồm các thao tác như join, group-by và xử lý chuỗi. Những tác vụ này yêu cầu tính toán nặng và là nơi sức mạnh xử lý song song của GPU thực sự phát huy, cung cấp tốc độ nhanh hơn đến 13 lần so với xử lý trên CPU. Tuy nhiên, các truy vấn đơn giản hoặc bị giới hạn bởi I/O có thể không cải thiện nhiều.
Liệu tăng tốc GPU luôn nhanh hơn dùng CPU?
Không, không phải lúc nào cũng vậy. Dù tăng tốc GPU tăng tốc đáng kể cho các thao tác nặng về tính toán, như tổng hợp phức tạp và join, các truy vấn bị giới hạn bởi I/O—khi nút thắt cổ chai là đọc/ghi dữ liệu ra đĩa—có thể không được lợi nhiều. Trong các trường hợp này, GPU không mang lại ưu thế hiệu năng đáng kể so với CPU và thời gian thực thi sẽ tương tự.
Tôi có cần viết lại truy vấn Polars để dùng GPU engine không?
Không, bạn không cần viết lại truy vấn. Polars GPU engine tích hợp liền mạch với các quy trình Polars hiện có. Bạn chỉ cần truyền engine="gpu" vào phương thức collect() để bật tăng tốc GPU. Nếu bất kỳ phần nào của truy vấn không được GPU engine hỗ trợ, nó sẽ tự động chuyển về CPU mà không cần bạn thay đổi gì.
Tôi có thể dùng Polars GPU engine trên bất kỳ máy nào không?
Để dùng Polars GPU engine, bạn cần máy có GPU NVIDIA tương thích với RAPIDS cuDF, một phần của thư viện CUDA-X. Nếu máy của bạn không có GPU tương thích, bạn có thể dùng các dịch vụ đám mây như Amazon SageMaker Studio Lab, Google Colab hoặc PaperSpace cung cấp quyền truy cập GPU miễn phí hoặc trả phí.
Thalia Barrera là Biên tập viên Khoa học Dữ liệu Cấp cao tại DataCamp, có bằng thạc sĩ Khoa học Máy tính và hơn một thập kỷ kinh nghiệm trong kỹ thuật phần mềm và kỹ thuật dữ liệu. Thalia yêu thích việc đơn giản hoá các khái niệm công nghệ cho kỹ sư và nhà khoa học dữ liệu thông qua các bài blog, hướng dẫn và khoá học video.
