如何快速read & parse csv文件

m
macaronsalt
楼主 (未名空间)

大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写了c++,竟然需要1个小时才跑完。

-单机运行
-没有多线程
-std::getline (要读取更快是不是要用更底层的接口?)

update:
0. background: 需要重复运行做数据测试。之前所有的配套库函数都使用了c++,所以继续使用c++接口这样不需要移植程序到其它语言。

1. 之后准备用database解决。一次性全部import到database,这样只需要第一次parse.

2. question:有几位版友推荐使用其他语言,从简单易用或者现成的库的角度我明白
。我不太明白,以performance见长的c++反而没有解决方案其他语言更快吗?

3. multi-threading: read:parse = 1:5.使用了多线程 producer/consumer,把总时
间减少到和read的时间一样,总时间缩减到大概1/6

4. IO: 进一部profiling确认是否有代码冗余或者硬盘本身的读写有问题。结果:只测试
std::getline,没有任何其他额外存储和处理,速度为6~8MB/s,而加上所有其他处理
大概在5~7MB/s。可以忽略代码的冗余或者bug,目前主要瓶颈在IO
-- TODO: 参考https://www.mitbbs.com/article_t/Programming/31573465.html,使
用github库看IO performance是否有改进
-- 增大read buffer,或者memory mapped file,测试IO performance是否有改进

w
walkrandom

把数据先load到内存
SSD到内存3000MB/s,10秒种。
单线程顺序parse,看有几个Atoi, Atof的操作
不会超过10分钟

y
yhangw

std::getline干这活儿很烂。

你试试perl和python,对简单文本逐行处理的优化做的不错。perl这块儿基本数没啥对手,python也应该比你的简单c++快很多。 10g文本,不管perl还是python,单核处理
,都应该在<10分钟以内完成。>

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

f
fantasist

听起来用Go非常合适?
l
lightroom

pandas几分钟应该能把10gb csv读到dataframe里

【在 macaronsalt(马卡龙加盐)的大作中提到:】
:大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c++,竟然需要1个小时才跑完。


a
askagain


【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

Times should be in seconds, it’s in D :-)
https://github.com/eBay/tsv-utils

Performance Studies
https://github.com/eBay/tsv-utils/blob/master/docs/Performance.md

Comparing TSV and CSV formats
https://github.com/eBay/tsv-utils/blob/master/docs/comparing-tsv-and-csv.md
a
askagain


【 在 askagain (aa) 的大作中提到: 】
: Times should be in seconds, it’s in D :-)
: https://github.com/eBay/tsv-utils
: Performance Studies
: https://github.com/eBay/tsv-utils/blob/master/docs/Performance.md
: Comparing TSV and CSV formats
: https://github.com/eBay/tsv-utils/blob/master/docs/comparing-tsv-and-csv.
md

The xsv in the performance study comparison is in Rust

https://github.com/BurntSushi/xsv
n
netghost

這種事情每天在各個it公司都會發生。然後其實最後只有兩條路:
1.搞懂系統,搞懂系統的人,寫shell script也會飛快。當然如果真要速度,用C,用C++,也都能更快。
2.繼續被下面一堆叫你用這個tool那個tool,這個新language那個新language的人當作打廣告的對象,maybe能當前能讓你這個操作快一些,然後最後你會發現數據進了那個
生態環境之後會繼續有無數的坑,這個時候這些人就不見了,或者給你推薦另外的
fancy tool。

怎麼選擇,沒辦法告訴你。前面一條,可能要涉及到改變基本額技術觀念,非常痛苦。後面的開始簡單,但是就一直和一幫evangelist打交道,並且就一直在裏面呆着。自己看着辦吧。
【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

y
yhangw

楼主这个事儿其实很微妙,相信很多人没注意过。 c++标准输入流,fstream,行读入,
这一块确实速度很慢,比针对文本流优化过的perl之类差远了。 要快的话,得用更底
层的接口。

对搞系统的人,这个问题最佳解决方案一般是perl。python也只是慢两三倍而已。

