Самый быстрый способ узнать минимум 3 чисел?

В программе я записал, 20% времени тратятся на обнаружение минимума 3 чисел во внутреннем цикле в этой стандартной программе:

static inline unsigned int
min(unsigned int a, unsigned int b, unsigned int c)
{
    unsigned int m = a;
    if (m > b) m = b;
    if (m > c) m = c;
    return m;
}

Там какой-либо путь состоит в том, чтобы ускорить это? Я соглашаюсь с ассемблерным кодом также для x86/x86_64.

Править: В ответ на некоторые комментарии:
* используемый Компилятор является gcc 4.3.3
* Насколько блок затронут, я - только новичок там. Я попросил блок здесь, изучать, как сделать это. :)
* у меня есть четырехъядерное выполнение Intel 64, таким образом, MMX/SSE и т.д. поддерживается.
* трудно отправить цикл здесь, но я могу сказать Вам, что это - в большой степени оптимизированная реализация levenshtein алгоритма.

Это - то, что компилятор дает мне для невстроенной версии минуты:

.globl min
    .type   min, @function
min:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %edx
    movl    12(%ebp), %eax
    movl    16(%ebp), %ecx
    cmpl    %edx, %eax
    jbe .L2
    movl    %edx, %eax
.L2:
    cmpl    %ecx, %eax
    jbe .L3
    movl    %ecx, %eax
.L3:
    popl    %ebp
    ret
    .size   min, .-min
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

Встроенная версия в рамках-O2 оптимизированного кода (даже мои маркеры mrk = 0xfefefefe, прежде и после вызова к минуте ()) становятся оптимизированными далеко gcc, таким образом, я не мог овладеть им.

Обновление: Я протестировал изменения, предложенные Nils, ephemient, однако нет никакого заметного повышения производительности, которое я получаю при помощи версий блока минуты (). Однако я получаю повышение на 12,5% путем компиляции программы с-march=i686, который я предполагаю, то, потому что целая программа извлекает пользу из новых более быстрых инструкций, что gcc генерирует с этой опцией. Спасибо за Ваших парней справки.

P.S. - я использовал рубинового профилировщика для измерения уровня (моя программа C является общей библиотекой, загруженной рубиновой программой), таким образом, я мог получить время, проведенное только для высокоуровневой C функции, вызванной рубиновой программой, которая заканчивает тем, что назвала минуту () вниз стеком. Посмотрите этот вопрос.

20
задан Community 23 May 2017 в 12:01
поделиться

10 ответов

Есть ли у вас в настройках контекстный процессор мультимедиа?

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.media',
)

Вы также можете попробовать установить в настройках следующие параметры, чтобы узнать, обнаруживаются ли сообщения отладки:

TEMPLATE_DEBUG = True
-121--3427933-

В Ruby 1.8/1.9 как sort , так и sort _ by реализованы в C, это грубый эквивалент того, как это работает:

Начните с [1,2,3,4] и вызовите sort _ по {rand} :

  1. (я изобрел несколько случайных чисел):

    Создается массив кортежей: [[0,12232, 1], [0,53434, 2], [0,333, 3], [0,99, 4]]

    В примерно эквивалентном Ruby-коде это: [1,2,3,4] .map {| x | [rand, x]}

  2. Быстрая сортировка Ruby выполняется для массива на основе первого элемента: (обратите внимание, что внутренняя реализация далеко не тривиальна и содержит тонну оптимизации для уже упорядоченных массивов и таковых)

    [[0.12232, 1],[0.333, 3],[0.53434, 2],[0.99, 4]]
    

    В Ruby этот шаг: ary.sort {| x, y | x [0] < = > y [0]}

  3. Указатели копируются из нового отсортированного массива в правильное положение в исходном массиве.

    [1,3,2,4]
    

    В Ruby этот шаг: ary.map {| x, y | y}

Этот метод иногда называют « преобразованием Шварцяна ». Кэширование означает, что дорогостоящая операция выполняется не более N раз. Это очень эффективный способ рандомизации массива.

Примечание : array.shuffle! будет наиболее эффективным встроенным способом перетасовки массива (на месте), поскольку он использует современную версию Фишера-Йейтса :

