Когда сборка происходит быстрее, чем C?

Прочитайте это и попробуйте, это поможет вам:

Таблица1

column11,column12,column13,column14

Таблица2

column21,column22,column23,column24


SELECT table1.column11,table1.column12,table2asnew1.column21,table2asnew2.column21 
FROM table1 INNER JOIN table2 AS table2asnew1 ON table1.column11=table2asnew1.column21  INNER TABLE table2 as table2asnew2 ON table1.column12=table2asnew2.column22

table2asnew1 - это пример таблицы 2, который соответствует table1.column11=table2asnew1.column21

и

table2asnew2, является еще одним экземпляром таблицы 2, который сопоставляется table1.column12=table2asnew2.column22

447
задан 6 revs, 5 users 62% 3 January 2018 в 15:58
поделиться

31 ответ

Вот пример реального мира: Фиксированная точка умножается на старых компиляторах.

Они не только прибывают удобные в устройства без плавающей точки, они сияют когда дело доходит до точности, поскольку они дают Вам 32 бита точности с предсказуемой ошибкой (плавайте, только имеет 23 бита, и более трудно предсказать потерю точности). т.е. универсальная форма абсолютный точность по всему диапазону, вместо близко к универсальной форме родственник точность (float).

<час>

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

<час>

C не имеет полного оператора умножения (2N-bit результат исходных данных N-bit). Обычный способ выразить это в C состоит в том, чтобы бросить исходные данные к более широкому типу и надеяться, что компилятор распознает, что верхние биты исходных данных не интересны:

// on a 32-bit machine, int can hold 32-bit fixed-point integers.
int inline FixedPointMul (int a, int b)
{
  long long a_long = a; // cast to 64 bit.

  long long product = a_long * b; // perform multiplication

  return (int) (product >> 16);  // shift by the fixed point bias
}

проблема с этим кодом состоит в том, что мы делаем что-то, чего нельзя непосредственно выразить на языке C. Мы хотим умножить два числа на 32 бита и получить результат на 64 бита, которого мы возвращаем средние 32 бита. Однако в C это умножается, не существует. Все, что можно сделать, должно продвинуть целые числа 64 бита и сделать 64*64 = 64 умножается.

x86 (и ARM, MIPS и другие) может однако сделать умножение в единственной инструкции. Некоторые компиляторы раньше игнорировали этот факт и генерировали код, который вызывает функцию библиотеки времени выполнения, чтобы сделать умножение. Сдвиг на 16 также часто делается библиотечной подпрограммой (также x86 может сделать такие сдвиги).

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

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

В дополнение к этому: использование ASM не является лучшим способом решить проблему. Большинство компиляторов позволяет Вам использовать некоторые ассемблерные инструкции во внутренней форме, если Вы не можете выразить их в C. ПО СРАВНЕНИЮ С NET2008 компилятором, например, выставляет 32*32=64, укусил mul как __ emul и 64 сдвига разряда как __ ll_rshift.

Используя intrinsics можно переписать функцию способом, что компилятор C имеет шанс понять то, что продолжается. Это позволяет коду быть встроенным, регистр, выделенное, общее устранение подвыражения и постоянное распространение могут быть сделаны также. Вы станете огромными повышение производительности по рукописному ассемблерному коду тот путь.

Для ссылки: конечный результат для фиксированной точки mul для компилятора VS.NET:

int inline FixedPointMul (int a, int b)
{
    return (int) __ll_rshift(__emul(a,b),16);
}

различие в производительности делений фиксированной точки еще больше. У меня были улучшения до фактора 10 для подразделения тяжелый код фиксированной точки путем записи нескольких asm-строк.

<час>

Используя Visual C++ 2013 дает тот же ассемблерный код для обоих путей.

gcc4.1 с 2007 также оптимизирует чистую версию C приятно. (Проводник компилятора Godbolt не имеет никаких более ранних версий gcc установленными, но по-видимому еще более старые версии GCC могли сделать это без intrinsics.)

