programing

큰 라이브러리를 사용하는 것은 본질적으로 코드를 느리게 만드는 것입니까?

goodcopy 2022. 8. 3. 21:45
반응형

큰 라이브러리를 사용하는 것은 본질적으로 코드를 느리게 만드는 것입니까?

저는 C나 C++와 같은 하위 언어에서 큰 라이브러리(GLibBoost 등)를 사용하는 것을 꺼리게 만드는 심리적 경련이 있습니다.마음속으로 생각해보면

이 도서관은 수천 시간을 들여서저보다 더 많은 언어를 아는 사람들에 의해 만들어졌어요그들의 저자와 팬들은 라이브러리가 빠르고 신뢰할 수 있으며, 이 기능은 매우 유용해 보이며, 제가 (나쁜) 바퀴를 재발명하는 것을 확실히 막을 수 있을 것이라고 말합니다.

하지만 젠장, 난 절대 그 라이브러리의 모든 기능을 사용하지 않을 거야.그것은 너무 크고 아마도 수년 동안 비대해졌을 것이다; 그것은 나의 프로그램이 끌고 다니는 또 다른 공과 사슬이다.

Torvalds rant (논란이 있긴 하지만) 또한 내 마음을 정확히 편안하게 해주지 않는다.

제 생각에 근거가 있나요, 아니면 제가 단지 불합리하거나 무지한 건가요?대형 라이브러리의 기능을 한두 개만 사용하더라도 해당 라이브러리에 링크하면 런타임 성능 오버헤드가 발생합니까?

특정 라이브러리가 무엇인지에 따라 다르겠지만, 저는 일반적으로 대규모 라이브러리가 본질적으로 비효율적인 기능을 가져올지 여부를 알고 싶습니다.

내가 옳고 그름을 알 수 있는 기술적 지식이 없을 때, 나는 이것에 대해 집착하고 중얼거리고 걱정하는 것에 지쳤다.

제발 절 고통에서 벗어나게 해주세요!

대형 라이브러리의 기능을 한두 개만 사용하더라도 해당 라이브러리에 링크하면 런타임 성능 오버헤드가 발생합니까?

일반적으로, 아니다.

해당 라이브러리에 위치 독립 코드가 많지 않은 경우, 요청 시 동적 링커가 라이브러리로 재배치하는 동안 시작 비용이 발생합니다.보통, 그것은 프로그램 시작의 일부입니다.그 이상의 런타임 퍼포먼스 효과는 없습니다.

링커는 빌드 시 정적으로 연결된 라이브러리에서 "데드 코드"를 제거하는 데도 능숙하므로 사용하는 정적 라이브러리의 오버헤드는 최소화됩니다.퍼포먼스도 반영되지 않습니다.

솔직히, 당신은 잘못된 것에 대해 걱정하고 있어요.

GLib에 대해서는 코멘트는 할 수 없지만, Boost의 많은 코드는 헤더 전용이며, 사용자가 사용하는 것에 대해서만 요금을 지불한다는 C++ 원칙을 고려하면 라이브러리는 매우 효율적입니다.링크할 필요가 있는 라이브러리가 몇 개 있습니다만(regex, 파일 시스템이 생각납니다), 그것들은 별개의 라이브러리입니다.Boost를 사용하면 대형 모노리식 라이브러리에 연결할 수 없고 사용하는 소형 구성 요소에만 연결할 수 있습니다.

물론 또 다른 질문은 '대안은 무엇인가?'입니다.Boost yourself boost에 있는 기능을 필요할 때 구현하시겠습니까?많은 유능한 사람들이 이 코드를 작업하여 다수의 컴파일러에서 작동하며 여전히 효율적이라는 것을 보증하고 있기 때문에, 이 작업은 그다지 간단하지 않을 수 있습니다.게다가 적어도 어느 정도는 바퀴를 재창조하고 있습니다.IMHO 당신은 이 시간을 더 생산적으로 보낼 수 있습니다.

부스트는 큰 도서관이 아닙니다.