纯程序员可能更关注一种语言。对整天和系统混的人,perl/python/c++/java甚至还有ruby其实多少都应该会一些,不同情况使用不同工具。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 這種事情每天在各個it公司都會發生。然後其實最後只有兩條路:
: 1.搞懂系統,搞懂系統的人,寫shell script也會飛快。當然如果真要速度,用C,
用C
: ++,也都能更快。
: 2.繼續被下面一堆叫你用這個tool那個tool,這個新language那個新language的人當作
: 打廣告的對象,maybe能當前能讓你這個操作快一些,然後最後你會發現數據進了那個
: 生態環境之後會繼續有無數的坑,這個時候這些人就不見了,或者給你推薦另外的
: fancy tool。
: 怎麼選擇,沒辦法告訴你。前面一條,可能要涉及到改變基本額技術觀念,非常痛苦。
: 後面的開始簡單,但是就一直和一幫evangelist打交道,並且就一直在裏面呆着。自己
: 看着辦吧。
: ...................

m
macaronsalt

谢谢你的思路。我一开始为了提高速度,就专门在reading部分没有任何parsing(atoi,atof,etc)的操作,是纯按行文本读取。读完后我才开始做parsing。

把文件放到SSD分区后,速度好一些,但是改进不是很大,所以感觉bottleneck应该还
是在程序本身。

【 在 walkrandom (walkrandom) 的大作中提到: 】
: 把数据先load到内存
: SSD到内存3000MB/s,10秒种。
: 单线程顺序parse,看有几个Atoi, Atof的操作
: 不会超过10分钟

m
macaronsalt

谢谢介绍语言。目前写的还是c++,之前写了不少library,近期不打算换了

10G 10min这个指标我记下了,谢谢

【 在 yhangw (老妖) 的大作中提到: 】
: std::getline干这活儿很烂。
: 你试试perl和python,对简单文本逐行处理的优化做的不错。perl这块儿基本数没啥对
: 手,python也应该比你的简单c++快很多。 10g文本,不管perl还是python,单核处理
: ,都应该在<10分钟以内完成。>: 了c

m
macaronsalt

后续:

我注意到持续reading会导致cpu和memory消耗大,风扇狂转,机器也开始卡顿。后来改了一下,每读取40MiB之后,做parsing。整体的运行会好很多
p
pptwo

前两天魏老师刚在版上说过iostream这套东西没法用...

【 在 yhangw (老妖) 的大作中提到: 】
: 楼主这个事儿其实很微妙,相信很多人没注意过。 c++标准输入流,fstream,行读入,
: 这一块确实速度很慢,比针对文本流优化过的perl之类差远了。 要快的话,得用更底
: 层的接口。
: 对搞系统的人,这个问题最佳解决方案一般是perl。python也只是慢两三倍而已。
: 纯程序员可能更关注一种语言。对整天和系统混的人,perl/python/c++/java甚至还有
: ruby其实多少都应该会一些,不同情况使用不同工具。
: 用C

b
bravesalmon

读写大文件用memory map。具体看boost手册
boost::iostreams::mapped_file()

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

n
netghost

我用shell也比他這個1小時快幾十倍啊。
【 在 yhangw (老妖) 的大作中提到: 】
: 楼主这个事儿其实很微妙,相信很多人没注意过。 c++标准输入流,fstream,行读入,
: 这一块确实速度很慢,比针对文本流优化过的perl之类差远了。 要快的话,得用更底
: 层的接口。
: 对搞系统的人,这个问题最佳解决方案一般是perl。python也只是慢两三倍而已。
: 纯程序员可能更关注一种语言。对整天和系统混的人,perl/python/c++/java甚至还有
: ruby其实多少都应该会一些,不同情况使用不同工具。
: 用C

d
digua

可以把parsing部分先注释掉,单做文件读取,看要花多少时间。

如果是文件读取的问题,可以用C文件库函数来做。据说C++流库的效率做的不好,虽然比较方便。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 谢谢你的思路。我一开始为了提高速度,就专门在reading部分没有任何parsing(
atoi,
: atof,etc)的操作,是纯按行文本读取。读完后我才开始做parsing。
: 把文件放到SSD分区后,速度好一些,但是改进不是很大,所以感觉bottleneck应该还
: 是在程序本身。

