Image

Listens: Kylie Minogue — [X #11] Wow

Category:

Полуночные открытия

Писал я как-то генерацию чисел с m установленными битами через рекурсию и в цикле:

template<void (*func)(int)>
void gen(int n, int m, int cn = 0, int cm = 0, int vec = 0)
{
    if (cn == n) func(vec);
    else {
        if (n - cn > m - cm) gen<func>(n, m, cn + 1, cm, vec * 2);
        if (cm < m) gen<func>(n, m, cn + 1, cm + 1, vec * 2 + 1);
    }
}

template<void (*func)(int)>
void gen2(int n, int m)
{
    for (int i = 0, len = (1 << n); i < len; ++i) {
        int c = 0;
        for (int u = i; u; u &= (u - 1)) ++c;
        if (c == m) func(i);
    }
}


Первая работает быстрее второй раза в три для \binom{2n}n, как и положено.

— Красиво ты ненулевые биты считаешь, — сказал Imageshuffle_c.
— Банальный трюк, — хмыкнул я и переставил ++c внутрь цикла. И время выполнения gen2(28, 14) вдруг возросло с 9.2 секунд до 11.2. Я воскликнул:
— Нипаняяятна! — и принялся разбираться.


for (int u = i; u; u &= (u - 1)) ++c;

Тело цикла:
.text:004012F8                 lea     edi, [eax-1]
.text:004012FB                 inc     ecx
.text:004012FC                 and     eax, edi
.text:004012FE                 jnz     short loc_4012F8


for (int u = i; u; u &= (u - 1), ++c);

Тело цикла:
.text:004012F8                 lea     edi, [eax-1]
.text:004012FB                 and     eax, edi
.text:004012FD                 inc     ecx
.text:004012FE                 test    eax, eax
.text:00401300                 jnz     short loc_4012F8


Компилятор вынужден был, придерживаясь семантики оператора запятая, поместить инкремент (inc ecx) после вычисления побитовой конъюнкции, из-за чего не смог воспользоваться ее результатом сразу же в условном переходе, для чего ему пришлось добавить лишнюю операцию — test eax, eax.

Этот случай, на мой взгляд, отлично иллюстрирует две простые истины.
Истина №1. Писать на ассемблере эффективнее компилятора практически невозможно.
Истина №2. Но язык ассемблера надо знать, чтобы не допускать глупостей в высокоуровневом коде.

В этом посте есть одно пасхальное яйцо для самых внимательных. Чтобы его найти, не обязательно знать программирование.