programing

언제 조립이 C보다 빠릅니까?

prostudy 2022. 5. 20. 21:38
반응형

언제 조립이 C보다 빠릅니까?

조립자를 아는 이유 중 하나는 때때로 더 높은 수준의 언어, 특히 C로 코드를 작성하는 것보다 더 성능이 좋은 코드를 작성하는 것이 고용될 수 있기 때문이다.하지만, 나는 그것이 완전히 거짓은 아니지만, 조립자가 실제로 더 많은 수행 코드를 생성하기 위해 사용될 수 있는 경우는 극히 드물고, 조립에 대한 전문적인 지식과 경험이 요구된다는 것을 여러 번 들었다.

이 질문은 조립자 지침서가 기계에 특유하고 휴대할 수 없는 것이거나 또는 조립자의 다른 어떤 측면일 것이라는 사실에도 들어가지 않는다.물론 이 것 말고도 어셈블리를 아는 데는 충분한 이유가 있지만, 이것은 어셈블러 대 상위 언어에 대한 확장된 담론이 아니라 사례와 데이터를 구하려는 구체적인 질문이라는 뜻이다.

현대 컴파일러를 사용하여 잘 작성된 C 코드보다 조립이 더 빠른 사례의 구체적인 예를 제공할 수 있는 사람이 있는가? 그리고 프로파일링 증거로 그 주장을 뒷받침할 수 있는가?나는 이 사건들이 존재한다고 확신하지만, 이 사건들이 어느 정도 논쟁거리가 될 것 같기 때문에 나는 이 사건들이 얼마나 난해한지 정확히 알고 싶다.

여기 실제 세계의 예가 있다: 오래된 컴파일러에 고정된 점의 곱하기.

이는 부동소수점이 없는 기기에서만 사용할 수 있는 것이 아니라 예측 가능한 오류로 32비트의 정밀도를 제공하므로 정밀도에 있어서는 빛난다(플로트는 23비트에 불과하고 정밀 손실은 예측하기 어렵다).즉, 전체 범위에 걸쳐 근접상대 정밀도 대신 균일한 절대 정밀도(behal expect 정밀도)float).


최신 컴파일러는 이 고정 지점 예를 잘 최적화하므로, 아직 컴파일러별 코드가 필요한 더 현대적인 예는 다음을 참조하십시오.

  • 64비트 정수 곱하기의 높은 부분을 얻는 방법:다음을 사용하는 휴대용 버전uint64_t32x32 => 64비트 곱셈이 64비트 CPU에서 최적화되지 못하므로 본질적인 요소 또는__int12864비트 시스템에서 효율적인 코드를 제공하십시오.
  • _Windows 32비트의 umul128: MSVC가 32비트 정수를 64로 곱할 때 항상 좋은 일을 하는 것은 아니기 때문에 본질적인 것이 많은 도움이 되었다.

C에는 풀 곱셈 연산자가 없다(N비트 입력에서 2N비트 결과).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로 표현할 수 없는 경우에 어떤 조립자 지시사항을 본질적인 형태로 사용할 수 있게 해준다.VS.예를 들어, NET2008 컴파일러는 32*32=64비트 mul을 __emul로, 64비트 shift를 _ll_rshift로 표시한다.

본질을 사용하면 C-컴파일러가 상황을 이해할 수 있는 기회를 갖는 방식으로 기능을 다시 작성할 수 있다.이것은 코드를 삽입할 수 있게 하고, 레지스터를 할당하며, 공통적인 하위표현 제거와 지속적인 전파도 할 수 있다.그렇게 하면 손으로 쓴 조립자 코드보다 성능이 크게 향상될 것이다.

참조용:VS에 대한 고정 지점 멀의 최종 결과.NET 컴파일러:

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

고정점 격차의 성능 차이는 더욱 크다.나는 두 줄의 asm-line을 써서 분할 중고정점 코드에 대한 요소 10까지 개선되었다.


Visual C++ 2013을 사용하면 두 가지 방법으로 동일한 어셈블리 코드를 얻을 수 있다.

2007년부터 gcc4.1은 또한 순수 C 버전을 잘 최적화한다. (Godbolt 컴파일러 탐색기는 어떤 이전 버전의 gcc도 설치되어 있지 않지만 아마도 더 오래된 GCC 버전도 본질 없이 이것을 할 수 있을 것이다.)

godbolt 컴파일러 탐색기에서 x86(32비트)에 대한 소스 + asm과 ARM을 참조하십시오. (불행히도 단순한 순수 C 버전에서 불량 코드를 생성할 수 있을 만큼 오래된 컴파일러가 없다.)


현대의 CPU는 C가 연산자가 전혀 없는 것을 할 수 있다. 예를 들어, 비트 스캔을 통해 첫 번째 또는 마지막 세트 비트를 찾을있다. (POSIX에는 다음과 같은 기능이 있다.)ffs()함수, 그러나 의미론은 x86과 일치하지 않는다.bsf/bsrhttps://en.wikipedia.org/wiki/Find_first_set)을 참조하십시오.

어떤 컴파일러는 때때로 정수의 세트 비트 수를 세어 a로 컴파일하는 루프를 인식할 수 있다.popcnt명령어(컴파일 시간에 활성화되어 있는 경우)를 사용해도 훨씬 신뢰할 수 있음__builtin_popcntGNU C에서 또는 SSE4.2가 설치된 하드웨어만 대상으로 하는 경우 x86에서: from.

또는 C++에서 에 할당std::bitset<32>사용하다.count()(이것은 언어가 표준 라이브러리를 통해 팝카운트의 최적화된 구현을 항상 올바른 것으로 컴파일하고 대상이 지원하는 모든 것을 활용할 수 있는 방법을 현저히 노출하는 방법을 찾은 경우)https://en.wikipedia.org/wiki/Hamming_weight#Language_support을 참조하십시오.

마찬가지로ntohl에 컴파일할 수 있다.bswap(엔디안 변환을 위한 x86 32비트 바이트 스왑)이 있는 일부 C 구현의 경우.


본질 또는 손으로 작성한 asm의 또 다른 주요 영역은 SIMD 지침에 의한 수동 벡터화다.컴파일러는 다음과 같은 간단한 루프에 나쁘지 않다.dst[i] += src[i] * 10.0;그러나 일이 더 복잡해질 때 종종 나쁜 행동을 하거나 아예 자동화를 하지 않는다.예를 들어, 스칼라 코드에서 컴파일러에 의해 자동으로 생성된 SIMD를 사용하여 아토이 구현 방법 같은 것을 얻을 수 없을 것이다.

수년 전에 나는 C에서 프로그램을 짜는 것을 가르치고 있었다.그 연습은 그래픽을 90도 회전시키는 것이었다.주로 곱셈과 나누기 등을 사용하고 있었기 때문에 완성하는 데 몇 분이 걸리는 해답을 가지고 돌아왔다.

비트 시프트를 이용해 문제를 재추적하는 방법을 보여줬더니, 그가 가지고 있는 최적화되지 않은 컴파일러에서 처리 시간이 30초 정도로 줄었다.

나는 방금 최적화 컴파일러를 받았는데 같은 코드가 < 5초 안에 그래픽을 회전시켰다.나는 컴파일러가 만들고 있는 조립 코드를 살펴봤고, 내가 본 바로는 거기서 결정했고, 그리고 나서 조립자를 쓰는 나의 시대는 끝났다는 것을 알았다.

구체적인 예시나 프로파일러 증거를 제시하지 않고 컴파일러보다 더 많은 것을 알고 있을 때 컴파일러보다 더 나은 조립자를 쓸 수 있다.

일반적인 경우, 현대의 C 컴파일러는 문제의 코드를 최적화하는 방법에 대해 훨씬 더 잘 알고 있다: 프로세서 파이프라인이 어떻게 작동하는지 알고 있고, 사람이 할 수 있는 것보다 더 빨리 명령을 재주문하려고 할 수 있다. 등등- 단순히 그것이 보드게임에 가장 적합한 사람보다 컴퓨터가 더 훌륭하거나 더 나은 것과 기본적으로 같다.n은 대부분의 인간보다 문제 공간에서의 검색을 더 빨리 만든다.비록 이론적으로 특정한 경우에 컴퓨터만큼 잘 할 수 있지만, 당신은 확실히 같은 속도로 그것을 할 수 없기 때문에, 몇 가지 이상의 경우에 그것을 실행할 수 없게 된다(즉, 당신이 조립자에서 몇 가지 이상의 루틴을 쓰려고 하면 컴파일러가 확실히 당신을 능가할 것이다).

반면에, 컴파일러가 많은 정보를 가지고 있지 않은 경우도 있다 - 나는 주로 컴파일러가 지식이 없는 다른 형태의 외부 하드웨어로 작업할 때 말한다.주된 예는 아마도 장치 드라이버일 것이다. 여기서 조립자는 해당 하드웨어에 대한 인간의 친밀한 지식과 결합되어 C 컴파일러가 할 수 있는 것보다 더 나은 결과를 산출할 수 있다.

다른 사람들은 특수 목적 지침을 언급했는데, 그것은 내가 위의 단락에서 말하고 있는 것인데, 컴파일러가 제한적이거나 전혀 알지 못할 수도 있는 지시사항으로, 인간이 더 빠른 코드를 쓸 수 있게 해준다.

내 직업에는 내가 알고 조립을 사용해야 하는 세 가지 이유가 있다.중요도 순서에 따라:

  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. 언어가 허락하지 않는 무언가를 하는 것.여기에는 - 프로세서 아키텍처 및 특정 프로세서 기능 가져오기, CPU에 없는 플래그 액세스(인간, C가 운반 플래그에 대한 액세스 권한을 줬으면 좋았을 텐데) 등이 포함된다.나는 아마 일 년에 한 번 또는 2년 정도 이것을 한다.

컴파일러가 부동 소수점 코드를 볼 때마다 오래된 불량 컴파일러를 사용할 경우 손으로 쓴 버전이 더 빠를 것이다.(2019년 업데이트: 이것은 현대 컴파일러들에게 일반적으로 사실이 아니다.특히 x87 이외의 항목에 대해 컴파일할 때 컴파일러는 스칼라 산술용 SSE2 또는 AVX 또는 x87의 레지스터 스택과 달리 플랫 FP 레지스터가 설정된 비 x86으로 더 쉬운 시간을 갖는다.)

주된 이유는 컴파일러가 어떤 강력한 최적화를 수행할 수 없기 때문이다.이 주제에 대한 논의를 위해서는 MSDN의 이 기사를 참조하십시오.다음은 어셈블리 버전이 C 버전보다 2배 빠른 예(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;
}

기본 릴리스 빌드를* 실행하는 내 PC의 일부 번호:

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

흥미로워서, 나는 루프를 dec/jnz로 바꾸었고 그것은 타이밍에 아무런 차이가 없었다 - 때로는 더 빠르고 때로는 더 느리다.메모리 제한적인 측면이 다른 최적화를 방해하는 것 같다. (편집자 주: FP 지연 병목현상은 추가 비용을 숨기기에 충분하다.loop. 홀수/짝수 원소에 대해 두 개의 카한 합계를 병렬로 수행하고 마지막에 합계를 추가하면 아마도 2배 정도 속도를 높일 수 있을 것이다.)

웁스, 나는 약간 다른 버전의 코드를 실행 중이었는데, 그것은 잘못된 방식으로 숫자를 출력했다(즉, C가 더 빨랐다!).결과 수정 및 업데이트.

당신이 생각하는 것보다 더 자주, C는 단지 C 기준이 그렇게 말한다고 해서 국회의 관점에서 부적절해 보이는 일을 할 필요가 있다.

정수 승격(예:C에서 문자 변수를 이동하려면 일반적으로 코드에서 실제로 한 비트 이동만 수행하면 된다.

그러나 표준은 컴파일러가 이동 전에 int로 확장된 기호를 실행하도록 강제하고 그 결과를 char로 잘라내 대상 프로세서의 아키텍처에 따라 코드를 복잡하게 만들 수 있다.

아무도 이런 말을 하지 않은 게 놀랍다.strlen()조립해서 쓰면 기능이 훨씬 빠르다!C에서 당신이 할 수 있는 최선은

int c;
for(c = 0; str[c] != '\0'; c++) {}

조립하는 동안 상당히 속도를 높일 수 있다.

mov esi, offset string
mov edi, esi
xor ecx, ecx

lp:
mov ax, byte ptr [esi]
cmp al, cl
je  end_1
cmp ah, cl
je end_2
mov bx, byte ptr [esi + 2]
cmp bl, cl
je end_3
cmp bh, cl
je end_4
add esi, 4
jmp lp

end_4:
inc esi

end_3:
inc esi

end_2:
inc esi

end_1:
inc esi

mov ecx, esi
sub ecx, edi

길이는 ecx이다.이것은 4자를 동시에 비교하기 때문에 4배 더 빠르다.그리고 eax와 ebx의 고순도 단어를 사용하면 이전의 C 루틴보다 8배 빨라질 것이라고 생각해라!

내 경험에서 몇 가지 예를 들어보자.

  • C에서 액세스할 수 없는 지침에 대한 액세스.예를 들어, 많은 아키텍처(x86-64, IA-64, DEC Alpha, 64비트 MIPS 또는 전력)PC)는 128비트 결과를 생성하는 64비트 곱하기 64비트 지원.GCC는 최근 그러한 지침에 대한 액세스를 제공하는 확장자를 추가했지만 그 전에 그러한 어셈블리가 필요했다.또한 RSA와 같은 기능을 구현할 때 64비트 CPU에 대한 액세스 권한은 크게 달라질 수 있으며, 때로는 성능이 4단계 향상되는 요소도 있다.

  • CPU별 플래그 액세스.나를 많이 물린 것은 운반 깃발이다. 다중 정밀한 추가 작업을 할 때 CPU 운반 비트에 접근할 수 없는 경우에는 대신 결과를 비교하여 CPU 운반 비트가 넘쳤는지 확인해야 한다. 이는 사지당 3-5개의 지침을 더 필요로 한다. 그리고 더 나쁜 것은 데이터 액세스 측면에서 상당히 직렬화되어 있어 현대의 초소형 성능을 떨어뜨린다.ar 프로세서수천 개의 그러한 정수를 연속적으로 처리할 때, addc를 사용할 수 있다는 것은 엄청난 승리다. (이동 비트에 경합하는 슈퍼칼라 문제도 있지만, 현대적인 CPU는 그것을 꽤 잘 처리한다.)

  • SIMD. 자기소개 컴파일러도 비교적 간단한 케이스만 할 수 있기 때문에 좋은 SIMD 성능을 원한다면 불행히도 코드를 직접 작성해야 하는 경우가 많다.물론 조립 대신 본질적인 것을 사용할 수는 있지만 일단 본질적인 수준에 도달하면, 단지 컴파일러를 레지스터 할당자 및 (공칭적으로) 명령 스케줄러로 사용하는 것뿐이다.(나는 단순히 컴파일러가 함수 프롤로그를 생성할 수 있기 때문에 SIMD를 위해 본질적인 것을 사용하는 경향이 있다.기능 호출 규칙과 같은 ABI 문제를 처리할 필요 없이 Linux, OS X, Windows에서 동일한 코드를 사용하지만 SSE 본질적인 것 외에 SSE 본질은 매우 좋지 않다. 즉, Altivec 본질은 내가 경험이 많지 않지만 더 나은 것 같다.)(현재) 벡터링 컴파일러가 알아낼 수 없는 것의 예로서, 비트슬라이징 AES 또는 SIMD 오류 수정에 대해 읽어 보십시오 - 알고리즘을 분석하고 그러한 코드를 생성할 수 있는 컴파일러를 상상할 수 있지만, 그런 똑똑한 컴파일러가 현존하는 것(기껏해야)에서 적어도 30년은 떨어져 있는 것처럼 느껴진다.

한편, 멀티코어 기계와 분산형 시스템은 다른 방향으로 가장 큰 성능의 많은 부분을 이동시켰다. 즉, 내부 루프를 조립하여 쓰는 속도를 20% 더 높이거나, 여러 코어에 걸쳐 실행함으로써 300% 더 높이거나, 또는 기계 클러스터에 걸쳐 실행함으로써 10000% 더 높이거나.그리고 물론 높은 수준의 최적화(선물, 메모화 등)는 C나 asm보다 ML이나 스칼라와 같은 높은 수준의 언어로 하는 것이 훨씬 쉬우며, 종종 훨씬 더 큰 성능의 승리를 제공할 수 있다.그래서 항상 그렇듯이, 트레이드오프가 이루어져야 한다.

나는 모든 답안(30개 이상)을 읽어 보았지만 단순한 이유를 찾지 못했다. Intel® 64와 IA-32 Architectures Optimization Reference Manual을 읽고 연습했다면 조립자가 C보다 빠르기 때문에 조립이 느린 이유는 조립을 쓰는 사람들이 Optimization Manual을 읽지 않았기 때문이다.

옛날 Intel 80286에서는 각 명령이 고정된 수의 CPU 사이클로 실행되었다.그러나 1995년 Pentium Pro가 출시된 이후 Intel 프로세서는 다음과 같은 Complex Pipelining을 활용하여 슈퍼스칼라가 되었다.주문 외 실행 및 레지스터 이름 변경.그 전에 1993년에 생산된 펜티엄에는 U와 V 파이프라인이 있었다.따라서 펜티엄은 서로 의지하지 않으면 한 번의 클럭 사이클에서 두 가지 간단한 지시를 실행할 수 있는 이중 파이프라인을 도입했다.하지만 이는 펜티엄 프로에 등장한 '주문 외 실행&등록기 명칭 변경'과 비교하면 아무것도 아니었다.펜티엄 프로에 도입된 이 접근법은 대부분의 최신 인텔 프로세서에서도 실질적으로 같다.

내가 몇 마디로 '순서가 맞지 않는 실행'에 대해 설명하겠다.가장 빠른 코드는 이전 결과에 따라 지침이 달라지지 않는 경우(예: 항상 전체 레지스터를 지워야 함).movzx작업 중인 레지스터의 이전 값에서 종속성을 제거하기 위해 CPU에 의해 내부 이름을 변경하여 명령이 병렬 또는 다른 순서로 실행되도록 할 수 있다.또는 일부 프로세서에서는 잘못된 종속성이 존재하여 Pentium 4에 대한 잘못된 종속성(inc/dec)과 같이 작업 속도가 느려질 수 있으므로 사용하고자 할 수 있다.add eax, 1대신에 또는inc eax깃발의 이전 상태에 대한 의존성을 제거한다.

시간이 허락하면 주문 외 실행 및 레지스터 이름 변경에 대한 자세한 내용을 읽을 수 있다.인터넷에는 많은 정보가 있다.

분기 예측, 부하 및 저장 단위 수, 마이크로옵스를 실행하는 관문 수, 메모리 캐시 일관성 프로토콜 등 그 밖에 본질적인 문제도 많지만, 고려해야 할 것은 '순서가 맞지 않는 실행'이다.대부분의 사람들은 그저 질서 없는 집행을 알지 못한다.따라서, 그들은 80286과 같은 조립 프로그램을 작성하는데, 그들의 지침이 맥락과 상관없이 실행되는데 일정한 시간이 걸릴 것으로 예상한다.동시에 C 컴파일러는 주문 외 실행을 인지하고 코드를 정확하게 생성한다.그렇기 때문에 그런 무식한 사람들의 코드는 느리지만, 박식해지면 코드는 더 빨라질 것이다.

또한 Out-of-Order Execution 외에도 많은 최적화 팁과 요령이 있다.위에서 언급한 최적화 매뉴얼을 읽어 보십시오:-)

그러나 조립언어는 최적화에 있어서는 그 나름의 단점이 있다.피터 코데스(아래 주석 참조)에 따르면 컴파일러가 수행하는 최적화 중 일부는 수기 조립체에서 대형 코드베이스를 유지할 수 없을 것이다.예를 들어, 어셈블리로 쓴다고 가정합시다.이 경우 인라인 함수(조립 매크로)가 일부 인수가 상수인 상태에서 이를 호출하는 함수로 인라인 함수(조립 매크로)를 완전히 변경할 필요가 있다.동시에, C 컴파일러는 그 일을 훨씬 더 단순하게 만들고, 서로 다른 방식으로 동일한 코드를 다른 통화 사이트에 입력한다.조립 매크로로 할 수 있는 일에는 한계가 있다.따라서 동일한 이점을 얻으려면 각 위치에서 동일한 논리를 수동으로 최적화하여 상수와 사용 가능한 레지스터를 일치시켜야 한다.

Walter Bright의 "불변성과 순수성 최적화"는 프로파일링된 테스트는 아니지만 손으로 작성한 ASM과 컴파일러 간의 차이점을 보여주는 좋은 예를 보여 준다.Walter Bright는 최적의 컴파일러를 쓰기 때문에 그의 다른 블로그 게시물을 볼 가치가 있을 것이다.

SIMD 명령을 사용하는 매트릭스 작업은 컴파일러에서 생성된 코드보다 더 빠를 것이다.

리눅스 조립체는 어떻게 하는지, 이 질문을 하고 조립체의 사용에 대한 장단점을 제시한다.

간단한 답은...어셈블리를 아는 사람(일명 옆에 참조가 있고, 모든 작은 프로세서 캐시와 파이프라인 기능 등을 이용하고 있다)은 어떤 컴파일러보다 훨씬 빠른 코드를 생산할 수 있는 능력이 보장된다.

하지만 요즘의 차이는 일반적인 적용에서 중요하지 않다.

정답이 아닌 1번 지점.
비록 당신이 프로그래밍을 하지 않더라도, 나는 적어도 하나의 조립자 명령어 세트를 아는 것이 유용하다고 생각한다.이것은 프로그래머들이 더 많이 알고 더 나은 사람이 되기 위한 끝없는 탐구의 일부다.또한 프레임워크에 들어갈 때 소스 코드가 없고 적어도 무슨 일이 일어나고 있는지 대략적으로 알 수 있을 때 유용하다.또한 JavaByteCode 및 를 이해하는 데 도움이 된다.Net IL은 둘 다 조립자와 유사하다.

코드의 양이 적거나 시간이 많을 때 질문에 답하기 위해.칩의 복잡성이 낮고 이러한 칩을 대상으로 하는 컴파일러의 경쟁이 좋지 않은 임베디드 칩에 사용하는 데 가장 유용하다.또한 제한된 장치의 경우 컴파일러에게 명령하기 어려운 방식으로 코드 크기/메모리 크기/성능을 거래하는 경우가 많다. 예를 들어, 이 사용자 동작은 자주 호출되지 않기 때문에 코드 크기가 작고 성능이 떨어지지만 유사해 보이는 이 다른 기능은 매초마다 사용되므로 코드 크기가 더 크고 코드 크기가 더 크다.더 빠른 성능그것은 숙련된 조립 프로그래머가 사용할 수 있는 일종의 트레이드오프다.

나는 또한 당신이 C 컴파일에서 코드화하고 생산된 어셈블리를 검사한 다음, 당신의 C 코드를 변경하거나 수정하고 어셈블리로 유지할 수 있는 많은 중간 지대가 있다는 것을 덧붙이고 싶다.

내 친구는 현재 소형 전기 모터 제어 칩인 마이크로컨트롤러에서 일하고 있다.그는 저급 c와 조립을 병행하여 일한다.그는 언젠가 나에게 48개의 지시에서 43개로 메인 루프를 줄인 직장에서의 좋은 하루를 말해준 적이 있다.그는 또한 256k 칩을 채울 수 있도록 코드가 커졌고 비즈니스가 새로운 기능을 원한다는 것과 같은 선택들에 직면해 있다.

  1. 기존 기능 제거
  2. 성능 저하로 기존 기능의 일부 또는 전체 크기를 줄이십시오.
  3. 더 높은 비용, 더 높은 전력 소비 및 더 큰 폼 팩터를 가진 더 큰 칩으로 이동하는 것을 지지하십시오.

나는 꽤 많은 포트폴리오나 언어, 플랫폼, 애플리케이션 유형을 가진 상업적 개발자로써 필자의 조립에 뛰어들 필요성을 느껴본 적이 없다.나는 내가 그것에 대해 얻은 지식을 얼마나 항상 높이 평가해 왔다.그리고 때로는 디버깅을 하기도 했다.

내가 "왜 내가 조립자를 배워야 하는가"라는 질문에 훨씬 더 많이 대답했다는 것을 알지만, 나는 그것이 언제 더 빠를까 보다 더 중요한 질문이라고 느낀다.

그러니 한 번 더 해보자. 너는 조립에 대해 생각해야 한다.

  • 낮은 수준의 운영 체제 기능에 대한 작업
  • 컴파일러 작업 중.
  • 극히 제한된 칩, 임베디드 시스템 등에 대한 작업

어느 것이 더 빠르거나 작거나 더 나은지 확인하기 위해 생성된 컴파일러와 어셈블리를 비교하는 것을 잊지 마십시오.

데이빗

C는 8비트, 16비트, 32비트, 64비트 데이터의 낮은 수준의 조작에 "가까이" 있지만, C가 지원하지 않는 몇 가지 수학 연산이 있어 특정 조립 지침 집합에서 우아하게 수행될 수 있다.

  1. 고정 점 곱하기:16비트 숫자 두 개의 제품은 32비트 숫자다.그러나 C의 법칙에 따르면 16비트 번호 2개의 제품은 16비트 번호로 되어 있고 32비트 번호 2개의 제품은 32비트 번호로, 두 경우 모두 하위 1/2이다.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의 위쪽 절반의 곱셈을 하고 기계 본연의 16x16 멀티플라이로 옳은 일을 하려고 애쓰고 있다는 것을 알 정도로 충분히 똑똑할 수 있다.아니면 어리석고 32x32 곱하기 도서관 전화를 받아야 할지도 모른다. 그건 너무 지나친 것이다. 왜냐하면 당신은 16비트 제품만 필요하기 때문이다. 하지만 C 표준은 당신 자신을 표현할 수 있는 어떤 방법도 제공하지 않는다.

  2. 특정 비트하이프 작업(회전/캐리리):

    // 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에서는 너무 비굴하지 않지만, 다시 말하지만, 컴파일러가 당신이 무엇을 하고 있는지 알 만큼 똑똑하지 않다면, 그것은 많은 "불필요한" 일을 할 것이다.많은 조립 지침 세트는 당신이 캐리어 레지스터의 결과를 가지고 좌우로 회전하거나 이동할 수 있게 해주므로, 당신은 포인터에서 자동 증분을 사용하여 어레이의 시작 부분에 포인터를 장착하고, 캐리어를 지우고, 32개의 8비트 우회전을 수행할 수 있다.

    다른 예로는 조립 시 우아하게 수행되는 선형 피드백 시프트 레지스터(LFSR)가 있다.N 비트(8, 16, 32, 64, 128 등) 한 덩어리를 취하여 전체를 1만큼 오른쪽으로 이동한 다음(위의 알고리즘 참조) 결과 캐리어가 1이면 다항식을 나타내는 비트 패턴으로 XOR를 사용하십시오.

그렇게 말했으니, 나는 심각한 성능 제약이 없다면 이런 기술들에 의존하지 않을 것이다.다른 사람들이 말했듯이, 어셈블리는 C 코드보다 문서화/디버그/테스트/유지하기가 훨씬 더 어렵다. 즉, 성능 향상에는 약간의 심각한 비용이 수반된다.

편집: 3.오버플로 검출은 조립(C에서는 실제로 할 수 없음)이 가능하며, 이는 일부 알고리즘을 훨씬 쉽게 만든다.

컴파일러가 생산하는 것을 분해하는 것을 보지 않았다면 잘 쓰여진 C 코드가 정말 빠른지 당신은 실제로 알 수 없다.여러 번 보고 '잘 쓴' 것이 주관적이었음을 보게 된다.

따라서 지금까지 가장 빠른 코드를 얻기 위해 조립자에게 글을 쓸 필요는 없지만, 같은 이유로 조립자를 알 만한 가치는 분명히 있다.

아마존닷컴은 많은 예들을 가지고 있다.

더 이상 적용되지 않을 수 있는 사용 사례(이상한 재미를 위한 경우):아미가에서는 CPU와 그래픽/오디오 칩이 RAM의 특정 영역(특정화된 최초의 2MB RAM)에 접근하기 위해 싸울 것이다.따라서 RAM이 2MB 이하일 때 복잡한 그래픽과 재생 사운드를 표시하면 CPU의 성능이 저하될 수 있다.

조립자에서는 그래픽/오디오 칩이 내부적으로 사용 중일 때(즉, 버스가 비어 있을 때) CPU가 RAM에 액세스하려고 시도할 정도로 교묘한 방법으로 코드를 간섭할 수 있다.그래서 당신의 지시사항, CPU 캐시의 교묘한 사용, 버스 타이밍을 재주문함으로써, 당신은 모든 명령어 시간을 재주어야만 했기 때문에 어떤 상위 언어로는 도저히 불가능했던 효과를 얻을 수 있었다. 심지어 NOP를 여기저기 삽입하여 서로 다른 칩들의 레이더에 노출되지 않도록 했다.

CPU의 NOP(No Operation - 아무것도 하지 않음) 명령이 실제로 전체 애플리케이션을 더 빠르게 실행할 수 있는 또 다른 이유다.

[EDIT] 물론 기술은 특정 하드웨어 설정에 따라 달라진다.많은 아미가 게임들이 더 빠른 CPU에 대처하지 못한 주된 이유였다.지시의 타이밍이 맞지 않았다.

어떤 특수 목적 지침을 사용할 때만 컴파일러는 지원하지 않는다.

다중 파이프라인과 예측 분기로 현대 CPU의 컴퓨팅 성능을 극대화하려면 조립 프로그램을 a) 사람이 b)를 유지하기가 더 불가능하도록 구성해야 한다.

또한 더 나은 알고리즘, 데이터 구조 및 메모리 관리를 통해 조립 시 수행할 수 있는 마이크로 최적화보다 최소한 더 큰 성능을 얻을 수 있을 것이다.

이미지로 재생할 때와 같이 이미지가 수백만 픽셀의 코지스트일 수 있기 때문에 고정된 루프.앉아서 제한된 수의 프로세서 레지스터를 가장 잘 활용하는 방법을 알아내는 것은 변화를 일으킬 수 있다.여기 실생활의 표본이 있다.

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

그리고 종종 프로세서는 컴파일러가 귀찮게 하기에는 너무 전문화된 난해한 지침을 가지고 있지만, 때때로 조립자 프로그래머는 그것들을 잘 활용할 수 있다.XLAT 지침을 예로 들어 보십시오.테이블 검색을 반복해서 해야 하는데 테이블이 256바이트로 제한되어 있다면 정말 좋다!

업데이트됨:오, 우리가 일반적으로 루프에 대해 말할 때 가장 중요한 것이 무엇인지 생각해 보자. 컴파일러는 종종 얼마나 많은 반복이 일반적인 경우가 될 지에 대해 전혀 알지 못한다.오직 프로그래머만이 루프가 여러 번 반복될 것이고 따라서 약간의 추가 작업으로 루프를 준비하는 것이 유익할 것이라는 것을 알고 있다. 또는 너무 몇 번 반복될 경우 실제로 설정은 예상된 반복보다 더 오래 걸릴 것이다.

역사적으로 성공했군

내가 훨씬 젊은 남자(1970년대)의 조립자였을 때, 내 경험상 코드의 속도보다는 코드의 크기에 더 중요했다.

상위 언어의 모듈이 예를 들어, 1300바이트의 코드였지만 모듈의 조립자 버전이 300바이트라면, 그 1K바이트는 애플리케이션을 16K 또는 32K의 메모리에 맞추려고 할 때 매우 중요했다.

그 당시 컴파일러들은 대단하지 않았다.

옛날 포트란에서

X = (Y - Z)
IF (X .LT. 0) THEN
 ... do something
ENDIF

그 당시 컴파일러는 SECT 명령을 한 다음 X에 대한 TEST 명령을 했다.조립자에서는 감산 후 상태 코드(LT 0, 0, GT 0)만 확인하면 된다.

현대 시스템과 컴파일러에게 그것은 어느 것도 관심사가 아니다.

나는 컴파일러가 하는 일을 이해하는 것이 여전히 중요하다고 생각한다.더 높은 수준의 언어로 코드화할 때 컴파일러가 루프 해제를 허용하거나 방지하는 방법을 이해해야 한다.

그리고 조건과 관련된 파이프 라이닝과 룩-어헤드 계산으로 컴파일러가 "지점-라이클리"를 할 때

프로세서별 레지스터에 읽기 또는 쓰기처럼 상위 언어에 의해 허용되지 않는 작업을 수행할 때 어셈블러는 여전히 필요하다.

그러나 대체로 코드를 컴파일하고 실행하는 방법에 대한 기본적인 이해 외에는 일반 프로그래머에게 더 이상 필요하지 않다.

그 질문은 약간 오해의 소지가 있다.답은 당신의 게시물 그 자체에 있다.컴파일러에 의해 생성된 어떤 문제보다 더 빨리 실행되는 특정 문제에 대해 항상 조립 솔루션을 작성할 수 있다.중요한 것은 컴파일러의 한계를 극복하기 위해서는 조립의 전문가가 되어야 한다는 것이다.경험이 풍부한 조립 프로그래머는 어떤 HLL로든 프로그램을 쓸 수 있는데, 이 프로그램은 미숙한 사람이 작성한 것보다 더 빠른 성능을 발휘한다.진실은 컴파일러에 의해 생성된 것보다 더 빨리 실행되는 조립 프로그램을 항상 쓸 수 있다는 것이다.

너무 오래전 일이라 구체적인 예를 들 수는 없지만, 수기 조립자가 어떤 컴파일러보다 성능이 뛰어난 경우는 얼마든지 있었다.이유:

  • 당신은 규칙을 호출하거나 레지스터에서 논쟁을 전달하는 것에서 벗어날 수 있다.

  • 레지스터 사용 방법을 신중하게 고려하고, 변수를 메모리에 저장하는 것을 피할 수 있다.

  • 점프 테이블과 같은 경우에는 인덱스를 경계로 검사하지 않아도 된다.

기본적으로 컴파일러는 최적화를 꽤 잘 하는데, 거의 "잘했다"는 말이지만, 매 사이클마다 큰 비용을 지불하고 있는 어떤 상황(그래픽 렌더링과 같은)에서는 코드를 알기 때문에 바로 가기를 사용할 수 있지만, 컴파일러는 안전한 편에 서야 하기 때문에 그럴 수 없었다.

사실, 나는 선 그리기 또는 다각형 채우기 루틴과 같은 루틴이 실제로 선 스타일, 폭, 패턴 등에 대한 지속적인 의사결정을 피하기 위해 스택에 작은 기계 코드 블록을 생성하여 그곳에서 실행한 그래픽 렌더링 코드에 대해 들어 본 적이 있다.

그렇긴 하지만, 컴파일러가 할 수 있는 일은 나에게 좋은 조립 코드를 만들어주지만 너무 영리하지는 않은 것이고, 그들은 대부분 그렇게 한다.사실, 내가 포트란에 대해 싫어하는 것 중 하나는 코드를 "최적화"하기 위해 서두르는 것인데, 대개는 이렇다 할 목적이 없다.

보통 앱에 성능 문제가 생기면 낭비적인 디자인 때문이다.요즘, 나는 전체 앱이 이미 수명이 1인치 이내에서 튜닝되고, 여전히 충분히 빠르지 않고, 꽉 끼는 내부 루프에서 모든 시간을 보내고 있지 않다면 결코 성능을 위해 조립자를 추천하지 않을 것이다.

추가: 나는 조립 언어로 쓰여진 앱을 많이 보았는데, C, 파스칼, 포트란 등의 언어에 비해 주된 속도 이점은 프로그래머가 조립자에서 코딩을 할 때 훨씬 더 신중했기 때문이다.그 또는 그녀는 언어와 상관없이 하루에 대략 100줄의 코드를 3-400개의 명령어와 같은 컴파일러 언어로 쓸 것이다.

모든 것은 너의 업무량에 달려있다.

일상적인 작업의 경우, C와 C++는 괜찮지만, 조립이 수행되어야 하는 특정 워크로드(비디오(압축, 압축 해제, 이미지 효과 등)가 있다.

또한 이러한 종류의 운영에 맞게 조정된 CPU별 칩셋 확장자(MME/MMX/SSE/whything)를 사용하는 것이 보통이다.

50마이크로초마다 발생하는 192~256비트의 비트 전치 작업이 있다.

고정된 지도(하드웨어 제약)에 의해 일어난다.C를 사용하여 만드는 데 약 10마이크로초가 걸렸다.내가 이것을 Assembler로 번역했을 때, 이 지도의 특정 특징, 특정 레지스터 캐싱, 비트 지향 연산 등을 고려했을 때, 수행하는데 3.5마이크로초도 걸리지 않았다.

나는 조립자가 더 빠를 때 일반적으로 스마트 조립 프로그래머가 컴파일러의 출력을 보고 "이것은 성능을 위한 중요한 경로고 나는 이것을 더 효율적으로 쓸 수 있다"고 말한 다음, 그 사람이 조립자를 수정하거나 처음부터 다시 쓰는 경우가 된다고 생각한다.

적합한 프로그래머를 제공한다면, 조립자 프로그램은 항상 C 상대 프로그램보다 더 빨리 만들어질 수 있다.적어도 조립자의 지시를 한 개도 빼낼 수 없는 C 프로그램을 만들기는 어려울 것이다.

Longpoke, 거기에는 단지 한 가지 한계가 있다: 시간.모든 변경을 코드로 최적화하고 레지스터를 할당하는 데 시간을 할애할 수 있는 리소스가 없으면 유출이 거의 없고 그렇지 않은 경우 컴파일러가 매번 승리할 것이다.코드를 수정하고 다시 컴파일하고 측정하십시오.필요한 경우 반복하십시오.

또한, 높은 수준의 측면에서도 많은 것을 할 수 있다.또한, 결과 어셈블리를 검사하면 코드는 쓰레기라는 인상을 줄 수 있지만, 실제로는 코드는 당신이 생각하는 것보다 더 빨리 실행될 것이다.예:

int y = 데이터[i]; // 여기서 몇 가지 일을 한다.call_function(y, ...);

컴파일러는 데이터를 읽고 스택(스필)에 밀어넣은 다음 스택에서 읽고 인수로 전달한다.음침하게 들리니깐요?그것은 실제로 매우 효과적인 대기 시간 보상이 될 수 있고 더 빠른 런타임을 초래할 수 있다.

// 최적화된 버전 call_function(data[i], ...); // 결국 그렇게 최적화되지 않았다.

최적화된 버전의 아이디어는 레지스터 압력을 줄이고 흘리는 것을 피한다는 것이었습니다.하지만 사실, "shitty" 버전이 더 빨랐답니다!

조립 코드를 보고, 단지 지시사항만 보고 결론을 내리는 것은, 더 많은 지시사항, 더 느리게, 더 많은 지시사항은 오판이다.

여기서 주목해야 할 것은 많은 국회 전문가들은 자신들이 아는 것은 많지만 아는 것은 거의 없다고 생각한다는 점이다.규칙도 건축에서 다음으로 바뀐다.예를 들어, 은색 불렛 x86 코드는 항상 가장 빠른 것이 없다.요즘은 규칙대로 하는 게 좋다.

  • 기억력이 느리다
  • 캐시가 빠르다
  • 캐시된 것을 더 잘 사용하려고 노력하다
  • 얼마나 자주 그리울꺼니?지연 시간 보상 전략이 있으십니까?
  • 단일 캐시 누락에 대해 10-100 ALU/FPU/SSE 지침을 실행할 수 있음
  • 애플리케이션 아키텍처는 중요하다.
  • ..하지만 문제가 아키텍처에 없을 때는 도움이 되지 않는다.

또한, 너무 많은 것을 컴파일러로 신뢰하는 것은 잘못 생각된 C/C++ 코드를 "이론적으로 최적의" 코드로 마술적으로 변환시키는 것이다.이 낮은 수준에서 "성능"에 신경을 쓴다면 사용하는 컴파일러와 툴 체인을 알아야 한다.

C/C++의 컴파일러는 기능이 우선 부작용을 가지기 때문에 일반적으로 하위 발음을 재주문하는 데 그다지 능숙하지 않다.기능언어는 이런 주의사항으로 어려움을 겪지는 않지만 현재의 생태계에 잘 맞지 않는다.컴파일러/링커/코드 생성기에 의해 작동 순서를 변경할 수 있는 완화된 정밀도 규칙을 허용하는 컴파일러 옵션이 있다.

이 주제는 다소 막다른 골목이다; 대부분의 사람들은 관련이 없고, 나머지는 이미 그들이 무엇을 하고 있는지 알고 있다.

이 모든 것이 요점이다: "당신이 무엇을 하고 있는지 이해하기 위해서", 그것은 당신이 무엇을 하고 있는지 아는 것과는 조금 다르다.

요즘은 C코드를 극도로 최적화하는 인텔 C++와 같은 컴파일러를 고려하면 컴파일러 출력과 경쟁하기가 매우 어렵다.

단답?가끔.

기술적으로 모든 추상화는 비용이 들고 프로그래밍 언어는 CPU가 작동하는 방식을 위한 추상화다.그러나 C는 매우 가깝다.수년 전 유닉스 계정에 접속해 다음과 같은 행운의 메시지를 받았을 때 크게 웃었던 기억이 난다.

C 프로그래밍 언어 - 조립 언어의 유연성과 조립 언어의 힘을 결합한 언어.

그것은 사실이기 때문에 웃긴다: C는 휴대용 조립 언어와 같다.

어셈블리 언어는 당신이 어떻게 쓰든 그냥 실행된다는 것을 주목할 필요가 있다.그러나 C와 조합 언어 사이에는 컴파일러가 존재하는데, 그것은 매우 중요한데, 왜냐하면 당신의 C 코드가 얼마나 빠른지 당신의 컴파일러가 얼마나 좋은지 엄청나게 많은 것을 가지고 있기 때문이다.

gcc가 등장했을 때, 매우 인기를 끌었던 것 중 하나는 그것이 많은 상업적인 UNIX 향과 함께 출하된 C 컴파일러보다 훨씬 더 좋았다는 것이다.ANSI C(이 K&R C 쓰레기는 없음)가 더 강력했을 뿐만 아니라, 일반적으로 더 빠른(더 빠른) 코드를 생산했다.항상은 아니지만 자주.

C에 대한 객관적인 기준이 없기 때문에 C와 조립자의 속도에 대한 포괄적인 규정이 없기 때문에 이 모든 것을 말씀드린다.

마찬가지로, 어셈블러는 실행 중인 프로세서, 시스템 사양, 사용 중인 명령 집합 등에 따라 많이 달라진다.역사적으로 두 개의 CPU 아키텍처 제품군이 있었다: CISC와 RISC.CISC에서 가장 큰 플레이어는 여전히 인텔 x86 아키텍처(및 명령어 세트)였다.RISC는 UNIX 세계(MIPS6000, Alpha, Sparc 등)를 장악했다.CISC는 마음과 마음을 위한 싸움에서 승리했다.

Anyway, the popular wisdom when I was a younger developer was that hand-written x86 could often be much faster than C because the way the architecture worked, it had a complexity that benefitted from a human doing it. RISC on the other hand seemed designed for compilers so noone (I knew) wrote say Sparc assembler. I'm sure such people existed but no doubt they've both gone insane and been institutionalized by now.

Instruction sets are an important point even in the same family of processors. Certain Intel processors have extensions like SSE through SSE4. AMD had their own SIMD instructions. The benefit of a programming language like C was someone could write their library so it was optimized for whichever processor you were running on. That was hard work in assembler.

There are still optimizations you can make in assembler that no compiler could make and a well written assembler algoirthm will be as fast or faster than it's C equivalent. The bigger question is: is it worth it?

Ultimately though assembler was a product of its time and was more popular at a time when CPU cycles were expensive. Nowadays a CPU that costs $5-10 to manufacture (Intel Atom) can do pretty much anything anyone could want. The only real reason to write assembler these days is for low level things like some parts of an operating system (even so the vast majority of the Linux kernel is written in C), device drivers, possibly embedded devices (although C tends to dominate there too) and so on. Or just for kicks (which is somewhat masochistic).

ReferenceURL : https://stackoverflow.com/questions/577554/when-is-assembly-faster-than-c

반응형