d
didadida

getline肯定不行

我记得刚编程的时候画图,一个点一个点的画
很慢

原先读、写Excel的时候,一个方格一个地读,贼慢,后来直接把整个worksheet读入一个数组,或者把数组整个写入worksheet

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

g
guvest

为什么没人考虑用c呢?os提供的找指针的lseek,fseek什么的,然后
C89标准库 fgets,fscanf什么的不行吗?读文件这点事情,代码不超过20行。
但是parse csv,要考虑全所有的corner cases,要50-100行左右。

【 在 pptwo(pp) 的大作中提到: 】

: 前两天魏老师刚在版上说过iostream这套东西没法用...

n
netghost

恩,寫 C++會在這事情上面用getline的,寫C也不會有太大幫助,可能略好一些,因
爲會逼着他不去用那些函數。

我覺得其實慢也不是問題,關鍵是爲啥他要去管這件事。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 为什么没人考虑用c呢?os提供的找指针的lseek什么的,然后
: C89标准库 fgets,fscanf什么的不行吗?读文件这点事情,代码不超过20行。
: 但是parse csv,要考虑全所有的corner cases,要50-100行左右。
: <br>: 前两天魏老师刚在版上说过iostream这套东西没法用...
: <br>

g
guvest

他读入的target也许是cpp数组,那自己写cpp可能是划算的。
第三方库,perl,python等肯定可以的。但是还要搞语言之间的接口。
还要考虑软件的deploy,未来的扩展维护。

如果是实战,我就安排俩人,一个搞定split文件,带测试一上午够了吧?
另一人找个csv模块,写好parse一批文件和放到数组这部分。也是一个上午。
时间是线性的。不确定性是最小的。即便在未来,应该也不会影响我的下一步骤。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 恩,寫 C++會在這事情上面用getline的,寫C也不會有太大幫助,可能略好一些,因
: 爲會逼着他不去用那些函數。
: 我覺得其實慢也不是問題,關鍵是爲啥他要去管這件事。

n
netghost

問題是,他現在是1個小時!

不管什麼target都不deserve這個速度,我用10年前只要100刀的機器,訋追昼妼懨
shell script也會他這個快N倍。

問題不在語言,在知道計算幹了啥。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 他读入的target也许是cpp数组,那自己写cpp可能是划算的。
: 第三方库,perl,python等肯定可以的。但是还要搞语言之间的接口。
: 还要考虑软件的deploy,未来的扩展维护。
: 如果是实战,我就安排俩人,一个搞定split文件,带测试一上午够了吧?
: 另一人找个csv模块,写好parse一批文件和放到数组这部分。也是一个上午。
: 时间是线性的。不确定性是最小的。即便在未来,应该也不会影响我的下一步骤。

g
guvest

可能哪里for loop弄错了吧?没测试?是不是楼主要再检查下。
用cpp自带stream也不至于吧?就5个2G文件。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 問題是,他現在是1個小時!
: 不管什麼target都不deserve這個速度,我用10年前只要100刀的機器,訋追昼妼懨
: shell script也會他這個快N倍。
: 問題不在語言,在知道計算幹了啥。

n
netghost

他没概念的话,写出这么慢也不奇怪。

【 在 guvest(我爱你老婆Anna) 的大作中提到: 】

: 可能哪里for loop弄错了吧?没测试?是不是楼主要再检查下。

: 用cpp自带stream也不至于吧?就5个2G文件。

b
bihai

微软C++?怀疑有bug。应该不超过你拷贝文件的时间。

磁盘操作应该最耗时,如果两个线程,一个读文件,一个parse,parse应该不增加总耗时。

当年用C#读文件就遇到这个bug,微软竟然一直读到文件结尾才发现是linux/unix文件
,又从头开始读入一行。就这样,每读一行,时间减少一点,最后越来越快。

C++ ifstream的速度不会低于C读文件的一半。

