python - 如何在 Python 中实现更快的文件 I/O?
问题描述
我有一个关于 Python的速度/效率相关问题:
我需要从嵌套的 JSON 文件中提取多个字段(写入.txt
文件后,它们有 ~ 64k行,当前代码段在 ~ 9 分钟内完成),其中每行可以包含浮点数和字符串。
通常,我会将所有数据放入numpy
并用于np.savetxt()
保存它..
我采取了简单地将线条组装成字符串的方式,但这相当慢。到目前为止,我正在做:
- 将每一行组装为一个字符串(从 JSON 中提取所需的字段)
- 将字符串写入相关文件
我有几个问题:
- 它导致更多单独的
file.write()
命令,这些命令也很慢(大约 64k * 8 次调用(对于 8 个文件))
所以我的问题是:
- 解决这类问题的好习惯是什么?一种可以平衡
speed vs memory-consumption
最有效的磁盘写入的方法。 - 我应该增加我的
DEFAULT_BUFFER_SIZE
吗?(目前是 8192)
我已经在 Every Programming Language和这个python org: I O 中检查了这个文件 I/O,但没有太大帮助,除了(在我理解之后,文件 io 应该已经在 python 3.6.x 中缓冲)我发现我的默认DEFAULT_BUFFER_SIZE
是 8192
.
这是我的片段的一部分-
def read_json_line(line=None):
result = None
try:
result = json.loads(line)
except Exception as e:
# Find the offending character index:
idx_to_replace = int(str(e).split(' ')[-1].replace(')',''))
# Remove the offending character:
new_line = list(line)
new_line[idx_to_replace] = ' '
new_line = ''.join(new_line)
return read_json_line(line=new_line)
return result
def extract_features_and_write(path_to_data, inp_filename, is_train=True):
# It's currently having 8 lines of file.write(), which is probably making it slow as writing to disk is involving a lot of overheads as well
features = ['meta_tags__twitter-data1', 'url', 'meta_tags__article-author', 'domain', 'title', 'published__$date',\
'content', 'meta_tags__twitter-description']
prefix = 'train' if is_train else 'test'
feature_files = [open(os.path.join(path_to_data,'{}_{}.txt'.format(prefix, feat)),'w', encoding='utf-8')
for feat in features]
with open(os.path.join(PATH_TO_RAW_DATA, inp_filename),
encoding='utf-8') as inp_json_file:
for line in tqdm_notebook(inp_json_file):
for idx, features in enumerate(features):
json_data = read_json_line(line)
content = json_data['meta_tags']["twitter:data1"].replace('\n', ' ').replace('\r', ' ').split()[0]
feature_files[0].write(content + '\n')
content = json_data['url'].split('/')[-1].lower()
feature_files[1].write(content + '\n')
content = json_data['meta_tags']['article:author'].split('/')[-1].replace('@','').lower()
feature_files[2].write(content + '\n')
content = json_data['domain']
feature_files[3].write(content + '\n')
content = json_data['title'].replace('\n', ' ').replace('\r', ' ').lower()
feature_files[4].write(content + '\n')
content = json_data['published']['$date']
feature_files[5].write(content + '\n')
content = json_data['content'].replace('\n', ' ').replace('\r', ' ')
content = strip_tags(content).lower()
content = re.sub(r"[^a-zA-Z0-9]", " ", content)
feature_files[6].write(content + '\n')
content = json_data['meta_tags']["twitter:description"].replace('\n', ' ').replace('\r', ' ').lower()
feature_files[7].write(content + '\n')
解决方案
来自评论:
为什么您认为 8 次写入会导致 8 次物理写入您的硬盘?文件对象本身会缓冲要写入的内容,如果它决定写入您的操作系统,您的操作系统不妨稍等一下,直到它物理写入 - 即使这样,您的 harrdrives 也有可能将文件内容保留一段时间直到它开始的缓冲区真正写。请参阅python 多久刷新一次文件?
您不应将异常用作控制流,也不应在不需要的地方递归。每次递归都会为函数调用准备新的调用堆栈——这需要资源和时间——并且所有这些都必须还原。
最好的办法是在将数据输入 json.load() 之前清理数据……下一个最好的办法是避免递归……尝试以下方式:
def read_json_line(line=None):
result = None
while result is None and line: # empty line is falsy, avoid endless loop
try:
result = json.loads(line)
except Exception as e:
result = None
# Find the offending character index:
idx_to_replace = int(str(e).split(' ')[-1].replace(')',''))
# slice away the offending character:
line = line[:idx_to_replace]+line[idx_to_replace+1:]
return result
推荐阅读
- webpack - 尝试添加 .scss 文件时 webpack 无法构建
- ruby-on-rails - 两个带有设计的 Rails 应用程序 - 同步身份验证 (Helpy.io)
- amazon-web-services - AWS:在 S3 静态内容以及 api 网关端点上映射域
- android - 如何使用 Firebase 向用户检索附近的面包店
- javascript - 反向遍历层次结构
- c# - 将计时器添加到列表对象
- node.js - 在 React/Express 应用程序中输入错误的 URL 或刷新会导致所有页面出错,直到用户返回主页
- python - 遍历列表列表,一次一个列表
- asp.net-core - 在 asp net core 中扩展 IServiceCollection
- r - R基于两列重置计数器