그것은 많은 작은 도서관들의 컬렉션이다.대부분은 너무 작아서 머리글 한두 개 안에 들어있어요사용.boost::noncopyable질질 끌지 않다boost::regex또는boost::thread코드를 입력합니다.서로 다른 도서관들이야같은 라이브러리 컬렉션의 일부로 배포됩니다.하지만 당신은 당신이 사용한 것에 대해서만 지불합니다.

그러나 일반적으로 말하면, Boost가 그 중 하나가 아니더라도 큰 라이브러리가 존재하기 때문입니다.

제 생각에 근거가 있나요, 아니면 제가 단지 불합리하거나 무지한 건가요?대형 라이브러리의 기능을 한두 개만 사용하더라도 해당 라이브러리에 링크하면 런타임 성능 오버헤드가 발생합니까?

근거가 없다, 어느 정도.직접 테스트할 수 있습니다.

작은 C++ 프로그램을 작성하여 컴파일합니다.이제 호출되지 않지만 정의된 새 함수를 추가합니다.프로그램을 다시 컴파일합니다.최적화가 유효하다고 가정하면 사용되지 않기 때문에 링커에 의해 삭제됩니다.따라서 추가 미사용 코드를 포함하는데 드는 비용은 0입니다.

물론 예외는 있다.코드가 글로벌 객체를 인스턴스화하면 해당 객체는 삭제되지 않을 수 있습니다(따라서iostreamheader는 실행 가능 파일 크기를 증가시킵니다.) 단, 일반적으로 원하는 수만큼 헤더를 포함하거나 라이브러리에 링크할 수 있습니다.또, 추가된 코드를 사용하지 않는 한, 프로그램의 크기, 퍼포먼스, 메모리 사용량에 영향을 주지 않습니다.

또 다른 예외는 .dll 또는 .so에 동적으로 링크할 경우 라이브러리 전체를 배포해야 하므로 사용되지 않는 코드를 제거할 수 없습니다.그러나 정적 라이브러리(.lib 또는 .a) 또는 포함된 헤더 파일과 같이 실행 파일에 정적 컴파일된 라이브러리는 일반적으로 링커에 의해 트리밍되어 사용되지 않는 기호를 제거할 수 있습니다.

대규모 라이브러리는 코드 퍼포먼스의 관점에서 다음과 같이 기능합니다.

  • 런타임 바이너리(대부분)가 있는 경우 메모리를 더 많이 차지합니다.boost런타임 바이너리가 필요하지 않습니다. "하이브리드 전용"입니다.OS는 라이브러리의 실제 사용 부분만 RAM에 로드하지만 로드되는 정보의 세밀도는 페이지 크기와 같기 때문에 필요한 것보다 더 많은 양을 로드할 수 있습니다(단, 시스템 상에서만 4Kb).
  • 런타임 바이너리가 필요한 경우 동적 링커로 로드하는 데 시간이 더 걸립니다.프로그램이 로드될 때마다 동적 링커는 메모리에 실제 주소와 함께 포함할 외부 라이브러리가 필요한 각 기능을 일치시켜야 합니다.시간이 좀 걸리지만, 조금밖에 걸리지 않습니다(단, 데스크톱 환경의 시작 등 많은 프로그램을 로드하는 규모에서는 문제가 되지만 선택지는 없습니다).

    , 공유(동적으로 링크된) 라이브러리의 외부 함수를 호출할 때마다 실행 시 1회의 추가 점프와 몇 번의 포인터 조정이 필요합니다.