另一个可能是你vector没有提前设置大小。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

g
guvest

这个写个小程序测试一下应该就会立即发现了吧?
很难想象ms cpp会出这个问题还不修好。

gcc 在windows的文件尾应该没这个问题。我一直是gcc或者Borland cpp, 自己有个叫
loadMatrix的c函数。未发现过文件尾问题。

【 在 bihai(学得不好) 的大作中提到: 】
<br>: 微软C ?怀疑有bug。应该不超过你拷贝文件的时间。
<br>: 磁盘操作应该最耗时,如果两个线程,一个读文件,一个parse,parse应该不增
加总耗
<br>: 时。
<br>: 当年用C#读文件就遇到这个bug,微软竟然一直读到文件结尾才发现是
linux/
unix文件
<br>: ,又从头开始读入一行。就这样,每读一行,时间减少一点,最后越来越快。
<br>: C ifstream的速度不会低于C读文件的一半。
<br>: 另一个可能是你vector没有提前设置大小。
<br>: 了c
<br>

s
sanwadie

我给个答案吧。对小文件,getline也不算错,但大文件就有些不妥。

问题的关键是文件太大,每次读得太少,造成大量io读操作,每个io调用都有代价。解决办法就是使用大块内存做read buffer,然后从buffer里读行。自己处理行不是很难
吧,就是判断 n,r的事情,但需要处理一下不满行的边界情况。这些都可以用Cpp做,真没必要再捣腾其他工具。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)

g
guvest

你低估了parse csv的难度。不信找个日常用csv的data engineer问问看多少麻烦事。
再一个问题,你只说了从大块内存里面读行。没说清楚大文件怎么读到大块内存里。

【 在 sanwadie (三娃爹) 的大作中提到: 】
: 我给个答案吧。对小文件,getline也不算错,但大文件就有些不妥。
: 问题的关键是文件太大,每次读得太少,造成大量io读操作,每个io调用都有代价。解
: 决办法就是使用大块内存做read buffer,然后从buffer里读行。自己处理行不是很难
: 吧,就是判断 n,r的事情,但需要处理一下不满行的边界情况。这些都可以用Cpp做,
: 真没必要再捣腾其他工具。
: 了c

s
skyeer

这个是说system call的cost吗?一般文件读写kernel里面都是buffered io,基本不用raw io。如果getline慢的话,system call的cost是一部分。但主要可能是memcpy的问题:它每次从kernel管理的page cache拷到application memory location,但每次
copy一行,memcpy的throughput成为bottleneck。

每次读page size的N倍(batch read)就可以有很好的性能了。读到application
buffer,然后再parse。
https://stackoverflow.com/questions/9371238/why-is-reading-lines-from-stdin-much-slower-in-c-than-python给了一个很好的解释怎么简单fix楼主提出的问题。

【 在 sanwadie (三娃爹) 的大作中提到: 】
: 我给个答案吧。对小文件,getline也不算错,但大文件就有些不妥。
: 问题的关键是文件太大,每次读得太少,造成大量io读操作,每个io调用都有代价。解
: 决办法就是使用大块内存做read buffer,然后从buffer里读行。自己处理行不是很难
: 吧,就是判断 n,r的事情,但需要处理一下不满行的边界情况。这些都可以用Cpp做,
: 真没必要再捣腾其他工具。
: 了c

y
yhangw

我写过db2的导出文件parser。 这逼倒出时候字符串带换行的,回头自己好导不回去。 有一阵子我闲着没事儿重写了一个导出导入utility,那时候re还不熟练,最初版花了
两天,但陆陆续续各种测试通过最终稳定都一个多月以后了,

【 在 guvest(我爱你老婆Anna) 的大作中提到: 】

: 你低估了parse csv的难度。不信找个日常用csv的data engineer问问看多少麻
烦事。

: 再一个问题,你只说了从大块内存里面读行。没说清楚大文件怎么读到大块内存里。

y
yhangw

getline的话 不调buffer 还真得这样