static VALUE
rb_ary_shuffle_bang(VALUE ary)
{
    long i = RARRAY_LEN(ary);

    rb_ary_modify(ary);
    while (i) {
  long j = rb_genrand_real()*i;
  VALUE tmp = RARRAY_PTR(ary)[--i];
  RARRAY_PTR(ary)[i] = RARRAY_PTR(ary)[j];
  RARRAY_PTR(ary)[j] = tmp;
    }
    return ary;
}
-121--1660238-

Убедитесь, что вы используете соответствующий настраивать -march , первый выход. GCC по умолчанию не использует какие-либо команды, которые не поддерживались на исходном i386, - позволяя ему использовать новые наборы команд, может иногда иметь БОЛЬШОЕ отличие! На -march = core2 -O2 Я получаю:

min:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %edx
    movl    12(%ebp), %ecx
    movl    16(%ebp), %eax
    cmpl    %edx, %ecx
    leave
    cmovbe  %ecx, %edx
    cmpl    %eax, %edx
    cmovbe  %edx, %eax
    ret

Использование cmov здесь может помочь вам избежать ветви задержек - и вы получаете его без какого-либо встроенного asm просто путем передачи в -march . При встраивании в более крупную функцию это, вероятно, будет еще более эффективным, возможно, всего четыре операции сборки. Если вам нужно что-то быстрее, посмотрите, можно ли получить SSE векторные операции работать в контексте вашего общего алгоритма.

11
ответ дан 29 November 2019 в 23:57
поделиться

Да, пост Сборка, но моя наивная оптимизация:

static inline unsigned int
min(unsigned int a, unsigned int b, unsigned int c)
{
    unsigned int m = a;
    if (m > b) m = b;
    if (m > c) return c;
    return m;
}
0
ответ дан 29 November 2019 в 23:57
поделиться

Во-первых, посмотрите на разборку. Это много скажет вам. Например, как написано, есть 2 if-quilentments (что означает, что существует 2 возможных ошибки отделения), но я предполагаю, что приличный современный компилятор C будет иметь некоторую умную оптимизацию, которая может сделать это без ветви. Мне было бы любопытно узнать.

Во-вторых, если ваша libc имеет специальные встроенные функции min / max, используйте их. GNU LIBC имеет FMIN / FMAX для плавающей точки, например, и они утверждают, что «на некоторых процессорах этих функций могут использовать специальные инструкции по машине для выполнения этих операций быстрее, чем эквивалентный C-код». Может быть, есть что-то подобное для Uints.

Наконец, если вы делаете это с кучей чисел параллельно, возможно, существует векторные инструкции для этого, что может обеспечить значительное ускорение. Но я даже видел, что неконкуратный код будет быстрее при использовании векторных единиц. Что-то вроде «Загрузите один UINT в векторный реестр, вызов векторной мин функции, получить результат», выглядит тупой, но на самом деле может быть быстрее.

1
ответ дан 29 November 2019 в 23:57
поделиться

Вы можете попробовать что-то вроде этого, чтобы сэкономить на декларации и ненужных сравнениях:

static inline unsigned int
min(unsigned int a, unsigned int b, unsigned int c)
{ 
    if (a < b)
    {
        if (a < c) 
             return a; 
        else 
             return c;
    }

    if (b < c)
        return b;
    else return c;
}
0
ответ дан 29 November 2019 в 23:57
поделиться

Мои взять на реализацию ассемблера X86, синтаксис GCC. Должен быть тривиальным для перевода на другой встроенный монтажный синтаксис:

int inline least (int a, int b, int c)
{
  int result;
  __asm__ ("mov     %1, %0\n\t"
           "cmp     %0, %2\n\t" 
           "cmovle  %2, %0\n\t"
           "cmp     %0, %3\n\t"
           "cmovle  %3, %0\n\t" 
          : "=r"(result) : 
            "r"(a), "r"(b), "r"(c)
          );
  return result;
}

Новая и улучшенная версия:

int inline least (int a, int b, int c)
{
  __asm__ (
           "cmp     %0, %1\n\t" 
           "cmovle  %1, %0\n\t"
           "cmp     %0, %2\n\t"
           "cmovle  %2, %0\n\t" 
          : "+r"(a) : 
            "%r"(b), "r"(c)
          );
  return a;
}

Примечание: это может или не может быть быстрее, чем C-кода.