개발자의 퍼포먼스 관점에서 보면 다음과 같습니다.

  • 외부 종속성을 추가합니다.당신은 다른 사람에게 의지하게 될 거예요.그 라이브러리의 무료 소프트웨어라도 수정하려면 추가 비용이 필요합니다.매우 낮은 수준의 프로그램(OS 커널을 말하는 것)의 개발자 중에는 누구에게도 의지하는 것을 싫어하는 사람도 있습니다.이것은 그들의 프로페셔널한 특권입니다.이렇게 소리친다.

    그러나 그것은 유익하다고 볼 수 있다.다른 사람들이 익숙해지면boost프로그램 내에서 익숙한 개념과 용어를 발견하고 이를 보다 효과적으로 이해하고 수정할 수 있습니다.

  • 큰 라이브러리에는 일반적으로 라이브러리 고유의 개념이 포함되어 있어 이해하는 데 시간이 걸립니다.QT를 고려합니다.여기에는 신호와 슬롯이 포함되어 있습니다.moc- 관련 인프라스트럭처전체 Qt의 크기에 비해 학습에는 적은 시간이 소요됩니다.하지만 그렇게 큰 도서관의 작은 부분을 사용한다면, 그것은 문제가 될 수 있습니다.

코드가 과잉되어도, 마법처럼 프로세서의 동작이 느려지는 것은 아닙니다.그냥 거기 앉아서 메모리만 조금 차지하고 있을 뿐이죠.

정적으로 링크하고 있고 링커가 조금이라도 타당하다면 실제로 사용하는 기능만 포함됩니다.

프레임워크, 라이브러리 세트 및 일부 개발 도구의 경우 플랫폼 기술이라는 용어를 사용합니다.플랫폼 테크놀로지는 코드의 크기와 퍼포먼스에 미치는 영향 이상의 비용이 있습니다.

  1. 프로젝트 자체가 라이브러리 또는 프레임워크로 사용되는 경우, 라이브러리를 사용하는 개발자에게 플랫폼 테크놀로지의 선택을 강요할 수 있습니다.

  2. 프로젝트를 소스 형식으로 배포하면 최종 사용자에게 플랫폼 테크놀로지의 선택을 강요할 수 있습니다.

  3. 선택한 프레임워크와 라이브러리를 모두 정적으로 링크하지 않으면 최종 사용자에게 라이브러리 버전 관리 문제가 발생할 수 있습니다.

  4. 컴파일 시간이 개발자의 생산성을 향상시킵니다.증분 링크, 미리 컴파일된 헤더, 적절한 헤더 의존성 관리 등은 컴파일 시간을 관리하는 데 도움이 되지만 일부 플랫폼 테크놀로지에 의해 대량의 인라인 코드에 관련된 컴파일러 성능 문제를 해결하지는 못합니다.

  5. 원본으로 배포된 프로젝트의 경우 컴파일 시간이 프로젝트의 최종 사용자에게 영향을 미칩니다.

  6. 많은 플랫폼 테크놀로지에는 독자적인 개발 환경 요건이 있습니다.이러한 요건이 누적되어 프로젝트의 신규 개발자가 컴파일 및 디버깅에 필요한 환경을 복제하는 것이 어렵고 시간이 많이 걸릴 수 있습니다.

  7. 실제로 일부 플랫폼 기술을 사용하면 프로젝트를 위한 새로운 프로그래밍 언어가 생성됩니다.이로 인해 신규 개발자가 기여하기가 더 어려워집니다.

모든 프로젝트에는 플랫폼 테크놀로지에 대한 의존성이 있지만, 이러한 의존성을 최소한으로 억제하면 많은 프로젝트에서 실질적인 이점이 있습니다.

이러한 라이브러리가 동적으로 연결되어 있으면 로드 시 약간의 오버헤드가 발생할 수 있습니다.이것은, 통상, 프로그램 실행에 소비하는 시간의 극히 일부입니다.

그러나 모든 것이 로드되면 오버헤드는 발생하지 않습니다.

모든 힘을 다 쓰고 싶지 않다면 사용하지 마세요.모듈 방식이라 원하는 부품은 사용하고 나머지는 무시할 수 있습니다.

크다고 해서 속도가 느려지는 것은 아닙니다.다른 답변과는 달리 헤더에 완전히 저장된 라이브러리와 오브젝트 파일에 저장된 라이브러리 사이에는 본질적인 차이가 없습니다.

