Ну так вот, попытался я и интерпретатор Ruby на 64 бита пересадить. Точнее, его Bignum-ы. По умолчанию никакой поддержки 64-битовых целых в качестве digit-ов почему-то не предусмотрено. «Ну фиг ли», — решил я, «тоже мне проблема», — добавил в include/ruby/defines.h несколько строчек и пересобрал:
#if defined(__x86_64__) # define BDIGIT unsigned long # define SIZEOF_BDIGITS SIZEOF_LONG # define BDIGIT_DBL __uint128_t # define BDIGIT_DBL_SIGNED __int128_t # define PRI_BDIGIT_PREFIX "" # define PRI_BDIGIT_DBL_PREFIX "" #elif ...
Ну ладно, даже собралось. Правда, по поводу random.c в процессе появилось несколько варнингов — деление на ноль, например :-))) Хрень в том, что там есть такая строчка:
#define DIGSPERINT (SIZEOF_INT/SIZEOF_BDIGITS)Почему-то предполагается, что эта шняга всегда не меньше единицы, и на неё спокойно делят. Только вот в нашем случае 4 / 8 = 0, хыхых. Надо будет поковырять rb_rand_dump и rb_rand_load.
Впрочем, с генерацией псевдорандомных длинных чисел разобраться удалось (функция limited_big_rand):
... #elif SIZEOF_BDIGITS == 4 # define BIG_GET32(big,i) (RBIGNUM_DIGITS(big)[(i)]) # define BIG_SET32(big,i,d) (RBIGNUM_DIGITS(big)[(i)] = (d)) #else /* SIZEOF_BDIGITS == 8 */ # define BIG_GET32(big,i) \ ((i) & 1 ? \ RBIGNUM_DIGITS(big)[(i)>>1] >> 32 : \ RBIGNUM_DIGITS(big)[(i)>>1] & 0xffffffff ) # define BIG_SET32(big,i,d) \ do { \ if ((i) & 1) \ RBIGNUM_DIGITS(big)[(i)>>1] = \ ((d) << 32) + (RBIGNUM_DIGITS(big)[(i)>>1] & 0xffffffff); \ else \ RBIGNUM_DIGITS(big)[(i)>>1] &= 0xffffffff00000000ULL, \ RBIGNUM_DIGITS(big)[(i)>>1] += (d); \ } while(0); #endif
Был и ещё один глюк: строки в числа преобразовывались верно, а вот наоборот — нифига. Чтобы пофиксить, добавил в функции rb_big2str0 (bignum.c) строки
#if SIZEOF_BDIGITS > 4 hbase *= hbase; #endifсразу после такого же сравнения с двойкой.
Пришлось также вручную подправить значение BASE в ext/bigdecimal/bigdecimal.h: в противном случае возникало переполнение. BASE должно равняться 10**9, а не 10**19. Было следующее:
... #elif SIZEOF_BDIGITS >= 8 # define RMPD_COMPONENT_FIGURES 19 # define RMPD_BASE ((BDIGIT)10000000000000000000U) #elif SIZEOF_BDIGITS >= 4 # define RMPD_COMPONENT_FIGURES 9 # define RMPD_BASE ((BDIGIT)1000000000U) #elif ...Вообще говоря, эта хрень только в trunk-е появилась, и здравая логика подсказывает, что во избежание переполнения здесь везде надо '>=' заменить на '>'. (По крайней мере, в старых стабильных версиях на 32-битных машинах BASE было равно 10000).
Ну по крайней мере арифметические операции, ради которых всё затевалось, вроде работают. И скорость числодробления действительно круто возросла: взять хотя бы тот же 1000000! — одна и та же версия ruby с 32-битными digit-ами считает его за 57 секунд, а с 64-битными — всего за 19. Жесть. То бишь действительно удвоение скорости засчёт бóльшей разрядности + дополнительный прирост засчёт использования полного набора регистров (по крайней мере мне кажется, что это основные факторы).
No comments:
Post a Comment