何不 Ack?Grep, Ack, Ag的搜索效率对比-阿里云开发者社区

开发者社区> 玄学酱> 正文

何不 Ack?Grep, Ack, Ag的搜索效率对比

简介: 何不 Ack?Grep, Ack, Ag的搜索效率对比 前言 我(@董伟明9 )经常看到很多程序员, 运维在代码搜索上使用ack, 甚至ag(the_silver_searcher ), 而我工作中95%都是用grep,剩下的是ag。
+关注继续查看

何不 Ack?Grep, Ack, Ag的搜索效率对比

前言

我(@董伟明9 )经常看到很多程序员, 运维在代码搜索上使用ack, 甚至ag(the_silver_searcher ), 而我工作中95%都是用grep,剩下的是ag。 我觉得很有必要聊一聊这个话题。

我以前也是一个运维, 我当时也希望找到最好的最快的工具用在工作的方方面面。 但是我很好奇为什么ag和ack没有作为linux发行版的内置部分。 内置的一直是grep。 我当初的理解是受各种开源协议的限制, 或者发行版的boss个人喜好。 后来我就做了实验, 研究了下他们到底谁快。 当时的做法也无非跑几个真实地线上log看看用时。 然后我也有了我的一个认识: 大部分时候用grep也无妨, 日志很大的时候用ag。

ack原来的域名是betterthangrep.com, 现在是beyondgrep.com。 好吧,其实我理解使用ack的同学, 也理解ack产生的原因。 这里就有个故事。

最开始我做运维使用shell, 经常做一些分析日志的工作。 那时候经常写比较复杂的shell代码实现一些特定的需求。 后来来了一位会perl的同学。 原来我写shell做一个事情, 写了20多行shell代码, 跑一次大概5分钟, 这位同学来了用perl改写, 4行, 一分钟就能跑完。 亮瞎我们的眼, 从那时候开始, 我就觉得需要学perl,以至于后来的python。

perl是天生用来文本解析的语言, ack的效率确实很高。 我想着可能是大家认为ack要更快更合适的理由吧。 其实这件事要看场景。 我为什么还用比较’土’的grep呢?

看一下这篇文章, 希望给大家点启示。不耐烦看具体测试过程的同学,可以直接看结论:

  1. 在搜索的总数据量较小的情况下, 使用grep, ack甚至ag在感官上区别不大
  2. 搜索的总数据量较大时, grep效率下滑的很多, 完全不要选
  3. ack在某些场景下没有grep效果高(比如使用-v搜索中文的时候)
  4. 在不使用ag没有实现的选项功能的前提下, ag完全可以替代ack/grep

实验条件

PS: 严重声明, 本实验经个人实践, 我尽量做到合理。 大家看完觉得有异议可以试着其他的角度来做。 并和我讨论。

  • 我使用了公司的一台开发机(gentoo)

  • 我测试了纯英文和汉语2种, 汉语使用了结巴分词的字典, 英语使用了miscfiles中提供的词典


  1. # 假如你是ubuntu: sudo apt-get install miscfiles
  2. wget https://raw.githubusercontent.com/fxsjy/jieba/master/extra_dict/dict.txt.big

实验前的准备

