Ho un progetto di implementazione della struttura dati da C, che esporta tipi di API per altri programmi. Recentemente mi piacerebbe fare qualche ottimizzazione sulla funzione Hot profilata da grofile. Ecco il progetto per il tuo riferimento.Ottimizzazione del ramo while in loop, perché meno istruzioni costano più tempo di esecuzione?
https://github.com/Incarnation-p-lee/libds C'è un binary_search_tree_node_insert funzione di caldo, come di seguito:
/*
* 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;
}
voglio ottimizzazione due else-if parte come è mezza a metà ramo, che possono avere un impatto sulla pipeline di molto. Quindi scrivo una macro binary_search_tree_insert_path_go_through per sostituire questi due rami. E l'implementazione come segue:
/*
* 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")
Ma il test di unità di questa funzione è sceso del 6-8% circa su questa modifica. Dal codice objdump (codice ottimizzato sulla mano destra), ha meno istruzioni, non riesco a capire perché è costato più tempo prima dell'ottimizzazione.
E ci sono alcuni dettagli per voi riferimento:
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;
};
Funziona su virtual-box con 2 nucleo di i5-3xxM, e cpuinfo come segue:
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:
In x86_64 a 64-bit macchina di LFS, Linux pli.lfs 3.10.10 # 1 SMP Sun 2 marzo 18:07 : 33 CST 2014 x86_64 GNU/Linux. –
Puoi fornire più specifiche del processore: 'cat/proc/cpuinfo'? – Jens
meno istruzioni non significa che verrà eseguito più velocemente. Se si utilizza -O3 il codice potrebbe essere molto più lungo rispetto ai livelli inferiori di ottimizzazioni. Anche un frammento con 1 o 2 istruzioni che formano un ciclo infinito o molto lungo verrà eseguito più a lungo di un programma con più istruzioni ma meno cicli –