Trong bài viết này, chúng ta sẽ tìm hiểu về caching trong Python. Ta sẽ hiểu caching là gì và cách sử dụng hiệu quả.
Caching là một kỹ thuật dùng để cải thiện hiệu năng ứng dụng bằng cách tạm thời lưu trữ các kết quả mà chương trình đã tính được để tái sử dụng khi cần sau này.
Trong hướng dẫn này, chúng ta sẽ học các kỹ thuật khác nhau để caching trong Python, bao gồm các decorator @lru_cache và @cache trong mô-đun functools.
Nếu bạn đang vội, hãy bắt đầu với một cách cài đặt caching thật ngắn gọn rồi tiếp tục với các chi tiết sau.
Câu trả lời ngắn: Cài đặt caching trong Python
Để tạo cache trong Python, ta có thể dùng decorator @cache từ mô-đun functools. Trong đoạn mã dưới đây, lưu ý rằng hàm print() chỉ được thực thi một lần:
import functools
@functools.cachedefsquare(n):print(f"Calculating square of {n}")return n * n
# Testing the cached functionprint(square(4))print(square(4))# Calculating square of 4# 16# 16Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Giả sử chúng ta cần giải một bài toán và mất một giờ để tìm được đáp án đúng. Nếu hôm sau phải giải lại đúng bài toán đó, sẽ hữu ích nếu ta có thể tái sử dụng phần việc đã làm trước đó thay vì bắt đầu lại từ đầu.
Caching trong Python tuân theo nguyên tắc tương tự—nó lưu các giá trị khi chúng được tính trong lời gọi hàm để tái sử dụng khi cần. Kiểu caching này còn được gọi là memoization.
Hãy xem một ví dụ ngắn tính tổng của một dải số lớn hai lần:
output =sum(range(100_000_001))print(output)
output =sum(range(100_000_001))print(output)# 5000000050000000# 5000000050000000Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Kết quả cho thấy cả hai lần gọi mất thời gian xấp xỉ như nhau (tùy thiết lập của chúng ta mà thời gian có thể nhanh hoặc chậm hơn).
Tuy nhiên, ta có thể dùng cache để tránh phải tính lại cùng một giá trị nhiều lần. Ta có thể gán lại tên sum bằng cách dùng hàm cache() trong mô-đun chuẩn functools:
Lần gọi thứ hai giờ chỉ mất vài micro giây thay vì hơn một giây vì kết quả tính tổng các số từ 0 đến 100.000.000 đã được tính và lưu trong cache—lần gọi thứ hai sử dụng giá trị đã được tính và lưu trước đó.
Ở trên, ta dùng decorator functools.cache() để thêm cache cho hàm dựng sẵn sum(). Nhân tiện, decorator trong Python là một hàm dùng để thay đổi hành vi của hàm khác mà không sửa mã của hàm đó một cách vĩnh viễn. Bạn có thể tìm hiểu thêm về decorator trong Hướng dẫn Python Decorators này.
Decorator functools.cache() được thêm vào Python từ phiên bản 3.9, nhưng ta có thể dùng functools.lru_cache() cho các phiên bản cũ hơn. Ở phần tiếp theo, chúng ta sẽ khám phá cả hai cách tạo cache, bao gồm cách dùng cú pháp decorator thường gặp như @cache.
Caching trong Python: Các phương pháp khác nhau
Mô-đun functools của Python có hai decorator để áp dụng caching cho hàm. Hãy cùng tìm hiểu functools.lru_cache() và functools.cache() qua một ví dụ.
Hãy viết một hàm sum_digits() nhận vào một dãy số và trả về tổng các chữ số của những số đó. Ví dụ, nếu ta dùng bộ giá trị (23, 43, 8) làm đầu vào, thì:
Tổng các chữ số của 23 là năm.
Tổng các chữ số của 43 là bảy.
Tổng các chữ số của 8 là tám.
Vì vậy, tổng chung là 20.
Đây là một cách để viết hàm sum_digits() của chúng ta:
defsum_digits(numbers):returnsum(int(digit)for number in numbers for digit instr(number))
numbers =23,43,8print(sum_digits(numbers))# 20Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Hãy dùng hàm này để khám phá các cách khác nhau tạo cache.
Caching thủ công trong Python
Trước tiên, hãy tạo cache thủ công. Dù ta cũng có thể tự động hóa điều này dễ dàng, việc tạo cache thủ công giúp ta hiểu quy trình.
Hãy tạo một từ điển và thêm các cặp khóa-giá trị mỗi lần ta gọi hàm với một giá trị mới để lưu kết quả. Nếu ta gọi hàm với một giá trị đã được lưu trong từ điển này, hàm sẽ trả về giá trị đã lưu mà không cần tính lại:
import random
import timeit
defsum_digits(numbers):if numbers notin sum_digits.my_cache:
sum_digits.my_cache[numbers]=sum(int(digit)for number in numbers for digit instr(number))return sum_digits.my_cache[numbers]
sum_digits.my_cache ={}
numbers =tuple(random.randint(1,1000)for _ inrange(1_000_000))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Lần gọi thứ hai tới sum_digits(numbers) nhanh hơn nhiều so với lần đầu vì nó dùng giá trị đã được lưu đệm.
Giờ hãy giải thích chi tiết hơn đoạn mã trên. Trước hết, lưu ý rằng ta tạo từ điển sum_digits.my_cache sau khi định nghĩa hàm dù ta dùng nó trong phần định nghĩa hàm.
Hàm sum_digits() kiểm tra xem đối số truyền vào hàm đã là một trong các khóa trong từ điển sum_digits.my_cache hay chưa. Tổng các chữ số chỉ được tính nếu đối số chưa có trong cache.
Vì đối số ta dùng khi gọi hàm đóng vai trò là khóa trong từ điển, nên nó phải là kiểu dữ liệu băm được (hashable). Danh sách không phải là hashable, nên ta không thể dùng nó làm khóa trong từ điển. Ví dụ, hãy thử thay numbers bằng một danh sách thay vì một bộ giá trị—điều này sẽ phát sinh TypeError:
# ...
numbers = [random.randint(1, 1000) for _ in range(1_000_000)]
# ...Được hỗ trợ bởi
Tạo cache thủ công rất phù hợp cho mục đích học tập, nhưng giờ hãy khám phá các cách nhanh hơn để làm việc này.
Caching với functools.lru_cache() trong Python
Python có decorator lru_cache() từ phiên bản 3.2. Chữ “lru” ở đầu tên hàm viết tắt cho "least recently used" (ít được dùng gần đây nhất). Ta có thể hình dung cache như một chiếc hộp lưu trữ các thứ hay dùng—khi đầy, chiến lược LRU sẽ bỏ đi mục mà ta đã lâu không dùng nhất để nhường chỗ cho thứ mới.
Hãy trang trí hàm sum_digits() bằng @functools.lru_cache:
import functools
import random
import timeit
@functools.lru_cachedefsum_digits(numbers):returnsum(int(digit)for number in numbers for digit instr(number))
numbers =tuple(random.randint(1,1000)for _ inrange(1_000_000))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Nhờ có caching, lần gọi thứ hai chạy nhanh hơn đáng kể.
Mặc định, cache lưu 128 giá trị đầu tiên được tính. Khi cả 128 vị trí đều đầy, thuật toán xóa giá trị ít được dùng gần đây nhất (LRU) để tạo chỗ cho các giá trị mới.
Ta có thể đặt kích thước tối đa khác cho cache khi trang trí hàm bằng tham số maxsize:
import functools
import random
import timeit
@functools.lru_cache(maxsize=5)
def sum_digits(numbers):
return sum(
int(digit) for number in numbers for digit in str(number)
)
# ...Được hỗ trợ bởi
Trong trường hợp này, cache chỉ lưu năm giá trị. Ta cũng có thể đặt đối số maxsize thành None nếu không muốn giới hạn kích thước cache.
Caching với functools.cache() trong Python
Python 3.9 bổ sung một decorator caching đơn giản và nhanh hơn—functools.cache(). Decorator này có hai đặc điểm chính:
Nó không có kích thước tối đa—tương tự như gọi functools.lru_cache(maxsize=None).
Nó lưu tất cả lời gọi hàm và kết quả của chúng (không dùng chiến lược LRU). Điều này phù hợp với các hàm có đầu ra tương đối nhỏ hoặc khi ta không cần lo về giới hạn kích thước cache.
Hãy dùng decorator @functools.cache cho hàm sum_digits():
import functools
import random
import timeit
@functools.cachedefsum_digits(numbers):returnsum(int(digit)for number in numbers for digit instr(number))
numbers =tuple(random.randint(1,1000)for _ inrange(1_000_000))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))print(
timeit.timeit("sum_digits(numbers)",globals=globals(),
number=1))Được hỗ trợ bởi
Trình soạn thảo Learn-by-Example, nhấn Tab để Vào. Nhấn Escape rồi Tab hoặc Shift+Tab để điều hướng ra ngoài.
Trang trí sum_digits() bằng @functools.cache tương đương với việc gán sum_digits cho functools.cache():
# ...
def sum_digits(numbers):
return sum(
int(digit) for number in numbers for digit in str(number)
)
sum_digits = functools.cache(sum_digits)Được hỗ trợ bởi
Cách này cho phép ta trang trí hàm chỉ với @cache.
Các chiến lược caching khác
Các công cụ sẵn có của Python triển khai chiến lược caching LRU, trong đó các mục ít được dùng gần đây nhất sẽ bị xóa để tạo chỗ cho giá trị mới.
Hãy xem qua một vài chiến lược caching khác:
Vào trước, ra trước (FIFO): Khi cache đầy, mục được thêm đầu tiên sẽ bị loại bỏ để nhường chỗ cho giá trị mới. Khác biệt giữa LRU và FIFO là LRU giữ lại các mục được dùng gần đây, còn FIFO loại bỏ mục cũ nhất bất kể tần suất sử dụng.
Vào sau, ra trước (LIFO): Mục vừa được thêm gần nhất sẽ bị loại bỏ khi cache đầy. Hãy tưởng tượng một chồng đĩa ở căng-tin. Chiếc đĩa được đặt lên gần nhất (vào sau) sẽ được lấy ra đầu tiên (ra trước).
Được dùng gần đây nhất (MRU): Giá trị vừa được dùng gần đây nhất sẽ bị loại bỏ khi cần thêm chỗ trong cache.
Thay thế ngẫu nhiên (RR): Chiến lược này loại bỏ ngẫu nhiên một mục để tạo chỗ cho mục mới.
Các chiến lược này cũng có thể kết hợp với các thước đo về thời hạn hiệu lực—điều này đề cập đến thời gian mà một dữ liệu trong cache được coi là hợp lệ hay còn phù hợp. Hãy hình dung một bài báo trong cache. Nó có thể được truy cập thường xuyên (LRU sẽ giữ lại), nhưng sau một tuần, tin tức có thể đã lỗi thời.
Caching trong Python: Các trường hợp sử dụng phổ biến
Từ đầu đến giờ, chúng ta dùng các ví dụ đơn giản cho mục đích học tập. Tuy vậy, caching có rất nhiều ứng dụng trong thực tế.
Trong khoa học dữ liệu, chúng ta thường thực hiện các phép toán lặp lại trên tập dữ liệu lớn. Việc dùng kết quả đã lưu đệm giúp giảm thời gian và chi phí khi phải lặp lại các phép tính giống nhau trên cùng tập dữ liệu.
Ta cũng có thể dùng caching để lưu các tài nguyên bên ngoài như trang web hoặc cơ sở dữ liệu. Hãy xét một ví dụ và lưu đệm một bài viết của DataCamp. Nhưng trước hết, ta cần cài đặt mô-đun bên thứ ba requests bằng cách chạy dòng lệnh sau trong terminal:
Lưu ý là chúng tôi đã lược bớt đầu ra vì nó rất dài. Tuy nhiên, hãy để ý rằng chỉ lần gọi đầu tiên tới get_article() mới in ra cụm từ Fetching article from {url}.
Đó là vì trang web chỉ được truy cập ở lần gọi đầu tiên. Kết quả được lưu trong cache của hàm. Khi ta yêu cầu cùng trang web lần thứ hai, dữ liệu lưu trong cache sẽ được trả về thay cho việc truy cập lại.
Caching giúp tránh các độ trễ không cần thiết khi lấy lại cùng một dữ liệu nhiều lần. Các API bên ngoài thường cũng có giới hạn tần suất gọi và chi phí khi truy xuất dữ liệu. Caching giúp giảm chi phí API và giảm khả năng chạm ngưỡng giới hạn tần suất.
Một trường hợp phổ biến khác là trong các ứng dụng machine learning nơi nhiều phép tính tốn kém cần được lặp lại. Ví dụ, nếu ta cần token hóa và vector hóa văn bản trước khi dùng trong mô hình học máy, ta có thể lưu đầu ra vào cache. Bằng cách này, ta sẽ không cần lặp lại các thao tác tốn tài nguyên tính toán.
Những thách thức thường gặp khi caching trong Python
Chúng ta đã tìm hiểu về các lợi ích của caching trong Python. Cũng có một số thách thức và hạn chế cần lưu ý khi triển khai cache:
Vô hiệu hóa cache và tính nhất quán: Dữ liệu có thể thay đổi theo thời gian. Do đó, các giá trị lưu trong cache cũng có thể cần được cập nhật hoặc loại bỏ.
Quản lý bộ nhớ: Lưu trữ lượng dữ liệu lớn trong cache cần bộ nhớ, và có thể gây ra vấn đề hiệu năng nếu cache tăng trưởng không kiểm soát.
Độ phức tạp: Việc thêm cache làm tăng độ phức tạp cho hệ thống trong quá trình tạo và bảo trì cache. Thường thì lợi ích vượt trội so với chi phí này, nhưng độ phức tạp tăng lên có thể dẫn đến các lỗi khó phát hiện và sửa chữa.
Kết luận
Chúng ta có thể dùng caching để tối ưu hiệu năng khi các phép toán tốn tài nguyên tính toán được lặp lại trên cùng dữ liệu.
Python có hai decorator để tạo cache khi gọi hàm: @lru_cache và @cache trong mô-đun functools.
Tuy nhiên, ta cần đảm bảo giữ cho cache được cập nhật và quản lý bộ nhớ đúng cách.
Nếu bạn muốn học thêm về caching và Python, hãy xem lộ trình kỹ năng gồm sáu khóa học Python Programming này.
Tôi học Vật lý và Toán ở bậc đại học tại Đại học Malta. Sau đó, tôi chuyển đến London và lấy bằng Tiến sĩ Vật lý tại Imperial College. Tôi nghiên cứu các kỹ thuật quang học mới để chụp ảnh võng mạc người. Hiện nay, tôi tập trung vào việc viết về Python, truyền đạt về Python và giảng dạy Python.
Tiếp tục phát triển kỹ năng Khoa học Dữ liệu hiện đại của bạn bằng cách tìm hiểu về các cấu trúc lặp (iterators) và biểu thức danh sách (list comprehensions).
Trong bài viết này, chúng ta sẽ tìm hiểu về caching trong Python. Ta sẽ hiểu caching là gì và cách sử dụng hiệu quả.
Caching là một kỹ thuật dùng để cải thiện hiệu năng ứng dụng bằng cách tạm thời lưu trữ các kết quả mà chương trình đã tính được để tái sử dụng khi cần sau này.
Trong hướng dẫn này, chúng ta sẽ học các kỹ thuật khác nhau để caching trong Python, bao gồm các decorator
@lru_cachevà@cachetrong mô-đunfunctools.Nếu bạn đang vội, hãy bắt đầu với một cách cài đặt caching thật ngắn gọn rồi tiếp tục với các chi tiết sau.
Câu trả lời ngắn: Cài đặt caching trong Python
Để tạo cache trong Python, ta có thể dùng decorator
@cachetừ mô-đunfunctools. Trong đoạn mã dưới đây, lưu ý rằng hàmprint()chỉ được thực thi một lần:Caching trong Python là gì?
Giả sử chúng ta cần giải một bài toán và mất một giờ để tìm được đáp án đúng. Nếu hôm sau phải giải lại đúng bài toán đó, sẽ hữu ích nếu ta có thể tái sử dụng phần việc đã làm trước đó thay vì bắt đầu lại từ đầu.
Caching trong Python tuân theo nguyên tắc tương tự—nó lưu các giá trị khi chúng được tính trong lời gọi hàm để tái sử dụng khi cần. Kiểu caching này còn được gọi là memoization.
Hãy xem một ví dụ ngắn tính tổng của một dải số lớn hai lần:
Chương trình phải tính tổng mỗi lần. Ta có thể xác nhận điều này bằng cách đo thời gian cho hai lần gọi:
Kết quả cho thấy cả hai lần gọi mất thời gian xấp xỉ như nhau (tùy thiết lập của chúng ta mà thời gian có thể nhanh hoặc chậm hơn).
Tuy nhiên, ta có thể dùng cache để tránh phải tính lại cùng một giá trị nhiều lần. Ta có thể gán lại tên
sumbằng cách dùng hàmcache()trong mô-đun chuẩnfunctools:Lần gọi thứ hai giờ chỉ mất vài micro giây thay vì hơn một giây vì kết quả tính tổng các số từ 0 đến 100.000.000 đã được tính và lưu trong cache—lần gọi thứ hai sử dụng giá trị đã được tính và lưu trước đó.
Ở trên, ta dùng decorator
functools.cache()để thêm cache cho hàm dựng sẵnsum(). Nhân tiện, decorator trong Python là một hàm dùng để thay đổi hành vi của hàm khác mà không sửa mã của hàm đó một cách vĩnh viễn. Bạn có thể tìm hiểu thêm về decorator trong Hướng dẫn Python Decorators này.Decorator
functools.cache()được thêm vào Python từ phiên bản 3.9, nhưng ta có thể dùngfunctools.lru_cache()cho các phiên bản cũ hơn. Ở phần tiếp theo, chúng ta sẽ khám phá cả hai cách tạo cache, bao gồm cách dùng cú pháp decorator thường gặp như@cache.Caching trong Python: Các phương pháp khác nhau
Mô-đun
functoolscủa Python có hai decorator để áp dụng caching cho hàm. Hãy cùng tìm hiểufunctools.lru_cache()vàfunctools.cache()qua một ví dụ.Hãy viết một hàm
sum_digits()nhận vào một dãy số và trả về tổng các chữ số của những số đó. Ví dụ, nếu ta dùng bộ giá trị(23, 43, 8)làm đầu vào, thì:23là năm.43là bảy.8là tám.Đây là một cách để viết hàm
sum_digits()của chúng ta:Hãy dùng hàm này để khám phá các cách khác nhau tạo cache.
Caching thủ công trong Python
Trước tiên, hãy tạo cache thủ công. Dù ta cũng có thể tự động hóa điều này dễ dàng, việc tạo cache thủ công giúp ta hiểu quy trình.
Hãy tạo một từ điển và thêm các cặp khóa-giá trị mỗi lần ta gọi hàm với một giá trị mới để lưu kết quả. Nếu ta gọi hàm với một giá trị đã được lưu trong từ điển này, hàm sẽ trả về giá trị đã lưu mà không cần tính lại:
Lần gọi thứ hai tới
sum_digits(numbers)nhanh hơn nhiều so với lần đầu vì nó dùng giá trị đã được lưu đệm.Giờ hãy giải thích chi tiết hơn đoạn mã trên. Trước hết, lưu ý rằng ta tạo từ điển
sum_digits.my_cachesau khi định nghĩa hàm dù ta dùng nó trong phần định nghĩa hàm.Hàm
sum_digits()kiểm tra xem đối số truyền vào hàm đã là một trong các khóa trong từ điểnsum_digits.my_cachehay chưa. Tổng các chữ số chỉ được tính nếu đối số chưa có trong cache.Vì đối số ta dùng khi gọi hàm đóng vai trò là khóa trong từ điển, nên nó phải là kiểu dữ liệu băm được (hashable). Danh sách không phải là hashable, nên ta không thể dùng nó làm khóa trong từ điển. Ví dụ, hãy thử thay
numbersbằng một danh sách thay vì một bộ giá trị—điều này sẽ phát sinhTypeError:Tạo cache thủ công rất phù hợp cho mục đích học tập, nhưng giờ hãy khám phá các cách nhanh hơn để làm việc này.
Caching với functools.lru_cache() trong Python
Python có decorator
lru_cache()từ phiên bản 3.2. Chữ “lru” ở đầu tên hàm viết tắt cho "least recently used" (ít được dùng gần đây nhất). Ta có thể hình dung cache như một chiếc hộp lưu trữ các thứ hay dùng—khi đầy, chiến lược LRU sẽ bỏ đi mục mà ta đã lâu không dùng nhất để nhường chỗ cho thứ mới.Hãy trang trí hàm
sum_digits()bằng@functools.lru_cache:Nhờ có caching, lần gọi thứ hai chạy nhanh hơn đáng kể.
Mặc định, cache lưu 128 giá trị đầu tiên được tính. Khi cả 128 vị trí đều đầy, thuật toán xóa giá trị ít được dùng gần đây nhất (LRU) để tạo chỗ cho các giá trị mới.
Ta có thể đặt kích thước tối đa khác cho cache khi trang trí hàm bằng tham số
maxsize:Trong trường hợp này, cache chỉ lưu năm giá trị. Ta cũng có thể đặt đối số
maxsizethànhNonenếu không muốn giới hạn kích thước cache.Caching với functools.cache() trong Python
Python 3.9 bổ sung một decorator caching đơn giản và nhanh hơn—
functools.cache(). Decorator này có hai đặc điểm chính:functools.lru_cache(maxsize=None).Hãy dùng decorator
@functools.cachecho hàmsum_digits():Trang trí
sum_digits()bằng@functools.cachetương đương với việc gánsum_digitschofunctools.cache():Lưu ý rằng ta cũng có thể dùng kiểu import khác:
Cách này cho phép ta trang trí hàm chỉ với
@cache.Các chiến lược caching khác
Các công cụ sẵn có của Python triển khai chiến lược caching LRU, trong đó các mục ít được dùng gần đây nhất sẽ bị xóa để tạo chỗ cho giá trị mới.
Hãy xem qua một vài chiến lược caching khác:
Các chiến lược này cũng có thể kết hợp với các thước đo về thời hạn hiệu lực—điều này đề cập đến thời gian mà một dữ liệu trong cache được coi là hợp lệ hay còn phù hợp. Hãy hình dung một bài báo trong cache. Nó có thể được truy cập thường xuyên (LRU sẽ giữ lại), nhưng sau một tuần, tin tức có thể đã lỗi thời.
Caching trong Python: Các trường hợp sử dụng phổ biến
Từ đầu đến giờ, chúng ta dùng các ví dụ đơn giản cho mục đích học tập. Tuy vậy, caching có rất nhiều ứng dụng trong thực tế.
Trong khoa học dữ liệu, chúng ta thường thực hiện các phép toán lặp lại trên tập dữ liệu lớn. Việc dùng kết quả đã lưu đệm giúp giảm thời gian và chi phí khi phải lặp lại các phép tính giống nhau trên cùng tập dữ liệu.
Ta cũng có thể dùng caching để lưu các tài nguyên bên ngoài như trang web hoặc cơ sở dữ liệu. Hãy xét một ví dụ và lưu đệm một bài viết của DataCamp. Nhưng trước hết, ta cần cài đặt mô-đun bên thứ ba
requestsbằng cách chạy dòng lệnh sau trong terminal:Sau khi cài
requests, ta có thể thử đoạn mã sau, đoạn này sẽ cố lấy cùng một bài viết DataCamp hai lần trong khi dùng decorator@lru_cache:Lưu ý là chúng tôi đã lược bớt đầu ra vì nó rất dài. Tuy nhiên, hãy để ý rằng chỉ lần gọi đầu tiên tới
get_article()mới in ra cụm từFetching article from {url}.Đó là vì trang web chỉ được truy cập ở lần gọi đầu tiên. Kết quả được lưu trong cache của hàm. Khi ta yêu cầu cùng trang web lần thứ hai, dữ liệu lưu trong cache sẽ được trả về thay cho việc truy cập lại.
Caching giúp tránh các độ trễ không cần thiết khi lấy lại cùng một dữ liệu nhiều lần. Các API bên ngoài thường cũng có giới hạn tần suất gọi và chi phí khi truy xuất dữ liệu. Caching giúp giảm chi phí API và giảm khả năng chạm ngưỡng giới hạn tần suất.
Một trường hợp phổ biến khác là trong các ứng dụng machine learning nơi nhiều phép tính tốn kém cần được lặp lại. Ví dụ, nếu ta cần token hóa và vector hóa văn bản trước khi dùng trong mô hình học máy, ta có thể lưu đầu ra vào cache. Bằng cách này, ta sẽ không cần lặp lại các thao tác tốn tài nguyên tính toán.
Những thách thức thường gặp khi caching trong Python
Chúng ta đã tìm hiểu về các lợi ích của caching trong Python. Cũng có một số thách thức và hạn chế cần lưu ý khi triển khai cache:
Kết luận
Chúng ta có thể dùng caching để tối ưu hiệu năng khi các phép toán tốn tài nguyên tính toán được lặp lại trên cùng dữ liệu.
Python có hai decorator để tạo cache khi gọi hàm:
@lru_cachevà@cachetrong mô-đunfunctools.Tuy nhiên, ta cần đảm bảo giữ cho cache được cập nhật và quản lý bộ nhớ đúng cách.
Nếu bạn muốn học thêm về caching và Python, hãy xem lộ trình kỹ năng gồm sáu khóa học Python Programming này.