Tất nhiên, bạn có thể kết hợp các giải pháp với nhiều thư viện. Hoặc, bạn có thể sử dụng msgspec
một thư viện mới cung cấp lược đồ, phân tích cú pháp nhanh và một số thủ thuật gọn gàng để giảm mức sử dụng bộ nhớ, tất cả trong một thư viện
Một điểm khởi đầu. tích hợp sẵn import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
0 và import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
1
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
Hãy bắt đầu bằng cách xem xét hai thư viện khác. mô-đun
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
0 tích hợp trong Python và thư viện import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
1 nhanh chóng. Chúng tôi sẽ xem lại ví dụ từ bài viết của tôi về phân tích cú pháp JSON trực tuyến. Cụ thể, chúng tôi sẽ phân tích một tệp ~25 MB mã hóa danh sách các đối tượng JSON [tôi. e. từ điển], trông giống như các sự kiện GitHub, người dùng thực hiện các thao tác với kho lưu trữ[{"id":"2489651045","type":"CreateEvent","actor":{"id":665991,"login":"petroav","gravatar_id":"","url":"//api.github.com/users/petroav","avatar_url":"//avatars.githubusercontent.com/u/665991?"},"repo":{"id":28688495,"name":"petroav/6.828","url":"//api.github.com/repos/petroav/6.828"},"payload":{"ref":"master","ref_type":"branch","master_branch":"master","description":"Solution to homework and assignments from MIT's 6.828 [Operating Systems Engineering]. Done in my spare time.","pusher_type":"user"},"public":true,"created_at":"2015-01-01T15:00:00Z"},
...
]
Mục tiêu của chúng tôi là tìm ra kho lưu trữ nào mà một người dùng nhất định đã tương tác với
Đây là cách bạn làm điều đó với mô-đun
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
0 tích hợp trong thư viện chuẩn Pythonimport json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
Và đây là cách bạn thực hiện với
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
1, thay đổi hai dòngimport orjson
with open["large.json", "rb"] as f:
data = orjson.loads[f.read[]]
user_to_repos = {}
for record in data:
# .. same as stdlib code ...
Đây là dung lượng bộ nhớ và thời gian mà hai tùy chọn này sử dụng
$ /usr/bin/time -f "RAM: %M KB, Elapsed: %E" python stdlib.py
5250 records
RAM: 136464 KB, Elapsed: 0:00.42
$ /usr/bin/time -f "RAM: %M KB, Elapsed: %E" python with_orjson.py
5250 records
RAM: 113676 KB, Elapsed: 0:00.28
Mức sử dụng bộ nhớ cũng tương tự, nhưng
import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
1 nhanh hơn, ở mức 280 mili giây thay vì 420 mili giâyTiếp theo, hãy xem xét msgspec
msgspec
. giải mã và mã hóa dựa trên lược đồ cho JSON
Đây là mã tương ứng sử dụng msgspec
;
from msgspec.json import decode
from msgspec import Struct
class Repo[Struct]:
name: str
class Actor[Struct]:
login: str
class Interaction[Struct]:
actor: Actor
repo: Repo
with open["large.json", "rb"] as f:
data = decode[f.read[], type=list[Interaction]]
user_to_repos = {}
for record in data:
user = record.actor.login
repo = record.repo.name
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
Mã này dài hơn và chi tiết hơn vì msgspec
cho phép bạn xác định lược đồ cho các bản ghi mà bạn đang phân tích cú pháp
Khá hữu ích, bạn không cần phải có lược đồ cho tất cả các trường. Mặc dù bản ghi JSON có rất nhiều trường [hãy xem ví dụ trước đó để xem tất cả dữ liệu], chúng tôi chỉ cho msgspec
biết về các trường mà chúng tôi thực sự quan tâm
Đây là kết quả của việc phân tích cú pháp với msgspec
$ /usr/bin/time -f "RAM: %M KB, Elapsed: %E" python with_msgspec.py
5250 records
RAM: 38612 KB, Elapsed: 0:00.09
Nhanh hơn nhiều và bộ nhớ ít hơn nhiều
Để tóm tắt ba tùy chọn mà chúng tôi đã thấy, cũng như giải pháp dựa trên
import orjson
with open["large.json", "rb"] as f:
data = orjson.loads[f.read[]]
user_to_repos = {}
for record in data:
# .. same as stdlib code ...
0 phát trực tuyếnGóiThời gianRAMSử dụng bộ nhớ cố địnhSchemaStdlib import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
0420ms136MB❌❌import json
with open["large.json", "r"] as f:
data = json.load[f]
user_to_repos = {}
for record in data:
user = record["actor"]["login"]
repo = record["repo"]["name"]
if user not in user_to_repos:
user_to_repos[user] = set[]
user_to_repos[user].add[repo]
print[len[user_to_repos], "records"]
1280ms114MB❌❌import orjson
with open["large.json", "rb"] as f:
data = orjson.loads[f.read[]]
user_to_repos = {}
for record in data:
# .. same as stdlib code ...
0300ms14MB✓❌msgspec
90ms39MB❌✓Giải pháp phát trực tuyến chỉ sử dụng một lượng bộ nhớ cố định để phân tích cú pháp; . Nhưng trong ba giải pháp đó, msgspec
có mức sử dụng bộ nhớ thấp hơn đáng kể và cho đến nay đây là giải pháp nhanh nhất
Ưu và nhược điểm của phân tích cú pháp dựa trên lược đồ
Vì msgspec
cho phép bạn chỉ định lược đồ nên chúng tôi có thể tạo các đối tượng Python chỉ cho những trường mà chúng tôi thực sự quan tâm. Điều đó có nghĩa là sử dụng RAM thấp hơn và giải mã nhanh hơn;
Chúng tôi cũng đã xác thực lược đồ miễn phí. Nếu một trong các bản ghi bằng cách nào đó bị thiếu một trường hoặc nếu giá trị là loại sai, chẳng hạn như một số nguyên thay vì một chuỗi, trình phân tích cú pháp sẽ phàn nàn. Với các thư viện JSON tiêu chuẩn, việc xác thực lược đồ phải diễn ra riêng biệt
Mặt khác
- Mức sử dụng bộ nhớ khi giải mã vẫn tỷ lệ với tệp đầu vào. Trình phân tích cú pháp JSON trực tuyến như
0 vẫn mang lại lợi ích của việc sử dụng bộ nhớ cố định trong quá trình phân tích cú pháp, bất kể tệp đầu vào lớn đến mức nàoimport orjson with open["large.json", "rb"] as f: data = orjson.loads[f.read[]] user_to_repos = {} for record in data: # .. same as stdlib code ...
- Chỉ định lược đồ liên quan đến mã hóa nhiều hơn và ít linh hoạt hơn để xử lý dữ liệu không hoàn hảo
Tìm hiểu thêm về msgspec
msgspec
có các tính năng bổ sung, như mã hóa, hỗ trợ MessagePack [một định dạng thay thế nhanh hơn cho JSON], v.v. Nếu bạn đang phân tích cú pháp các tệp JSON một cách thường xuyên và bạn đang gặp phải các vấn đề về hiệu suất hoặc bộ nhớ hoặc bạn chỉ muốn các lược đồ tích hợp sẵn, hãy cân nhắc dùng thử
Bài viết tiếp theo. Ngoài cProfile. Chọn công cụ phù hợp để tối ưu hóa hiệu suất
Bài viết trước. Nút cổ chai của bạn ở đâu?
Xử lý dữ liệu quá chậm?
Bạn có thể nhận được kết quả nhanh hơn từ quy trình khoa học dữ liệu của mình—và cũng nhận lại được một số tiền—nếu bạn có thể tìm ra lý do tại sao mã của mình chạy chậm
Xác định các nút thắt cổ chai về hiệu suất và ngốn bộ nhớ trong khoa học dữ liệu sản xuất của bạn Các công việc Python với Sciagraph, trình lược tả luôn bật cho các công việc sản xuất hàng loạt
Tìm hiểu các kỹ năng kỹ thuật phần mềm Python thực tế mà bạn có thể sử dụng trong công việc của mình
Đăng ký nhận bản tin của tôi và tham gia cùng hơn 6500 nhà phát triển Python và nhà khoa học dữ liệu học các công cụ và kỹ thuật thực tế, từ hiệu suất Python đến đóng gói Docker, với một bài viết mới miễn phí trong hộp thư đến của bạn mỗi tuần