헤더 전용 라이브러리는 간접적인 이점을 얻을 수 있습니다.대부분의 템플릿 기반 라이브러리는 헤더 전용이어야 하며(또는 많은 코드가 헤더로 끝납니다), 템플릿은 많은 최적화 기회를 제공합니다.그러나 일반적인 오브젝트 파일라이브러리에서 코드를 가져와 모두 헤더로 이동하는 것은 보통 그다지 좋은 효과가 없습니다(코드가 부풀어오를 수 있습니다).

특정 라이브러리의 실제 답변은 일반적으로 전체 구조에 따라 달라집니다."부스트"를 거대한 것으로 생각하기 쉽습니다.사실, 그것은 거대한 도서관 소장품입니다. 대부분은 개별적으로 상당히 작습니다.개별 라이브러리는 기술, 목표 등이 서로 다르기 때문에 전체적으로 Boost에 대해 많은 것을(의미 있게) 말할 수 없습니다.그 중 일부(예: 포맷, 할당)는 사용자가 직접 수행할 가능성이 매우 높은 거의 모든 작업보다 속도가 느립니다.다른 업체(예: 풀)는 사용자가 직접 할 수 있는 작업을 제공하지만, 그렇지 않을 수도 있으므로 최소한 약간의 속도 향상을 얻을 수 있습니다.일부(예: uBlas)는 강력한 템플릿 마법을 사용하여 다른 어떤 사용자보다 빠르게 실행하지만, 극소수의 사용자만 단독으로 달성할 수 있습니다.

물론 실제로 개별적으로 큰 도서관도 꽤 있습니다.많은 경우 사용자가 직접 작성하는 것보다 속도가 느립니다.특히, 그들(대부분?)의 대부분은 여러분이 스스로 쓸 가능성이 있는 거의 모든 것보다 훨씬 더 일반적인 것을 시도합니다.그것이 반드시 느린 코드로 이어지는 것은 아니지만, 그 방향에는 확실히 강한 경향이 있습니다.다른 많은 코드와 마찬가지로, 상업적으로 라이브러리를 개발할 때 고객들은 속도의 크기보다 기능에 훨씬 더 관심을 갖는 경향이 있습니다.

또, 일부의 라이브러리에서는, 많은 스페이스와 코드(그리고, 적어도 몇개의 경우)를 문제 해결에 소비하고 있습니다.예를 들어, 몇 년 전에 저는 이미지 처리 라이브러리를 사용했습니다.200개 이상의 이미지 포맷을 지원하는 것은 매우 인상적으로 들렸습니다(어떤 면에서는 실제로 그랬습니다). 하지만 십여 개 이상의 포맷을 처리하는 데 사용한 적은 거의 없습니다(그리고 그 절반만 지원해도 충분했을 것입니다).OTOH, 모든 것에도 불구하고 꽤 빨랐습니다.더 적은 수의 시장을 지원하면 코드가 실제로 느려질 정도로 시장을 제한했을 수 있습니다(예를 들어, JPEG를 IJG보다 빠르게 처리했습니다).

다른 사용자가 말했듯이 동적 라이브러리를 추가할 때 약간의 오버헤드가 발생합니다.라이브러리를 처음 로드할 때 재배치 작업을 수행해야 합니다. 단, 라이브러리가 올바르게 컴파일되어 있으면 비용이 적게 듭니다.검색해야 하는 라이브러리의 수가 증가하기 때문에 개별 기호를 찾는 비용도 증가합니다.

다른 동적 라이브러리를 추가하는 데 드는 메모리 비용은 실제로 사용하는 라이브러리의 양에 따라 크게 달라집니다.코드 페이지가 디스크에서 로드되는 것은 코드의 어떤 것이 실행될 때까지입니다.그러나 라이브러리 파일에 내장된 헤더, 기호 테이블 및 해시 테이블과 같은 다른 데이터가 로드됩니다. 이러한 데이터는 일반적으로 라이브러리의 크기에 비례합니다.

동적 라이브러리의 프로세스와 오버헤드를 설명하는 glibc의 주요 기여자인 Ulrich Drepper의 훌륭한 문서가 있습니다.

링커의 동작에 따라 다릅니다.일부 링커는 게으르고 라이브러리에 있는 모든 코드를 포함합니다.보다 효율적인 링커는 라이브러리에서 필요한 코드만 추출합니다.두 가지 타입 모두 경험이 있습니다.

