Wednesday, June 1, 2011

Ruby, 64-bit, Bignum и все-все-все

Итак, свершилось! Переехал я на 64 бита, благо Intel Atom N450 их поддерживает. Вроде как всё летает просто: ещё бы, целых 8 новых регистров задействуются! Библиотеки для длинной арифметики, как и полагается, ускорились в два раза.

Ну так вот, попытался я и интерпретатор 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. Жесть. То бишь действительно удвоение скорости засчёт бóльшей разрядности + дополнительный прирост засчёт использования полного набора регистров (по крайней мере мне кажется, что это основные факторы).

Monday, May 30, 2011

охренительно

Ради любопытства слил сегодня свежий ruby из trunk-а, скомпилял. С удивлением обнаружил, что в 1.9.3dev (rev. 31825) наконец-то заимплементили Тоом-Кука! Не FFT, конечно, но всё же прогресс налицо. (Написать, что ли, летом Шёнхаге-Штрассена? — за два месяца можно управиться =)...)
Произошёл сий коммит, кстати говоря, совсем недавно — 22 мая (rev. 31695).
Да и в целом скорость повыше, чем у 1.9.2-p336, на котором я сидел до этого.
Так что надо почаще обновляться, пожалуй =)

Но охренительно не только это. Есть такая замечательная вещь, как Intel Vtune Amplifier — вроде как самый ништяковый профайлер на сегодняшний день (по крайней мере, для Intel-овских процессоров). Цены на лицензионный продукт просто зверские (900$). Но если побродить по сайту, ВНЕЗАПНО оказывается, что "developers who are developing software on their own time without compensation" могут скачать некоммерческую версию вообще на халяву! (правда, есть один нюанс: только для linux; причём у меня установка превратилась в долгое ковыряние с компиляцией их драйверов; впрочем, для убунты вроде поставляется с уже скомпилированными дровами).

Ну и про ещё одну забавную штуку напишу, коль скоро она соответствует заголовку: оказывается, есть гомеоморфный тору семигранник, у которого каждая грань соседствует со всеми остальными. У кого руки чешутся — можете даже склеить, там внизу есть ссылочка на pdf с развёрткой. Получите конструктивную (в наибуквальнейшем смысле :-)) оценку снизу хроматического числа тора.