C 프로그램을 최적화하기 위해 어떤 코딩 기법을 사용합니까?
몇 년 전에 저는 비교적 고위급 임베디드 C 프로그래머 자리의 후보자들을 인터뷰하는 패널에 있었습니다.
제가 질문한 표준 질문 중 하나는 최적화 기술에 관한 것이었습니다.후보자들 중에 답이 없는 분들이 계셔서 상당히 놀랐습니다.
그렇다면 후세를 위한 목록을 작성하기 위해 C 프로그램을 최적화할 때 보통 어떤 기술과 구성을 사용합니까?
속도와 크기에 대한 최적화에 대한 답변이 모두 수락되었습니다.
첫 번째 일은 - 너무 일찍 최적화하지 마세요.코드 덩어리를 신중하게 최적화하는 데 시간을 들이는 것은 드물지 않은 일입니다. 하지만 이 코드는 자신이 생각했던 병목 현상이 아니라는 것을 알게 되었습니다.아니면 다른 말로 하면 "빨리 만들기 전에, 잘 되게 하라"는 것입니다.
코드를 최적화하기 전에 알고리즘을 최적화할 수 있는 옵션이 있는지 조사합니다.코드를 최적화하는 것보다 불량한 알고리즘을 최적화함으로써 성능 향상을 찾는 것이 더 쉬울 것입니다. 알고리즘을 변경할 때 폐기해야 합니다.
우선 최적화해야 하는 이유를 생각해 보십시오.무엇을 이루고자 합니까?예를 들어, 어떤 이벤트에 대한 응답 시간을 개선하기 위해 시간이 중요한 영역을 최소화하기 위해 실행 순서를 변경할 수 있는 기회가 있다면 해결합니다.예를 들어, 어떤 외부 인터럽트에 대한 대응을 개선하려고 할 때, 사건 사이의 데드 타임에 어떤 준비를 할 수 있습니까?
코드를 최적화해야 한다고 결정했으면 어느 비트를 최적화하시겠습니까?프로파일러를 사용합니다.가장 자주 사용하는 부분에 관심을 집중합니다.
그렇다면 그 부분에 대해 어떻게 할 수 있겠습니까?
- 상태 점검 최소화조건(예: 루프에 대한 종료 조건)을 확인하는 것은 실제 처리에 소비되지 않는 시간입니다.루프 언롤링과 같은 기술로 상태 점검을 최소화할 수 있습니다.
- 일부 상황에서는 기능 포인터를 사용하여 조건 점검을 제거할 수도 있습니다.예를 들어 상태 머신을 구현하는 경우 개별 상태에 대한 핸들러를 작은 함수로 구현하고(프로토타입이 균일한) 다음 핸들러의 함수 포인터를 저장하여 "다음 상태"를 저장하는 것이 개별 상태에 구현된 핸들러 코드가 있는 큰 스위치 문을 사용하는 것보다 더 효율적이라는 것을 알 수 있습니다.사건 진술서YMMV.
- 기능 호출을 최소화합니다.함수 호출은 일반적으로 상황 저장(예: 레지스터에 포함된 로컬 변수를 스택에 기록하고 스택 포인터를 저장하는 등) 부담을 가지므로 호출하지 않아도 시간이 절약됩니다.(공간이 아닌 속도에 최적화하는 경우) 한 가지 옵션은 인라인 기능을 사용하는 것입니다.
- 함수 호출이 불가피한 경우 함수로 전달되는 데이터를 최소화합니다.예를 들어 포인터를 통과하는 것이 구조를 통과하는 것보다 더 효율적일 가능성이 높습니다.
- 속도를 최적화할 때 플랫폼에 적합한 기본 크기의 데이터 유형을 선택합니다.예를 들어 32비트 프로세서에서는 8비트 또는 16비트 값보다 32비트 값을 조작하는 것이 더 효율적일 수 있습니다.(측면 참고 - 컴파일러가 사용자가 생각하는 대로 수행하고 있는지 확인할 필요가 있습니다.컴파일러가 8비트 값에 대해 16비트 산술을 수행할 것을 고집하는 상황이 발생했습니다. 모든 변환 및 변환을 함께 수행합니다.
- 미리 계산할 수 있는 데이터를 찾고 초기화 중에 계산하거나 컴파일 시에 계산하는 것이 더 좋습니다.예를 들어 CRC를 구현할 때 크기에는 좋지만 성능에는 문제가 있는 다항식을 직접 사용하여 CRC 값을 즉시 계산하거나, 크기에 비해 훨씬 빠른 구현인 모든 중간 값의 표를 생성할 수 있습니다.
- 데이터를 현지화합니다.데이터 덩어리를 자주 조작하는 경우, 프로세서는 데이터를 모두 캐시에 저장함으로써 속도를 높일 수 있습니다.또한 컴파일러는 보다 현지화된 데이터에 적합한 더 짧은 명령어(예: 32비트 대신 8비트 오프셋을 사용하는 명령어)를 사용할 수 있습니다.
- 같은 맥락에서 기능을 현지화합니다.같은 이유로.
- 수행 중인 작업에 대해 가정할 수 있는 사항을 파악하고 이를 활용할 수 있는 방법을 찾을 수 있습니다.예를 들어, 8비트 플랫폼에서 32비트 값에 대해 수행하는 유일한 연산이 증분일 경우 일반 산술 연산을 사용하는 대신 이 목적을 위해 특별히 인라인을 사용(또는 매크로를 생성)함으로써 컴파일러보다 더 잘 수행할 수 있음을 발견할 수 있습니다.
- 값비싼 지시를 피하세요. 분할이 대표적인 예입니다.
- "register" 키워드는 여러분의 친구가 될 수 있습니다(비록 여러분의 컴파일러가 여러분의 레지스터 사용법에 대해 꽤 좋은 아이디어를 가지고 있기를 바랍니다)."register"를 사용하려면 먼저 "register"할 지역 변수를 선언해야 할 가능성이 높습니다.
- 데이터 유형과 일치해야 합니다.데이터 유형(예: 단축 및 int, 중복 및 float)의 혼합에 대해 산술을 수행하는 경우 컴파일러는 각 불일치에 대해 암시적 유형 변환을 추가하고 있습니다.이것은 불필요할 수도 있는 낭비된 CPU 사이클입니다.
위에 나열된 대부분의 옵션은 나쁜 영향 없이 정상적인 업무 수행의 일부로 사용할 수 있습니다.그러나 최고의 성능을 발휘하고자 한다면: - 오류 검사를 (안전하게) 비활성화할 수 있는 부분을 조사합니다.권장되지는 않지만, 공간과 주기를 절약할 수 있습니다. - 코드의 일부를 어셈블러에서 수작업으로 만듭니다.이는 물론 코드를 더 이상 휴대할 수 없음을 의미하지만 문제가 아닌 경우에는 여기에서 비용을 절감할 수 있습니다.사용자가 마음대로 사용할 수 있는 레지스터로 데이터를 이동하는 데 시간이 걸릴 수 있습니다(즉, 컴파일러의 레지스터 사용을 충족하기 위해).또한 컴파일러가 자체적으로 꽤 잘 수행해야 한다는 것을 유의하십시오. (물론 예외도 있습니다.)
다른 사람들이 말했듯이 프로파일, 프로파일 프로파일입니다.
실제 기술에 관해서는 아직 언급되지 않은 것으로 생각됩니다.
핫&콜드 데이터 분리:CPU의 캐시 내에서 유지하는 것은 매우 중요합니다.데이터 구조를 자주 액세스하는("핫") 섹션과 거의 액세스하지 않는("콜드") 섹션으로 나누는 것이 이를 지원하는 한 가지 방법입니다.
예:다음과 같은 고객을 위한 구조가 있다고 가정합니다.
struct Customer
{
int ID;
int AccountNumber;
char Name[128];
char Address[256];
};
Customer customers[1000];
이제 ID와 계정 번호에 많이 액세스하고 이름과 주소는 많이 액세스하고 싶지 않다고 가정해 보겠습니다.당신이 할 일은 그것을 둘로 나누는 것입니다.
struct CustomerAccount
{
int ID;
int AccountNumber;
CustomerData *pData;
};
struct CustomerData
{
char Name[128];
char Address[256];
};
CustomerAccount customers[1000];
이와 같이 "고객" 어레이를 루프로 이동할 때 각 엔트리는 12바이트에 불과하므로 캐시에 더 많은 엔트리를 넣을 수 있습니다.렌더링 엔진의 내부 루프와 같은 상황에 적용할 수 있다면 이는 큰 성공이 될 수 있습니다.
제가 좋아하는 기술은 좋은 프로파일러를 사용하는 것입니다.병목 현상이 어디에 있는지 알려주는 좋은 프로필이 없다면 어떤 속임수와 기술도 도움이 되지 않을 것입니다.
내가 접했던 가장 일반적인 기술은 다음과 같습니다.
- 고리풀기
- 더 나은 캐시 프리페치를 위한 루프 최적화(즉, NxM 단일 작업 대신 M 사이클의 N 작업 수행)
- 데이터 정렬
- 인라인 함수
- 손으로 만든 아스미 토막글
일반적인 권장 사항에 관해서는 대부분 이미 소리가 들립니다.
- 더 좋은 알고리즘을 고르다
- 프로파일러를 사용합니다.
- 성능이 20~30% 향상되지 않으면 최적화하지 않음
저수준 최적화의 경우:
- fmpeg(모든 코드 측정을 위한 클럭 레벨 정확도)에서 START_TIMER/STOP_TIMER 매크로.
- 프로파일링은 물론 프로파일링도 가능합니다.
- 엄청난 양의 손으로 코딩된 어셈블리(x264의 /common/x86 디렉토리에서 wc-l을 수행한 다음 대부분의 코드가 템플릿으로 작성되었다는 것을 기억하십시오).
- 일반적으로 신중하게 코딩합니다. 일반적으로 짧은 코드가 더 좋습니다.
- 영리한 하위 수준 알고리즘입니다. 제가 쓴 64비트 비트스트림 라이터처럼 if 한 개만 사용하고 if는 사용하지 않습니다.
- 명시적 쓰기 결합.
- 인텔의 캐시라인 분할 문제와 같은 프로세서의 중요한 이상한 측면을 고려하는 것입니다.
- 조기 종료 점검 비용이 이를 통해 얻는 속도보다 훨씬 적은 손실 또는 거의 손실 없이 조기 종료할 수 있는 경우를 찾습니다.
- 중앙값 계산과 같이 x86 SIMD 장치에 훨씬 더 적합한 작업을 위한 실제로 인라인 어셈블리(MMX 지원을 위해 컴파일 시간 확인 필요).
- 우선, 더 나은/더 빠른 알고리즘을 사용합니다.설계상 느린 코드를 최적화하는 점은 없습니다.
- 속도 최적화 시 메모리를 속도와 맞바꾸세요: 사전 계산된 값의 룩업 테이블, 바이너리 트리, 시스템 호출의 보다 빠른 맞춤형 구현...
- 메모리 거래 속도: 인메모리 압축 사용
힙을 사용하지 않도록 합니다.크기가 동일한 개체에는 obstack 또는 pool-allocator를 사용합니다.수명이 짧은 작은 것들을 스택에 올려놓습니다. alloca는 여전히 존재합니다.
모든 악의 근원은 성숙기에 최적화하는 것입니다!;)
애플리케이션은 설계상 CPU 시간이 많이 필요하지 않기 때문에 디스크와 메모리의 이진 파일 크기에 초점을 맞춥니다.제가 주로 하는 일은 정적 크기의 어레이를 찾고 동적으로 할당된 메모리로 교체하는 것입니다. 나중에 메모리를 확보하는 데 추가적인 노력을 기울일 가치가 있습니다.바이너리의 크기를 줄이기 위해 컴파일 타임에 초기화되는 큰 배열을 찾고 초기화를 런타임으로 설정합니다.
char buf[1024] = { 0, };
/* becomes: */
char buf[1024];
memset(buf, 0, sizeof(buf));
그러면 바이너리에서 1024개의 제로 바이트가 제거됩니다.DATA 섹션은 대신 런타임에 스택에 버퍼를 생성하고 0으로 채웁니다.
편집: 네, 그리고 저는 물건을 현금화하는 것을 좋아합니다.C가 특정한 것은 아니지만 캐싱하는 것에 따라 성능이 크게 향상될 수 있습니다.
추신: 리스트가 완성되면 알려주세요, 매우 궁금합니다. ;)
가능하면 임의의 숫자가 아닌 0과 비교하고, 특히 루프에서는 0과 비교합니다. 0과 비교하는 것은 별도의 빠른 어셈블러 명령으로 구현되는 경우가 많기 때문입니다.
예를 들어, 가능하다면 쓰기
for (i=n; i!=0; --i) { ... }
대신에
for (i=0; i!=n; ++i) { ... }
언급되지 않은 또 다른 것:
- 귀사의 요구사항을 파악하십시오. 발생 가능성이 없거나 발생할 수 없는 상황에 최적화하지 말고, 최대한의 비용 절감에 집중하십시오.
기본/일반:
- 문제가 없을 때는 최적화하지 마십시오.
- 플랫폼/CPU 파악...
- ...철저하게 알고 있습니다.
- 당신의 ABI를 알고있습니다.
- 컴파일러가 최적화 작업을 하도록 내버려 두시오.
실제로 도움이 된 몇 가지 사항:
크기/메모리 옵션:
- bool 저장에 비트 필드 사용
- 큰 전역 배열을 유니언과 중첩하여 재사용(주의)
속도 선택(주의):
- 가능한 경우 미리 계산된 테이블 사용
- 중요한 기능/데이터를 빠른 메모리에 저장
- 자주 사용하는 글로벌용 전용 레지스터 사용
- 0까지 카운트, 0 플래그는 무료입니다.
요약하기 어려운...
데이터 구조:
- 사용 사례에 따라 데이터 구조를 분할하는 것은 매우 중요합니다.플로우 컨트롤을 기반으로 접근하는 데이터를 보유하는 구조가 일반적입니다.이러한 상황은 캐시 사용량을 크게 줄일 수 있습니다.
- 캐시 라인 크기 및 프리페치 규칙을 고려합니다.
- 구조의 구성원을 순서를 바꾸어서 코드에서 순차적으로 액세스하려면 다음과 같이 하십시오.
알고리즘:
- 문제에 대해 생각하고 올바른 알고리즘을 찾는 시간을 갖습니다.
- 선택한 알고리즘의 한계를 알고 있습니다(10개의 요소를 정렬하는 radix-sort/quick-sort는 최선의 선택이 아닐 수 있습니다).
로우 레벨:
- 최신 프로세서의 경우 본체가 작은 루프는 롤을 풀지 않는 것이 좋습니다.프로세서는 이를 위한 자체 감지 메커니즘을 제공하며 파이프라인의 전체 섹션을 단락시킵니다.
- HW 프리페처를 신뢰합니다.물론 데이터 구조가 잘 설계되어 있다면 ;)
- L2 캐시 라인 미스에 신경을 씁니다.
- 프로세서가 코어당 캐시를 더 작게 유지하기 때문에 애플리케이션의 로컬 작업 세트를 최대한 줄이십시오(C2D는 코어당 최대 3MB를 즐겼으며, iCore7은 코어당 최대 256KB + 쿼드 코어 다이의 경우 모든 코어에 공유 8MB를 제공합니다).
무엇보다 중요한 것은: 일찍 측정하고, 자주 측정하고, 절대로 가정하지 않으며, 프로파일러가 검색한 데이터를 기반으로 사고와 최적화를 수행합니다(PTU를 사용하십시오).
또 다른 힌트는 성능이 애플리케이션 성공의 핵심이며 설계 시 고려해야 하며 명확한 성능 목표를 가져야 한다는 것입니다.
이것은 완전한 것과는 거리가 멀지만 흥미로운 근거를 제공해야 합니다.
오늘날 최적화에서 가장 중요한 것은 다음과 같습니다.
- 캐시 존중 - 단순한 패턴으로 메모리에 접근하고, 재미로 루프를 풀지 않도록 합니다.포인터 추적이 많은 데이터 구조 대신 배열을 사용하면 적은 양의 데이터를 사용하는 것이 더 빠를 수 있습니다.그리고 너무 큰 것은 만들지 마세요.
- 지연 방지 - 다른 계산이 즉시 필요한 경우 분할 등을 방지합니다.다른 메모리 액세스(즉, a[b[c])에 의존하는 메모리 액세스는 좋지 않습니다.
- 예측 불가능성 방지 - 예측 불가능한 조건 또는 지연 시간이 더 많이 발생하는 조건을 가진 많은 if/else는 여러분을 정말 엉망으로 만들 것입니다.여기에 유용한 수많은 분기 없는 수학 트릭이 있지만 지연 시간을 늘리고 필요한 경우에만 유용합니다.그렇지 않으면 단순한 코드를 작성하고 미친 루프 조건을 갖지 마십시오.
코드를 복사하여 붙여넣기(예: 루프 언롤링)하거나 루프의 순서를 손으로 다시 정렬하는 등의 최적화 작업에 신경 쓰지 마십시오.컴파일러는 보통 당신보다 이것을 더 잘하지만, 대부분은 그것을 되돌릴 만큼 똑똑하지 않습니다.
코드 실행 프로파일을 수집하면 50%의 효과를 얻을 수 있습니다.나머지 50%는 이러한 보고서를 분석하는 것을 다루고 있습니다.
또한 GCC 또는 VisualC++를 사용하는 경우 컴파일러가 이전 실행에서 정보를 가져온 후 명령을 다시 예약하여 CPU를 더 행복하게 만드는 "profile guided optimization"을 사용할 수 있습니다.
인라인 기능!여기 프로파일링 팬들에게 영감을 받아 저는 제 애플리케이션을 프로파일링했고 MP3 프레임에서 비트 시프트를 수행하는 작은 기능을 발견했습니다.애플리케이션에서 모든 기능 호출의 약 90%를 수행하기 때문에 인라인 및 voila로 만들었습니다. 프로그램은 이전에 사용했던 CPU 시간의 절반을 사용합니다.
대부분의 임베디드 시스템에서는 프로파일링 도구가 없었기 때문에 프로파일러를 사용하는 것은 좋지만 그다지 실용적이지는 않습니다.
속도 최적화의 첫 번째 규칙은 - 중요한 경로를 찾는 것입니다.
보통 이 길은 그리 길지도 않고 그리 복잡하지도 않다는 것을 알게 될 것입니다.이것을 어떻게 최적화하느냐는 여러분이 무엇을 하고 있느냐와 여러분의 능력에 달려 있다고 일반적으로 말하기는 어렵습니다.예를 들어 당신이 원하는 것은 보통 중요한 경로에서 memcpy를 피하므로, 당신은 DMA를 사용하거나 최적화할 필요가 있지만, 당신의 hw가 DMA를 가지고 있지 않다면 어떻게 해야 합니까? 만약 다시 쓰지 않는다면 memcpy 구현이 최선인지 확인하세요.
동적 할당을 내장된 상태에서 전혀 사용하지 마십시오. 그러나 어떤 이유로 동적 할당을 사용하는 경우 중요한 경로에서 수행하지 마십시오.
스레드 우선 순위를 올바르게 구성합니다. 올바른 것은 실제 질문이며 시스템에 따라 다를 수 있습니다.
우리는 타임스탬프와 인덱스를 저장하는 병목, 단순 매크로를 분석하기 위해 매우 간단한 도구를 사용합니다.90%의 사례에서 시간을 보내는 장소를 찾을 수 있는 실행(2-3개)은 거의 없습니다.
그리고 마지막은 코드 리뷰는 매우 중요한 것입니다.대부분의 경우 코드 검토 중 성능 문제를 매우 효과적인 방법으로 방지합니다. :)
- 성능을 측정합니다.
- 현실적이고 사소한 벤치마크를 사용합니다."작은 N에게는 모든 것이 빠르다"는 것을 기억하세요.
- 프로파일러를 사용하여 핫스팟을 찾습니다.
- 동적 메모리 할당, 디스크 액세스, 데이터베이스 액세스, 네트워크 액세스 및 사용자/커널 전환의 수가 줄어듭니다. 이는 종종 핫스팟이 되기 때문입니다.
- 성능을 측정합니다.
또한 성능을 측정해야 합니다.
때로는 공간이 더 많은지 아니면 속도가 더 빠른지를 결정해야 하므로 거의 반대의 최적화로 이어질 수 있습니다.예를 들어, 공간을 최대한 활용하기 위해 구조물(예: #pragma pack(1))을 포장하고 구조물에 비트 필드를 사용합니다.보다 빠른 속도를 위해 프로세서 기본 설정에 맞춰 포장하고 비트 필드를 방지합니다.
또 다른 방법은 realoc을 통해 어레이를 확장하는 데 적합한 크기 조정 알고리즘을 선택하거나 특정 애플리케이션을 기반으로 자신만의 힙 관리자를 작성하는 것입니다.컴파일러와 함께 제공되는 것이 모든 응용 프로그램에 가능한 최선의 해결책이라고 가정하지 마십시오.
만약 누군가가 그 질문에 대한 답을 가지고 있지 않다면, 그것은 그들이 많이 알지 못할 수도 있습니다.
그들이 많은 것을 알고 있는 것일 수도 있습니다.저는 많은 것을 알고 있습니다(IMHO :-). 만약 제가 그 질문을 받았다면, 저는 여러분에게 다음과 같이 반문하고 있을 것입니다.왜 그게 중요하다고 생각하세요?
문제는 성능에 대한 모든 우선순위 개념이 특정 상황에 의해 정보를 받지 못하면 정의에 의해 추측된다는 것입니다.
성과를 위해서는 코딩 기술을 아는 것도 중요하다고 생각하지만, 진단을 통해 문제가 있고 그것이 무엇인지 밝혀지기 전까지는 사용하지 않는 것을 아는 것이 더욱 중요하다고 생각합니다.
이제 저는 제 자신을 반박하고, 만약 그렇게 한다면, 문제를 야기하는 디자인 접근법을 어떻게 인식해야 하는지를 배울 수 있을 것입니다. 그래서 문제를 피할 수 있고, 초보자에게는 너무 이른 최적화처럼 들립니다.
구체적인 예를 들어보자면, 이것은 최적화된 C 어플리케이션입니다.
멋진 리스트.위 목록에서 보지 못한 팁을 하나 덧붙여서 어떤 경우에는 최소한의 비용으로 엄청난 최적화를 달성할 수 있습니다.
바이패스 링커
당신이 있다면, 은 그냥 이 main.c lib를 할 수 있습니다.c라고 하는 두개의 파일로 나누어진 어떤 애플리케이션을 가지고 있다면, 많은 경우에 당신은 단지 a를 추가할 수 있습니다.
\#include "lib.c"
당신의 메인에서.c 그것은 완전히 링커를 우회할 것이고 컴파일러를 위한 훨씬 더 효율적인 최적화를 가능하게 할 것입니다.
파일 간 종속성을 최적화하는 효과는 동일하지만 변경 비용은 일반적으로 더 높습니다.
때때로 구글은 최고의 알고리즘 최적화 도구입니다.제가 복잡한 문제를 가지고 있을 때, 약간의 검색을 통해 몇몇 박사학위를 가진 사람들이 이것과 잘 알려진 문제 사이의 지도를 찾았고 이미 대부분의 작업을 완료했다는 것을 알 수 있습니다.
저는 좀 더 효율적인 알고리즘을 사용하여 최적화하는 것을 추천합니다. 그것을 사후적인 생각으로 하지 말고 처음부터 그렇게 코딩하세요.컴파일러가 대상 프로세서에 대해 당신보다 더 많이 알고 있기 때문에 작은 것들에 대해 상세한 정보를 얻도록 하세요.
우선, 저는 루프를 사용하여 물건을 조회하는 일을 거의 하지 않고, 해시테이블에 항목을 추가한 다음 해시테이블을 사용하여 결과를 조회합니다.
예를 들어 조회할 문자열과 가능한 50개의 값이 있습니다.따라서 50 strcmps를 수행하는 대신 해시 테이블에 50개의 문자열을 모두 추가하고 각 문자열에 고유한 번호를 부여합니다(단 한 번만 수행하면 됨).그런 다음 해시 테이블에서 대상 문자열을 찾아보면 50개의 경우가 모두 있는 하나의 큰 스위치(또는 함수 포인터)가 있습니다.
일반적인 입력 세트(CSS 규칙 등)로 항목을 검색할 때 빠른 코드를 사용하여 가능한 유일한 솔루션을 추적한 다음 생각을 반복하여 일치하는 것을 찾습니다.일치하는 항목이 있으면 결과를 해시 테이블(캐시로)에 저장한 다음 나중에 동일한 입력 세트를 받으면 캐시 결과를 사용합니다.
빠른 코드를 위한 주요 도구는 다음과 같습니다.
hashtable - 빠른 조회 및 캐싱 결과용
qsort - 내가 사용하는 유일한 유형입니다.
bsp - 영역별 검색용 (맵 렌더링 등)
언급URL : https://stackoverflow.com/questions/110684/what-coding-techniques-do-you-use-for-optimising-c-programs
'programing' 카테고리의 다른 글
특정 쿠폰을 사용하는 WooCommerce 주문 목록을 검색하는 방법은? (0) | 2023.09.20 |
---|---|
인코딩 / 디코딩.EXE를 베이스64로 (0) | 2023.09.20 |
현재 선택한 항목을 보존하면서 HTML 선택 옵션을 값별로 정렬하는 가장 효율적인 방법은 무엇입니까? (0) | 2023.09.20 |
자바 프로젝트:ApplicationContext를 로드하지 못했습니다. (0) | 2023.09.20 |
C/C++에서 div 또는 ldiv를 사용하는 이유는 무엇입니까? (0) | 2023.09.20 |