development

정적 링크와 동적 링크

big-blog 2020. 2. 26. 07:54
반응형

정적 링크와 동적 링크


특정 상황에서 동적 연결보다 정적 연결을 선택하거나 그 반대로 선택해야하는 성능상의 이유가 있습니까? 나는 다음을 들었거나 읽었지만 그 주제에 대해 그 진실성을 보증 할만 큼 충분히 알지 못합니다.

1) 정적 연결과 동적 연결의 런타임 성능 차이는 대개 무시할 만합니다.

2) (1) 정적 링크를 사용하면 컴파일러가 코드와 라이브러리 코드를 모두 최적화 할 수 있기 때문에 프로파일 데이터를 사용하여 프로그램 핫 패스를 최적화하는 프로파일 링 컴파일러를 사용하는 경우 사실이 아닙니다. 동적 연결을 사용하면 코드 만 최적화 할 수 있습니다. 대부분의 시간이 라이브러리 코드를 실행하는 데 소비되는 경우 큰 차이를 만들 수 있습니다. 그렇지 않으면 (1)이 여전히 적용됩니다.


  • 동적 링크는 전체 자원 소비를 줄일있습니다 (둘 이상의 프로세스가 동일한 라이브러리를 공유하는 경우 (물론 "같은"버전 포함)). 이것이 대부분의 환경에서 그 존재를 이끌어내는 주장이라고 생각합니다. 여기서 "자원"에는 디스크 공간, RAM 및 캐시 공간이 포함됩니다. 물론 동적 링커가 충분히 유연하지 않으면 DLL 지옥 의 위험이 있습니다.
  • 동적 버그 수정 및 라이브러리로 업그레이드하는 것이 수단을 연결하는 전파 개선하기 위해 귀하의 무엇이든을 출시 할 필요없이 제품을.
  • 플러그인은 항상 동적 연결을 요구합니다.
  • 정적 링크는 코드가 매우 제한된 환경 (부팅 프로세스 또는 복구 모드에서)에서 실행된다는 것을 알 수 있음을 의미합니다 .
  • 정적 링크는 바이너리 를 다양한 사용자 환경에 보다 쉽게 ​​배포 할 수있게 합니다 (더 크고 더 많은 리소스가 필요한 프로그램을 전송하는 비용으로).
  • 정적 링크를 사용하면 시작 시간이 약간 빨라질있지만 프로그램 크기와 복잡성 OS로드 전략의 세부 사항에 따라 다릅니다 .

의견 및 기타 답변에 매우 관련성 높은 제안을 포함하도록 일부 수정. 이 문제를 해결하는 방법은 실행하려는 환경에 따라 다릅니다. 최소 내장 시스템에는 동적 연결을 지원하기에 충분한 리소스가 없을 수 있습니다. 약간 더 큰 작은 시스템은 동적 연결을 통해 RAM 절약이 매우 매력적이기 때문에 메모리가 작기 때문에 동적 연결을 잘 지원할 수 있습니다. 완전한 소비자 PC는 Mark가 지적한 바와 같이 막대한 자원을 가지고 있으며, 편의 문제로 인해이 문제에 대한 생각을 이끌어 낼 수 있습니다.


성능 및 효율성 문제를 해결하려면 다음 에 따라 다릅니다 .

일반적으로 동적 라이브러리에는 함수 어드레싱에서 이중 디스패치 또는 간접적 인 간접 레이어가 필요하며 약간의 속도가 필요할 수있는 일종의 풀 레이어가 필요합니다 (그러나 함수 호출 시간은 실제로 실행 시간의 큰 부분입니까 ???).