命令行里没有足够强的csvparser. 除非特简单的情况。把re搞到awk里当shell又不如
直接perl了。 光各种引号嵌套就能让不熟悉的喝一壶。

【 在 netghost(Up to Isomorphism) 的大作中提到: 】

: 問題是,他現在是1個小時!

: 不管什麼target都不deserve這個速度,我用10年前只要100刀的機器,訋追昼妼懨

: shell script也會他這個快N倍。

: 問題不在語言,在知道計算幹了啥。

d
digua

这个问题是对的。C或C++有函数调用,可以扩展打开文件的file buffer,这样比自己
读到buffer里再处理更简单。

【 在 skyeer (看到你的眼) 的大作中提到: 】
: 这个是说system call的cost吗?一般文件读写kernel里面都是buffered io,基本不用
: raw io。如果getline慢的话,system call的cost是一部分。但主要可能是memcpy的问
: 题:它每次从kernel管理的page cache拷到application memory location,但每次
: copy一行,memcpy的throughput成为bottleneck。
: 每次读page size的N倍(batch read)就可以有很好的性能了。读到application
: buffer,然后再parse。
: https://stackoverflow.com/questions/9371238/why-is-reading-lines-from-
stdin-
: much-slower-in-c-than-python给了一个很好的解释怎么简单fix楼主提出的问题。

n
netghost

對的,getline肯定是不該用的。但是深層的問題不在這裏。

PS,他這個csv肯定不是那種麻煩額情況,否則他用C++不可能馬上能寫出來能用的。基本上就是逗號分割,不需要複雜parse的那種。我們就限制在這種簡單數據量較大的情
況來討論就行。
【 在 yhangw (老妖) 的大作中提到: 】
: getline的话 不调buffer 还真得这样
: 命令行里没有足够强的csvparser. 除非特简单的情况。把re搞到awk里当shell又不如
: 直接perl了。 光各种引号嵌套就能让不熟悉的喝一壶。
:
: 問題是,他現在是1個小時!
:
: 不管什麼target都不deserve這個速度,我用10年前只要100刀的機器,訋追昼妼懨
:
: shell script也會他這個快N倍。
:
: 問題不在語言,在知道計算幹了啥。
:

s
sanwadie

我们这里讨论有些盲目,楼主用vmstat,iostat查看一下系统系统cpu,iowate,读写
次数和读写字节等信息,判断一下先
n
netghost

從效率來說,這事情和文件大小關係不大。只不過文件大讓不正確的做法爆露了而已了。

【 在 sanwadie (三娃爹) 的大作中提到: 】
: 我给个答案吧。对小文件,getline也不算错,但大文件就有些不妥。
: 问题的关键是文件太大,每次读得太少,造成大量io读操作,每个io调用都有代价。解
: 决办法就是使用大块内存做read buffer,然后从buffer里读行。自己处理行不是很难
: 吧,就是判断 n,r的事情,但需要处理一下不满行的边界情况。这些都可以用Cpp做,
: 真没必要再捣腾其他工具。
: 了c

n
netghost

這個更是開始亂測了。。
【 在 sanwadie (三娃爹) 的大作中提到: 】
: 我们这里讨论有些盲目,楼主用vmstat,iostat查看一下系统系统cpu,iowate,读写
: 次数和读写字节等信息,判断一下先

p
pptwo

我估计他是csv某个field需要查找另一个然后算点啥,结果写成了线性查找。

getline再慢,跟上普通硬盘的速度都是没问题的,10G也就花1分钟。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 從效率來說,這事情和文件大小關係不大。只不過文件大讓不正確的做法爆露了而已了。

g
guvest

我也是这个感觉(尽管我20年没用过cpp了),getline不会这么慢。
也可能是for loop弄错了,有bug。
【 在 pptwo (pp) 的大作中提到: 】
: 我估计他是csv某个field需要查找另一个然后算点啥,结果写成了线性查找。
: getline再慢,跟上普通硬盘的速度都是没问题的,10G也就花1分钟。
: 了。

m
macaronsalt

是的,其他的code都在cpp里面