Посмотрите источник + asm для (32-разрядного) x86 и ARM на [1 120] проводник компилятора Godbolt . (К сожалению, это не имеет никаких компиляторов достаточно взрослыми для создания плохого кода из простой чистой версии C.)

<час>

современные центральные процессоры могут сделать, вещи C не имеют операторов для [1 128] во всем , как popcnt или разрядное сканирование, чтобы найти, что первый или последний набор укусил . (POSIX имеет ffs() функция, но ее семантика не соответствует x86 bsf / bsr. См. https://en.wikipedia.org/wiki/Find_first_set).

Некоторые компиляторы могут иногда распознавать цикл, который считает число битов набора в целом числе, и скомпилируйте его в popcnt инструкция (если включено во время компиляции), но это намного более надежно для использования __builtin_popcnt в GNU C, или на x86, если Вы только нацелены на аппаратные средства с SSE4.2: _mm_popcnt_u32 от [1 112] .

Или в C++, присвойте std::bitset<32> и используйте .count(). (Это - случай, где язык нашел способ портативно выставить оптимизированную реализацию popcount через стандартную библиотеку, способом который будет всегда компилировать во что-то корректное, и может использовать в своих интересах то, что поддерживает цель.) См. также https://en.wikipedia.org/wiki/Hamming_weight#Language_support.

Точно так же ntohl может скомпилировать в [1 116] (x86 32-разрядная подкачка байта для преобразования порядка байтов) на некоторых реализациях C, которые имеют его.

<час>

Другой главной областью для intrinsics или рукописного asm является ручная векторизация с инструкциями SIMD. Компиляторы не плохи с простыми циклами как [1 117], но часто имеют проблемы или не автовекторизуют вообще, когда вещи становятся более сложными. Например, Вы вряд ли получите что-нибудь как [1 124], Как реализовать atoi, использующий SIMD? сгенерированный автоматически компилятором из скалярного кода.

259
ответ дан 6 revs, 6 users 61% 4 January 2018 в 01:58
поделиться
  • 1
    @User789 можно хотеть проверить Lo-Dash.js или Lazy.js для сравнения производительности. Underscore будет использовать собственную функцию карты от браузера на мобильном устройстве. Я думаю для циклов, будет всегда превосходить по характеристикам, но I' m не уверенный в этом для мобильного телефона. – Pete 7 April 2014 в 17:15

практическое руководство блока Linux , задает этот вопрос и дает за и против использования блока.

5
ответ дан pseudosaint 4 January 2018 в 01:58
поделиться

Одна из возможностей к CP/M-86 версия Поли-Паскаля (одноуровневый элемент к Turbo Pascal) состояла в том, чтобы заменить "use-bios-to-output-characters-to-the-screen" средство стандартной программой машинного языка, которой в сущности дали x, и y и строку для помещения там.

Это позволило обновлять экран очень, намного быстрее, чем прежде!

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

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

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

4
ответ дан Thorbjørn Ravn Andersen 4 January 2018 в 01:58
поделиться

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

0
ответ дан webclimber 4 January 2018 в 01:58
поделиться

На это очень трудно ответить а именно, потому что вопрос очень неконкретен: что такое точно "современный компилятор"?

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

1
ответ дан Michael Borgwardt 4 January 2018 в 01:58
поделиться

Один из более известных отрывков блока от цикла отображения текстур Michael Abrash (, экс-причинил боль подробно здесь ):

add edx,[DeltaVFrac] ; add in dVFrac
sbb ebp,ebp ; store carry
mov [edi],al ; write pixel n
mov al,[esi] ; fetch pixel n+1
add ecx,ebx ; add in dUFrac
adc esi,[4*ebp + UVStepVCarry]; add in steps

В наше время большая часть экспресса компиляторов усовершенствовала конкретные инструкции ЦП как intrinsics, т.е. функции, которые компилируются вниз в фактическую инструкцию. Visual C++ MS поддерживает intrinsics для MMX, SSE, SSE2, SSE3 и SSE4, таким образом, необходимо волноваться меньше о раскрытии к блоку для использования в своих интересах конкретных инструкций платформы. Visual C++ может также использовать в своих интересах фактическую архитектуру, для которой Вы нацелены с соответствующей установкой ARCH/.

4
ответ дан MSN 4 January 2018 в 01:58
поделиться

http://cr.yp.to/qhasm.html имеет много примеров.

4
ответ дан Vincent 4 January 2018 в 01:58
поделиться

На это могло бы стоить посмотреть Неизменная Оптимизация и Чистота Walter Bright , это не представленный тест, но показывает Вам, один хороший пример различия между рукописным и компилятором генерировал ASM. Walter Bright пишет оптимизирующие компиляторы, таким образом, могло бы стоить посмотреть на его другие сообщения в блоге.

6
ответ дан 2 revs, 2 users 80% 4 January 2018 в 01:58
поделиться

Учитывая правильного программиста, Ассемблерные программы могут всегда делаться быстрее, чем их дубликаты C (по крайней мере незначительно). Было бы трудно создать программу C, где Вы не могли вынуть по крайней мере одну инструкцию Ассемблера.

4
ответ дан Beep beep 4 January 2018 в 01:58
поделиться

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

Для максимизации вычислительной мощности современного ЦП с несколькими конвейерами и прогнозирующим ветвлением необходимо структурировать программу сборки способом, которая делает это a) почти невозможный для человека записать b) еще более невозможный поддержать.

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