그러나 모두 동일한 라이브러리를 많이 호출하는 여러 프로세스를 실행하는 경우 정적 링크를 사용하는 것과 관련하여 동적 링크를 사용할 때 캐시 라인이 절약되어 실행 성능이 향상 될 수 있습니다. (현대 OS가 정적으로 링크 된 바이너리에서 동일한 세그먼트를 발견 할만큼 똑똑하지 않다면 누구도 아는가?

또 다른 문제 : 로딩 시간. 당신은 언젠가 로딩 비용을 지불합니다. 이 비용을 지불 할 때 OS의 작동 방식과 사용하는 링크에 따라 다릅니다. 어쩌면 필요할 때까지 지불하지 않을 수도 있습니다.

정적-대-동적 링크는 전통적 으로 최적화 문제가 아닙니다 . 둘 다 객체 파일에 대한 별도의 컴파일을 포함하기 때문입니다. 그러나 이것은 필요하지 않습니다. 컴파일러는 원칙적으로 "정적 라이브러리"를 요약 된 AST 형식으로 "컴파일"하고이 AST를 기본 코드 용으로 생성 된 AST에 추가하여 "링크"하여 글로벌 최적화를 강화할 수 있습니다. 내가 사용하는 시스템 중 어느 것도이 작업을 수행하지 않으므로 작동 방식에 대해서는 언급 할 수 없습니다.

성능 질문에 대답하는 방법은 항상 테스트하는 것입니다 (배포 환경과 최대한 비슷한 테스트 환경을 사용하십시오).


1) DLL 함수 호출은 항상 추가 간접 점프를 사용한다는 사실에 기반합니다. 오늘날 이것은 일반적으로 무시할 수 있습니다. DLL 내부에서는 위치 독립적 인 코드를 생성 할 수 없기 때문에 i386 CPU에 약간의 오버 헤드가 있습니다. amd64에서 점프는 프로그램 카운터를 기준으로 할 수 있으므로 크게 향상되었습니다.

2) 맞습니다. 프로파일 링을 통한 최적화를 통해 일반적으로 약 10-15 %의 성능을 얻을 수 있습니다. 이제 CPU 속도가 한계에 도달 했으므로 그만한 가치가 있습니다.

(3) 링커는 캐시를 효율적으로 그룹화하여 기능을 정렬 할 수 있으므로 고가의 캐시 레벨 누락이 최소화됩니다. 또한 특히 응용 프로그램의 시작 시간에 영향을 줄 수 있습니다 (Sun C ++ 컴파일러에서 본 결과를 기반으로 함)

그리고 DLL을 사용하면 죽은 코드 제거를 수행 할 수 없다는 것을 잊지 마십시오. 언어에 따라 DLL 코드도 최적이 아닐 수 있습니다. 컴파일러는 클라이언트가 덮어 쓰는지 여부를 알지 못하므로 가상 함수는 항상 가상입니다.

이러한 이유로 DLL이 실제로 필요하지 않은 경우 정적 컴파일 만 사용하십시오.

편집 (사용자 밑줄로 댓글에 답하기 위해)

다음은 위치 독립적 코드 문제에 대한 좋은 자료입니다. http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

설명 된 바와 같이 x86에는 15 비트 점프 범위 외에 무조건적인 점프 및 호출에 대한 AFAIK가 없습니다. 그렇기 때문에 32K 이상의 기능을 가진 (생성기의) 기능이 항상 문제가되었고 임베디드 트램폴린이 필요했습니다.

그러나 Linux와 같이 널리 사용되는 x86 OS에서는 SO / DLL 파일이 gcc스위치로 생성되지 않는지 -fpic(간접 점프 테이블 사용을 강제) 신경 쓰지 않아도됩니다 . 그렇지 않으면 코드는 일반 링커가 재배치하는 것처럼 고정됩니다. 그러나이 작업을 수행하는 동안 코드 세그먼트를 공유 할 수 없게 만들고 디스크에서 메모리로 코드를 완전히 매핑하고 사용할 수 있기 전에 코드를 모두 만져야합니다 (대부분의 캐시를 비우고 TLB를 치는 등). 이것이 느리게 여겨 질 때 ... 너무 느리다