【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 他读入的target也许是cpp数组,那自己写cpp可能是划算的。
: 第三方库,perl,python等肯定可以的。但是还要搞语言之间的接口。
: 还要考虑软件的deploy,未来的扩展维护。
: 如果是实战,我就安排俩人,一个搞定split文件,带测试一上午够了吧?
: 另一人找个csv模块,写好parse一批文件和放到数组这部分。也是一个上午。
: 时间是线性的。不确定性是最小的。即便在未来,应该也不会影响我的下一步骤。

c
chebyshev

用老魏给的方案,把那两个库测一下,修一修。放弃单线程这个要求。单线程的库不好找。这可能是最适合你的solution。

lightroom说的protobuf很可能也是非常promising的办法,还能学一个有用的新技能。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 是的,其他的code都在cpp里面

m
macaronsalt

对时间进行了统计

纯读取文件速度 6MB/s,parsing速度比较慢,read:parse = 1:5,会进一步减低速度。

昨天写了多线程处理,一个线程读做producer,其他线程parse做consumer。速度就提
上来了,基本和parsing持平。

我是parse文件做backtesting,需要测试不同情况,有时还要iterate程序,每次跑程
序都parse一遍太慢了。之后准备用数据库解决,一次性import,以后一直用。不过后
续import就不是10G量级,可能TB量级,所以之后还是有read/parse的问题,还是需要
解决,绕不开。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 問題是,他現在是1個小時!
: 不管什麼target都不deserve這個速度,我用10年前只要100刀的機器,訋追昼妼懨
: shell script也會他這個快N倍。
: 問題不在語言,在知道計算幹了啥。

m
macaronsalt

看到了这个帖子,https://www.mitbbs.com/article_t/Programming/31573465.html

去试一下这两个库
n
netghost

6MB/s 這實在是太慢了。。。這東西如果是放硬盤上的,怎麼做到比60MB/s低的?
如果是SSD上的,怎麼做到比200MB/s低的?parse就不說了,scanf也沒這麼慢了。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 对时间进行了统计
: 纯读取文件速度 6MB/s,parsing速度比较慢,read:parse = 1:5,会进一步减低速
度。
: 昨天写了多线程处理,一个线程读做producer,其他线程parse做consumer。速度就提
: 上来了,基本和parsing持平。
: 我是parse文件做backtesting,需要测试不同情况,有时还要iterate程序,每次跑程
: 序都parse一遍太慢了。之后准备用数据库解决,一次性import,以后一直用。不过后
: 续import就不是10G量级,可能TB量级,所以之后还是有read/parse的问题,还是需要
: 解决,绕不开。

m
macaronsalt

数据之前放在HDD上,现在放在SSD上,我也没想到这么慢。用下面的代码,单纯地读,没有任何额外的存储和parse处理,单纯读文件,速度8.4MB/s。

std::ifstream file(filename.c_str());
// some error checking code
std::string line;
while (std::getline(file, line)) {}

之前加上所有其他处理并且用多线程做parse整体平均速度在6.xMB/s,所以目前主要瓶颈在IO了

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 6MB/s 這實在是太慢了。。。這東西如果是放硬盤上的,怎麼做到比60MB/s低的?
: 如果是SSD上的,怎麼做到比200MB/s低的?parse就不說了,scanf也沒這麼慢了。
: 度。

n
netghost

不能這麼寫的。。這樣還不如fgets。

還是那句話,如果我現在給你1G的文件,每行只有一個數,你知道怎麼快速搞定嗎?

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 数据之前放在HDD上,现在放在SSD上,我也没想到这么慢。用下面的代码,单纯地读,
: 没有任何额外的存储和parse处理,单纯读文件,速度8.4MB/s。
: std::ifstream file(filename.c_str());
: // some error checking code
: std::string line;
: while (std::getline(file, line)) {}
: 之前加上所有其他处理并且用多线程做parse整体平均速度在6.xMB/s,所以目前主要瓶
: 颈在IO了

d
digua

