所以我写了一个 Python 程序来处理一个小数据处理任务。
这是我想要的计算的虚构语言的非常简短的规范:
parse "%s %lf %s" aa bb cc | group_by aa | quickselect --key=bb 0:5 | \
flatten | format "%s %lf %s" aa bb cc
也就是说,对于每一行,解析出一个单词、一个浮点数和另一个单词。将它们视为玩家 ID、分数和日期。我想要每个球员的前五名得分和日期。数据量不小,但也不大;大约 630 兆字节。
我想知道我应该用什么真正的可执行语言来编写它以使其同样简短(如下面的 Python)但要快得多。
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys
top_5 = {}
for line in sys.stdin:
aa, bb, cc = line.split()
# We want the top 5 for each distinct value of aa. There are
# hundreds of thousands of values of aa.
bb = float(bb)
if aa not in top_5: top_5[aa] = []
current = top_5[aa]
current.append((bb, cc))
# Every once in a while, we drop the values that are not in
# the top 5, to keep our memory footprint down, because some
# values of aa have thousands of (bb, cc) pairs.
if len(current) > 10:
current.sort()
current[:-5] = []
for aa in top_5:
current = top_5[aa]
current.sort()
for bb, cc in current[-5:]:
print aa, bb, cc
这是一些示例输入数据:
3 1.5 a
3 1.6 b
3 0.8 c
3 0.9 d
4 1.2 q
3 1.5 e
3 1.8 f
3 1.9 g
这是我从中得到的输出:
3 1.5 a
3 1.5 e
3 1.6 b
3 1.8 f
3 1.9 g
4 1.2 q
有 7 个值3
,因此我们删除c
和d
值,因为它们的bb
值将它们排除在前 5 之外。因为4
只有一个值,所以它的“前 5”只包含那个值。
这比在 MySQL 中执行相同的查询运行得更快(至少,我们发现执行查询的方式),但我很确定它大部分时间都花在 Python 字节码解释器上。我认为用另一种语言,我可能会让它每秒处理数十万行而不是每分钟。所以我想用一种实现速度更快的语言来编写它。
但我不确定选择什么语言。
我一直无法弄清楚如何在 SQL 中将其表达为单个查询,实际上我对 MySQL 的能力甚至只是
select * from foo into outfile 'bar';
输入数据的能力不感兴趣。
C 是一个显而易见的选择,但是诸如line.split()
对 2 元组列表进行排序和制作哈希表之类的事情需要编写一些标准库中没有的代码,所以我最终会得到 100 行或更多的代码,而不是 14 行。
C++ 似乎是一个更好的选择(它在标准库中有字符串、映射、对和向量),但似乎使用 STL 的代码会更加混乱。
OCaml 会很好,但它是否具有等效的line.split()
,我会对其地图的性能感到难过吗?
Common Lisp 可能有用吗?
是否有类似 Matlab 的数据库计算等价物,可以让我将循环向下推入快速代码?有人试过猪吗?
(编辑:通过提供一些示例输入和输出数据来回应 davethegr8 的评论,并修复了 Python 程序中的错误!)
(附加编辑:哇,这个评论线程到目前为止真的很棒。谢谢大家!)
编辑:
2007 年在 sbcl-devel 上提出了一个非常相似的问题(感谢 Rainer!),这是awk
Will Hartung 用于生成一些测试数据的脚本(尽管它没有真实数据的 Zipfian 分布):
BEGIN {
for (i = 0; i < 27000000; i++) {
v = rand();
k = int(rand() * 100);
print k " " v " " i;
}
exit;
}