따라서 더 이상 이점이 없습니다.

나는 OS (Solaris 또는 FreeBSD는) 난 그냥이 일을하지 않았기 때문에 내 유닉스 시스템을 구축하여 나에게 문제를주고 내가 적용될 때까지 추락 이유를 궁금해 무엇을 기억하지 않습니다 -fPICgcc.


동적 연결은 LGPL 과 같은 일부 라이센스 요구 사항을 충족시키는 유일한 실용적인 방법 입니다.


나는 dnmckee가 언급 한 요점에 동의합니다.

  • 정적으로 링크 된 응용 프로그램은 누락되거나 잘못된 위치에 설치 될 때 문제를 일으킬 수있는 추가 파일 종속성 (.dll / .so)이 적거나 없기 때문에 배포하기가 더 쉬울 수 있습니다.

정적으로 링크 된 빌드를 수행하는 한 가지 이유는 실행 파일이 완전히 닫혔는지, 즉 모든 심볼 참조가 올바르게 확인되었는지 확인하기위한 것입니다.

지속적인 통합을 사용하여 구축되고 테스트 된 대규모 시스템의 일부로 야간 회귀 테스트는 정적으로 링크 된 실행 파일 버전을 사용하여 실행되었습니다. 때로는 동적으로 연결된 실행 파일이 성공적으로 연결 되어도 심볼이 확인되지 않고 정적 링크가 실패하는 것을 알 수 있습니다.

이것은 일반적으로 공유 라이브러리 내에 깊숙이 자리 잡은 기호에 미스 펠트 이름이있어 정적으로 링크되지 않을 때 발생했습니다. 깊이 우선 또는 폭 우선 평가를 사용하더라도 동적 링커에서 모든 기호를 완전히 해석하지는 않으므로 완전히 닫히지 않은 동적으로 연결된 실행 파일로 마무리 할 수 ​​있습니다.


1 / 동적 연결 대 정적 연결이 벤치마킹되었고 동적 연결로 전환하기에 충분히 작은 차이가 결정되지 않은 프로젝트를 수행했습니다 (테스트의 일부가 아니 었습니다. 결론 만 알고 있습니다).

2 / 동적 링크는 종종 PIC (Position Independent Code, 코드가로드 된 주소에 따라 수정 될 필요가없는 코드)와 관련됩니다. 아키텍처에 따라 PIC는 또 다른 속도 저하를 가져올 수 있지만 두 실행 파일 사이에서 동적으로 링크 된 라이브러리를 공유하기 위해 필요합니다 (OS가 보안 조치로로드 주소의 무작위 화를 사용하는 경우 동일한 실행 파일의 두 프로세스까지). 모든 OS가 두 가지 개념을 분리 할 수 ​​있는지는 확실하지 않지만 Solaris와 Linux는 HP-UX와 마찬가지로 ISTR도 마찬가지입니다.

3 / 나는 "쉬운 패치"기능을 위해 동적 링크를 사용하는 다른 프로젝트에 참여했습니다. 그러나이 "쉬운 패치"를 사용하면 작은 수정본을 조금 더 쉽고 복잡한 버전으로 배포 할 수 있습니다. 우리는 종종 잘못된 버전이 토큰이기 때문에 모든 것을 푸시하고 고객 사이트의 문제를 추적해야했습니다.

내 결론은 내가 제외하고 정적 링크를 사용했다는 것입니다.

  • 동적 링크에 의존하는 플러그인과 같은 것들

  • 공유가 중요한 경우 (C / C ++ 런타임, GUI 라이브러리와 같이 여러 프로세스에서 동시에 사용하는 큰 라이브러리는 ... 독립적으로 관리되고 ABI가 엄격하게 정의되는 경우가 많습니다)

"쉬운 패치"를 사용하려면 위의 큰 라이브러리와 같이 라이브러리를 관리해야한다고 주장합니다. 수정 된 ABI와는 독립적이어야하며 수정으로 변경해서는 안됩니다.