我会分成英语和汉语2种文件, 文件大小为1MB, 10MB, 100MB, 500MB, 1GB, 5GB。 没有更多是我觉得在实际业务里面不会单个日志文件过大的。 也就没有必要测试了(就算有, 可以看下面结果的趋势)。用下列程序深入测试的文件:


  1. cat make_words.py
  2. # coding=utf-8
  3. import os
  4. import random
  5. from cStringIO import StringIO
  6. EN_WORD_FILE = '/usr/share/dict/words'
  7. CN_WORD_FILE = 'dict.txt.big'
  8. with open(EN_WORD_FILE) as f:
  9. EN_DATA = f.readlines()
  10. with open(CN_WORD_FILE) as f:
  11. CN_DATA = f.readlines()
  12. MB = pow(1024, 2)
  13. SIZE_LIST = [1, 10, 100, 500, 1024, 1024 * 5]
  14. EN_RESULT_FORMAT = 'text_{0}_en_MB.txt'
  15. CN_RESULT_FORMAT = 'text_{0}_cn_MB.txt'
  16. def write_data(f, size, data, cn=False):
  17. total_size = 0
  18. while 1:
  19. s = StringIO()
  20. for x in range(10000):
  21. cho = random.choice(data)
  22. cho = cho.split()[0] if cn else cho.strip()
  23. s.write(cho)
  24. s.seek(0, os.SEEK_END)
  25. total_size += s.tell()
  26. contents = s.getvalue()
  27. f.write(contents + '\n')
  28. if total_size > size:
  29. break
  30. f.close()
  31. for index, size in enumerate([
  32. MB,
  33. MB * 10,
  34. MB * 100,
  35. MB * 500,
  36. MB * 1024,
  37. MB * 1024 * 5]):
  38. size_name = SIZE_LIST[index]
  39. en_f = open(EN_RESULT_FORMAT.format(size_name), 'a+')
  40. cn_f = open(CN_RESULT_FORMAT.format(size_name), 'a+')
  41. write_data(en_f, size, EN_DATA)
  42. write_data(cn_f, size, CN_DATA, True)

好吧, 效率比较低是吧? 我自己没有vps, 公司服务器我不能没事把全部内核的cpu都占满(不是运维好几年了)。 假如你不介意htop的多核cpu飘红, 可以这样,耗时就是各文件生成的时间短板。这是生成测试文件的多进程版本:


  1. # coding=utf-8
  2. import os
  3. import random
  4. import multiprocessing
  5. from cStringIO import StringIO
  6. EN_WORD_FILE = '/usr/share/dict/words'
  7. CN_WORD_FILE = 'dict.txt.big'
  8. with open(EN_WORD_FILE) as f:
  9. EN_DATA = f.readlines()
  10. with open(CN_WORD_FILE) as f:
  11. CN_DATA = f.readlines()
  12. MB = pow(1024, 2)
  13. SIZE_LIST = [1, 10, 100, 500, 1024, 1024 * 5]
  14. EN_RESULT_FORMAT = 'text_{0}_en_MB.txt'
  15. CN_RESULT_FORMAT = 'text_{0}_cn_MB.txt'
  16. inputs = []
  17. def map_func(args):
  18. def write_data(f, size, data, cn=False):
  19. f = open(f, 'a+')
  20. total_size = 0
  21. while 1:
  22. s = StringIO()
  23. for x in range(10000):
  24. cho = random.choice(data)
  25. cho = cho.split()[0] if cn else cho.strip()
  26. s.write(cho)
  27. s.seek(0, os.SEEK_END)
  28. total_size += s.tell()
  29. contents = s.getvalue()
  30. f.write(contents + '\n')
  31. if total_size > size:
  32. break
  33. f.close()
  34. _f, size, data, cn = args
  35. write_data(_f, size, data, cn)
  36. for index, size in enumerate([
  37. MB,
  38. MB * 10,
  39. MB * 100,
  40. MB * 500,
  41. MB * 1024,
  42. MB * 1024 * 5]):
  43. size_name = SIZE_LIST[index]
  44. inputs.append((EN_RESULT_FORMAT.format(size_name), size, EN_DATA, False))
  45. inputs.append((CN_RESULT_FORMAT.format(size_name), size, CN_DATA, True))
  46. pool = multiprocessing.Pool()
  47. pool.map(map_func, inputs, chunksize=1)

