使用python处理23个G(约一千万条)的文本所需时间,线程Thread跟协程async、await对比

说明

有 1000 个 20M ⼤⼩的⽂本⽂件,⽂件中每⾏数据的格式固定为: {“name”: “xx”,“timestamp”: xx, “content”: “xx”}
name: 字符串, 长度为 32 个字节以内,
timestamp: 毫秒级时间戳,
content: 字符串,⻓度为 1024 个字节以内

文件地址

https://mc-public-resource-cn.s3.cn-north-
1.amazonaws.com.cn/records.zip

编写程序完成如下要求

1、找出所有 name 为 zhangsan 的⾏,并按照 timestamp 从⼩到⼤排序
2、 将排序好的结果重新写⼊新的⽂件,规则如下: 按照 timestamp 以天为维度组织⽂件,新⽂件的命名规则为 年-月-日,例如(2022-1-12), 将所有位于同⼀天的数据存放于同⼀个⽂件中

要求
  1. 开发语⾔可以根据⾃⼰的偏好在 python、java、go 中选择,可以⾃由的选择框架、 库
  2. 在硬件性能恒定(内存 16 G)的情况下,尽可能的缩短程序耗时
目录结构如下,result是运行结果保存目录

请添加图片描述

多线程代码如下:主要是多线程模块,json模块,time模块
import os
import time
import json
from threading import Thread


class Main:
    """处理文件"""
    def __init__(self, file_path, result_path):
        self.file_path = file_path
        self.result_path = result_path
        self.filename_list = os.listdir(self.file_path)
        self.filename_list = [self.file_path + i for i in self.filename_list if i.endswith('.txt')]  # 文件名列表
        self.date_dict = {}  # 存放日期对应的值,为列表
        # 结果保存目录
        try:
            os.mkdir(self.result_path)
        except FileExistsError as e:
            print('目录已存在')

    def read_files(self, file):
        """按日期分类文件内容"""
        with open(file, mode='r', encoding='utf8') as f:
            for line in f:
                if line[10:12] == '张三':
                    item = json.loads(line)  # json转为dict类型
                    timestamp = item.get('timestamp')  # 获取时间戳
                    date = time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000))  # 时间戳转为str类型日期

                    if date not in self.date_dict.keys():
                        self.date_dict[date] = []  # 日期为key,value为列表
                        print('添加字典key', date)

                    # 筛选,添加到日期对应的字典里面
                    self.date_dict[date].append(item)
        print('读取一个文件并分类完成')

    def write_file(self, filename):
        """写入文件"""
        with open(self.result_path + filename + '.txt', mode='w', encoding='utf8') as file:
            for line in self.date_dict[filename]:
                json.dump(line, file, ensure_ascii=False)
                file.write('\n')
        print('写入完成一个文件')

    def run(self):
        """运行"""
        # 读取文件,分类到列表套
        task_list = []
        for file in self.filename_list:
            task = Thread(target=self.read_files, args=(file,))
            task.start()
            task_list.append(task)
        [_.join() for _ in task_list]
        print('开始排序字典value')

        # 按列表内每个字典的时间戳排序
        for item in self.date_dict.values():
            item.sort(key=lambda d: d['timestamp'])
            print('一个列表排序完成')

        # 将排序好的写入文件
        l = []
        for filename in self.date_dict.keys():
            t = Thread(target=self.write_file, args=(filename,))
            t.start()
            l.append(t)
        [i.join() for i in l]


if __name__ == '__main__':
    start = time.time()
    # 目录路径(相对路径):源文件路径,结果保存路径
    test = Main(file_path=r'records\\', result_path=r'result\\')
    test.run()
    end = time.time() - start
    print('运行时间:', end)
最终运行时间:130秒

请添加图片描述

协程async,await代码如下
"""async await 协程方式"""

import os
import time
import json
import asyncio
# from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


async def read_files(file: str, date_dict: dict):
    """按日期分类文件内容"""
    with open(file, mode='r', encoding='utf8') as f:
        for line in f:
            if line[10:12] == '张三':
                item = json.loads(line)  # json转为dict类型
                timestamp = item.get('timestamp')  # 获取时间戳
                date = time.strftime("%Y-%m-%d", time.localtime(timestamp / 1000))  # 时间戳转为str类型日期

                if date not in date_dict.keys():
                    date_dict[date] = []  # 日期为key,value为列表
                    print('添加字典key', date)

                # 筛选,添加到日期对应的字典里面
                date_dict[date].append(item)
    print('读取一个文件并分类完成')


async def write_file(filename, result_path, date_dict):
    """写入文件"""
    with open(result_path + filename + '.txt', mode='w', encoding='utf8') as file:
        for line in date_dict[filename]:
            json.dump(line, file, ensure_ascii=False)
            file.write('\n')
    print('写入完成一个文件')


async def main(file_path, result_path):
    """主函数"""
    filename_list = os.listdir(file_path)
    filename_list = [file_path + i for i in filename_list if i.endswith('.txt')]  # 文件名列表
    # 结果保存目录
    try:
        os.mkdir(result_path)
    except FileExistsError as e:
        print('目录已存在')

    # 读取文件
    read_files_tasks = []
    date_dict = {}  # 存放日期对应的值,为列表
    for file in filename_list:
        read_files_tasks.append(read_files(file, date_dict))
    await asyncio.wait(read_files_tasks)

    # 写入文件
    write_file_tasks = []
    for filename in date_dict.keys():
        write_file_tasks.append(write_file(filename=filename, result_path=result_path, date_dict=date_dict))
    await asyncio.wait(write_file_tasks)


if __name__ == '__main__':
    import cProfile
    start = time.time()

    # 初始目录
    file_path = r'records\\'
    result_path = r'result\\'

    # 启动
    asyncio.run(main(file_path=file_path, result_path=result_path))

    # 性能测试
    # cProfile.run('asyncio.run(main(file_path=file_path, result_path=result_path))')

    print('运行时间:', time.time() - start)

运行结束91秒

请添加图片描述

python版本使用3.7,电脑配置信息如下

请添加图片描述

总体思路

使用的都是python内置的库跟函数,每一个文件使用一个线程,读取一行先截取字符串判断name是否等于张三,后用json模块将每一行转换为dict类型获取时间戳转换为“年-月-日”格式放到一个字典里面,每一个独立的日期为key,值为列表套字典格式,筛选出来的完全足够放在内存中,而不用先写入文件。最终使用lambda对字典内列表按时间戳排序:item.sort(key=lambda d: d[‘timestamp’]),然后再使用多线程写入文件。

结论

硬盘的读写速度是主要限制因素。
刚开始读取文件时先判断,再转换为json能缩短大量时间。
使用字典的 in 判断元素是否存在也能缩短运行时间。
列表的sort方法排序也能缩短运行时间。
GIL锁的存在,使得只能一个线程工作,阻塞挂起,还不如协程。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值