Skip to content

Instantly share code, notes, and snippets.

@zeux
Last active December 18, 2015 04:39
Show Gist options
  • Save zeux/5727262 to your computer and use it in GitHub Desktop.
Save zeux/5727262 to your computer and use it in GitHub Desktop.
Наш ответ С++ оптимизаторам (http://habrahabr.ru/post/182428/)
// 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