一句话加速grep近30倍
最近做一个项目,需要从表达矩阵中提取单个特定基因的表达值。最开始时文件比较小,使用awk
单个读取处理也很快,但后来数据多了,从一个1.2 G
的文件中提取单个基因的表达需要30 s
,用grep
来写需要25 S
,这在平时写程序是可以接受的,但在网站上是接受不了的。所以就想着如何优化一下。
探索下来优化也很简单,把grep
换为LC_ALL=C grep
再加其它参数速度就快了近30倍,把时间控制在1 s
左右。
下面是整个探索过程 (写这篇总结文章是在早晨,服务器不繁忙,所以下面的示例中只能看出来快了5
倍左右。这也表明不加LC_All=C
时grep
受服务器负载影响较大,加了之后则几乎不受影响。)
获取单基因表达量
查看下文件大小
ls -sh 334d41a7-e34a-4bab-841c-eb07bd84513f.txt
# 1.2G 334d41a7-e34a-4bab-841c-eb07bd84513f.txt
查看下文件内容
head 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | cut -f 1,2
# Rnu2-1 -0.52
# Tmsb4Xp6 11.81
# S100A14 1.99
# Krt17 1.26
# Aldh1A1 6.92
# Fxyd3 0.56
# Rnu2-2P 0.35
# Rarres1 6.03
# Rnvu1-7 9.53
# Lcn2 3.44
假设基因名字大小写一致时使用awk
提取其表达信息,用时14 s
。
time awk '{if($1=="Tmsb4Xp6") print $2;}' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >1
real 0m14.569s
user 0m12.943s
sys 0m0.626s
实际上大小写可能不一致而需要转换,耗时17 s
。
time awk '{if(tolower($1)=="tmsb4xp6") print $2;}' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >2
real 0m17.638s
user 0m17.031s
sys 0m0.595s
采用grep
命令提取 (-i
忽略大小写),用时5 s
。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -i 'Tmsb4Xp6' >4
real 0m5.454s
user 0m5.134s
sys 0m1.272s
上面的grep
是全句匹配,想着加上^
匹配行首是否会减少匹配量,速度能快一些,效果不明显,用时4 s
。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -iP '^Tmsb4Xp6' >5
real 0m4.262s
user 0m3.984s
sys 0m1.233s
grep
是处理匹配关系,获得的是包含关键词但不一定全等于关键词,加一个-w
参数,匹配更精确些,耗时6.7 s
。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -iPw '^Tmsb4Xp6' >6
real 0m6.723s
user 0m6.390s
sys 0m1.348s
从上面来看,采用正则限定并不能提速,还是采用固定字符串方式提取,速度也差不多,耗时5 s
。(fgrep
等同于grep -F
)
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | fgrep -i 'Tmsb4Xp6' >7
real 0m5.496s
user 0m5.128s
sys 0m1.366s
主角出场,加上LC_ALL=C
后,速度明显提升了,只需要1 s
时间。
time LC_ALL=C fgrep -i '^Tmsb4Xp6' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >8
real 0m1.027s
user 0m0.671s
sys 0m0.355s
多次测试下来,发现添加LC_ALL=C
后grep
命令快了很多,而且多次测试速度都很稳定 (不论服务器是繁忙还是空闲)。这里面的原理是涉及字符搜索空间的问题,我们操作的文件只包含字母、字符、数字,没有中文或其它复杂符号时都是适用的,具体原理和更多评估可查看文末的两篇参考链接,了解更多信息。
为了简化应用,我们可以alias grep='LC_ALL=C grep'
(把这句话放到~/.bashrc
或~/.bahs_profile
里面(具体用法见:PATH和path,傻傻分不清)),后续再使用grep
时就可以直接得到速度提升了。
time grep -F -i '^Tmsb4Xp6' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt
real 0m1.013s
user 0m0.679s
sys 0m0.334s
那如果获取多个基因怎么操作呢?
一个方式是使用正则表达式,多个基因一起传递过去,分别匹配,耗时4.6 s
。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C grep -iP 'Tmsb4Xp6|Sox1|Sox2|Sox3'
real 0m4.654s
user 0m4.366s
sys 0m1.227s
或者还是使用固定字符串查找模式,把所有基因每行一个写入文件a
,然后再去匹配,耗时2.5 s
,且测试发现在基因数目少于10
时(这是通常的应用场景),基因多少影响不大 (这也说明能用固定字符串查找时最好显示指定)。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C fgrep -i -f a >11
real 0m2.539s
user 0m2.191s
sys 0m1.249s
这里还比较了另外2个号称比grep
快的命令ag
和rg
在这个应用场景没体现出性能优势。
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C ag -i '^Tmsb4Xp6|Sox1|Sox2|Sox3' >10
real 0m11.281s
user 0m9.713s
sys 0m5.326s
time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | rg -iF -f a >12
real 0m4.337s
user 0m3.444s
sys 0m2.787s
- https://www.inmotionhosting.com/support/website/speed-up-grep-searches-with-lc-all/
- https://stackoverflow.com/questions/42239179/fastest-way-to-find-lines-of-a-file-from-another-larger-file-in-bash#
- 使用CoreOs,Docker和Nirmata部署微服务类型的应用
- .NET 4 上的REST 框架
- 结合游戏开发与人工智能研究,游戏大厂 Ubisoft 成立AI研发部门
- Quartz.NET的管理工具
- Python-执行系统命令
- css3 UI 修饰——回顾
- Windows Phone 7实战 第一天 设计启动页面和应用程序图标
- css3响应式布局设计——回顾
- Windows Phone 7 实战第二天 二维码QRcode
- Python-面向对象编程
- SQL Server 2012将与Hadoop无缝集成
- 系统性能优化一例
- Android中include标签的使用
- css3弹性盒子模型——回顾。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 1.5w字,30图带你彻底掌握 AQS!
- 原创 | codeforces 1419D2,有趣的思维题
- 如何实现四元数的运算
- 最牛一篇布隆过滤器详解
- 编写一个IDEA插件之:开发环境准备那些坑
- 编写一个IDEA插件之:使用PSI分析Java代码
- 编写一个IDEA插件之:自动生成Java代码
- 编写一个IDEA插件之:事件监听
- 重新加载故障节点上的 Ceph 卷
- 一个Spring Bean从诞生到逝去的九次人生转折!
- 原创 | 详解git rebase,让你走上git大神之路
- 启用chrome浏览器内置的二维码生成插件
- ZeroLogon漏洞(CVE-2020-1472)防御性指南
- 原创 | 随机数大家都会用,但是你知道生成随机数的算法吗?
- 原创 | codeforces 1425E,一万种情况的简单题