熟练使用小工具让自己从机械工作中解脱
一直以来我都相信熟练地使用各种小工具能大大提升生产力,把人们从无聊的机械工作中解脱出来。昨天我就在做一些无聊的机械工作,正好这些技能就派上了用场。我觉得可以把它分享出来,或许就会有年轻人被安利了。
无聊的机械工作
我们写了一个数据库,昨天我要做的事情是在不同的实验设定下运行标准的 TPC-C 测试。我们用的 TPC-C 测试程序是 py-tpcc,每次运行它会产生如下格式的结果:
==================================================================================================
Execution Results after 60 seconds
--------------------------------------------------------------------------------------------------
Executed Time (µs) Rate
DELIVERY 722 30254463.91105652 23.86 txn/s
NEW_ORDER 8680 21493624.687194824 403.84 txn/s
ORDER_STATUS 765 401196.0029602051 1906.80 txn/s
PAYMENT 8276 5778122.663497925 1432.30 txn/s
STOCK_LEVEL 774 1124487.1616363525 688.31 txn/s
--------------------------------------------------------------------------------------------------
TOTAL 19217 59051894.426345825 325.43 txn/s
我首先要做的就是跑实验,然后把上面 txn/s
前面的这6个数字粘贴到 Google Docs 上。同一个实验需要跑多次,这样我们才能给出一个误差的范围。再加上有若干组不同的实验设定,其实数据点数还蛮多的。
一开始我的做法就是跑实验,然后两个窗口不停切换复制粘贴。做了两三次之后觉得实在是太憋屈了,感觉整天的时间都要耗在这上面。大好时光竟然来做这个?还不如睡觉呢!于是我决定稍微花点时间让它自动化进行。
自动运行实验
第一步就是要让实验能够自己运行,然后把结果保存好。花5分钟时间写好这么一个小脚本,保存成 run-all.sh
:
run_mongodb() {
clients=$1
cd ~/tmp/apavlo-pytpcc/pytpcc
./tpcc.py --config=mongodb.config --duration 60 --clients $clients --warehouses 1 mongodb >> result-w1-mongodb-denorm-$clients.txt
printf "use tpcc\n db.dropDatabase()\n" | mongo
sleep 10
}
run() {
run_mongodb "$@"
run_mongodb "$@"
run_mongodb "$@"
run_mongodb "$@"
run_mongodb "$@"
}
run 1
run 2
run 5
run 10
这里我定义了两个函数:run_mongodb
会运行一次实验,实验的参数实际上只有一个——客户端数量——通过 $1
传入。实验的结果每次会追加到一个文本文件里面,这个文本文件的文件名对于同样的实验参数是一样的,也就是说,同样的实验会把结果保存在同一个文件中。另一个函数 run
就很简单了,把同样参数的实验跑5遍。最后写上我们想要运行的实验参数,这里写了分别测试1个、2个、5个和10个客户的情况。
运行 bash run-all.sh
,看一下没出什么问题,就可以去玩耍了。
提取实验数据
做了顿饭回来实验就跑好了,并且每组实验都保存在各自的文件中。下一步的工作就是把实验数据粘贴到 Google Docs 上面。这里的基本思路是,把数据提取出来,转换成 CSV 格式。这样一来就可以用表格处理软件打开 CSV 文件,复制,然后粘贴到 Google Docs 上面了。
观察一下 pytpcc 的输出,我们会发现要把其中的数据转换成 CSV 格式还是很简单的:
- 从文件一行一行读入
- 按照空白字符分割成若干个部分
- 如果第一个部分不是
DELIVERY
等6个关键词,就跳过 - 如果是的话,把分割后的第4个部分取出来,这就是我们需要的数据
- 特殊处理一下
TOTAL
,在遇到这一行的时候我们在输出里面添加一个换行,表示一次实验结束了
很容易地就可以写出下面的 Python 代码:
import sys
with open(sys.argv[1]) as f:
for line in f:
split = line.strip().split()
if not split:
continue
if split[0] in ['DELIVERY', 'NEW_ORDER', 'ORDER_STATUS', 'PAYMENT', 'STOCK_LEVEL']:
sys.stdout.write(split[3])
sys.stdout.write(',')
elif split[0] == 'TOTAL':
sys.stdout.write(split[3])
sys.stdout.write('\n')
非常幸运的是,昨天我写下这个代码之后,直接运行就得到了我想要的结果:
$ python3 read_result.py ~/tmp/apavlo-pytpcc/pytpcc/result-w1-mongodb-denorm-10.txt
10.15,52.02,162.75,103.98,65.60,62.64
10.14,52.74,158.00,105.61,64.93,63.34
10.11,52.25,159.85,104.15,64.26,62.80
10.32,52.55,152.49,105.50,65.46,63.55
10.20,52.62,161.14,105.56,65.41,63.47
下面的事情就是对所有的结果都运行一遍这个小脚本,用 find
就行了:
$ find . -name 'result-*.txt' -print -exec python3 read_result.py {} \;
./result-mongodb-tx-5.txt
6.56,87.02,67.13,56.97,43.89,48.88
6.86,89.11,62.66,40.82,44.81,44.41
6.51,88.26,58.73,57.49,44.01,48.43
3.08,90.08,66.50,58.72,45.89,35.54
6.48,88.07,63.08,58.27,43.21,49.31
./result-mongodb-tx-2.txt
8.06,97.98,71.89,69.76,53.31,58.84
8.30,101.53,55.65,69.67,53.62,60.42
8.01,99.31,77.18,68.49,52.79,58.75
7.97,100.58,75.25,69.49,52.39,59.36
3.99,99.87,57.47,51.13,56.04,41.89
...
值得注意的是,find
的输出是无序的,对我们来说要粘贴到 Google Docs 上面汇总当然是希望有序的比较好整理。第一反应没有想到特别简练的命令来解决这个问题,不过简单拼凑一下也是很容易:for x in `find . -name 'result-*.txt' -print | sort -V`; do echo $x; python3 read_result.py $x; done
,也是一行就搞定。解释一下:find . -name 'result-*.txt' -print | sort -V
先执行 find
找出所有符合要求的文件,然后管道传给 sort
进行排序,其中 -V
可以让 result-10.txt
排到 result-2.txt
后面,这样我们就得到了有序的文件名列表了。外面是一个循环,依次遍历文件名列表,把当前文件名放在 $x
中,在循环体中先打印出文件名再执行 python 脚本。
把上面命令的输出保存成文件,并把后缀命名为 .csv
,就可以愉快地用表格软件打开啦。
处理实验数据
把上面数据贴到 Google Docs 之后,我把其中 TOTAL
的那一列数据复制下来。现在我希望先把数据5个5个地分组(也就是同一个设定的多次试验)。说实在的,我虽然现在用习惯了 Sublime Text,但是我还真不知道怎么在里面实现这个效果。不过没关系,我可以回到老朋友 Vim,用宏可以轻松地解决:
qa
开始宏录制,保存成宏a
$
跳到行末Ctrl + v
开始块选择jjj
选择4行Shift + a
在每行的行尾插入,
我想插入逗号ESC
回到普通模式V
开始行选择jjjj
向下选择5行J
合并这5行j
走到下一行(也就是下一组数据的起点)
q
退出宏录制34@a
重复执行34次宏a
这样数据按照每行5个分好了。后面我就回到 Sublime Text 里面,把它处理成 Python 能处理的格式。同样,块选择依然是一个非常好用的功能。
最后,简单地调用一下 matplotlib
就可以把图画出来了。
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
import collections
DATA = collections.OrderedDict([
...
])
plt.style.use('ggplot')
plt.xlabel('The Number of Concurrent Clients')
plt.ylabel('Transactions per Second')
for i, (title, data) in enumerate(DATA.items()):
xs = np.array([t[0] for t in data])
ys = np.array([np.mean(t[1]) for t in data])
es = np.array([np.std(t[1]) for t in data])
bar = plt.errorbar(xs, ys, yerr=es, label=title)
plt.legend()
plt.tight_layout()
plt.savefig('tpcc.pdf')
总结
- 善用各种工具可以提升工作效率,甚至熟悉快捷键也很重要(当然,要到熟练掌握还是需要不断的练习的)
- 不需要要求自己精通每一个工具,只需要懂得常用的几个用法就好
- 把不同的工具组合在一起可以发挥巨大的功效