41
ответ дан 4 revs, 3 users 75% 4 January 2018 в 01:58
поделиться

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

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

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

    for (int y=0; y < imageHeight; y++) {
        for (int x=0; x < imageWidth; x++) {
           // do something
        }
    }
    

    "делают что-то, что часть" обычно происходит на порядке нескольких миллионов раз (т.е., между 3 и 30). Путем очистки циклов в тот "делают что-то" фаза, увеличение производительности чрезвычайно увеличено. Я обычно не запускаю там - я обычно запускаю путем написания кода, чтобы работать сначала, затем приложить все усилия для рефакторинга C, чтобы быть естественно лучше (лучший алгоритм, меньше загрузки в цикле и т.д.). Я обычно должен читать блок для наблюдения то, что продолжается и редко должно писать это. Я делаю это, возможно, каждые два или три месяца.

  3. выполнение чего-то язык не позволит мне. Они включают - получение архитектуры процессора и определенных функций процессора, доступ к флагам не в ЦП (человек, мне действительно жаль, что C не предоставил Вам доступ к флагу переноса), и т.д. Я делаю это, возможно, один раз в год или два года.

46
ответ дан 3 revs, 3 users 83% 4 January 2018 в 01:58
поделиться

Вариант использования, который не мог бы больше применяться, но для Вашего удовольствия компьютерного фаната: На Amiga ЦП и графические/аудио микросхемы боролись бы за доступ к определенной области RAM (первые 2 МБ RAM, чтобы быть конкретными). Таким образом, когда у Вас было только 2 МБ RAM (или меньше), отображение сложной графики плюс проигрывание звука уничтожит производительность ЦП.

В ассемблере, Вы могли чередовать свой код таким умным способом, которым ЦП только попытается получить доступ к RAM, когда графические/аудио микросхемы были заняты внутренне (т.е. когда шина была свободна). Таким образом путем переупорядочения инструкций, умного использования кэша ЦП, синхронизации шины, Вы могли достигнуть некоторых эффектов, которые были просто не возможным использованием любого высокоуровневого языка, потому что у Вас была ко времени каждая команда, даже вставьте NOPs тут и там для хранения различных микросхем от каждого радара других.

, Который является другой причиной, почему NOP (Никакая Операция - ничего не делают) инструкция ЦП может на самом деле сделать Ваше целое выполнение приложения быстрее.