用C标准库的FILE函数集,扩大file buffer,应该能解决这个问题。除了文件I/O之外
,现有的C++ code是不用改动的。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 数据之前放在HDD上,现在放在SSD上,我也没想到这么慢。用下面的代码,单纯地读,
: 没有任何额外的存储和parse处理,单纯读文件,速度8.4MB/s。
: std::ifstream file(filename.c_str());
: // some error checking code
: std::string line;
: while (std::getline(file, line)) {}
: 之前加上所有其他处理并且用多线程做parse整体平均速度在6.xMB/s,所以目前主要瓶
: 颈在IO了
d
digua

如果数据中都是数字,标准的做法是把文本parse后转换成binary格式,下一次直接读
binary文件会快得多,getline的问题也没有了。File buffer还是要加大。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 对时间进行了统计
: 纯读取文件速度 6MB/s,parsing速度比较慢,read:parse = 1:5,会进一步减低速
度。
: 昨天写了多线程处理,一个线程读做producer,其他线程parse做consumer。速度就提
: 上来了,基本和parsing持平。
: 我是parse文件做backtesting,需要测试不同情况,有时还要iterate程序,每次跑程
: 序都parse一遍太慢了。之后准备用数据库解决,一次性import,以后一直用。不过后
: 续import就不是10G量级,可能TB量级,所以之后还是有read/parse的问题,还是需要
: 解决,绕不开。

m
macaronsalt

那就不能按行读了,需要一次性读一个大的block,之后自己做切分得到line。我的情
况应该也类似,虽然每一行有一些,但是每次只读一行的确效率太低。

具体接口形式我看到的是使用
std::setvbuf(file, 0, _IONBF, 0);
std::fread(buffer, 1, size, file)

话说回来,C++标准库的底层实现难道没有把buffer reading自动考虑吗,文件系统不
是底层自动会prefetch或者buffered reading吗?

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 不能這麼寫的。。這樣還不如fgets。
: 還是那句話,如果我現在給你1G的文件,每行只有一個數,你知道怎麼快速搞定嗎?

p
pptwo

C/C++读文件怎么可能没有buffer,setvbuf一般都是用来关掉buffer自己管理的。

不管getline还是fgets,8M/s都是太离谱了,你的parsing肯定不是字符串转换
这种简单工作,别折腾csv这块,去改进parsing的算法吧。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 那就不能按行读了,需要一次性读一个大的block,之后自己做切分得到line。我的情
: 况应该也类似,虽然每一行有一些,但是每次只读一行的确效率太低。
: 具体接口形式我看到的是使用
: std::setvbuf(file, 0, _IONBF, 0);
: std::fread(buffer, 1, size, file)
: 话说回来,C++标准库的底层实现难道没有把buffer reading自动考虑吗,文件系统不
: 是底层自动会prefetch或者buffered reading吗?

g
guvest

很可能parse的分支循环有bug。
【 在 pptwo (pp) 的大作中提到: 】
: C/C++读文件怎么可能没有buffer,setvbuf一般都是用来关掉buffer自己管理的。
: 不管getline还是fgets,8M/s都是太离谱了,你的parsing肯定不是字符串转换
: 这种简单工作,别折腾csv这块,去改进parsing的算法吧。

d
digua

默认的buffer size比较小,一般是几个KB,这是为什么要加大buffer size。有的文件系统有预取功能,但要看具体系统和设置。看你的测试结果,肯定是没有。

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 那就不能按行读了,需要一次性读一个大的block,之后自己做切分得到line。我的情
: 况应该也类似,虽然每一行有一些,但是每次只读一行的确效率太低。
: 具体接口形式我看到的是使用
: std::setvbuf(file, 0, _IONBF, 0);
: std::fread(buffer, 1, size, file)
: 话说回来,C++标准库的底层实现难道没有把buffer reading自动考虑吗,文件系统不
: 是底层自动会prefetch或者buffered reading吗?

m
macaronsalt

楼上我写了一贴,是只测getline的,没有任何其他逻辑,读存储在SSD上面的csv文件
,6~8MB/s

【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 很可能parse的分支循环有bug。

l
lightroom

