V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
sjmcefc2
V2EX  ›  Python

读取大文件,最快的方式是什么?

  •  
  •   sjmcefc2 · 2018-05-05 07:51:25 +08:00 · 12069 次点击
    这是一个创建于 2154 天前的主题,其中的信息可能已经有所发展或是发生改变。

    for line in files:? 这个是读取大文件最快的方式吗

    54 条回复    2018-05-06 12:36:39 +08:00
    sjmcefc2
        1
    sjmcefc2  
    OP
       2018-05-05 08:04:40 +08:00
    with open("file_name", 'r') as input:
    for line in input:
    #process
    这种真的非常慢啊
    sjmcefc2
        2
    sjmcefc2  
    OP
       2018-05-05 08:07:14 +08:00
    100G 的文本。大家有好方式吗
    swulling
        3
    swulling  
       2018-05-05 08:10:37 +08:00 via iPhone   ❤️ 2
    sjmcefc2
        5
    sjmcefc2  
    OP
       2018-05-05 08:34:50 +08:00
    mmap 的话,如何逐行读取呢
    NUT
        6
    NUT  
       2018-05-05 08:46:17 +08:00
    java 用 RandomAccessFile 读流的时候判断是\n 就行
    ltoddy
        7
    ltoddy  
       2018-05-05 08:50:40 +08:00
    肯定是文件流啊,然后一部分一部分的读取.
    fs.createReadStream()
    LosLord
        8
    LosLord  
       2018-05-05 09:01:40 +08:00 via Android
    Java 有个根据指针读取的类
    sjmcefc2
        9
    sjmcefc2  
    OP
       2018-05-05 09:11:51 +08:00
    python 的 mmap 用行读取的方法?这样行吗,但是感觉还是很慢
    with open(STAT_FILE, "r+b") as f:
    m=mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    while True:
    line=m.readline()
    if line == '': break
    print line.rstrip()

    必须其他语言?
    sjmcefc2
        10
    sjmcefc2  
    OP
       2018-05-05 09:14:52 +08:00
    主要用来按行读取,然后 split,然后判断每个字段的编码,java 有这些? go 能满足?速度如何?
    qsnow6
        11
    qsnow6  
       2018-05-05 09:16:23 +08:00
    之所以慢,是因为要一次性把大文件读取到内存中。
    使用流就行了,不是换语言能解决的
    sjmcefc2
        12
    sjmcefc2  
    OP
       2018-05-05 09:26:32 +08:00
    @qsnow6 能给一个 python 流的例子吗
    用 mmap,42m 读了 900 万条
    yunpiao111
        13
    yunpiao111  
       2018-05-05 09:26:47 +08:00 via Android
    如果是文本类的话 单机多核可以用 pyspark
    notreami
        14
    notreami  
       2018-05-05 09:36:53 +08:00
    不读,最快。
    sjmcefc2
        15
    sjmcefc2  
    OP
       2018-05-05 09:40:17 +08:00
    @yunpiao111 混合编码文本,单机多核心,pyspark 有逐行读取的案例?貌似在 api 里面没看到逐行读取的。。。。
    sjmcefc2
        16
    sjmcefc2  
    OP
       2018-05-05 09:40:39 +08:00
    @notreami 太崇拜你了,哲学啊。
    sjmcefc2
        17
    sjmcefc2  
    OP
       2018-05-05 09:42:49 +08:00
    @qsnow6 流读取会把不可见的分隔符给搞掉吧,比如 hive 的^A
    swulling
        18
    swulling  
       2018-05-05 09:52:08 +08:00
    你得先找到瓶颈啊,别瞎猜测是读取慢。来来来我给你验证下。

    人工生成 1000 万行的文件
    $ time python ./numbers.py > out
    python ./numbers.py > out 6.37s user 0.24s system 89% cpu 7.395 total

    逐行读取,然后输出到 stdout,也就 7s 钟

    你说『 42m 读了 900 万条』,那么瓶颈我觉得大部分是出在你的处理逻辑上,管读取什么事情?
    crayygy
        19
    crayygy  
       2018-05-05 09:52:53 +08:00 via iPhone
    之前遇到过要读 10G 的文本,后来采取了先排序,分割,筛选一次,剩下差不多 2G,最后再进一步分段读取
    swulling
        20
    swulling  
       2018-05-05 09:53:11 +08:00
    用 Profile 去分析下耗时,优化要先分析,后优化。不能脑子一拍,肯定是 Python 大文件读取慢,就开始吭叽吭叽优化这个...
    dychenyi
        21
    dychenyi  
       2018-05-05 09:56:20 +08:00
    如果没有严格的上下文依赖的话,那么分割大文件,多线程读取,分割成几块就用几个线程,处理边界,数据结构最后再合并。
    个人有个 25G 的文件用 mmap+多线程读取 5 分钟之内。
    一定要用 mmap,因为这是最快的读文件方式。
    swulling
        22
    swulling  
       2018-05-05 09:57:07 +08:00
    1000 万行,每行 100 个字符,总共 1.9G 文件。Python 使用 mmap 每行都输出一下,需要
    python ./numbers.py > out 8.81s user 6.27s system 33% cpu 45.088 total

    如果是 100G 文件,也不过几分钟而已
    dychenyi
        23
    dychenyi  
       2018-05-05 09:58:12 +08:00
    还有怎么按行读取,遇到\n 就是新行啊。自己处理。
    sjmcefc2
        24
    sjmcefc2  
    OP
       2018-05-05 10:18:33 +08:00
    @swulling print 一行,没有其他逻辑处理。

    @crayygy 10G,最后剩下 2G,这得多大的重复?丢失多少信息?

    @swulling 目前只是一行行的读,然后在每行中 split 出每一个段。mmap 貌似会吃掉这个分界符。这样后续我就不行了
    tabris17
        25
    tabris17  
       2018-05-05 10:20:59 +08:00
    你要按行读取必须从头开始扫描啊,找出 N 个回车换行符,快不起来的
    sjmcefc2
        26
    sjmcefc2  
    OP
       2018-05-05 10:21:42 +08:00
    @dychenyi 按行读取没啥问题,就是貌似 mmap 之后,把我的分界符号吃掉了呢
    sjmcefc2
        27
    sjmcefc2  
    OP
       2018-05-05 10:25:00 +08:00
    with open("test1.txt","r+b") as f:
    mm = mmap.mmap(f.fileno(),0)
    while True:
    line = mm.readline()
    print line
    if line == '':
    break
    for v in line.split('^A'):#这个分界符不起作用了
    print v
    m.close()
    widewing
        28
    widewing  
       2018-05-05 11:26:06 +08:00 via Android
    @sjmcefc2 python 特殊符号不是这么转义的吧
    ioth
        29
    ioth  
       2018-05-05 11:30:15 +08:00
    什么类型“大”文件?后续怎么处理?
    crb912
        30
    crb912  
       2018-05-05 11:58:37 +08:00 via Android
    for line in input: 这个什么时候成了读取文件?

    明明是遍历,open 的时候,文件就已经一次性读入内存了好吧。
    silymore
        31
    silymore  
       2018-05-05 12:55:54 +08:00 via iPhone
    @sjmcefc2 你 print 是最慢的,把 print 去掉再试试
    swulling
        32
    swulling  
       2018-05-05 13:02:09 +08:00 via iPhone
    @sjmcefc2 把代码贴出来,写错了吧😄
    sjmcefc2
        33
    sjmcefc2  
    OP
       2018-05-05 13:23:10 +08:00
    @widewing ctrl+v+a,这个弄错了,不好意思。
    @ioth 带有分隔符的文本文件,后续按照分隔符拆分。
    @crb912 好吧,我错了,只是想问如何才能更快的遍历

    @silymore print 最慢?那我去掉。现在 for line in input 和 mmap 一起运行,觉得 mmap 还没有前者快?错觉?





    with open("test1.txt","r+b") as f:
    mm = mmap.mmap(f.fileno(),0,prot=mmap.PROT_READ)
    while True:
    line = mm.readline()
    #print line
    if line == '':
    break
    for v in line.split('^A'):
    # print chardet.detect(v)
    #print chardet.detect(v)['encoding']
    try:
    if(chardet.detect(v)['encoding'] in ['ascii','none','utf-8','GB2312','GBK','Big5','GB18030','windows-1252']):
    print v.decode(chardet.detect(v)['encoding']).encode('utf-8')
    else:
    print v.decode('utf-8').encode('utf-8')
    except:
    with open('error_mmap.txt','a') as e:
    e.write(line)
    m.close()
    ioth
        34
    ioth  
       2018-05-05 14:11:58 +08:00
    纯文本文件处理,没什么区别吧,读写文件都是依赖操作系统。
    dychenyi
        35
    dychenyi  
       2018-05-05 15:29:01 +08:00
    @ioth “”常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而 mmap 操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap 的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此 mmap 效率更高。“”
    zhicheng
        36
    zhicheng  
       2018-05-05 16:54:28 +08:00
    不知道也不要瞎讲,open 的时候文件并不会一次性读入内存。
    fakevam
        37
    fakevam  
       2018-05-05 17:39:28 +08:00
    mmap 更慢,多想想就知道,page fault 明显更多了,而且是 major page fault
    sjmcefc2
        38
    sjmcefc2  
    OP
       2018-05-05 17:43:39 +08:00
    @fakevam 目前看起来确实是 mmap 慢一些,当然我是两种都读一个文件,不知道是不是有影响。
    anjiannian
        39
    anjiannian  
       2018-05-05 18:50:32 +08:00
    @sjmcefc2 python 里面,for line in input 就是逐行读取,print 慢
    AX5N
        40
    AX5N  
       2018-05-05 19:07:49 +08:00
    把需求讲具体,不同需求下做法必定不同。有时候是直接把全部文件直接载入内存最快,但有时候不是。甚至有时候不能使用 python,python 一定会成为瓶颈
    AX5N
        41
    AX5N  
       2018-05-05 19:16:26 +08:00
    @AX5N 按我经验
    如果瓶颈在 IO 上的话,一次直接把所有内容都读进内存是最快的。
    如果瓶颈在对内容细致地分析 /计算上的话,例如你要遍历 10G 文本里每个字节的话,那就必须得换语言才行。
    如果瓶颈在于整体的处理速度的话,例如你要压缩一段文件。那这个就随便了,随便读取一部分处理完再继续读取就好。
    pathbox
        42
    pathbox  
       2018-05-05 19:27:26 +08:00 via iPhone
    逐行读取

    分块读取

    用 shell 脚本应该会很快很快
    livc
        43
    livc  
       2018-05-05 19:31:30 +08:00
    awk ?
    AX5N
        44
    AX5N  
       2018-05-05 19:33:10 +08:00
    @sjmcefc2 你这个文本有多少行?
    AX5N
        45
    AX5N  
       2018-05-05 19:48:54 +08:00
    @sjmcefc2
    100G 的文本有点夸张。

    先说读取
    假如你的硬盘是 HDD,按照 120M/s 来算的话,读取 10G 需要 850s ;
    如果你的硬盘是 SSD,按照 450M/s 来算的话,需要 230s ;
    无论你怎么来,无论你用什么办法,应该是不会慢过这个速度多少的,也不会快过这个速度多少。

    再说处理
    100G 的文本估计 10 亿行应该是有的吧,这个数量真的是太大了,单线程处理的话,python 速度慢的缺点会被暴露无遗。
    所以建议你多进程、多线程来处理。

    你这个每行之间没有联系,可以靠多线程来提高性能。如果碰上那种每行之间互有联系的,只能换 c++慢慢跑,python 绝对不行。
    redsonic
        46
    redsonic  
       2018-05-05 19:57:00 +08:00
    像这类文件访存的效率问题都是处理时间和空间把戏。如果一个大文件要在短时间内随机访问上万次,用 mmap 应该是最好的。如果只是访问那么几次还不如 open write read。不过一般情况下无脑用 iostream 难道还不够快?
    bfpiaoran
        47
    bfpiaoran  
       2018-05-05 23:11:17 +08:00
    100G 不大 昨天我们这有读 6T 的 用的 seek 不知道速度怎么样。。。。
    swulling
        48
    swulling  
       2018-05-06 00:28:54 +08:00 via iPad
    @sjmcefc2 你这个瓶颈不在 IO,是你具体处理逻辑太慢。你做个 Profile 就知道了
    speedywind
        49
    speedywind  
       2018-05-06 00:28:57 +08:00 via Android
    不管什么语言,逐行读取肯定会慢一点,最快的方法是用 buff,
    konakona
        50
    konakona  
       2018-05-06 00:29:35 +08:00
    看要怎么处理,一般是分段分段(多少行到多少行,或者其他标识符号来记忆)提取后执行处理。
    如果是要在 100GB 的文本里进行搜索……=v=呵呵呵呵呵呵呵呵
    speedywind
        51
    speedywind  
       2018-05-06 00:30:07 +08:00 via Android
    一次读取 4k 的数据,因为一般现在分区都是 4k 对齐的
    memorybox
        52
    memorybox  
       2018-05-06 10:14:44 +08:00
    这个问题其实有时候可以取巧处理的,比如用 parallel 工具,我觉得挺有意思,写了一篇文章,希望有帮助:

    http://happy123.me/blog/2018/05/06/how-to-improve-performance-your-cmd-by-parallel/
    sjmcefc2
        53
    sjmcefc2  
    OP
       2018-05-06 11:36:51 +08:00
    @livc 目前只会 sed
    @memorybox 这个可以试试。
    BBCCBB
        54
    BBCCBB  
       2018-05-06 12:36:39 +08:00
    with open("file_name", 'r') as input:
    for line in input:
    #process


    我记得这种就是流式读取的啊???
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1081 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 18:56 · PVG 02:56 · LAX 11:56 · JFK 14:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.