[РЕДАКТИРОВАНИЕ], Конечно, техника зависит от определенной настройки оборудования. Который был главной причиной, почему много игр Amiga не могли справиться с более быстрыми центральными процессорами: синхронизация инструкций была выключена.

15
ответ дан 2 revs 4 January 2018 в 01:58
поделиться
  • 1
    После использования обновление компоновщика для получения более новой версии начальной загрузки я должен перекопировать их снова право?, я думал, что мог извлечь некоторую автоматическую пользу из использования компоновщика. – Artisan 1 October 2013 в 14:37

Хотя C "близок" к управлению низкого уровня 8-разрядными, 16-разрядными, 32-разрядными, 64-разрядными данными, существует несколько математических операций, не поддерживаемых C, который может часто выполняться изящно в определенных наборах инструкции по сборке:

  1. умножение Фиксированной точки: продуктом двух 16-разрядных чисел является 32-разрядное число. Но в правилах в C говорится, что продуктом двух 16-разрядных чисел является 16-разрядное число, и продуктом двух 32-разрядных чисел является 32-разрядное число - нижняя половина в обоих случаях. Если Вы хотите вершина , половина 16x16 умножается, или 32x32 умножаются, необходимо играть в игры с компилятором. Общий метод состоит в том, чтобы бросить к большей-,-чем-необходимый разрядной ширине, умножиться, сдвиг вниз, и вспомнить:

    int16_t x, y;
    // int16_t is a typedef for "short"
    // set x and y to something
    int16_t prod = (int16_t)(((int32_t)x*y)>>16);`
    

    В этом случае компилятор может быть достаточно умным, чтобы знать, что Вы действительно просто пытаетесь добраться, верхняя половина 16x16 умножают и делают правильную вещь с собственным компонентом машины 16x16multiply. Или это может быть глупо и потребовать, чтобы вызов библиотеки, чтобы сделать 32x32 умножился, это - путь излишество, потому что Вам только нужны 16 битов продукта - но стандарт C не дает Вам способа выразиться.

  2. Certain bitshifting операции (вращение/нести):

    // 256-bit array shifted right in its entirety:
    uint8_t x[32];
    for (int i = 32; --i > 0; )
    {
       x[i] = (x[i] >> 1) | (x[i-1] << 7);
    }
    x[0] >>= 1;
    

    Это не слишком неэлегантно в C, но снова, если компилятор не достаточно умен для понимания то, что Вы делаете, он собирается сделать большую "ненужную" работу. Много наборов инструкции по сборке позволяют Вам вращаться или смещаться слева/справа с результатом в регистре переноса, таким образом, Вы могли выполнить вышеупомянутое в 34 инструкциях: загрузите указатель на начало массива, очистите перенос и выполните 32 8-разрядных сдвига вправо, с помощью автоинкремента на указателе.

    Для другого примера, существуют линейные сдвиговые регистры обратной связи (LFSR), которые изящно выполняются в блоке: Возьмите блок битов N (8, 16, 32, 64, 128, и т.д.), сместите все это прямо на 1 (см. выше алгоритма), затем если получающийся перенос равняется 1 затем Вы XOR в небольшом шаблоне, который представляет многочлен.

Однако я не обратился бы к этим методам, если у меня не было серьезных ограничений производительности. Как другие сказали, блок намного более трудно зарегистрировать/отладить/тестировать/поддержать, чем код C: увеличение производительности идет с некоторыми серьезными затратами.

редактирование: 3. Водосливное обнаружение возможно в блоке (не может действительно сделать этого в C), это делает некоторые алгоритмы намного легче.

38
ответ дан 2 revs 4 January 2018 в 01:58
поделиться

В значительной степени каждый раз, когда компилятор видит код с плавающей точкой, рукописная версия будет более быстрой. Основная причина - то, что компилятор не может выполнить устойчивые оптимизации. См. эту статью от MSDN для обсуждения предмета. Вот пример, где версия блока является дважды скоростью как версией C (скомпилированный с VS2K5):

#include "stdafx.h"
#include <windows.h>

float KahanSum
(
  const float *data,
  int n
)
{
   float
     sum = 0.0f,
     C = 0.0f,
     Y,
     T;

   for (int i = 0 ; i < n ; ++i)
   {
      Y = *data++ - C;
      T = sum + Y;
      C = T - sum - Y;
      sum = T;
   }

   return sum;
}

float AsmSum
(
  const float *data,
  int n
)
{
  float
    result = 0.0f;

  _asm
  {
    mov esi,data
    mov ecx,n
    fldz
    fldz
l1:
    fsubr [esi]
    add esi,4
    fld st(0)
    fadd st(0),st(2)
    fld st(0)
    fsub st(0),st(3)
    fsub st(0),st(2)
    fstp st(2)
    fstp st(2)
    loop l1
    fstp result
    fstp result
  }

  return result;
}

int main (int, char **)
{
  int
    count = 1000000;

  float
    *source = new float [count];

  for (int i = 0 ; i < count ; ++i)
  {
    source [i] = static_cast <float> (rand ()) / static_cast <float> (RAND_MAX);
  }

  LARGE_INTEGER
    start,
    mid,
    end;

  float
    sum1 = 0.0f,
    sum2 = 0.0f;

  QueryPerformanceCounter (&start);

  sum1 = KahanSum (source, count);

  QueryPerformanceCounter (&mid);

  sum2 = AsmSum (source, count);

  QueryPerformanceCounter (&end);

  cout << "  C code: " << sum1 << " in " << (mid.QuadPart - start.QuadPart) << endl;
  cout << "asm code: " << sum2 << " in " << (end.QuadPart - mid.QuadPart) << endl;

  return 0;
}

И некоторые числа от моего ПК, выполняющего сборку конечных версий по умолчанию <глоток> * :

  C code: 500137 in 103884668
asm code: 500137 in 52129147

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

Возгласы, я выполнял немного отличающуюся версию кода, и он произвел числа наоборот (т.е. C был быстрее!). Зафиксированный и обновленный результаты.

62
ответ дан 4 revs, 2 users 99% 4 January 2018 в 01:58
поделиться
  • 1
    Почему не только ссылка символа папка поставщика для установленной библиотеки от общественности... Тогда Ваше использование его как ссылка по сравнению с копированием. – LeviXC 30 March 2014 в 18:26

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

  • Вы могли отклониться от соглашений о вызовах, передающих аргументов в регистрах.

  • Вы могли тщательно рассмотреть, как использовать регистры и постараться не хранить переменные в памяти.

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

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

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

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

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

Добавленный: я видел много приложений, записанных в ассемблере и основном преимуществе скорости перед языком как C, Паскаль, Фортран, и т.д. был то, потому что программист был намного более осторожным при кодировании в ассемблере. Он собирается записать примерно 100 строк кода в день, независимо от языка, и на языке компилятора это собирается равняться 3 или 400 инструкциям.

13
ответ дан 3 revs 4 January 2018 в 01:58
поделиться

Укажите на тот, который не является ответом.
, Даже если Вы никогда не программируете в нем, я нахожу полезным знать по крайней мере одну ассемблерную систему команд. Это - часть программистов бесконечные поиски, чтобы знать больше и поэтому быть лучше. Также полезный при продвижении в платформы у Вас нет исходного кода к и имеющий, по крайней мере, общее представление, что продолжается. Это также помогает Вам понять JavaByteCode и.Net IL, поскольку они оба подобны ассемблеру.

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

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

Мой друг работает над микро контроллерами, в настоящее время микросхемы для управления маленькими электродвигателями. Он работает в комбинации низкого уровня c и блока. Он когда-то сказал мне о хорошем дне на работе, где он уменьшил основной цикл с 48 инструкций до 43. Он также сталкивается с выбором как код, вырос для заполнения 256k микросхемы, и бизнес желает новую возможность, сделайте Вас

  1. , Удаляют существующую функцию
  2. , Уменьшают размер некоторых или все существующие функции, возможно, за счет производительности.
  3. Защитник, перемещающийся в большую микросхему с более высокой стоимостью, более высокую потребляемую мощность и больший форм-фактор.

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

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

так позволяет попытке еще раз, необходимо думать о блоке

  • работа над низкоуровневой функцией операционной системы
  • Работа над компилятором.
  • Работа над чрезвычайно ограниченной микросхемой, встроенная система и т.д.

Не забывает сравнивать Ваш блок с компилятором, сгенерированным для наблюдения, который быстрее/меньше/лучше.

David.

15
ответ дан 3 revs, 2 users 97% 4 January 2018 в 01:58
поделиться

Не давая определенного примера или доказательства профилировщика, можно записать лучший ассемблер, чем компилятор, когда Вы знаете больше, чем компилятор.

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

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

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

56
ответ дан Liedman 4 January 2018 в 01:58
поделиться
  • 1
    ... или php artisan asset:publish --path="vendor/twitter/bootstrap/dist/" bootstrap – madpoet 11 March 2014 в 18:46

Короткий ответ? Иногда.

Технически каждая абстракция имеет стоимость, и язык программирования является абстракцией для того, как ЦП работает. C однако очень близко. Несколько лет назад я не забываю смеяться вслух, когда я вошел в систему своя учетная запись UNIX и получил следующее сообщение состояния (когда такие вещи были популярны):

Язык программирования C - язык, который комбинирует гибкость ассемблера с питанием ассемблера.

Это забавно, потому что это верно: C похож на портативный ассемблер.

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

то, Когда gcc вышел на сцену одна из вещей, которые сделали его столь популярным, было то, что это было часто настолько лучше, чем компиляторы C, которые поставлялись со многими коммерческими ароматами UNIX. Не только было это ANSI C (ни один из этого K& R C мусор), был больше устойчивое и обычно производило лучше (более быстрый) код. Не всегда, но часто.

я говорю Вам все это, потому что нет никакой всеохватывающей нормы о скорости C и ассемблера, потому что нет никакого объективного стандарта для C.

Аналогично, ассемблер варьируется много, в зависимости от какого процессора Вы работаете, Ваша системная спецификация, какую систему команд Вы используете и так далее. Исторически было два семейства архитектур ЦП: CISC и RISC. Крупнейший плеер в CISC был и все еще является архитектурой Intel x86 (и система команд). RISC доминировал над миром UNIX (MIPS6000, Альфа, Sparc и так далее). CISC выиграл сражение за основы и умы.

Так или иначе, популярная мудрость, когда я был младшим разработчиком, была то, что рукописный x86 мог часто быть намного быстрее, чем C, потому что способ, которым работала архитектура, она имела сложность, которая извлекла выгоду от человека, делающего ее. RISC, с другой стороны, казался разработанным для компиляторов так никто (я знал), записал, говорит ассемблер Sparc. Я уверен, что такие люди существовали, но несомненно они и сошли с ума и были узаконены к настоящему времени.

Системы команд являются важным моментом даже в том же семействе процессоров. Определенные процессоры Intel имеют расширения как SSE через SSE4. AMD имел их собственные инструкции SIMD. Преимущество языка программирования как C было кем-то, мог записать их библиотеку, таким образом, это было оптимизировано для того, какой бы ни процессор Вы работали. Это было тяжелой работой в ассемблере.

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

В конечном счете, хотя ассемблер был продуктом своего времени и был более популярным в то время, когда циклы ЦП были дорогими. В наше время ЦП, который стоит $5-10 для производства (Intel Atom), может сделать в значительной степени что-либо, что любой мог хотеть. Единственная настоящая причина для записи ассемблера в эти дни для низкоуровневых вещей как некоторые части операционной системы (несмотря на это, подавляющее большинство ядра Linux записано в C), драйверы устройств, возможно встроенные устройства (хотя C имеет тенденцию доминировать там также), и так далее. Или только для ударов (который является несколько мазохистским).

23
ответ дан 2 revs, 2 users 96% 4 January 2018 в 01:58
поделиться

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

8
ответ дан Doug T. 4 January 2018 в 01:58
поделиться

Все это зависит от Вашей рабочей нагрузки.

Для повседневной эксплуатации, C и C++ очень хорошо, но существуют определенные рабочие нагрузки (любой преобразовывает видео вовлечения (сжатие, распаковка, эффекты изображений, и т.д.)), которые в значительной степени требуют сборки, чтобы быть производительными.

Они также обычно включают использование ЦП определенные расширения чипсета (MME/MMX/SSE/whatever), которые настраиваются для тех видов операции.

7
ответ дан Larry Osterman 4 January 2018 в 01:58
поделиться

Вы на самом деле не знаете, действительно ли Ваш правильно написанный код C быстр, если Вы не посмотрели на дизассемблирование того, что производит компилятор. Много раз Вы смотрите на него и видите, что "правильно написанный" было субъективно.

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

9
ответ дан 2 revs 4 January 2018 в 01:58
поделиться

Операции над матрицей с помощью инструкций SIMD, вероятно, быстрее, чем компилятор сгенерировал код.

14
ответ дан Mehrdad Afshari 4 January 2018 в 01:58
поделиться

Жесткие циклы, как при проигрывании с изображениями, начиная с изображения могут cosist миллионов пикселей. Присаживание и выяснение, как лучше всего использовать ограниченное количество регистров процессора, могут иметь значение. Вот реальный образец:

http://danbystrom.se/2008/12/22/optimizing-away-ii/

Затем часто процессоры имеют некоторые тайные инструкции, которые слишком специализированы, чтобы компилятор обеспокоился, но при случае ассемблерный программист может хорошо использовать их. Возьмите инструкцию XLAT, например. Действительно большой, если необходимо сделать поиск по таблице в цикле и , таблица ограничена 256 байтами!

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

10
ответ дан 3 revs, 2 users 94% 4 January 2018 в 01:58
поделиться

Много лет назад я преподавал кого-то к программе в C. Осуществление должно было повернуть диаграмму через 90 градусов. Он возвратился с решением, которое заняло несколько минут для завершения, главным образом потому что он использовал, умножается и делится и т.д.

, я показал ему, как переделать проблему с помощью сдвигов разряда, и время для обработки свелось приблизительно к 30 секундам на неоптимизирующем компиляторе, который он имел.

я только что получил оптимизирующий компилятор, и тот же код повернул диаграмму в < 5 секунд. Я посмотрел на ассемблерный код, который компилятор генерировал, и от того, что я видел решенный тут же, что мои дни записи ассемблера были закончены.

134
ответ дан 4 revs, 4 users 56% 4 January 2018 в 01:58
поделиться

В течение многих дней, где скорость процессора измерялась в МГц и размере экрана, был ниже 1 мегапикселя, известный прием, чтобы иметь более быстрый дисплей должен был развернуть циклы: операция записи для каждой строки развертки экрана. Это избежало наверху поддержания индекса цикла! Вместе с обнаружением экранного обновления это было довольно эффективно.
Это - что-то, что компилятор C не сделал бы... (хотя часто можно выбрать между оптимизацией для скорости или для размера, я предполагаю бывшее использование некоторые подобные приемы.)

я знаю, что некоторые люди любят писать Приложения Windows в ассемблере. Они утверждают, что они быстрее (трудно для доказательства) и меньший (действительно!).
, Очевидно, в то время как это интересно сделать, это, вероятно, напрасно тратится время (за исключением изучения цели, конечно!), особенно для операций GUI... Теперь, возможно, некоторые операции, как поиск строки в файле, могут быть оптимизированы тщательно записанным ассемблерным кодом.

1
ответ дан PhiLho 4 January 2018 в 01:58
поделиться

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

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

-3
ответ дан Cory R. King 4 January 2018 в 01:58
поделиться

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

4
ответ дан 22 November 2019 в 22:57
поделиться

У меня есть операция транспонирования битов, которая должна быть выполнена на 192 или 256 бит при каждом прерывании, которая происходит каждые 50 микросекунд.

Это происходит с помощью фиксированной карты (аппаратные ограничения ). На создание C потребовалось около 10 микросекунд. Когда я перевел это на Ассемблер, принимая во внимание специфические особенности этой карты, специфическое кэширование регистров и использование битовых операций; на выполнение потребовалось менее 3,5 микросекунд.

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

Несколько примеров из моего опыта:

  • Доступ к инструкциям, которые недоступны из C. Например, многие архитектуры (например, x86-64, IA-64, DEC Alpha и 64-битный MIPS или PowerPC) поддерживают 64-битное умножение на 64-битное, дающее 128-битный результат. GCC недавно добавил расширение, обеспечивающее доступ к таким инструкциям, но до этого требовалась сборка. И доступ к этой инструкции может иметь огромное значение для 64-битных ЦП при реализации чего-то вроде RSA - иногда повышение производительности в 4 раза

  • Доступ к специфичным для ЦП флагам. То, что меня сильно укусило, - это флаг переноса; при выполнении сложения с множественной точностью, если у вас нет доступа к биту переноса ЦП, нужно вместо этого сравнить результат, чтобы увидеть, не переполнен ли он, что требует еще 3-5 инструкций на каждую конечность; и того хуже, которые довольно последовательны с точки зрения доступа к данным, что убивает производительность на современных суперскалярных процессорах. При обработке тысяч таких целых чисел подряд возможность использования addc является огромным преимуществом (существуют суперскалярные проблемы с конкуренцией за бит переноса, но современные процессоры справляются с этим довольно хорошо)

  • SIMD. Даже компиляторы с автовекторизацией могут выполнять только относительно простые случаи, поэтому, если вам нужна хорошая производительность SIMD, к сожалению, часто необходимо писать код напрямую. Конечно, вы можете использовать встроенные функции вместо сборки, но как только вы перейдете на уровень встроенных функций, вы все равно будете писать сборку, просто используя компилятор в качестве распределителя регистров и (номинально) планировщика инструкций. (Я обычно использую встроенные функции для SIMD просто потому, что компилятор может генерировать прологи функций и еще много чего для меня, поэтому я могу использовать тот же код в Linux, OS X и Windows, не имея дело с проблемами ABI, такими как соглашения о вызове функций, но другие чем то, что встроенные функции SSE на самом деле не очень хорошие - Altivec кажутся лучше, хотя у меня нет большого опыта с ними). В качестве примеров того, что (нынешний) векторизующий компилятор не может понять, прочтите о битовом разрезе AES или исправлении ошибок SIMD - можно представить компилятор, который мог бы анализировать алгоритмы и генерировать такие кода, но мне кажется, что такой умный компилятор находится по крайней мере на 30 лет от существующего (в лучшем случае).

С другой стороны, многоядерные машины и распределенные системы сдвинули многие из самых больших достижений производительности в другом направлении. - получите дополнительное ускорение на 20% при написании ваших внутренних циклов в сборке, или на 300%, запустив их на нескольких ядрах, или на 10000%, запустив их на кластере машин. И, конечно же, высокоуровневые оптимизации (такие как фьючерсы, мемоизация и т. Д.) Часто намного проще выполнить на языке более высокого уровня, таком как ML или Scala, чем на C или asm, и часто может обеспечить гораздо больший выигрыш в производительности. Так что, как всегда, приходится идти на компромисс.

12
ответ дан 22 November 2019 в 22:57
поделиться

The simple answer... One who knows assembly well (aka has the reference beside him, and is taking advantage of every little processor cache and pipeline feature etc) is guaranteed to be capable of producing much faster code than any compiler.

However the difference these days just doesn't matter in the typical application.

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

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