이것은 리눅스 및 성능 의미의 공유 라이브러리에 대한 훌륭한 세부 사항에 대해 설명합니다.


정말 간단합니다. 소스 코드를 변경할 때 빌드 될 때까지 10 분 또는 20 초를 기다리시겠습니까? 내가 참을 수있는 것은 20 초입니다. 그 외에도, 나는 칼을 꺼내거나 별도의 편집과 연결을 사용하여 다시 안락 지대로 가져올 수있는 방법에 대해 생각하기 시작합니다.


유닉스 계열 시스템에서 동적 연결은 '루트'가 원격 위치에 설치된 공유 라이브러리가있는 응용 프로그램을 사용하기가 어려워 질 수 있습니다. 동적 링커는 일반적으로 루트 권한이있는 프로세스에 대해 LD_LIBRARY_PATH 또는 이와 동등한 것에주의를 기울이지 않기 때문입니다. 때로는 정적 링크가 하루를 절약합니다.

또는 설치 프로세스에서 라이브러리를 찾아야하지만 여러 버전의 소프트웨어가 시스템에 공존하기가 어려울 수 있습니다.


동적 링크는 OS가 동적 라이브러리를 찾아로드하는 데 추가 시간이 필요합니다. 정적 링크를 사용하면 모든 것이 함께 있으며 메모리에 한 번만로드됩니다.

또한 DLL Hell을 참조하십시오 . 이것은 OS가로드하는 DLL이 응용 프로그램과 함께 제공된 DLL이 아니거나 응용 프로그램이 기대하는 버전이 아닌 시나리오입니다.


라이브러리가 사용 된 하드웨어에 종속 된 경우 동적 연결의 가장 좋은 예입니다. 고대에는 C 수학 라이브러리가 동적으로 결정되어 각 플랫폼이 모든 프로세서 기능을 사용하여 최적화 할 수있었습니다.

더 좋은 예는 OpenGL입니다. OpenGl은 AMD와 NVidia에서 다르게 구현되는 API입니다. 하드웨어가 다르기 때문에 AMD 카드에서 NVidia 구현을 사용할 수 없습니다. OpenGL을 프로그램에 정적으로 연결할 수 없습니다. 여기에서 동적 링크가 사용되어 모든 플랫폼에 대해 API를 최적화 할 수 있습니다.


아직 논의되지 않은 또 다른 문제는 라이브러리의 버그 수정입니다.

정적 연결을 사용하면 라이브러리를 다시 작성해야 할뿐만 아니라 실행 파일을 다시 연결하고 재배포해야합니다. 라이브러리가 하나의 실행 파일에서 사용 된 경우에는 문제가되지 않을 수 있습니다. 그러나 다시 연결하고 재배포해야하는 실행 파일이 많을수록 고통이 커집니다.

동적 링크를 사용하면 동적 라이브러리를 다시 빌드하고 재배포하면됩니다.


정적 링크는 전체 프로그램을 다시 컴파일 해야하는 변경 사항을 적용하기 위해 하나의 exe 만 제공합니다. 동적 연결에서는 dll 만 변경하고 exe를 실행할 때 변경 사항이 런타임에 선택되며 동적 연결 (예 : 창)을 통해 업데이트 및 버그 수정을보다 쉽게 ​​제공 할 수 있습니다.


극단적 인 수준의 정적 연결이 응용 프로그램 및 시스템 성능에 막대한 영향을 줄 수있는 시스템이 엄청나게 증가하고 있습니다.

나는 종종 "임베디드 시스템 (embedded systems)"이라고 불리는 것을 언급하는데, 그 중 다수는 현재 범용 운영 체제를 점점 더 많이 사용하고 있으며, 이러한 시스템은 상상할 수있는 모든 것에 사용됩니다.

매우 일반적인 예는 Busybox를 사용하는 GNU / Linux 시스템을 사용하는 장치 입니다. 커널과 루트 파일 시스템을 모두 포함하는 부팅 가능한 i386 (32 비트) 시스템 이미지를 빌드하여 NetBSD최대한 활용 했습니다. 후자 crunchgen는 하드 링크 가있는 정적 링크 된 단일 바이너리를 포함합니다. 그 자체가 표준 전체 기능 시스템 프로그램 (대부분 툴체인 제외)을 모두 포함 하고있는 (최종적으로 274 개) 모든 프로그램을 포함 하며 크기 는 20 메가 바이트 미만 입니다. 루트 파일 시스템이 압축되지 않고 완전히 RAM에 있더라도 메모리를 테스트 할 수는 없었습니다.

정적 링크 바이너리의 시작 시간이 빠르며 훨씬 빠를 수 있다고 이전 포스트에서 언급 했지만 특히 모든 객체 코드가 동일한 링크에 연결된 경우에는 그림의 일부일뿐입니다 파일, 특히 운영 체제가 실행 파일에서 직접 코드 페이징을 요구하는 경우. 이 이상적인 시나리오에서 프로그램의 시작 시간은 문자 그대로 무시할 init수 있습니다. 요청 된 프로그램이없는 경우에도 거의 모든 코드 페이지가 이미 메모리에 있고 셸 (및 실행중인 다른 백그라운드 프로세스)에서 사용 중이기 때문입니다. 프로그램의 런타임 요구 사항을 충족시키기 위해 한 페이지의 메모리 만로드하면되므로 부팅 이후로 실행 된 적이 있습니다.

그러나 그것은 여전히 ​​전체 이야기가 아닙니다. 또한 일반적으로 모든 바이너리를 정적 링크하여 전체 개발 시스템에 NetBSD 운영 체제 설치를 빌드하고 사용합니다. 비록 디스크 공간이 엄청나게 더 많이 필요하지만 (툴체인과 X11 정적 링크를 포함한 모든 것을 가진 x86_64의 경우 총 ~ 6.6GB) (특히, 모든 프로그램에 대해 전체 디버그 기호 테이블을 ~ 2.5GB까지 사용할 수있는 경우) 결과는 여전히 전체적으로 빠르게 실행되며 일부 작업의 경우 라이브러리 코드 페이지를 공유하기 위해 사용되는 일반적인 동적 연결 시스템보다 적은 메모리를 사용합니다. 디스크는 저렴하고 (빠른 디스크조차도) 자주 사용하는 디스크 파일을 캐시하는 메모리도 비교적 저렴하지만 CPU주기는 실제로는 아니고 ld.so모든 프로세스가 시작될 마다 시작 비용을 지불합니다.시작하는 데는 많은 프로세스를 시작해야하는 작업 (특히 개발 시스템의 컴파일러와 같은 동일한 프로그램을 계속해서 사용하는 경우)에서 몇 시간과 몇 시간 동안 CPU주기가 소요됩니다. 정적 링크 툴체인 프로그램은 시스템의 전체 OS 다중 아키텍처 빌드 시간을 몇 시간 단축 할 수 있습니다. 아직 툴체인을 단일 crunchgen바이너리 로 빌드하지는 않았지만 CPU 캐시의 승리로 인해 빌드 시간이 더 오래 걸릴 것으로 생각됩니다.


정적 링크에는 프로그램이 단일 실행 파일에 필요한 파일을 포함합니다.

동적 링크는 평소와 같이 DLL을 필요로하는 실행 파일을 만들어 같은 디렉토리에 있거나 DLL이 시스템 폴더에있을 수 있습니다.

(DLL = 동적 링크 라이브러리)

동적으로 연결된 실행 파일은 더 빠르게 컴파일되며 리소스가 많이 소모되지는 않습니다.

참고 URL : https://stackoverflow.com/questions/1993390/static-linking-vs-dynamic-linking



반응형