어느 타입의 링커를 사용해도 소규모 라이브러리의 걱정은 줄어듭니다.작은 라이브러리의 최악의 경우는, 미사용의 코드가 소량인 것입니다.많은 작은 라이브러리는 빌드 시간을 늘릴 수 있습니다.단점은 빌드 시간과 코드 공간입니다.

링커에 대한 흥미로운 테스트는 Hello World의 클래식 프로그램입니다.

#include <stdio>
#include <stdlib>
int main(void)
{
  printf("Hello World\n");
  return EXIT_SUCCESS;
}

printf함수는 필요한 모든 포맷으로 인해 많은 의존성을 가지고 있습니다.느리지만 빠른 링커는 모든 기호를 해결하기 위한 "표준 라이브러리"를 포함할 수 있습니다.보다 효율적인 라이브러리에는 다음이 포함됩니다.printf그리고 그 의존성.이로 인해 링커가 느려집니다.

위의 프로그램은 다음을 사용하여 이 프로그램과 비교할 수 있습니다.puts:

#include <stdio>
#include <stdlib>
int main(void)
{
  puts("Hello World\n");
  return EXIT_SUCCESS;
}

일반적으로는puts버전은 다음 버전보다 작아야 합니다.printf버전, 이유puts에는 포맷이 필요 없기 때문에 의존성이 낮아집니다.레이지 링커에 의해 생성되는 코드사이즈는printf프로그램.

요약하면 라이브러리 크기 결정은 링커에 따라 달라집니다.구체적으로는 링커의 효율입니다.많은 작은 라이브러리는 링커의 효율성에 덜 의존하지만 구축 프로세스는 더 복잡하고 느립니다.

  1. 일반적으로 퍼포먼스에 관한 우려는 그것을 즐겁게 하지 않는 것입니다.왜냐하면 그것이 문제라고 추측하는 것이 문제이기 때문입니다.왜냐하면 그것이 문제라는 것을 모르면 추측하는 것이 '프리미어 최적화'의 이면에 있는 중심 개념이기 때문입니다.퍼포먼스 문제에 대처하는 것은, 문제가 발생했을 때에 진단하는 입니다.이전에는 진단하지 않았습니다.그 문제들은 당신이 짐작할 수 없었던 것들이에요.여기 확장 예가 있습니다.

  2. 그렇게 하면 코드로든 라이브러리로든 퍼포먼스 문제를 일으키기 쉬운 설계 방법을 알 수 있습니다.(라이브러리는 퍼포먼스에 문제가 있을 수 있습니다.)이를 학습하여 프로젝트에 적용하면 시기상조하게 최적화되지만 어쨌든 문제를 회피하는 효과가 있습니다.학습 내용을 요약하면, 너무 많은 추상화 계층과 과장된 클래스 계층(특히 알림 스타일의 업데이트로 가득 찬 계층)이 성능 문제의 원인인 경우가 많습니다.

동시에 서드파티 라이브러리 등에 대한 당신의 신중을 공유합니다.서드파티 패키지가 「시너리」를 위해서 「레버리」되는 프로젝트를 몇번이나 실시해 왔습니다.그 후, 벤더는 물거품이 되거나, 제품을 포기하거나, 또는 Microsoft가 OS를 변경했기 때문에, 제품을 폐기하거나, 폐기하거나 하는 일이 있었습니다.그러면 서드파티 패키지에 크게 의존하던 우리 제품이 작동하지 않게 되고, 오리지널 프로그래머들이 떠난 지 오래 동안 우리 쪽에 많은 비용이 소요됩니다.

"또 다른 볼과 체인"정말?

아니면 처음부터 애플리케이션을 지원하는 안정적이고 안정적인 플랫폼입니까?

다른 프로젝트에 사용하고 있기 때문에, 「너무 크고 비대하게」라이브러리를 좋아하는 사람도 있습니다.