Это зависит от многих факторов. Обычно CMOv выигрывает, если ветви не предсказуются (на некоторых архитектурах X86) OTOH INLine Assembler всегда является проблемой для оптимизатора, поэтому наказание на оптимизацию для окружающего кода может быть возведена на все преимущества ..

BTW Sudhanshu, было бы интересно Чтобы услышать, как этот код выполняет с помощью TestData.

6
ответ дан 29 November 2019 в 23:57
поделиться

Это все хорошие ответы. Рискуя быть обвиненным в том, что я не ответил на вопрос, я бы посмотрел и на другие 80%. Стекшоты - мой любимый способ найти код, который стоит оптимизировать, особенно если это вызовы функций, которые, как Вы выяснили, Вам не совсем нужны.

0
ответ дан 29 November 2019 в 23:57
поделиться

Как насчет использования начального состояния...

int enter_dblquotes = 0;

%x DBLQUOTES
%%

\"  { BEGIN(DBLQUOTES); enter_dblquotes++; }

<DBLQUOTES>*\" 
{ 
   if (enter_dblquotes){
       handle_this_dblquotes(yytext); 
       BEGIN(INITIAL); /* revert back to normal */
       enter_dblquotes--; 
   } 
}
         ...more rules follow...

Он был аналогичен этому эффекту (flex использует % s или % x для указания ожидаемого состояния. Когда вход Flex обнаруживает кавычку, он переключается в другое состояние, а затем продолжает лексировать до тех пор, пока не достигнет другой кавычки, в которой он возвращается в нормальное состояние.

-121--1095784-

Sinkhole предприятия:

1: прочитайте, что доступ к базе данных должен быть на отдельном уровне

2: Эй, у нас есть уровень базы данных.

2 (b) Эй, у нас даже есть делегированный слой, чтобы абстрагироваться от нашей базы данных.

3: Примените закон утечек абстракций -i.e, так как в делегатах есть методы, которые получают вещи, просто предположим, что они там, чтобы использовать без мысли о последствиях - как в вызове «getPurchaseOrder ()» 10 раз подряд на странице, даже если getPurchaseOrder () является методом, который оборачивается 5 отдельных вызовов базы данных.

4: Сидите и наслаждайтесь своими веб-страницами, которые загружаются с 100 отдельными вызовами базы данных (к сожалению, не преувеличение).

Не уверен, что я бы назвал это антиобразцом? Может быть «Слои не свободны»?

-121--4294089-

На моем AMD Phenom этот запасной час примерно на 1,5% быстрее:

static inline unsigned int
min(unsigned int a, unsigned int b, unsigned int c)
{
    asm("cmp   %1,%0\n"
        "cmova %1,%0\n"
        "cmp   %2,%0\n"
        "cmova %2,%0\n"
        : "+r" (a) : "r" (b), "r" (c));
    return a;
}

Результаты могут варьироваться; Некоторые процессоры x86 не очень хорошо работают с CMOV.

5
ответ дан 29 November 2019 в 23:57
поделиться

Расширения инструкций SSE2 содержат целое число min обучение, которое может выбрать 8 минимумов одновременно. Смотри _mm_mulhi_epu16 в в http://www.intel.com/software/products/compilers/clin/docs/ug_cpp /comm1046.htm

5
ответ дан 29 November 2019 в 23:57
поделиться

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

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

0
ответ дан 29 November 2019 в 23:57
поделиться

Если предположить, что ваш компилятор не готов к обеду, то он должен скомпилироваться до двух сравнений и двух условных ходов. Лучше не бывает.

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

Первое, что нужно проверить, это то, что рутина на самом деле становится inlined. Компилятор не обязан это делать, и если он генерирует вызов функции, то это будет очень дорого стоить для такой простой операции.

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

Правка: Если вы хотите векторизовать код, и используете недавний процессор x86, то вы захотите использовать инструкцию SSE4.1 pminud (intrinsic: _mm_min_epu32), которая берет два вектора по четыре беззнаковых инта в каждом и производит вектор из четырех беззнаковых инта. Каждый элемент результата является минимумом соответствующих элементов в двух входах.

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

11
ответ дан 29 November 2019 в 23:57
поделиться
Другие вопросы по тегам:

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