Эффективность Поразрядного XOR в C++ по сравнению с большим количеством читаемых методов

Я недавно писал некоторый код для исследовательского проекта, что я продолжаю работать, где эффективность очень важна. Я рассматривал очистку некоторых обычных методов, я выполняю в вещах и использовании поразрядного XORs вместо этого. То, что я задаюсь вопросом, - то, если это сделает, если различие (если я выполняю эту операцию, говорят, что несколько миллионов раз) или если это - то же после того, как я использую 03 в g ++.

Два примера, которые приходят на ум:

У меня был экземпляр, где (я работаю с чисто положительным ints), я должен был изменить n на n-1, если бы n был нечетен или n к (n+1), если n был ровен. Я полагал, что у меня было несколько опций:

if(n%2) // or (n%2==0) and flip the order
    n=n-1
else
    n=n+1

или

n=n+2*n%2-1; //This of course was silly, but was the only non-bitwise 1 line I could come up with

Наконец:

n=n^1;

Все методы ясно делают то же самое, но мое чувство состояло в том, что третий будет самым эффективным.

Следующий пример находится на более общих сведениях. Скажите, что я сравниваю два положительных целых числа, будет один из них работать лучше, чем другие. Или будет различие действительно не быть примечательным, даже если я выполню эту операцию несколько миллионов раз:

if(n_1==n_2)
if(! (n_1 ^ n_2) )
if( n_1 ^ n_2) else \do work here

Компилятор просто сделает ту же операцию во всех этих экземплярах? Мне просто любопытно, если существует экземпляр, когда я должен использовать битовые операции и не доверять компилятору, чтобы сделать работу для меня.

Fixed:In исправляют оператор проблемы.

8
задан JSchlather 22 February 2010 в 01:38
поделиться

7 ответов

Для потоковой передачи и разговора с клиентами необходимо определить протокол: Поиск RTP и RTSP в Интернете. Это должно дать вам довольно хорошее представление о том, что вам нужно, чтобы реализовать эти протоколы или даже создать свой собственный.

Что касается реализации, посмотрите на проект red5: http://red5.org/

Посмотрите также на Xuggler: http://www.xuggle.com/xuggler/ Этот проект поможет вам сохранить много строк кода. Отметим, что его развитие затормозилось.

Ура.

-121--3159578-

Операторы Switch работают только на интегральных значениях. Поэтому сообщение об ошибке - «switch quantity not a integer». Я не думаю, что это техническое ограничение настолько, насколько это вне синтаксиса языка.

-121--1453364-

Это достаточно легко проверить, просто запустите разборку. Посмотрите:

f.c:

unsigned int f1(unsigned int n)
{
  n ^= 1;  
  return n;
}

unsigned int f2(unsigned int n)
{
  if (n % 2)
    n=n-1;
  else
    n=n+1;

  return n;
}

Сборка и разборка:

$ cc -O3 -c f.c 
$ otool -tV f.o 
f.o:
(__TEXT,__text) section
_f1:
00  pushq   %rbp
01  movq    %rsp,%rbp
04  xorl    $0x01,%edi
07  movl    %edi,%eax
09  leave
0a  ret
0b  nopl    _f1(%rax,%rax)
_f2:
10  pushq   %rbp
11  movq    %rsp,%rbp
14  leal    0xff(%rdi),%eax
17  leal    0x01(%rdi),%edx
1a  andl    $0x01,%edi
1d  cmovel  %edx,%eax
20  leave
21  ret

Похоже, что f1 () немного короче, независимо от того, имеет ли это значение в действительности, зависит от какого-либо эталонного теста.

9
ответ дан 5 December 2019 в 08:23
поделиться

Является ли n^=1 быстрее, чем if ( n%2 ) --n; else ++n;? Да. Я бы не ожидал, что компилятор оптимизирует это. Поскольку побитовая операция намного лаконичнее, возможно, стоит ознакомиться с XOR и, возможно, добавить комментарий к этой строке кода.

Если это действительно критично для функциональности вашей программы, это также можно считать проблемой переносимости: если вы тестируете на своем компиляторе, и он работает быстро, то при тестировании на другом компиляторе вас, скорее всего, ждет сюрприз. Обычно это не является проблемой для алгебраических оптимизаций.

Является ли x^y быстрее, чем x==y? Нет. Делать что-то окольными путями, как правило, нехорошо.

1
ответ дан 5 December 2019 в 08:23
поделиться

Мне нужно было изменить n на n-1, если n было четным, или n на (n + 1), если n было нечетным.

В этом случае, независимо от эффективности, n = n ^ 1 неверно .

Для вашего второго случая == будет столь же эффективным (если не более), чем любой другой.


В общем, когда дело доходит до оптимизации, вы должны протестировать ее самостоятельно . Если потенциальная оптимизация не заслуживает сравнительного анализа, ее не стоит делать.

4
ответ дан 5 December 2019 в 08:23
поделиться

Хороший компилятор оптимизирует n%2, но вы всегда можете проверить ассемблер. Если вы видите деления, начните оптимизировать его самостоятельно, потому что деление - это примерно такой же медленный процесс, как и все остальные.

0
ответ дан 5 December 2019 в 08:23
поделиться

Вы должны доверять своему компилятору. gcc/++ - это продукт многолетней разработки, и он способен выполнить любую оптимизацию, о которой вы, вероятно, думаете. И, скорее всего, если вы начнете играть, вы испортите его усилия по оптимизации вашего кода.