等待一段时间后,测试的文件生成了。目录下是这样的:


  1. $ls -lh
  2. total 14G
  3. -rw-rw-r-- 1 vagrant vagrant 2.2K Mar 14 05:25 benchmarks.ipynb
  4. -rw-rw-r-- 1 vagrant vagrant 8.2M Mar 12 15:43 dict.txt.big
  5. -rw-rw-r-- 1 vagrant vagrant 1.2K Mar 12 15:46 make_words.py
  6. -rw-rw-r-- 1 vagrant vagrant 101M Mar 12 15:47 text_100_cn_MB.txt
  7. -rw-rw-r-- 1 vagrant vagrant 101M Mar 12 15:47 text_100_en_MB.txt
  8. -rw-rw-r-- 1 vagrant vagrant 1.1G Mar 12 15:54 text_1024_cn_MB.txt
  9. -rw-rw-r-- 1 vagrant vagrant 1.1G Mar 12 15:51 text_1024_en_MB.txt
  10. -rw-rw-r-- 1 vagrant vagrant 11M Mar 12 15:47 text_10_cn_MB.txt
  11. -rw-rw-r-- 1 vagrant vagrant 11M Mar 12 15:47 text_10_en_MB.txt
  12. -rw-rw-r-- 1 vagrant vagrant 1.1M Mar 12 15:47 text_1_cn_MB.txt
  13. -rw-rw-r-- 1 vagrant vagrant 1.1M Mar 12 15:47 text_1_en_MB.txt
  14. -rw-rw-r-- 1 vagrant vagrant 501M Mar 12 15:49 text_500_cn_MB.txt
  15. -rw-rw-r-- 1 vagrant vagrant 501M Mar 12 15:48 text_500_en_MB.txt
  16. -rw-rw-r-- 1 vagrant vagrant 5.1G Mar 12 16:16 text_5120_cn_MB.txt
  17. -rw-rw-r-- 1 vagrant vagrant 5.1G Mar 12 16:04 text_5120_en_MB.txt

确认版本


  1. $ ack --version # ackubuntu下叫`ack-grep`
  2. ack 2.12
  3. Running under Perl 5.16.3 at /usr/bin/perl
  4. Copyright 2005-2013 Andy Lester.
  5. This program is free software. You may modify or distribute it
  6. under the terms of the Artistic License v2.0.
  7. $ ag --version
  8. ag version 0.21.0
  9. $ grep --version
  10. grep (GNU grep) 2.14
  11. Copyright (C) 2012 Free Software Foundation, Inc.
  12. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
  13. This is free software: you are free to change and redistribute it.
  14. There is NO WARRANTY, to the extent permitted by law.
  15. Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

实验设计

为了不产生并行执行的相互响应, 我还是选择了效率很差的同步执行, 我使用了ipython提供的%timeit。 测试程序的代码如下:


  1. import re
  2. import glob
  3. import subprocess
  4. import cPickle as pickle
  5. from collections import defaultdict
  6. IMAP = {
  7. 'cn': ('豆瓣', '小明明'),
  8. 'en': ('four', 'python')
  9. }
  10. OPTIONS = ('', '-i', '-v')
  11. FILES = glob.glob('text_*_MB.txt')
  12. EN_RES = defaultdict(dict)
  13. CN_RES = defaultdict(dict)
  14. RES = {
  15. 'en': EN_RES,
  16. 'cn': CN_RES
  17. }
  18. REGEX = re.compile(r'text_(\d+)_(\w+)_MB.txt')
  19. CALL_STR = '{command} {option} {word} {filename} > /dev/null 2>&1'
  20. for filename in FILES:
  21. size, xn = REGEX.search(filename).groups()
  22. for word in IMAP[xn]:
  23. _r = defaultdict(dict)
  24. for command in ['grep', 'ack', 'ag']:
  25. for option in OPTIONS:
  26. rs = %timeit -o -n10 subprocess.call(CALL_STR.format(command=command, option=option, word=word, filename=filename), shell=True)
  27. best = rs.best
  28. _r[command][option] = best
  29. RES[xn][word][size] = _r
  30. # 存起来
  31. data = pickle.dumps(RES)
  32. with open('result.db', 'w') as f:
  33. f.write(data)

温馨提示, 这是一个灰常耗时的测试。 开始执行后 要喝很久的茶…

我来秦皇岛办事完毕(耗时超过1一天), 继续我们的实验。

我想要的效果

我想工作的时候一般都是用到不带参数/带-i(忽略大小写)/-v(查找不匹配项)这三种。 所以这里测试了:

  1. 英文搜索/中文搜索
  2. 选择了2个搜索词(效率太低, 否则可能选择多个)
  3. 分别测试’’/’-i’/’-v’三种参数的执行
  4. 使用%timeit, 每种条件执行10遍, 选择效率最好的一次的结果
  5. 每个图代码一个搜索词, 3搜索命令, 一个选项在搜索不同大小文件时的效率对比

