-
Notifications
You must be signed in to change notification settings - Fork 29
Description
My English is not good, and then I'll switch to Chinese. Explaining in my native language won't lead to too many mistakes.
在跑JOB这个benchmark时候踩好几个坑,在issue里做一些说明,如果后面有人踩坑可以看看,大致的思路应该是对的,但是实现起来也不是很优雅,所以也就不pr了。
HASH_TABLE_SIZE
这个问题是在SUBQUERY_CARD_PULL_ANCHOR和CARD_PUSH_ANCHOR时会出现的,也就是获取subquery时候的报错,因为JOB中存在非常多的subquery,定义在src/backend/pilotscope/parse_json.c中的HASH_TABLE_SIZE定义的只有1023,肯定不够,我设置了16384是没有问题的,反正肯定要大于10k。
这个问题肯定会遇到,所以放第一个说,后面需要分条件说明了,就是是否设置了geqo = off
geqo
geqo是是否启用遗传算法来优化join order,默认为true
在subquery手动注入到pg的工作中,可以看到他们是关闭了geqo的:https://github.com/heriec/End-to-End-CardEst-Benchmark/blob/master/dockerfile/init_pgsql.sh
但是pilotscope是没有这样说的要求的,这在JOB中生成非常多的subquery时,会触发geqo(stats不会存在这个问题)。
geqo = on
具体调用路径在src/backend/optimizer/path/allpaths.c中的make_rel_from_joinlist
这个函数里面的
if (levels_needed == 1)
{
/*
* Single joinlist node, so we're done.
*/
return (RelOptInfo *) linitial(initial_rels);
}
else
{
/*
* Consider the different orders in which we could join the rels,
* using a plugin, GEQO, or the regular join search code.
*
* We put the initial_rels list into a PlannerInfo field because
* has_legal_joinclause() needs to look at it (ugly :-().
*/
root->initial_rels = initial_rels;
if (join_search_hook)
return (*join_search_hook) (root, levels_needed, initial_rels);
else if (enable_geqo && levels_needed >= geqo_threshold)
return geqo(root, levels_needed, initial_rels);
else
return standard_join_search(root, levels_needed, initial_rels);
}当开启enable_geqo 同时levels_needed >= geqo_threshold就会调用geqo_eval()
也就是说:
- 如果 表的数量 < geqo_threshold(默认 12) → 用动态规划算法。
- 如果 表的数量 ≥ geqo_threshold → 启动 GEQO。
调用geqo() 会在 planner 决定用 GEQO 代替标准的动态规划 join search 时触发。
geqo 的调用在 src/backend/optimizer/geqo/geqo_main.c然后调用一些其他函数,直到调用到 位于src/backend/optimizer/geqo/geqo_eval.c的
geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)就不贴详细代码了,自己看源码,具体就是遗传算法期间临时创建一个内存 cxt,用完会被释放
mycontext = AllocSetContextCreate(CurrentMemoryContext,
"GEQO",
ALLOCSET_DEFAULT_SIZES);所以在 subquery 调用的时候会调用src/backend/pilotscope/utils/hashtable.c的create_entry函数创建 entry:
Entry* entry = (Entry*)palloc(sizeof(Entry));
entry->key = (char*)palloc(strlen(key) + 1);
entry->value = (char*)palloc(strlen(value) + 1);分配内存空间会被分配到GEQO这个 cxt 上
mycontext = AllocSetContextCreate(CurrentMemoryContext,
"GEQO",
ALLOCSET_DEFAULT_SIZES);如果没有使用就是使用MessageContext,GEQO是他的 children(pg里的内存是树状的)
所以当 GEQO 的内存在释放后,之前创建的Entry是存在 count_table 的,但是GEQO会释放这块的内存,导致后面从count_table中get时候内存找不到对应数据,只是为了解决这个bug的话,我就把create_entry时单独处理了一下,感觉不是很好的处理方式,仅作思路参考:
// create entry
Entry* create_entry(const char* key, const char* value)
{
MemoryContext oldcxt = NULL;
if (strcmp(CurrentMemoryContext->name, "MessageContext") != 0)
{
oldcxt = MemoryContextSwitchTo(CurrentMemoryContext->parent);
}
Entry* entry = (Entry*)palloc(sizeof(Entry));
entry->key = (char*)palloc(strlen(key) + 1);
entry->value = (char*)palloc(strlen(value) + 1);
entry->next = NULL;
strcpy(entry->key, key);
strcpy(entry->value, value);
if (oldcxt != NULL)
{
MemoryContextSwitchTo(oldcxt);
}
return entry;
}geqo = off
好,你说我不这么麻烦了,我直接关了不就行了,哈哈,那你就遇到下一个坑,还是JOB过大的subquery导致的,在上面的HASH_TABLE_SIZE我们给count_table分配容量时候太小出现问题了,现在在CARD_PUSH_ANCHOR时存subquery2card又出现问题了,具体在调用函数store_aimodel_subquery2card()里面的,在src/backend/pilotscope/anchor2struct.c中:
int table_size = card_push_anchor->card_num * card_push_anchor->card_num;
table = create_hashtable(table_size);table是个全局对象
table_size定义为$card_num^2$,这就导致了card_num大于10k时,非常大,我们知道他是为了防止碰撞,但是这样需要分配太大的空间,毕竟都是简化实现的,这里也不存在什么动态扩容的代码(你要想实现你就自己写),所以也就选合理的table_size,保守一点就用「略大于 n 的素数」作为哈希表容量:
int is_prime(int n) {
if (n < 2) return 0;
if (n == 2) return 1;
if (n % 2 == 0) return 0;
for (int i = 3; i * i <= n; i += 2)
if (n % i == 0)
return 0;
return 1;
}
int next_prime(int x) {
if (x <= 2) return 2;
if (x % 2 == 0) x++;
while (!is_prime(x)) x += 2;
return x;
}使用:
// avoid hash confict
int n = card_push_anchor->card_num;
int table_size = next_prime(n * 2); 多说一句,qeqo生成的subquery_2_card顺序和普通的dp生成的顺序是不一样的,具体可以自己debug看下
上面就是JOB所有踩坑的部分了,至少这么写能跑完JOB,如果有错误敬请指正
水平有限,本以为小bug,结果排查了很久。这个工作还是很有意思的,目前看只能大厂来做真正把AI4DB的东西放到db中,工作量太大了还不好发paper...