실제로, 「너무 크고 비대하게」라이브러리를 사용하지 않기 때문에, 소프트웨어 조작을 거부하는 경우가 있습니다.

엄밀히 말하면 그렇다는 거죠그러나 이러한 비효율성은 실질적으로 매우 드물게 중요합니다.여기서는 C, C++, D와 같이 정적 컴파일된 언어를 가정합니다.

최신 OS에서 실행 파일을 메모리에 로드하면 주소 공간이 해당 파일에 매핑됩니다.즉, executable이 아무리 크더라도 사용되지 않는 페이지 크기의 코드 블록 전체가 있으면 물리적 메모리에 영향을 주지 않습니다.단, 32비트 시스템에서는 주소 공간이 낭비될 수 있으며, 경우에 따라서는 이것이 약간 문제가 될 수 있습니다.

라이브러리에 링크할 때, 좋은 링커는 사용하지 않는 여분의 데이터를 버립니다.단, 특히 템플릿 인스턴스화의 경우 이러한 현상이 항상 발생하는 것은 아닙니다.따라서 바이너리는 엄밀하게 필요한 것보다 약간 더 클 수 있습니다.

사용하는 코드와 인터리브를 많이 사용하지 않는 코드가 있는 경우 CPU 캐시의 공간을 낭비할 수 있습니다.그러나 캐시 라인이 작기 때문에(보통 64바이트), 실질적으로 중요한 정도는 거의 발생하지 않습니다.

목표가 무엇인지 스스로에게 물어보십시오.현재의 미드레인지 워크스테이션입니까?문제 없습니다.오래된 하드웨어인지, 아니면 제한된 임베디드 시스템인지, 그렇다면 그럴 수 있습니다.

이전 포스터에서 설명한 바와 같이 코드가 있는 것만으로도 성능 면에서 큰 비용이 들지 않습니다(캐시의 위치를 줄이고 로딩 시간을 늘릴 수 있습니다).

저는 Microsoft Windows 및 Windows 구축 시 작업하고 있습니다.SIZE용으로 컴파일된 빌드가 SPEED용으로 컴파일된 빌드보다 빠릅니다.페이지 폴트 발생이 적기 때문입니다.

FFTW와 ATLAS는 매우 큰 라이브러리입니다.이상하게도 이들은 세계에서 가장 빠른 소프트웨어, 즉 슈퍼컴퓨터에서 실행되도록 최적화된 애플리케이션에서 큰 역할을 합니다.아니요, 대규모 라이브러리를 사용한다고 해서 코드가 느려지는 것은 아닙니다. 특히 FFT 또는 BLAS 루틴을 직접 구현하는 경우에는 더욱 그렇습니다.

You are very right to be worried, especially when it comes to boost. It's not so much due to anyone writing them being incompetent but due to two issues.

  1. Templates are just inherently bloated code. This didn't matter as much 10 years ago, but nowadays the CPU is much faster than memory access and this trend continues. I'd almost say templates are an obsolescent feature.

It's not so bad for user code which is usually somewhat practical, but in many libraries everything is defined in terms of other templates or template on on multiple items (meaning exponential template code explosions).

Simply adding in iostream adds about 3 mb (!!!) to your code. Now add in some boost nonsense and you have 30 mb of code if you sinply declare a couple of particularly weird data structures.

Worse, you can't even easily profile this. I can tell you the difference between code written by me and code from template libraries is DRAMATIC but for a more naieve approach you may think you are doing worse from a simple test, but the cost in code bloat will take its tool in a large realworld app.

  1. Complexity. When you look at the things in Boost, they are all things that complicate your code to a huge degree. Things like smart pointers, functors, all sorts of complicated stuff. Now, I won't say it's never a good idea to use this stuff, but pretty much all of it has a big cost of some kind. Especially if you don't understand exactly, I mean exactly, what it's doing.

But people rave about it and pretend it has something to do with 'design' so people get the impression it is the way you should do everything, not just some extremely specialized tools that should be used seldom. If ever.

ReferenceURL : https://stackoverflow.com/questions/2247465/does-using-large-libraries-inherently-make-slower-code

반응형