Last active
December 18, 2015 04:39
-
-
Save zeux/5727262 to your computer and use it in GitHub Desktop.
Наш ответ С++ оптимизаторам (http://habrahabr.ru/post/182428/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Идея в том, чтобы заменить сравнения сложением с overflow в специально оставленный carry bit; | |
// поскольку каждое значение имеет свой carry bit, сложения можно делать "параллельно" uint64 операциями | |
// 2. Очевидно, решение "почти" кроссплатформенно и почти C (наверное, скомпилируется как C99?). | |
// Из существенного тут используется знание о порядке bitfields -- мне не хотелось переписывать весь | |
// код на битовые операции с uint64 -- и нарушается strict aliasing. Очевидно, исправляется просто. | |
// 3. А вот и тайминги (cl64 /Ox /TP): | |
// Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x64 | |
// Generated rows: 100000000 | |
// C-search took 0.778000 seconds. | |
// C++-optimized search took 0.307000 seconds. | |
// C-optimized-search took 0.171000 seconds. | |
// 4. С++ версия у меня компилируется 5 минут, omfg. Я тоже люблю шаблонную комбинаторику, но всему есть предел! | |
// 5. Очевидно, мне "повезло" - все поля влезли в uint64 с запасом на carry bits, даже еще лишние битики остались. | |
// Автору впрочем тоже повезло что полей всего на 4 тысячи комбинаций. | |
#include <stdio.h> /* printf, scanf, NULL */ | |
#include <stdlib.h> /* calloc, exit, free */ | |
#include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */ | |
#include <string.h> | |
const size_t c_array_size = 100000000; | |
/* Fields */ | |
enum T_field_enum { amount_of_money_e, gender_e, age_e, code_e, height_e, /*<<<<<- add fields here */ last_e }; | |
/* Row */ | |
struct T_cash_account_row { | |
unsigned reserved0:1; | |
unsigned code:20; // 0 - 1000000 | |
unsigned carry_code:1; | |
unsigned gender:1; // 0 - 1 | |
unsigned carry_gender:1; | |
unsigned age:7; // 0 - 100 | |
unsigned carry_age:1; | |
unsigned reserved1:1; | |
unsigned amount_of_money:20;// 0 - 1000000 | |
unsigned carry_amount_of_money:1; | |
unsigned height:9; // 0 – 300 | |
unsigned carry_height:1; | |
}; | |
/* ----------------------------------------------------------------------- */ | |
/* Generate random data for the one row */ | |
static inline struct T_cash_account_row generate_row() { | |
struct T_cash_account_row cash_account_row; | |
memset(&cash_account_row, 0, sizeof(cash_account_row)); | |
cash_account_row.age = rand() % 100; | |
cash_account_row.amount_of_money = (rand() % 1000)*(rand() % 1000); | |
cash_account_row.code = (rand() % 1000)*(rand() % 1000); | |
cash_account_row.gender = rand() % 2; | |
cash_account_row.height = rand() % 300; | |
return cash_account_row; | |
} | |
/* ----------------------------------------------------------------------- */ | |
/* Filters */ | |
struct T_range_filters { | |
struct T_cash_account_row begin, end; | |
/* bytes array or bitset from https://gist.github.com/jmbr/667605 */ | |
unsigned char use_filter[last_e]; | |
}; | |
/* ----------------------------------------------------------------------- */ | |
/* C optimized search */ | |
static inline size_t search_optimized(struct T_cash_account_row const*const __restrict array_ptr, const size_t c_array_size, | |
struct T_cash_account_row *const __restrict result_ptr, struct T_range_filters const*const __restrict range_filters) | |
{ | |
struct T_cash_account_row begin_add, end_add, mask_add; | |
memset(&begin_add, 0, sizeof(begin_add)); | |
memset(&end_add, 0, sizeof(end_add)); | |
memset(&mask_add, 0, sizeof(mask_add)); | |
#define FILL(enum, field, bits) \ | |
if (range_filters->use_filter[enum]) { \ | |
begin_add.field = (1 << bits) - range_filters->begin.field; \ | |
begin_add.carry_##field = range_filters->begin.field == 0; \ | |
end_add.field = (1 << bits) - 1 - range_filters->end.field; \ | |
mask_add.carry_##field = 1; \ | |
} | |
FILL(code_e, code, 20); | |
FILL(gender_e, gender, 1); | |
FILL(age_e, age, 7); | |
FILL(amount_of_money_e, amount_of_money, 20); | |
FILL(height_e, height, 9); | |
#undef FILL | |
unsigned long long begin_add_i = *(unsigned long long*)&begin_add; | |
unsigned long long end_add_i = *(unsigned long long*)&end_add; | |
unsigned long long mask_add_i = *(unsigned long long*)&mask_add; | |
size_t result_size = 0; | |
size_t i; /* loop index */ | |
for(i = 0; i < c_array_size; ++i) { | |
unsigned long long value = *(unsigned long long*)&array_ptr[i]; | |
unsigned long long begin_result = (value + begin_add_i) ^ mask_add_i; | |
unsigned long long end_result = value + end_add_i; | |
if (((begin_result | end_result) & mask_add_i) == 0) | |
{ | |
result_ptr[result_size] = array_ptr[i], ++result_size; | |
} | |
} | |
return result_size; | |
} | |
/* ----------------------------------------------------------------------- */ | |
/* Compare row with filters */ | |
static inline unsigned char test_predicate(struct T_cash_account_row const*const __restrict row, | |
struct T_range_filters const*const __restrict range_filters) | |
{ | |
return | |
(!range_filters->use_filter[amount_of_money_e] || | |
(row->amount_of_money >= range_filters->begin.amount_of_money && | |
row->amount_of_money <= range_filters->end.amount_of_money)) && | |
(!range_filters->use_filter[gender_e] || | |
(row->gender >= range_filters->begin.gender && row->gender <= range_filters->end.gender)) && | |
(!range_filters->use_filter[age_e] || | |
(row->age >= range_filters->begin.age && row->age <= range_filters->end.age)) && | |
(!range_filters->use_filter[code_e] || | |
(row->code >= range_filters->begin.code && row->code <= range_filters->end.code)) && | |
(!range_filters->use_filter[height_e] || | |
(row->height >= range_filters->begin.height && row->height <= range_filters->end.height)); | |
} | |
/* ----------------------------------------------------------------------- */ | |
/* search */ | |
static inline size_t search(struct T_cash_account_row const*const __restrict array_ptr, const size_t c_array_size, | |
struct T_cash_account_row *const __restrict result_ptr, struct T_range_filters const*const __restrict range_filters) | |
{ | |
size_t result_size = 0; | |
size_t i; /* loop index */ | |
for(i = 0; i < c_array_size; ++i) { | |
if(test_predicate(array_ptr + i, range_filters)) | |
result_ptr[result_size] = array_ptr[i], ++result_size; | |
} | |
return result_size; | |
} | |
/* ----------------------------------------------------------------------- */ | |
int main() { | |
size_t i; /* loop index */ | |
struct T_cash_account_row *const array_ptr = ( struct T_cash_account_row *)calloc(c_array_size, sizeof(struct T_cash_account_row)); | |
if (array_ptr == NULL) { | |
printf ("calloc error\n"); | |
exit(1); | |
} | |
/* initialize random seed: */ | |
/* srand (time(NULL)); */ | |
/* Fill table random data */ | |
for(i = 0; i < c_array_size; ++i) | |
array_ptr[i] = generate_row(); | |
printf ("Generated rows: %d \n", c_array_size); | |
struct T_range_filters range_filters = {}; | |
range_filters.use_filter[amount_of_money_e] = rand()%1 + 0; | |
range_filters.use_filter[gender_e] = rand()%1 + 0; | |
range_filters.use_filter[height_e] = rand()%1 + 0; | |
range_filters.begin.age = rand() % 100; | |
range_filters.end.age = range_filters.begin.age + 5; | |
range_filters.use_filter[age_e] = rand()%1 + 1; | |
range_filters.begin.code = rand() % 30000; | |
range_filters.end.code = range_filters.begin.code + 5; | |
range_filters.use_filter[code_e] = rand()%1 + 1; | |
struct T_cash_account_row *const result_ptr = ( struct T_cash_account_row *)calloc(c_array_size, sizeof(struct T_cash_account_row)); | |
size_t result_size; | |
clock_t end, start; | |
printf ("C-optimized-Searching...\n"); | |
start = clock(); | |
result_size = search_optimized(array_ptr, c_array_size, result_ptr, &range_filters); | |
end = clock(); | |
float co_took_time = (float)(end - start); | |
printf ("C-optimized-search took %f seconds.\n", co_took_time/CLOCKS_PER_SEC); | |
printf ("Found rows: %d \n", result_size); | |
printf ("C-Searching...\n"); | |
start = clock(); | |
result_size = search(array_ptr, c_array_size, result_ptr, &range_filters); | |
end = clock(); | |
float c_took_time = (float)(end - start); | |
printf ("C-search took %f seconds.\n", c_took_time/CLOCKS_PER_SEC); | |
printf ("The C++ faster than C: %f times \n", c_took_time/co_took_time); | |
printf ("Found rows: %d \n", result_size); | |
/*for(i = 0; i < result_size; ++i) { | |
printf ("%d, %d, %d, %d, %d \n", | |
result_ptr[i].age, result_ptr[i].amount_of_money, result_ptr[i].code, result_ptr[i].gender, result_ptr[i].height); | |
}*/ | |
int qwerty; | |
scanf ("%d",&qwerty); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment