C/C++에서의 어셈블리 언어 사용
코드의 특정 부분을 최적화하고 속도를 높이기 위해 프로그래머들이 어셈블리 언어로 그 부분을 쓴다는 것을 어딘가에서 읽은 것으로 기억합니다.질문이 있습니다.
- 이 연습은 지금도 하고 있나요? 그리고 어떻게 하는 거죠?
- 어셈블리 언어로 쓰는 것은 너무 번거롭고 구식이지 않나요?
- C 코드를 컴파일하면(-O3 플래그 유무에 관계없이), 컴파일러는 코드 최적화를 실시해, 모든 라이브러리를 링크 해, 코드를 바이너리 오브젝트 파일로 변환합니다.따라서 프로그램을 실행하면 바이너리 등 가장 기본적인 형태로 되어 있습니다.그렇다면 '어셈블리 언어'를 유도하는 것이 어떤 도움이 될까요?
저는 이 개념을 이해하려고 노력하고 있습니다.도움말이나 링크에 매우 감사합니다.
업데이트: dbemerlin의 요청에 따라 포인트 3을 변경 - 컴파일러가 생성하는 것보다 더 효과적인 어셈블리 코드를 작성할 수 있을 수 있지만, 어셈블러 전문가가 아닌 경우 대부분의 경우 컴파일러가 코드를 보다 잘 최적화하기 때문에 코드 실행 속도가 느려집니다.
어셈블리 언어로 되돌리는 데 유용한 유일한 방법은
CPU 명령에는 C++에 해당하는 기능이 없습니다(예: 단일 명령-복수 데이터 명령, BCD 또는 10진수 연산).
- 또한 컴파일러는 이러한 연산을 정리하는 추가 함수를 제공하지 않습니다(예를 들어 C++11 Standard에는 비교 및 스왑을 포함한 원자 연산이 있습니다).
<cstdlib>지수와 나머지를 효율적으로 얻기 위한ldiv/ 등) - 또한 적절한 서드파티 라이브러리가 없습니다(http://mitpress.mit.edu/catalog/item/default.asp?tid=3952&ttype=2) 등).
또는
- 또한 컴파일러는 이러한 연산을 정리하는 추가 함수를 제공하지 않습니다(예를 들어 C++11 Standard에는 비교 및 스왑을 포함한 원자 연산이 있습니다).
설명할 수 없는 이유로 옵티마이저가 최적의 CPU 명령을 사용하지 못하고 있습니다.
...그리고...
- 이러한 CPU 명령어를 사용하면 보틀 넥 코드의 퍼포먼스가 대폭 향상됩니다.
인라인 어셈블리를 사용하여 C++로 쉽게 표현할 수 있는 연산(예: 2개의 값을 추가하거나 문자열을 검색하는 것)을 수행하는 것은 다음과 같은 이유로 적극적으로 역효과를 가져옵니다.
- 컴파일러는 이것을 똑같이 잘 하는 방법을 알고 있다
- 이것을 확인하려면 , 어셈블리 출력(예:
gcc -S) 또는 기계코드를 분해합니다.
- 이것을 확인하려면 , 어셈블리 출력(예:
- 레지스터 할당, CPU 명령 등에 관한 선택을 인위적으로 제한하고 있기 때문에 하드코드된 명령어를 실행하는 데 필요한 값을 CPU 레지스터를 준비하는 데 시간이 더 오래 걸릴 수 있습니다.그 후, 향후의 명령어를 위해 최적의 할당으로 되돌리는 데 더 오랜 시간이 걸릴 수 있습니다.
- 컴파일러 옵티마이저는 서로 다른 레지스터를 지정하는 등가 성능 명령 사이에서 선택할 수 있으며, 특정 레지스터를 통해 모든 것을 강제적으로 직렬화하는 동안 단일 코어가 하나의 사이클 동안 여러 명령을 처리할 수 있도록 레지스터를 선택할 수 있습니다.
- 공정하게 말하면, GCC는 CPU를 정확한 레지스터로 제한하지 않고 특정 유형의 레지스터에 대한 요구를 표현할 수 있는 방법을 가지고 있으며, 여전히 그러한 최적화를 허용하고 있지만, 내가 본 유일한 인라인 어셈블리는 이것을 다루는 유일한 인라인 어셈블리이다.
- 컴파일러 옵티마이저는 서로 다른 레지스터를 지정하는 등가 성능 명령 사이에서 선택할 수 있으며, 특정 레지스터를 통해 모든 것을 강제적으로 직렬화하는 동안 단일 코어가 하나의 사이클 동안 여러 명령을 처리할 수 있도록 레지스터를 선택할 수 있습니다.
- 새로운 CPU 모델이 같은 논리 조작에 대해 1000% 고속의 다른 명령과 함께 내년에 나온다면 컴파일러 벤더는 컴파일러를 사용하여 컴파일러를 갱신할 가능성이 높기 때문에 컴파일러의 프로그램을 재컴파일하면 다른 사람이 소프트웨어를 관리하는 것보다 더 유리합니다.
- 컴파일러는, 1개의 솔루션을 하드 코드 하는 경우는, 그 솔루션이 가장 낮은 공통의 컴파일러가 되어 있을 필요가 있는, 또는, 컴파일러가 말하는 타겟 아키텍처에 최적인 어프로치를 선택합니다.
#ifdef- ed는 ed - 어셈블리 언어는 CPU와 컴파일러 모두에서 C++만큼 이식성이 없습니다.또한 명령어를 포팅하는 것처럼 보여도 클럽버나 인수 전달 규약 등에 안전한 재등록 오류를 일으킬 수 있습니다.
- 다른 프로그래머들은 조립을 모르거나 익숙치 않을 수 있다
C가 도입되었을 때 생성된 머신코드에 대해 호들갑을 떨었던 많은 하드코어 어셈블리 언어 프로그래머들을 설득해야 했다는 점을 염두에 둘 필요가 있다고 생각합니다.당시 머신의 CPU 전력과 RAM은 적었기 때문에 아주 작은 것에도 호들갑을 떨지 않을 수 없었습니다.x86과 같은 프로세서의 어셈블리 언어는 점점 복잡해지고 있으며 성능에는 실행 파이프라인, 캐시 및 기타 요소가 관련되어 있습니다.더 이상 명령당 주기 테이블에서 값을 추가할 수 없습니다.컴파일러 라이터는, 이러한 모든 미묘한 요소(특히 CPU 제조원에 근무하고 있는 것은, 다른 컴파일러의 부하를 증가시키는 요인)에 대해 시간을 할애쓰고 있습니다.어셈블리의 프로그래머가 코드의 효율이 뛰어난 최적화 컴파일러에 의해 생성되는 것보다 훨씬 뛰어난 평균화를 실현하는 것은 이제 비사소한 애플리케이션보다 실용적이지 않습니다.또한 어셈블리 프로그래머는 훨씬 더 나쁜 결과를 얻을 가능성이 높습니다.따라서 조립품의 사용은 결합 및 유지관리 비용만큼 측정 가능하고 유용한 차이를 만드는 시간으로 제한해야 합니다.
너는 고전책을 읽어야 한다.Zen of Code Optimization및 후속 조치Zen of Graphics Programming마이클 애버쉬의 작품이에요
요약해서 그는 첫 번째 책에서 한계까지 밀어붙인 어셈블리 프로그래밍을 사용하는 방법을 설명했다.후속 조치에서 프로그래머는 오히려 C와 같은 상위 수준의 언어를 사용해야 하며 필요한 경우 어셈블리를 사용하여 매우 구체적인 부분을 최적화해야 한다고 설명했습니다.
이러한 생각의 전환의 한 가지 이유는 한 세대의 프로세서에 매우 최적화된 프로그램이 고급 언어(예를 들어 새로운 명령어를 사용하는 컴파일러나 기존 명령어의 성능 및 동작 등)에서 컴파일된 코드와 비교하여 같은 프로세서 패밀리의 차세대에서는 (어느 정도) 느려질 수 있다는 것을 알았기 때문입니다.프로세서의 세대에서 다른 세대로의 변경).
또 다른 이유는 컴파일러가 매우 우수하고 오늘날 공격적으로 최적화되어 있기 때문에 일반적으로 C 코드를 어셈블리로 변환하는 알고리즘에 대해 작업하는 것이 훨씬 더 효율적이기 때문입니다.GPU(그래픽 카드 프로세서) 프로그래밍에서도 Cuda 또는 OpenCL을 사용하여 실행할 수 있습니다.
또, 일반적으로 하드웨어를 매우 세밀하게 제어하기 위해서 어셈블리를 사용할 필요가 있는(적어도) 경우도 있습니다.그러나 OS 커널 코드에서도 보통 매우 작은 부분이며 많은 코드는 아닙니다.
(1) 예, 이것을 시험하는 가장 쉬운 방법은 인라인 어셈블리를 사용하는 것입니다.이것은 컴파일러에 의존하지만 보통 다음과 같습니다.
__asm
{
mov eax, ebx
}
(2) 이것은 매우 주관적이다.
(3) 컴파일러가 생성하는 것보다 더 효과적인 어셈블리 코드를 작성할 수 있기 때문입니다.
「이 연습은 지금도 행해지고 있습니까?」--> 화상 처리, 신호 처리, AI(효율적인 행렬 곱셈 등)등의 분야에서 행해집니다.macbook 트랙패드에서의 스크롤 제스처 처리도 어셈블리 코드일 것입니다.즉각적이기 때문입니다.--> C# 어플리케이션에서도 처리됩니다(https://blogs.msdn.microsoft.com/winsdk/2015/02/09/c-and-fastcall-how-to-make-them-work-together-without-ccli-shellcode/) 참조).
"어셈블리 언어로 쓰는 것은 너무 번거롭고 오래된 것 아닌가요?" --> 망치나 스크루드라이버와 같은 도구이기 때문에 시계 제조기 스크루드라이버가 필요한 작업도 있습니다.
- "C 코드를 컴파일할 때(-O3 플래그가 있든 없든) 컴파일러는 코드 최적화를 수행합니다.어셈블리 언어'를 유도하는 것이 어떤 도움이 될까요? --> 저는 @jalf가 말한 것이 마음에 듭니다.어셈블리를 쓰는 방법으로 C 코드를 쓰면 이미 효율적인 코드가 됩니다.그러나 이를 위해서는 어셈블리 언어로 코드를 작성하는 방법을 생각해야 합니다.그러면 데이터가 복사되는 모든 위치를 이해할 수 있습니다(또한 데이터가 불필요해질 때마다 고통을 느낄 수 있습니다).어셈블리 언어를 사용하면 생성되는 명령을 확인할 수 있습니다.C코드가 효율적이라고 해도 컴파일러마다 어셈블리가 효율적이라는 보장은 없습니다.( https://lucasmeijer.com/posts/cpp_unity/) -- > 어셈블리 언어를 사용하면 바이너리를 배포할 때 AVX 또는 SSE용으로 최적화된CPU 기능에 따라 CPU를 테스트하고 다른 브런치를 만들 수 있습니다.단, 1개의 바이너리만 배포할 필요가 있습니다.내장 함수를 사용하면 C++ 또는에서도 가능합니다.NET Core 3. (https://devblogs.microsoft.com/dotnet/using-net-hardware-intrinsics-api-to-accelerate-machine-learning-scenarios/) 참조)
우선 프로그램 프로파일을 작성해야 합니다.다음으로 C 또는 C++ 코드에서 가장 많이 사용되는 경로를 최적화합니다.장점이 명확하지 않으면 어셈블러에서 다시 쓰지 않습니다.어셈블러를 사용하면 코드 유지보수가 어려워지고 휴대성이 떨어집니다.매우 드문 경우를 제외하고는 코드 유지보수는 불필요합니다.
프로세서를 지정하지 않은 것 같습니다.프로세서 및 환경에 따라 답변이 다릅니다.일반적인 대답은 그렇다이다. 그것은 여전히 행해지고 있다. 확실히 오래된 것은 아니다.일반적인 이유는 컴파일러입니다.컴파일러는 일반적으로 최적화를 잘하지만 특정 타깃에 대해서는 잘하지 못하는 경우가 있습니다.어떤 사람들은 한 가지 목표에는 정말 능숙하지만 다른 목표에는 그다지 능숙하지 않다.대부분의 경우, 대부분의 경우 휴대용 어셈블러가 아닌 휴대용 C 코드를 원합니다.그러나 C 라이브러리는 여전히 memcpy 및 컴파일러가 그것을 구현하는 매우 빠른 방법이 있다는 것을 단순히 파악할 수 없는 다른 루틴을 손으로 처리합니다.이 코너 케이스는 컴파일러의 최적화에 시간을 할애할 가치가 없기 때문에 어셈블러로 해결하면 됩니다.이 타겟이 asm을 사용하고 타겟이 asm을 사용하고 타겟이 asm을 사용하는 경우 C를 사용합니다.그래서 이런 일이 여전히 일어나고 있고, 저는 어떤 지역에서는 영원히 계속되어야 한다고 주장합니다.
X86은 오랜 역사를 가지고 있는 자체입니다.실용적인 방법으로 어셈블러 한 방울을 항상 고속으로 쓸 수 없고 특정 날짜에 특정 프로세서의 루틴을 확실하게 최적화할 수 있으며 컴파일러를 실행할 수 없습니다.몇몇 특정한 경우를 제외하고는 그것은 일반적으로 소용없다.교육적이긴 하지만 전반적으로 시간을 들일 가치가 없다.또, 프로세서의 보틀 넥이 없어졌기 때문에, 범용 C 컴파일러의 서투른 것으로 충분합니다.퍼포먼스는 다른 곳에서 확인하실 수 있습니다.
embedded, arm, mips, avr, msp430, pic 등을 의미하는 기타 플랫폼operating system을 실행하고 있는 경우와 실행하지 않는 경우가 있습니다.캐시 또는 데스크탑에 있는 다른 기능을 사용하고 있는 경우와 실행하지 않는 경우가 있습니다.컴파일러의 약점을 알 수 있습니다.또한 프로그래밍 언어는 프로세서가 아닌 프로세서에서 계속 발전하고 있습니다.C는 아마도 낮은 수준의 언어라고 생각되는 경우에도 명령어 집합과 일치하지 않습니다.컴파일러를 능가하는 어셈블러 세그먼트를 생성할 수 있는 경우가 항상 있습니다.반드시 병목현상이 되는 세그먼트는 아니지만, 프로그램 전체에서 개선되는 경우가 많습니다.당신은 여전히 그것을 하는 것의 가치를 확인해야 합니다.임베디드 환경에서는 제품의 성공과 실패를 좌우할 수 있습니다.사용하시는 제품이 더 많은 전력을 필요로 하는 제품에 25달러를 투자하고 있는 경우, 부동산이나 고속 프로세서를 탑재하여 조립기를 사용할 필요가 없습니다.그러나 경쟁사는 유닛당 10달러 이하의 비용을 지출하고 있습니다.또한 ASM을 C와 조합하여 더 작은 메모리, 더 적은 전력, 더 저렴한 부품 등을 사용할 수 있습니다.NRE가 복구되는 한 혼합 ASM 솔루션은 장기적으로 개선될 것입니다.
진정한 임베디드 시장은 전문 엔지니어가 있는 전문 시장입니다.또 다른 임베디드 시장, 임베디드 Linux roku, tivo 등임베디드 폰 등은, 서드 파티의 개발자가 필요하기 때문에, 모두 휴대형 operating system을 탑재할 필요가 있습니다.따라서 플랫폼은 임베디드 시스템이라기보다는 데스크톱에 가까워야 합니다.앞에서 설명한 바와 같이 C 라이브러리 또는 운영체제에 포함되어 있는 경우 어셈블러 최적화가 몇 가지 있을 수 있지만, 데스크톱과 마찬가지로 소프트웨어를 수동으로 최적화하는 대신 휴대할 수 있도록 하드웨어를 더 많이 투입해야 합니다.또, 서드파티의 성공을 위해서 어셈블러가 필요한 경우는, 제품 라인 또는 임베디드 OS에 장해가 발생합니다.
The biggest concern I have is that this knowledge is being lost at an alarming rate. Because nobody inspects the assembler, because nobody writes in assembler, etc. Nobody is noticing that the compilers have not been improving when it comes to the code being produced. Developers often think they have to buy more hardware instead of realizing that by either knowing the compiler or how to program better they can improve their performance by 5 to several hundred percent with the same compiler, sometimes with the same source code. 5-10% usually with the same source code and compiler. gcc 4 does not always produce better code than gcc 3, I keep both around because sometimes gcc3 does better. Target specific compilers can (not always do) run circles around gcc, you can see a few hundred percent improvement sometimes with the same source code different compiler. Where does all of this come from? The folks that still bother to look and/or use assembler. Some of those folks work on the compiler backends. The front end and middle are fun and educational certainly, but the backend is where you make or break quality and performance of the resulting program. Even if you never write assembler but only look at the output from the compiler from time to time (gcc -O2 -s myprog.c) it will make you a better high level programmer and will retain some of this knowledge. If nobody is willing to know and write assembler then by definition we have given up in writing and maintaining compilers for high level languages and software in general will cease to exist.
Understand that with gcc for example the output of the compiler is assembly that is passed to an assembler which turns it into object code. The C compiler does not normally produce binaries. The objects when combined into the final binary, are done by the linker, yet another program that is called by the compiler and not part of the compiler. The compiler turns C or C++ or ADA or whatever into assembler then the assembler and linker tools take it the rest of the way. Dynamic recompilers, like tcc for example, must be able to generate binaries on the fly somehow, but I see that as the exception not the rule. LLVM has its own runtime solution as well as quite visibly showing the high level to internal code to target code to binary path if you use it as a cross compiler.
So back to the point, yes it is done, more often than you think. Mostly has to do with the language not comparing directly to the instruction set, and then the compiler not always producing fast enough code. If you can get say dozens of times improvement on heavily used functions like malloc or memcpy. Or want to have a HD video player on your phone without hardware support, balance the pros and cons of assembler. Truly embedded markets still use assembler quite a bit, sometimes it is all C but sometimes the software is completely coded in assembler. For desktop x86, the processor is not the bottleneck. The processors are microcoded. Even if you make beautiful looking assembler on the surface it wont run really fast on all families x86 processors, sloppy, good enough code is more likely to run about the same across the board.
I highly recommend learning assembler for non-x86 ISAs like arm, thumb/thumb2, mips, msp430, avr. Targets that have compilers, particularly ones with gcc or llvm compiler support. Learn the assembler, learn to understand the output of the C compiler, and prove that you can do better by actually modifying that output and testing it. This knowledge will help make your desktop high level code much better without assembler, faster and more reliable.
It depends. It is (still) being done in some situations, but for the most part, it is not worth it. Modern CPUs are insanely complex, and it is equally complex to write efficient assembly code for them. So most of the time, the assembly you write by hand will end up slower than what the compiler can generate for you.
Assuming a decent compiler released within the last couple of years, you can usually tweak your C/C++ code to gain the same performance benefit as you would using assembly.
A lot of people in the comments and answers here are talking about the "N times speedup" they gained rewriting something in assembly, but that by itself doesn't mean too much. I got a 13 times speedup from rewriting a C function evaluating fluid dynamics equations in C, by applying many of the same optimizations as you would if you were to write it in assembly, by knowing the hardware, and by profiling. At the end, it got close enough to the theoretical peak performance of the CPU that there would be no point in rewriting it in assembly. Usually, it's not the language that's the limiting factor, but the actual code you've written. As long as you're not using "special" instructions that the compiler has difficulty with, it's hard to beat well-written C++ code.
Assembly isn't magically faster. It just takes the compiler out of the loop. That is often a bad thing, unless you really know what you're doing, since the compiler performs a lot of optimizations that are really really painful to do manually. But in rare cases, the compiler just doesn't understand your code, and can't generate efficient assembly for it, and then, it might be useful to write some assembly yourself. Other than driver development or the like (where you need to manipulate the hardware directly), the only place I can think of where writing assembly may be worth it is if you're stuck with a compiler that can't generate efficient SSE code from intrinsics (such as MSVC). Even there, I'd still start out using intrinsics in C++, and profile it and try to tweak it as much as possible, but because the compiler just isn't very good at this, it might eventually be worth it to rewrite that code in assembly.
use this:
__asm__ __volatile__(/*assembly code goes here*/);
the __asm__ can also just be asm.
The __volatile__ stops the compiler from making further optimizations.
There's very few reasons to use assembly language these days, even low-level constructs like SSE and the older MMX have built-in intrinsics in both gcc and MSVC (icc too I bet but I never used it).
Honestly, optimizers these days are so insanely aggressive that most people couldn't match even half their performance writing code in assembly. You can change how data is ordered in memory (for locality) or tell the compiler more about your code (through #pragma), but actually writing assembly code... doubt you'll get anything extra from it.
@VJo, note that using intrinsics in high level C code would let you do the same optimizations, without using a single assembly instruction.
And for what it's worth, there have been discussions about the next Microsoft C++ compiler, and how they'll drop inline assembly from it. That speaks volumes about the need for it.
Take a look here, where the guy improved performances 6 times using assembly code. So, the answer is : it is still being done, but the compiler is doing pretty good job.
On my work, I used assembly on embedded target (micro controller) for low level access.
But for a PC software, I don't think it is very usefull.
I have an example of assembly optimization I've done, but again it's on an embedded target. You can see some examples of assembly programming for PCs too, and it creates really small and fast programs, but usually not worth the effort (Look for "assembly for windows", you can find some very small and pretty programs).
My example was when I was writing a printer controller, and there was a function that was supposed to be called every 50 micro-seconds. It has to do reshuffling of bits, more or less. Using C I've been able to do it in about 35microseconds, and with assembly I've done it in about 8 microseconds. It's a very specific procedure but still, something real and necessary.
On some embedded devices (phones and PDAs), it's useful because the compilers are not terribly mature, and can generate extremely slow and even incorrect code. I have personally had to work around, or write assembly code to fix, the buggy output of several different compilers for ARM-based embedded platforms.
- Yes. Use either inline assembly or link assembly object modules. Which method you should use depends on how much assembly code you need to write. Usually it's OK to use inline assembly for a couple of lines and switch to separate object modules once if it's more than one function.
- Definitely, but sometimes it's necessary. The prominent example here would be programming an operating system.
- Most compilers today optimize the code you write in a high-level language much better than anyone could ever write assembly code. People mostly use it to write code that would otherwise be impossible to write in a high-level language like C. If someone uses it for anything else means he is either better at optimization than a modern compiler (I doubt that) or just plain stupid, e.g. he doesn't know what compiler flags or function attributes to use.
ReferenceURL : https://stackoverflow.com/questions/4202687/using-assembly-language-in-c-c
'source' 카테고리의 다른 글
| Stringization - 어떻게 작동합니까? (0) | 2022.08.20 |
|---|---|
| Android에서 다른 응용 프로그램에서 작업을 시작하는 방법 (0) | 2022.08.20 |
| 경고:Assert 유형의 assertEquals 메서드는 더 이상 사용되지 않습니다. (0) | 2022.08.19 |
| Vue 플러그인에서 Vuex에 액세스하는 방법 (0) | 2022.08.19 |
| Java에서 문자열 반전 (0) | 2022.08.19 |