【发布时间】:2015-06-27 01:53:03
【问题描述】:
我有一个用 C 语言实现的数据结构项目,它为其他程序导出各种 API。最近想对 grofile 分析的 Hot 函数做一些优化。这是供您参考的项目。
https://github.com/Incarnation-p-lee/libds 有一个热函数 binary_search_tree_node_insert,如下:
/*
* RETURN the pointer of inserted node of the binary search tree
* If root is NULL or node is NULL, RETURN NULL.
*/
struct binary_search_tree *
binary_search_tree_node_insert(struct binary_search_tree *root,
struct binary_search_tree *node)
{
register struct binary_search_tree **iter;
if (!node || !root) {
pr_log_warn("Attempt to access NULL pointer.\n");
} else {
iter = &root;
while (*iter) {
if (node->chain.nice == (*iter)->chain.nice) {
if (*iter == node) {
pr_log_info("Insert node exist, nothing will be done.\n");
} else {
doubly_linked_list_merge((*iter)->chain.link, node->chain.link);
}
return *iter;
#ifndef OPT_HOT
} else if (node->chain.nice > (*iter)->chain.nice) {
iter = &(*iter)->right;
} else if (node->chain.nice < (*iter)->chain.nice) {
iter = &(*iter)->left;
#else
} else {
binary_search_tree_insert_path_go_through(node, iter);
#endif
}
}
return *iter = node;
}
return NULL;
}
我想优化两个 else-if 部分,因为它是半到半分支,这可能会对管道产生很大影响。所以我写了一个宏 binary_search_tree_insert_path_go_through 替换这两个分支。实现如下:
/*
* 1. node->nice => rbx, *iter => rcx.
* 2. compare rbx, and 0x8(rcx).
* 3. update iter.
*/
#define binary_search_tree_insert_path_go_through(node, iter) \
asm volatile ( \
"mov $0x18, %%rax\n\t" \
"mov $0x20, %%rdx\n\t" \
"mov 0x8(%1), %%rbx\n\t" \
"mov (%0), %%rcx\n\t" \
"cmp 0x8(%%rcx), %%rbx\n\t" \
"cmovg %%rdx, %%rax\n\t" \
"lea (%%rcx, %%rax), %0\n\t" \
:"+r"(iter) \
:"r"(node) \
:"rax", "rbx", "rcx", "rdx")
但是这个函数的单元测试对于这个变化已经下降了大约 6-8%。从 objdump 代码(右手边的优化代码)来看,它的指令较少,我很难理解为什么优化前要花费更多时间。
还有一些细节供大家参考:
struct collision_chain {
struct doubly_linked_list *link;
sint64 nice;
};
/*
* binary search tree
*/
struct binary_search_tree {
struct collision_chain chain;
sint32 height; /* reserved for avl */
/* root node has height 0, NULL node has height -1 */
union {
struct binary_search_tree *left;
struct avl_tree *avl_left; /* reserved for avl */
struct splay_tree *splay_left; /* reserved for splay */
};
union {
struct binary_search_tree *right;
struct avl_tree *avl_right; /* reserved for avl */
struct splay_tree *splay_right; /* reserved for splay */
};
};
struct doubly_linked_list {
uint32 sid;
void *val;
struct doubly_linked_list *next;
struct doubly_linked_list *previous;
};
它运行在具有2核i5-3xxM的virtual-box上,cpuinfo如下:
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
stepping : 9
microcode : 0x19
cpu MHz : 2568.658
cache size : 6144 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 5
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 lahf_lm
bogomips : 5137.31
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
stepping : 9
microcode : 0x19
cpu MHz : 2568.658
cache size : 6144 KB
physical id : 0
siblings : 2
core id : 1
cpu cores : 2
apicid : 1
initial apicid : 1
fpu : yes
fpu_exception : yes
cpuid level : 5
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 lahf_lm
bogomips : 5137.31
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
【问题讨论】:
-
在 x86_64 64 位 lfs 机器中,Linux pli.lfs 3.10.10 #1 SMP Sun Mar 2 18:07:33 CST 2014 x86_64 GNU/Linux。
-
您能否提供更多处理器细节:
cat /proc/cpuinfo? -
指令少并不意味着它会运行得更快。如果您使用 -O3,则代码很可能比较低级别的优化更长。即使是具有 1 或 2 条指令形成无限或非常长循环的 sn-p 也会比具有更多指令但更少循环的程序运行时间更长
-
汇编比较代码在gcc下用-O3编译。
-
这里
nice的类型是什么,这个变量的可能取值范围是多少?
标签: c optimization data-structures