go 新手:用 bufio的scan按行读取 约2.5G的fasta文件,发现读取得很慢,慢得不敢想象

0106WeiWeiDeng · 2019-01-14 14:34:46 · 3250 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2019-01-14 14:34:46 的主题,其中的信息可能已经有所发展或是发生改变。

我是一个GO新手,以前是用C/C++的(非科班)。前段时间,听说GO语言写代码效率与Python一样快,而且执行效率与C差不多,而且goroutine 比thread 更加轻量,更加灵活,并发性能更好,就花了一个星期,粗略地看了一遍 The GO Programming Language 这本书,然后就用GO重写以前的C/C++代码作为练手,就发现bufio的scan按行读取很慢,不知道是什么原因,还有就是我打算先将文件读到内存中,然后用goroutines做一些并行(解开for循环),发现文件都没有读完,goroutines就产生了。代码如下(不会用markdown,所以就截图了,红色方框处就是读取文件的代码):

无标题.jpg


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3250 次点击  ∙  1 赞  
加入收藏 微博
20 回复  |  直到 2021-10-13 14:43:28
0106WeiWeiDeng
0106WeiWeiDeng · #1 · 6年之前

我不知道是bufio的scan慢,还是字符串string的+=操作慢,或者是其他原因

0106WeiWeiDeng
0106WeiWeiDeng · #2 · 6年之前

请大神给一些优化的建议

dosecin
dosecin · #3 · 6年之前
0106WeiWeiDeng0106WeiWeiDeng #1 回复

我不知道是bufio的scan慢,还是字符串string的+=操作慢,或者是其他原因

string是不可改变的,+=操作是重新分配内存,改成[]byte,或者stringbuilder试试

aixinaxc
aixinaxc · #4 · 6年之前

你的文件那么大,就不适合+了,

qs-he
qs-he · #5 · 6年之前

不是慢,是该api的特性,毕竟按行来读得嘛。这么大的文件肯定不能全部一次性读入内存,你可以考虑对这个文件字节数组进行预先分段读取,然后对段进行携程,并发来做,然后再汇总。

0106WeiWeiDeng
0106WeiWeiDeng · #6 · 6年之前
dosecindosecin #3 回复

#1楼 @0106WeiWeiDeng string是不可改变的,+=操作是重新分配内存,改成[]byte,或者stringbuilder试试

谢谢,我换成 []byte 试试

0106WeiWeiDeng
0106WeiWeiDeng · #7 · 6年之前
qs-heqs-he #5 回复

不是慢,是该api的特性,毕竟按行来读得嘛。这么大的文件肯定不能全部一次性读入内存,你可以考虑对这个文件字节数组进行预先分段读取,然后对段进行携程,并发来做,然后再汇总。

我不太懂你说的啊,你说的是用ReadAll 一次性将文件读到 []byte 中,然后再进行进一步的处理?还是直接用携程并发读取文件,可以这样干吗?

0106WeiWeiDeng
0106WeiWeiDeng · #8 · 6年之前

我要操作的文件一般都很大啊,一般都是以G为单位的,小到几G,大到上百G...

moryaden
moryaden · #9 · 6年之前

大文件全部读取到内存,一般是不合理的操作。

看逻辑,基本上读取文件,找到某种匹配,就记录到某处。并没有需要针对已经找到的数据做互相关联计算。

所以,建议的做法,不需要把所有找到的结果都缓存在某个数组,而是,分段式操作,完成一个段落,直接输入(放文件,打屏都可以)。

如果后续还是发现慢,可以用一个goroutine读文件块,分成多个互相不干扰的块,丢给一个worker池去处理,会更加快。

qs-he
qs-he · #10 · 6年之前

@0106WeiWeiDeng 不是哦,获取文件的尺寸,然后像切蛋糕一样,多分几块,然后并发携程去读。

qs-he
qs-he · #11 · 6年之前

@0106WeiWeiDeng 不要一次readall,一般情况下没有那么多的连续内存给你。读文件是可以选择偏移地址开始读取的,所以要像切蛋糕那样把文件字节分成几块,然后分别处理。

0106WeiWeiDeng
0106WeiWeiDeng · #12 · 6年之前
0106WeiWeiDeng0106WeiWeiDeng #6 回复

#3楼 @dosecin 谢谢,我换成 []byte 试试

最终,我用了strings.builder, 大概三秒就读完了,谢谢你的建议

0106WeiWeiDeng
0106WeiWeiDeng · #13 · 6年之前

12楼 @0106WeiWeiDeng 但是内存占用是5G,是文件的两倍,应该是strings.builder中 buf 的容量优化问题。

0106WeiWeiDeng
0106WeiWeiDeng · #14 · 6年之前

谢谢大家的建议,最终我决定还是用strings.builder来连拼接字符串,strings.builder 是go 1.10添加的新特性,拼接字符串速度很快。可能内存占用是个问题,但是很难预知字符串最终的长度是多少,不好预先设置 strings.builder 中buf 的容量。而且,我觉得对我来说内存占用不是问题,速度才是最重要的。

0106WeiWeiDeng
0106WeiWeiDeng · #15 · 6年之前
qs-heqs-he #11 回复

@0106WeiWeiDeng 不要一次readall,一般情况下没有那么多的连续内存给你。读文件是可以选择偏移地址开始读取的,所以要像切蛋糕那样把文件字节分成几块,然后分别处理。

我想我知道你的意思了,你说先遍历文件,得到那些需要读的字节开始位置的相对偏移量,然后再将这些字节读入内存,但是我想这种方法,需要非常小心才行。

aixinaxc
aixinaxc · #16 · 6年之前

@0106WeiWeiDeng 那你拼接就用stringbuilder,或者stringbuffer呀。+号适合少量拼接

qs-he
qs-he · #17 · 6年之前

@0106WeiWeiDeng 读取文件属性,获取文件字节大小

lysShub
lysShub · #18 · 4年之前
yanhs
yanhs · #19 · 3年之前

用biogo里面的fasta 和 seqio, 速度会快很多, 但是仍然赶不上python中的pyfaidx, 没人去开发go的库啊,我准备自己写个类似于pyfaidx的go库。

yanhs
yanhs · #20 · 3年之前

faidx利用内存指针二进制读取, 速度应该是最快的。 楼主使用os.Open(), 然后使用户os.seek将文件指针移动到指定位置, 这个位置根据fai文件来获取, 这样做应该能实现和faidx一样的速度, 而且内存占用很小, 果然指针才是王道。。。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传