글로벌 변수는 더 빠른 코드를 의미합니까?
최근 1996년에 쓴 게임 프로그래밍 기사에서 글로벌 변수를 사용하는 것이 매개 변수를 통과하는 것보다 빠르다는 것을 읽었습니다.
이것이 사실이었는가, 만약 사실이었다면, 오늘날에도 여전히 사실인가?
간단한 답변 - 아니요, 훌륭한 프로그래머는 작업에 적합한 도구를 알고 사용하여 코드가 요건을 충족하지 않는 체계적인 방법으로 최적화함으로써 코드를 빠르게 진행할 수 있습니다.
더 긴 답변 - 이 기사는 특별히 잘 작성되지 않았지만, 프로그램 속도 향상에 대한 일반적인 조언이 아니라 '더 빠른 블릿을 하는 15가지 방법'이라고 생각합니다.이것을 일반적인 사례로 추정하는 것은 글의 장점을 어떻게 생각하든 간에 글쓴이의 요점을 놓치고 있다.
퍼포먼스 어드바이스를 찾고 있다면 샘플 코드의 어설션을 뒷받침하기 위해 구체적인 코드 변경을 특정하거나 제시하지 않은 기사에 신뢰성이 전혀 없습니다.또, 코드의 측정이 좋은 아이디어라고는 제안하지 않습니다.코드를 개선하는 방법을 보여주지 않을 경우, 왜 코드를 포함합니까?
FAR 포인터는 오래 전에 PC에서 문제가 되지 않게 되었습니다.
진지한 게임 개발자(또는 다른 전문 프로그래머)는 다음과 같은 조언에 대해 크게 웃을 것입니다.
주장을 완전히 빼거나, 아니면 그냥 덧셈을 할 수 있습니다.
#define NDEBUG
최종 버전을 컴파일할 때 사용합니다.
이 15가지 힌트 중 하나의 장점을 평가하려면 14년 전의 기사이므로 코드를 최신 컴파일러(Visual C++10이라고 함)로 컴파일하여 글로벌 변수(또는 기타 힌트)를 사용하여 빠르게 할 수 있는 영역을 특정해 보시기 바랍니다.
[장난입니다] 이 기사는 완전히 무시하고 스택 오버플로에 대해 특정 퍼포먼스에 관한 질문을 하는 것이 좋습니다.이렇게 하면 답변이 동료 검토되고 예시 코드 또는 양호한 외부 증거로 뒷받침되며 최신 정보가 제공됩니다.]
파라미터에서 글로벌 변수로 전환하면 다음 세 가지 중 하나가 발생합니다.
- 그것은 더 빨리 달린다
- 그것은 똑같이 실행된다
- 속도가 느리다
단순하지 않은 콘크리트 케이스에서 어떤 것이 더 빠른지 확인하려면 성능을 측정해야 합니다.1996년에도 그랬고 지금도 그랬고 내일도 그랬다.
퍼포먼스는 잠시 제쳐두고 대규모 프로젝트의 글로벌 변수에는 의존관계가 생기기 때문에 유지보수와 테스트가 거의 항상 어려워집니다.
오늘날 퍼포먼스상의 이유로 글로벌 변수의 정당한 용도를 찾으려 할 때, 나는 프리트의 답변의 예에 매우 찬성합니다: 마이크로컨트롤러 프로그램이나 디바이스 드라이버에 매우 자주 필요한 변수입니다.극단적인 경우는 글로벌 변수 전용 프로세서 레지스터입니다.
글로벌 변수의 성능과 파라미터 전달에 대해 추론할 때 컴파일러가 이를 구현하는 방법은 관련이 있습니다.글로벌 변수는 일반적으로 고정된 위치에 저장됩니다.때때로 컴파일러는 글로벌에 액세스하기 위해 직접 주소 지정을 생성합니다.그러나 때때로 컴파일러는 하나 더 간접적인 방법을 사용하고 글로벌을 위해 일종의 기호 테이블을 사용합니다.AIX용 IIRC gcc는 15년 전에 이 작업을 수행했습니다.이 환경에서는 작은 유형의 지구본이 로컬 및 파라미터 전달보다 항상 느렸습니다.
한편 컴파일러는 스택에 파라미터를 푸시하거나 레지스터 또는 양쪽의 혼합으로 파라미터를 전달할 수 있습니다.
플랫폼과 프로그램에 따라 다르며 실제로 타이밍을 측정해야 하는 등의 문제에 대해서는 누구나 적절한 경고 답변을 이미 제시했습니다.이상, x86 및 PowerPC에서의 게임 프로그래밍의 구체적인 사례에 대한 질문에 직접 답변해 드리겠습니다.
1996년에는 파라미터를 스택에 푸시할 때 추가 명령이 필요하여 인텔 CPU 파이프라인 내에서 잠시 정지하는 경우가 있었습니다.이 경우 파라미터가 완전히 전달되지 않고 리터럴주소로부터의 데이터를 읽는 것으로부터, 속도가 매우 저하될 가능성이 있습니다.
이는 x86 또는 Power에서는 더 이상 해당되지 않습니다.대부분의 게임 콘솔에 사용되는 PC.일반적으로 글로벌 사용은 다음 두 가지 이유로 파라미터 전달보다 느립니다.
- 파라미터 전달이 보다 잘 구현되었습니다.최신 CPU는 레지스터에 파라미터를 전달하므로 함수의 파라미터 목록에서 값을 읽는 것이 메모리 로드 작업보다 빠릅니다.x86은 레지스터 섀도우링과 스토어 포워딩을 사용하기 때문에 데이터를 스택과 백으로 교환하는 것처럼 보이는 것은 실제로는 단순한 레지스터 이동일 수 있습니다.
- 대부분의 성능 고려 사항에서 데이터 캐시 지연은 CPU 클럭 속도를 훨씬 능가합니다.많이 사용되는 스택은 거의 항상 캐시에 있습니다.임의의 글로벌주소에서 로드하면 캐시 미스(캐시 미스)가 발생할 수 있습니다.메모리 컨트롤러는 메인 RAM에서 데이터를 가져와야 하기 때문에 큰 패널티가 발생합니다.('대규모'는 600 사이클 이상입니다).
더 빠르다는 게 무슨 말이야?
사실 글로벌 변수가 있는 프로그램을 이해하는 데는 없는 프로그램보다 훨씬 더 많은 시간이 걸립니다.
프로그래머가 글로벌을 사용하여 프로그램을 실행할 때 걸리는 시간이 사용자가 얻는 시간보다 짧다면 글로벌을 사용하는 것이 더 빠르다고 말할 수 있습니다.
하지만 프로그램은 2년 동안 하루에 한 번씩 10명씩 진행된다고 생각해 보세요.또한 글로벌을 사용하지 않을 경우 2.84632초, 글로벌을 사용할 경우 2.84217초(0.00415초 증가)가 소요됩니다.이는 TOTAL 런타임의 727초 단축입니다.10분의 실행 시간을 얻는 것은 프로그래머의 시간과 관련하여 글로벌하게 도입할 가치가 없습니다.
프로세서 명령어를 회피하는 코드(짧은 코드)는 어느 정도 고속입니다.하지만 얼마나 더 빠를까요?별로!또한 컴파일러 최적화 전략으로 인해 코드가 작아질 수도 있습니다.
오늘날 이것은 매우 특정 애플리케이션에 대한 최적화일 뿐입니다.일반적으로 울트라 타임 크리티컬 드라이버나 마이크로 컨트롤 코드의 경우입니다.
보수성과 정확성의 문제는 차치하고, 기본적으로 글로벌과 파라미터에 관해 성능을 좌우하는 두 가지 요소가 있습니다.
글로벌하게 작성하면 복사를 피할 수 있습니다.그게 조금 더 빠르네요.파라미터의 값을 전달할 때는 파라미터가 로컬복사 상에서 기능하여 발신자의 데이터 복사를 손상시키지 않도록 복사해야 합니다.적어도 이론상으로는.일부 최신 옵티마이저는 코드가 올바르게 동작하고 있는 것을 확인하면 상당히 까다로운 작업을 수행합니다.함수는 자동으로 삽입될 수 있으며, 컴파일러는 함수가 파라미터에 아무런 영향을 미치지 않고 복사만 최적화한다는 것을 알 수 있습니다.
글로벌하게 작성하면 캐시에 대한 거짓말이 됩니다.함수에 모든 변수와 몇 가지 매개변수가 깔끔하게 포함되어 있으면 데이터가 모두 한 곳에 있는 경향이 있습니다.변수 중 일부는 레지스터에 저장되고 일부는 서로 바로 옆에 있기 때문에 즉시 캐시에 저장될 수 있습니다.많은 글로벌 변수를 사용하는 것은 기본적으로 캐시에 대한 병리학적 동작입니다.다양한 글로벌이 동일한 기능에서 사용된다는 보장은 없습니다.위치는 사용법과 명확한 상관관계가 없습니다.작업 세트가 작기 때문에 어디에 있든 상관없습니다.그리고 모두 캐시에 저장됩니다.
이 모든 것이 내 위에 있는 포스터에 의해 만들어진 요점을 말해준다.
파라미터에서 글로벌 변수로 전환하면 다음 세 가지 중 하나가 발생합니다.
* it runs faster * it runs the same * it runs slower
단순하지 않은 콘크리트 케이스에서 어떤 것이 더 빠른지 확인하려면 성능을 측정해야 합니다.1996년에도 그랬고 지금도 그랬고 내일도 그랬다.
사용하는 컴파일러의 특정 동작과 코드를 실행하기 위해 사용하는 하드웨어의 정확한 상세 내용에 따라서는 글로벌 변수가 매우 적은 성능의 이점이 될 수 있습니다.그 가능성은 실험적으로 너무 느리게 실행되는 코드에서 시도해 볼 가치가 있을 수 있습니다.당신의 실험의 답은 내일 바뀔 수 있기 때문에 아마도 헌신할 가치가 없을 것이다.따라서 정답은 거의 항상 "올바른" 디자인 패턴을 선택하고 더 추한 디자인을 피하는 것입니다.프로젝트를 의도적으로 축소하기 전에 보다 나은 알고리즘, 보다 효율적인 데이터 구조 등을 찾아보십시오.장기적으로는 훨씬 더 좋은 결과입니다.
그리고 dev time vs user time 인수와 별도로 dev time vs.무어의 시간 논쟁.무어의 법칙에 따라 컴퓨터가 매년 절반 정도 빨라진다고 가정하면 간단한 반올림 수치로 볼 때 매주 1%씩 꾸준히 진보한다고 가정할 수 있습니다.1% 정도 개선할 수 있는 미세 최적화를 검토하고 있으며, 복잡한 작업으로부터 1주일이 더 걸리는 경우, 일주일만 쉬어도 사용자의 평균 실행 시간에 동일한 효과가 있습니다.
마이크로 최적화로 컴파일러가 그러한 방법을 사용하지 않고 생성할 수 있는 최적화에 의해 사라질 수 있습니다.실제로 글로벌을 사용하면 컴파일러 최적화가 금지될 수 있습니다.일반적으로 신뢰할 수 있고 유지보수가 가능한 코드는 더 큰 가치가 있으며, 글로벌은 이에 도움이 되지 않습니다.
함수 파라미터를 대체하기 위해 글로벌을 사용하면 이러한 모든 기능이 재진입하지 않게 됩니다.멀티 스레딩을 사용하는 경우 문제가 될 수 있습니다.196년의 게임 개발에서는 일반적인 관례가 아니라 멀티 코어 프로세서의 등장으로 인해 더욱 일반적입니다.재귀에도 문제가 없지만 재귀에도 문제가 없습니다.
중요한 코드 집합에서 알고리즘과 데이터 구조의 높은 수준의 최적화에 더 많은 마일리지가 있을 수 있다.또한 파라미터 전달을 회피하는 옵션(특히 C++ 클래스 멤버 변수)도 글로벌 변수 이외에 열려 있습니다.
코드에서 글로벌 변수를 자주 사용하는 것이 성능에 측정 가능하거나 유용한 차이를 만든다면 먼저 디자인에 의문을 제기하겠습니다.
글로벌 변수에 내재된 문제와 이를 회피하는 몇 가지 방법에 대한 설명은 Jack Gannsle의 A Pox on Globals를 참조하십시오.이 기사는 임베디드 시스템 개발에 관한 것이지만 일반적으로 해당됩니다.이는 일부 임베디드 시스템 개발자가 게임 개발에서 사용하는 것과 같은 잘못된 이유로 글로벌을 사용할 충분한 이유가 있다고 생각하기 때문입니다.
파라미터 전달 대신 글로벌파라미터 사용을 고려하고 있는 경우, 이는 파라미터 전달이 필요한 긴 메서드/함수 체인이 있음을 의미합니다.이 경우 파라미터에서 글로벌 변수로 전환하여 CPU 사이클을 절약할 수 있습니다.
따라서, 상황에 따라 다르다고 말하는 사람들은 그들이 틀렸다고 생각합니다.REGISTER 파라미터가 전달되더라도 파라미터를 콜처로 푸시하기 위한 CPU 사이클과 오버헤드가 계속 증가합니다.
하지만 - 난 절대 그러지 않아.CPU는 현재 우수하며, 12Mhz 8086이 문제가 될 수 있는 경우도 있습니다.요즘은 임베디드나 초터보 충전 성능 코드를 쓰지 않으면 코드 로직이 깨지지 않고 모듈식으로 잘 되는 코드를 고집합니다.
And lastly, leave machine language code generation to compiler - guys who designed it are best at knowing how their baby performs and will make your code run at its best.
In general (but it may depend greatly on compiler and platform implementation), passing parameters mean writing them onto the stack which you would not need with global variable.
That said, global variable may mean include page refresh in the MMU or memory controller whereas the stack may be located in much faster memory available to the processor...
Sorry, no good answer for a general question like this, just measure it (and try different scenarios too)
It was faster when we had <100mhz processors. Now that that processors are 100x faster this 'problem' is 100x less significant. It wasnt a big deal then, it was a big deal when you did it in assembly and had no (good) optimizer.
Says the guy who programmed on a 3mhz processor. Yes you read that right and 64k was NOT enough.
I see a lot of theoretical answers, but no practical advice for your scenario. What I'm guessing is that you have a large number of parameters to pass down through a number of function calls, and you're worried about accumulated overhead from many levels of call frames and many parameters at each level. Otherwise your concern is completely unfounded.
If this is your scenario, you should probably put all of the parameters in a "context" structure and pass a pointer to that structure. This will ensure data locality, and makes it so you don't have to pass more than one argument (the pointer) at each function call.
Parameters accessed this way are slightly more expensive to access than true function arguments (you need an extra register to hold the pointer to the base of the structure, as opposed to the frame pointer which would serve this purpose with function arguments), and individually (but probably not with cache effects factored in) more expensive to access than global variables in normal, non-PIC code. However, if your code is in a shared library/DLL using position independent code, the cost of accessing parameters passed by pointer to struct is cheaper than accessing a global variable and identical to accessing static variables, due to GOT and GOT-relative addressing. This is another reason never to use global variables for parameter passing: if you may eventually put your code in a shared library/DLL, any possible performance benefits will suddenly backfire!
Like everything else: yes and no. There is no one answer because it depends on context.
Counterpoints:
Imagine programming on Itanium where you have hundreds of registers. You can put quite a few globals into those, which will be faster than the typical way globals are implemented in C (some static address (although they might just hardcode the globals into instructions if they are word length)). Even if the globals are in cache the whole time, registers may still be faster.
In Java, overuse of globals (statics) can decrease performance because of initialization locks that have to be done. If 10 classes want to access some static class, they all have to wait for that class to finish initializing its static fields, which can take anywhere form no time up to forever.
In any case, global state is just bad practice, it raises code complexity. Well designed code is naturally fast enough 99.9% of the time. It seems like newer languages are removing global state all together. E removes global state because it violates their security model. Haskell removes state all together. The fact that Haskell exists and has implementations that outperform most other languages is proof enough for me that I will never use globals again.
Also, in the near future, when we all have hundreds of cores, global state isn't really going to help much.
It might still be true, under some circumstances. A global variable might be as fast as a pointer to a variable, where its pointer is stored in/passed through registers only. So, it is a question about the count of registers, you can use.
To speed-optimize a function call, you could do several other things, that might perform better with global-variable-hacks:
- Minimize the count of local variables in the function to a few (explicit) register variables.
- Minimize the count of parameters of the function, i.e. by using pointers to structures instead of using the same parameter-constellations in functions that call each other.
- Make the function "naked", that means that it does not use the stack at all.
- Use "proper-tail-calls" (does neither work with java/-bytecode nor java-/ecma-script)
- If there is no better way, hack yourself sth like TABLES_NEXT_TO_CODE, which locates your global variables next to the function code. In functional languages this is a backend-optimization that uses the function-pointer as data-pointer, too; but as long as you do not program in a functional language, you only need to locate those variables beside those used by the function. Then again, you only want this to remove the stack-handling from your function. If your compiler generates assembler code that handles the stack, then there is no point in doing this, you could use pointers instead.
I've found this "gcc attribute overview": http://www.ohse.de/uwe/articles/gcc-attributes.html
and I can give you these tags for googling: - Proper Tail Call (it is mostly relevant to imperative backends of functional languages) - TABLES_NEXT_TO_CODE (it is mostly relevant to Haskell and LLVM)
But you have 'spaghetti code', when you often use global variables.
ReferenceURL : https://stackoverflow.com/questions/3952670/do-global-variables-mean-faster-code
'programing' 카테고리의 다른 글
vue typescript 클래스 구성 요소와 vueraggable (0) | 2022.08.10 |
---|---|
정적 로컬 변수로 포인터를 반환하는 것은 안전합니까? (0) | 2022.08.10 |
같은 패키지에 roxygen2와 doxygen을 사용하고 있습니까? (0) | 2022.08.09 |
VueJS 2 + TypeScript: 계산된 값이 데이터에 의해 정의된 속성을 감지하지 않음 (0) | 2022.08.09 |
부울에 따라 nuxt 링크 사용 안 함 (0) | 2022.08.09 |