把std::ios_base::sync_with_stdio 设成false, 再测一下

【在 macaronsalt(马卡龙加盐)的大作中提到:】
:楼上我写了一贴,是只测getline的,没有任何其他逻辑,读存储在SSD上面的csv文件
:,6~8MB/s

b
bihai

你用来测的文件是多大的?多用几个不同行数的给个比较,比如2G, 200M,是成比例的吗?

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 楼上我写了一贴,是只测getline的,没有任何其他逻辑,读存储在SSD上面的csv文件
: ,6~8MB/s

s
silverhawk

老大,机器多少core的?

如果只能用C++我也不知道什么好,如果可以用python,选择多了,前段时间用pytorch的multi process dataloader,binary读进来decode 之后line by line
,多个core机子上100G+ 几分钟不到

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 大概有10G的数据,一共5个大小均等的文件,csv格式,每行大概100~200 char,写
了c
: ++,竟然需要1个小时才跑完。
: -单机运行
: -没有多线程
: -std::getline (要读取更快是不是要用更底层的接口?)
: update:
: 0. background: 需要重复运行做数据测试。之前所有的配套库函数都使用了c++,所以
: 继续使用c++接口这样不需要移植程序到其它语言。
: 1. 之后准备用database解决。一次性全部import到database,这样只需要第一次
parse.
: 2. question:有几位版友推荐使用其他语言,从简单易用或者现成的库的角度我明白
: ...................

s
silverhawk

6M/s好慢,这个看起来是没有把IO的Tput用起来?是不是每行读了进来做CSV parse?
这个时候CPU其实高低没有太大用处,因为IO太低,CPU根本被打不满。可以直接一个大chunk读进来,比如32M,然后内存中parse?SSD至少到100M的速度才行吧,把IO Tput
提高起来,速度肯定快很多

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 对时间进行了统计
: 纯读取文件速度 6MB/s,parsing速度比较慢,read:parse = 1:5,会进一步减低速
度。
: 昨天写了多线程处理,一个线程读做producer,其他线程parse做consumer。速度就提
: 上来了,基本和parsing持平。
: 我是parse文件做backtesting,需要测试不同情况,有时还要iterate程序,每次跑程
: 序都parse一遍太慢了。之后准备用数据库解决,一次性import,以后一直用。不过后
: 续import就不是10G量级,可能TB量级,所以之后还是有read/parse的问题,还是需要
: 解决,绕不开。

s
skyeer

By default a file buffer size may be too small (some mentions that it is 512 bytes). So you may want to set it as a large value.

See https://stackoverflow.com/questions/5166263/how-to-get-iostream-to-
perform-better for other people's opinions for the exact question.

For performance related issues, first we need to understand what is the
bottleneck. If the parser is the bottleneck, then all such io performance
tuning doesn't help.

【 在 pptwo (pp) 的大作中提到: 】
: C/C++读文件怎么可能没有buffer,setvbuf一般都是用来关掉buffer自己管理的。
: 不管getline还是fgets,8M/s都是太离谱了,你的parsing肯定不是字符串转换
: 这种简单工作,别折腾csv这块,去改进parsing的算法吧。

m
macaronsalt

可能很多版友没有看之前的楼,目前parser不是bottleneck

我之后后续尝试几个不同形式的buffer read改进IO这一块
g
guvest

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

【 在 macaronsalt (马卡龙加盐) 的大作中提到: 】
: 可能很多版友没有看之前的楼,目前parser不是bottleneck
: 我之后后续尝试几个不同形式的buffer read改进IO这一块

g
genetics123

这个方案简单不折腾。
其实pandas读取10G的文件绰绰有余,我直接读取过50G的文件,处理起来也很快。如果line by line就更快了。
【 在 silverhawk (silverhawk) 的大作中提到: 】
: 老大,机器多少core的?
: 如果只能用C++我也不知道什么好,如果可以用python,选择多了,前段时间用
pytorch
: 的multi process dataloader,binary读进来decode 之后line by line
: ,多个core机子上100G+ 几分钟不到
: 了c
: parse.