我先说结论

  1. 在搜索的总数据量较小的情况下, 使用grep, ack甚至ag在感官上区别不大
  2. 搜索的总数据量较大时, grep效率下滑的很多, 完全不要选
  3. ack在某些场景下没有grep效果高(比如使用-v搜索中文的时候)
  4. 在不使用ag没有实现的选项功能的前提下, ag完全可以替代ack/grep

渲染图片的gist可以看这里benchmarks.ipynb。 它的数据来自上面跑的结果在序列化之后存入的文件。

chartchart-1chart-2chart-3chart-4chart-5chart-6  

chart

chart-7chart-8chart-9chart-10chart-11

chart-7


----------------------------------------------------------------------------------------------------------------------------

原文发布时间:2015-03-18
本文来自云栖合作伙伴“linux中国”

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
9570 0
PostgreSQL 11 相似图像搜索插件 imgsmlr 性能测试与优化 2 - 单机分区表 (dblink 异步调用并行) (4亿图像)
标签 PostgreSQL , imgsmlr , GiST , 图像特征值 , 小波转换 背景 续 《PostgreSQL 11 相似图像搜索插件 imgsmlr 性能测试 1 - 单机单表 (4亿图像)》 使用分区表+dblink异步接口并行调用。
862 0
PostgreSQL 11 相似图像搜索插件 imgsmlr 性能测试与优化 1 - 单机单表 (4亿图像)
标签 PostgreSQL , imgsmlr , GiST , 图像特征值 , 小波转换 背景 imgsmlr是PostgreSQL的一款支持以图搜图的插件, https://github.com/postgrespro/imgsmlr 这个插件新增了: 1、几种图像特征值数据类型, 2、图像特征值相似算子, 3、图像特征值相似排序索引支持, 4、图像相似排序的索引(通过扩展GiST索引接口实现)支持, 5、png,gif等图像格式特征值提取函数。
1373 0
看动画学算法之:平衡二叉搜索树AVL Tree
平衡二叉搜索树是一种特殊的二叉搜索树。为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜索树所有的节点都是右节点,那么这个二叉搜索树将会退化成为链表。从而导致搜索的时间复杂度变为O(n),其中n是二叉搜索树的节点个数。 而平衡二叉搜索树正是为了解决这个问题而产生的,它通过限制树的高度,从而将时间复杂度降低为O(logn)。
14 0
用PostgreSQL 做实时高效 搜索引擎 - 全文检索、模糊查询、正则查询、相似查询、ADHOC查询
用PostgreSQL 做实时高效 搜索引擎 - 全文检索、模糊查询、正则查询、相似查询、ADHOC查询作者digoal 日期2017-12-05 标签PostgreSQL , 搜索引擎 , GIN , ranking , high light , 全文检索 , 模糊查询 , 正则查询 , 相似查询 , ADHOC查询 背景字符串搜索是非常常见的业务需求,它包括: 1、前缀+模糊查询。
5455 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13267 0
PostgreSQL 相似搜索插件介绍大汇总 (cube,rum,pg_trgm,smlar,imgsmlr,pg_similarity) (rum,gin,gist)
标签 PostgreSQL , cube , rum , pg_trgm , smlar , imgsmlr , pg_similarity , gin , gist , 倒排 , 相似 , 向量 , 特征 , 图像 , 文本 , 字符串 , 全文检索 背景 在搜索业务场景中,相似搜索是一个非常常见的需求。
2457 0
PostgreSQL 11 相似图像搜索插件 imgsmlr 性能测试与优化 3 - citus 8机128shard (4亿图像)
标签 PostgreSQL , imgsmlr , GiST , 图像特征值 , 小波转换 , citus 背景 续 《PostgreSQL 11 相似图像搜索插件 imgsmlr 性能测试 1 - 单机单表 (4亿图像)》 《PostgreSQL 11 相似图像搜索插件 imgsmlr 性能...
1049 0
+关注
玄学酱
这个时候,玄酱是不是应该说点什么...
20710
文章
438
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载