【问题标题】:slow aggregate on multiple columns多列上的慢聚合
【发布时间】:2015-03-26 13:44:09
【问题描述】:

这个聚合多列的 kdb 查询大约需要 31 秒,而 J 需要 3 秒

有没有更快的方法在 kdb 中求和?

最终这将针对 32 位版本的分区数据库运行

/test 1 - using symbols

n: 13000000;
cust: n?`8;
prod: n?`8;
v: n?100
a:([]cust:cust; prod:prod ;v:v)

/query 1 - using simple by
q)\t select sum(v) by cust, prod from a
31058

/query 2 - grouping manually
\t {sum each x[`v][group[flip (x[`cust]; x[`prod])]]}(select v, cust, prod from a)
12887

/query 3 - simpler method of accessing
\t {sum each a.v[group x]} (flip (a.cust;a.prod))
11576

/test 2 - using strings, very slow
n: 13000000;
cust: string n?`8;
prod: string n?`8;
v: n?100
a:([]cust:cust; prod:prod ;v:v)

q)\t select sum(v) by cust, prod from a
116745

比较J码

n=:13000000
cust=: _8[\ a. {~ (65+?(8*n)#26)
prod=: _8[\ a. {~ (65+?(8*n)#26)
v=: ?.n#100

agg=: 3 : 0 
keys=:i.~ |: i.~ every (cust;prod)
c=.((~.keys) { cust)
p=.((~.keys) { prod)
s=.keys +//. v
c;p;s
)

NB. 3.57 seconds
6!:2 'r=.agg 0'
3.57139

 ({.@$) every r
13000000 13000000 13000000

更新: 从 kdbplus 论坛,我们可以将速度差异降低到大约 2 倍

q)\t r:(`cust`prod xkey a inds) + select sum v by cust,prod from a til[count a] except inds:(select cust,prod from a) ? d:distinct select cust,prod from a
6809

更新 2:为每个 @user3576050 添加了另一个数据集 该数据集的总行数相同,但每组分布 4 个实例

n: 2500000
g: 4
v: (g*n)?100
cust: (g*n)#(n?`8)
prod: (g*n)#(n?`8)
b:([]cust:cust; prod:prod ;v:v)
q)\ts select sum v by cust, prod from b
9737 838861968

之前的查询在新数据集上运行不佳

q)\ts r:(`cust`prod xkey b inds) + select sum v by cust,prod from a til[count b] except inds:(select cust,prod from b) ? d:distinct select cust,prod from b
17181 671090384

【问题讨论】:

  • 查询本身很简单,但是您可以尝试将 parted 或 group 属性应用于 cust 和/或 prod,这将使其运行得更快(以初始时间为代价)实际采取的属性)
  • 谢谢,根据我的实际数据,我确实有 g# 属性,但它似乎没有帮助。可能是 g# 属性不能跨分区有效地工作?
  • 这很奇怪pg 属性应该会产生一些影响。我能想到的唯一另一件事是将表格分成更小的子集,类似于code.kx.com/wiki/Cookbook/alternateInMemLayouts
  • 我很难相信在公平的比较中 kdb 会慢近 10 倍,但我不知道 J 所以无法检查你的 J 代码。你确定你没有在 J 代码中预先计算一些东西吗?
  • @terrylynch - J 代码中没有预先计算任何内容。

标签: kdb


【解决方案1】:

如果您更新此数据的频率低于查询它的频率,那么预先计算组索引如何?创建单个查询的成本大致相同,而且它允许以约 30 倍的速度进行查询。

q)\ts select sum v by cust,prod from b
14014 838861360
q)\ts update g:`g#{(key group x)?x}flip(cust;prod)from`b
14934 1058198384
q)\ts select first cust,first prod,sum v by g from b
473 201327488
q)

结果与行顺序和架构详细信息相匹配:

q)(select sum v by cust,prod from b)~`cust`prod xasc 2!delete g from select first cust,first prod,sum v by g from b
1b
q)

(顺便说一句,我对 J 基本上一无所知,但我的猜测是它正在计算一个类似的多列组索引。不幸的是(目前?)q 的 g 索引仅限于纯矢量数据——如果可能的话以某种方式将其应用于custprod组合,我希望我们会从简单的查询中看到类似我的结果。)

【讨论】:

    【解决方案2】:

    您正在使用病态数据集,一组长度为 8 的随机符号将很少有重复,从而使分组变得多余。

    q)n:13000000; (count distinct n?`8)%n
    0.9984848
    

    p#/g# 属性(在上面的 cmets 中提到)出于同样的原因不会对性能产生影响。

    使用更合适的数据,您会看到更好的性能。

    q)n:1000000
    q)
    q)a:([]cust:n?`8; prod:n?`8; v:n?100)
    q)b:([]cust:n?`3; prod:n?`3; v:n?100)
    q)
    q)\ts select sum v by cust, prod from a
    3779 92275568
    q)
    q)\ts select sum v by cust, prod from b
    762 58786352
    

    【讨论】:

    • 谢谢,我在上面加了一个比较有代表性的例子,运行起来还是比J慢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    相关资源
    最近更新 更多