0
ответ дан 5 December 2019 в 08:23
поделиться

n ^= 1 и n1==n2 - это, вероятно, лучшее, что вы можете сделать, но на самом деле, если вы стремитесь к максимальной эффективности, не стоит просматривать код в поисках подобных мелочей.

Вот пример того, как действительно настраивать производительность.

Не ждите, что низкоуровневые оптимизации сильно помогут, пока проба не докажет, что именно на них следует сосредоточиться.

0
ответ дан 5 December 2019 в 08:23
поделиться

Единственный способ узнать наверняка - это проверить. Я должен согласиться с тем, что потребуется довольно умный компилятор, чтобы произвести такой же эффективный вывод для:

if(n%2) // or (n%2==0) and flip the order
    n=n-1
else
    n=n+1

, как он мог бы для n ^ = 1; , но я в последнее время не проверял ничего подобного достаточно, чтобы сказать с уверенностью.

Что касается вашего второго вопроса, я сомневаюсь, что это имеет значение - сравнение на равенство быстро завершится для любого из этих методов. Если вам нужна скорость, главное, что нужно сделать, - это вообще не задействовать ветку - например, что-то вроде:

if (a == b)
    c += d;

можно записать как: c + = d * (a == b); . Глядя на язык ассемблера, второй часто выглядит немного запутанным (с уродливой глупостью, чтобы получить результат сравнения флагов в нормальный регистр), но все же часто работает лучше, избегая каких-либо ветвей.

Изменить: по крайней мере, компиляторы, которые у меня есть (gcc и MSVC), не генерируют cmov для if , но они создают набор ] для * (a == b) . Я расширил код до чего-то тестируемого.

Edit2: Поскольку Potatoswatter поднял другую возможность использования поразрядного умножения и вместо умножения, я решил проверить это вместе с другими. Вот код с добавлением:

#include <time.h>
#include <iostream>
#include <stdlib.h>

int addif1(int a, int b, int c, int d) { 
    if (a==b) 
        c+=d;
    return c;
}

int addif2(int a, int b, int c, int d) {
    return c += d * (a == b);
}

int addif3(int a, int b, int c, int d) {
    return c += d & -(a == b);
}

int main() {
    const int iterations = 50000;
    int x = rand();
    unsigned tot1 = 0;
    unsigned tot2 = 0;
    unsigned tot3 = 0;

    clock_t start1 = clock();
    for (int i=0; i<iterations; i++) {
        for (int j=0; j<iterations; j++) 
            tot1 +=addif1(i, j, i, x);
    }
    clock_t stop1 = clock();

    clock_t start2 = clock();
    for (int i=0; i<iterations; i++) {
        for (int j=0; j<iterations; j++) 
            tot2 +=addif2(i, j, i, x);
    }
    clock_t stop2 = clock();

    clock_t start3 = clock();
    for (int i=0; i<iterations; i++) {
        for (int j=0; j<iterations; j++) 
            tot3 +=addif3(i, j, i, x);
    }
    clock_t stop3 = clock();

    std::cout << "Ignore: " << tot1 << "\n";
    std::cout << "Ignore: " << tot2 << "\n";
    std::cout << "Ignore: " << tot3 << "\n";

    std::cout << "addif1: " << stop1-start1 << "\n";
    std::cout << "addif2: " << stop2-start2 << "\n";
    std::cout << "addif3: " << stop3-start3 << "\n";    
    return 0;
}

Теперь самое интересное: результаты для третьей версии весьма интересны. Для MS VC ++ мы получаем примерно то, что, вероятно, ожидает большинство из нас:

Ignore: 2682925904
Ignore: 2682925904
Ignore: 2682925904
addif1: 4814
addif2: 3504
addif3: 3021

Использование & вместо * дает определенное улучшение - почти ] такое же улучшение, как * уступает if . С gcc результат немного отличается:

Ignore: 2680875904
Ignore: 2680875904
Ignore: 2680875904
addif1: 2901
addif2: 2886
addif3: 7675

В этом случае код, использующий if , намного ближе к скорости кода, использующего * , но код, использующий & медленнее любого из них - на много медленнее! В случае, если кому-то интересно, я нашел это достаточно удивительным, что я пару раз перекомпилировал с разными флагами, повторно запускал несколько раз с каждым и так далее, и результат был полностью согласован - код, использующий & всегда был значительно медленнее.

Плохой результат с третьей версией кода, скомпилированной с помощью gcc, возвращает нас к тому, что я сказал начать с [и заканчивает это редактирование]:

Как я сказал вначале, «единственный способ узнать о конечно, это проверка "- но, по крайней мере, в этом ограниченном тестировании умножение последовательно превосходит if . Может быть некоторая комбинация компилятора, флагов компилятора, ЦП, шаблона данных, количества итераций и т. Д., Которая благоприятствует if умножению - нет никаких сомнений в том, что разница достаточно мал, чтобы испытание, идущее в другом направлении, было вполне правдоподобным. Тем не менее, я считаю, что эту технику стоит знать; для основных компиляторов и процессоров он кажется достаточно эффективным (хотя он определенно более полезен с MSVC, чем с gcc).

[возобновление edit2:] результат с использованием gcc и демонстрирует степень, в которой 1) микрооптимизации могут быть / являются специфичными для компилятора и 2) насколько реальные результаты могут отличаться от ожиданий.

2
ответ дан 5 December 2019 в 08:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: