대규모 애플리케이션에서 STL을 피해야합니까?


24

이것은 이상한 질문으로 들릴 수 있지만 부서에서 다음 상황에 문제가 있습니다.

우리는 서버 응용 프로그램을 개발 중입니다. 서버 응용 프로그램을 처리하기 위해 필요에 따라 동적으로로드하고 나중에 언로드하는 것을 고려할 때조차도 점점 커지고 있습니다. 성능 문제.

그러나 우리가 사용하는 함수는 STL 객체로 입력 및 출력 매개 변수를 전달 하고 있으며 스택 오버플로 답변에서 언급했듯이 이것은 매우 나쁜 생각입니다. (포스트에는 몇 가지 ± 솔루션과 해킹이 포함되어 있지만 모두 견고하지는 않습니다.)

분명히 우리는 입력 / 출력 매개 변수를 표준 C ++ 유형으로 바꾸고 함수 내부에서 STL 객체를 만들 수 있지만 성능 저하가 발생할 수 있습니다.

하나의 단일 PC가 더 이상 처리 할 수 ​​없을 정도로 커질 수있는 응용 프로그램 구축을 고려중인 경우 STL을 기술로 사용해서는 안된다고 결론을 내릴 수 있습니까?

이 질문에 대한 추가 배경 : 질문에 대한
오해가있는 것 같습니다. 문제는 다음과 같습니다.
응용 프로그램이 작업을 완료하기 위해 엄청난 양의 성능 (CPU, 메모리)을 사용하고 있으며이 작업을 분할하고 싶습니다. 다른 부분으로 (프로그램이 이미 여러 기능으로 분리되어 있기 때문에) 내 응용 프로그램에서 일부 DLL을 만들고 해당 DLL의 내보내기 테이블에 일부 기능을 넣는 것은 어렵지 않습니다. 다음과 같은 상황이 발생합니다.

+-----------+-----------+----
| Machine1  | Machine2  | ...
| App_Inst1 | App_Inst2 | ...
|           |           |    
| DLL1.1    | DLL2.1    | ...
| DLL1.2    | DLL2.2    | ...
| DLL1.x    | DLL2.x    | ...
+-----------+-----------+----

App_Inst1은 Machine1에 설치된 응용 프로그램의 인스턴스이고 App_Inst2는 Machine2에 설치된 동일한 응용 프로그램의 인스턴스입니다.
DLL1.x는 Machine1에 설치된 DLL이고 DLL2.x는 Machine2에 설치된 DLL입니다.
DLLx.1은 내 보낸 기능 1을 다룹니다.
DLLx.2는 내 보낸 function2를 다룹니다.

이제 Machine1에서 function1과 function2를 실행하고 싶습니다. 이것이 Machine1에 과부하가 걸리므로 App_Inst2에 메시지를 보내 해당 응용 프로그램 인스턴스가 function2를 수행하도록 요청하고 싶습니다.

function1 및 function2의 입력 / 출력 매개 변수는 STL (C ++ 표준 형식 라이브러리) 개체이며, 정기적으로 고객이 App_Inst1, App_Inst2, DLLx.y를 업데이트 할 것으로 예상 할 수 있습니다 (그러나 모두는 아니지만 고객은 Machine1을 업그레이드 할 수는 있지만 Machine2가 아니거나 응용 프로그램 만 업그레이드하지만 DLL은 업그레이드하지 않으며 그 반대도 마찬가지입니다 ...). 인터페이스 (입력 / 출력 매개 변수)가 변경되면 고객은 업그레이드를 완료해야합니다.

그러나 언급 된 StackOverflow URL에서 언급했듯이 App_Inst1 또는 DLL 중 하나의 간단한 재 컴파일로 인해 전체 시스템이 손상 될 수 있으므로이 게시물의 원래 제목이 STL (C ++ 표준 템플릿)의 사용을 거부합니다 큰 응용 프로그램의 경우 라이브러리).

이에 의해 몇 가지 질문 / 의견이 해결 되었기를 바랍니다.


44
실행 파일 크기 때문에 성능 문제가 있습니까? 모든 소프트웨어가 동일한 컴파일러로 컴파일되었다고 가정하는 것이 현실적인지 (예를 들어 빌드 서버에서 하나의 빌드에서) 실제로 독립적 인 팀으로 분할 하려는지에 대한 세부 사항을 추가 할 수 있습니까?
nvoigt

5
기본적으로 모든 C ++ 프로젝트가 동일한 컴파일러 버전과 동일한 C ++ 컴파일러 설정으로 컴파일되고 소스의 일관된 스냅 샷 (버전)에서 컴파일되도록하려면 전담 작업이 "빌드 관리자"및 "릴리스 관리자"인 사람이 필요합니다. 코드 등. 일반적으로 이것은 "연속 통합"이라는 배너 아래에서 처리됩니다. 온라인으로 검색하면 많은 기사와 도구를 찾을 수 있습니다. 오래된 관행은 자체 강화할 수 있습니다. 오래된 관행은 모든 관행이 구식이 될 수 있습니다.
rwong

8
링크 된 질문에서 허용되는 답변은 일반적으로 C ++ 호출에 문제가 있음을 나타냅니다. 따라서 "C ++이지만 STL이 아님"은 도움이되지 않습니다. 안전한 C가되기 위해서는 베어 C를 사용해야합니다.
Frax

52
성능 문제를 처리 할 수 ​​있도록 필요할 때 동적으로로드하고 나중에 언로드합니다. "성능 문제"는 무엇입니까? 메모리에서 DLL과 같은 것을 언로드하여 해결할 수있는 너무 많은 메모리를 사용하는 것 이외의 문제는 알지 못합니다. 문제가있는 경우 가장 쉬운 수정은 더 많은 RAM을 구입하는 것입니다. 당신은 프로파일 실제 성능 병목 현상을 식별하는 응용 프로그램을? 이것은 XY 문제처럼 들리므로 지정되지 않은 "성능 문제"가 있고 누군가가 이미 솔루션을 결정했습니다.
Andrew Henle

4
@MaxBarraclough "STL"은 C ++ 표준 라이브러리에 포함 된 템플릿 컨테이너 및 함수의 대체 이름으로 완벽하게 수용됩니다. 실제로 Bjarne Stroustrup과 Herb Sutter가 작성한 C ++ 코어 가이드 라인은 이에 대해 이야기 할 때 "STL"을 반복해서 참조합니다. 당신은 그것보다 훨씬 더 권위있는 소스를 얻을 수 없습니다.
Sean Burton

답변:


110

이것은 냉정한 고전적인 XY 문제입니다.

당신의 진짜 문제는 성능 문제입니다. 그러나 귀하의 질문에 따르면 성능 문제가 실제로 어디에서 발생했는지에 대한 프로파일 링이나 기타 평가를 수행하지 않았다는 것이 분명합니다. 대신 코드를 DLL로 분할하면 문제를 해결할 수있는 마술 적으로 문제가 해결되기를 기대하고 있습니다.

대신 실제 문제를 해결해야합니다. 실행 파일이 여러 개인 경우 속도 저하를 일으키는 실행 파일을 확인하십시오. 당신이 그것을하고있는 동안, 실제로 잘못 처리 된 이더넷 드라이버 나 그와 비슷한 것이 아니라 모든 처리 시간이 걸리는 프로그램인지 확인하십시오. 그런 다음 코드에서 다양한 작업을 프로파일 링하십시오. 고정밀 타이머는 당신의 친구입니다. 고전적인 솔루션은 코드 덩어리에 대한 평균 및 최악의 처리 시간을 모니터링하는 것입니다.

데이터가 있으면 문제를 해결하는 방법을 찾은 다음 최적화 할 위치를 해결할 수 있습니다.


54
"대신에 코드를 DLL로 분할하면 문제를 마술처럼 해결할 수 있기를 희망합니다. (기록 상으로는 그렇지 않을 것입니다)"-+1 운영 체제는 거의 확실히 수동 페이징을 요구하지 않고 자동으로 DLL에로드 및 언로드 기능과 동일한 결과 를 달성하는 요구 페이징을 구현 합니다 . OS 가상 메모리 시스템보다 한 번 사용 된 코드 조각이 얼마나 오래 걸려야 하는지를 더 잘 예측하더라도 (실제로는 아닐 수도 있음) OS는 DLL 파일을 캐시하고 어쨌든 노력을 무효화합니다 .
Jules

@ Jules 업데이트 참조-DLL이 별도의 컴퓨터에만 존재한다는 것을 분명히 했으므로이 솔루션이 작동하는 것을 볼 수 있습니다. 그러나 이제는 통신 오버 헤드가 발생하므로 확실하지 않습니다.
이즈 카타

2
@ Izkata-여전히 명확하지는 않지만 설명 된 것은 로컬 또는 원격 인 각 기능의 버전을 (런타임 구성에 따라) 동적으로 선택하고 싶다고 생각합니다. 그러나 주어진 컴퓨터에서 절대 사용되지 않는 EXE 파일의 일부는 단순히 메모리에로드되지 않으므로이 목적으로 DLL을 사용할 필요는 없습니다. 표준 빌드에 모든 함수의 두 버전을 모두 포함시키고 함수 포인터 표 (또는 C ++ 호출 가능 객체 또는 원하는 메소드)를 만들어 각 함수의 적절한 버전을 호출하십시오.
Jules

38

여러 물리적 시스템간에 소프트웨어를 분할해야하는 경우 시스템간에 데이터를 전달할 때 특정 형식의 직렬화 형식을 가져야합니다. 경우에 따라서는 실제로는 시스템간에 동일한 정확한 바이너리를 보낼 수 있기 때문입니다. 대부분의 직렬화 방법에는 STL 유형을 처리하는 데 아무런 문제가 없으므로 대소 문자가 아닙니다.

응용 프로그램을 공유 라이브러리 (DLL)로 분할해야하는 경우 (성능상의 이유로이를 수행하기 전에 실제로 성능 문제를 해결해야합니다) STL 객체를 전달하는 것이 문제가 될 수 있지만 반드시 그런 것은 아닙니다. 제공 한 링크에서 이미 설명했듯이 동일한 컴파일러 및 동일한 컴파일러 설정을 사용하는 경우 STL 객체 전달이 작동합니다. 사용자가 DLL을 제공하면이를 쉽게 계산하지 못할 수 있습니다. 모든 DLL을 제공하고 모든 것을 함께 컴파일하면 DLL을 신뢰할 수 있고 DLL 경계를 넘어 STL 객체를 사용하는 것이 가능해집니다. STL 관련 문제는 아니지만 객체 소유권을 전달할 때 여러 개의 서로 다른 힙을 얻지 않도록 컴파일러 설정을 계속 지켜봐야합니다.


1
예, 특히 DLL / so 경계를 통해 할당 된 개체를 전달하는 방법에 대한 부분입니다. 일반적으로 다중 할당 자 문제를 완전히 피할 수있는 유일한 방법은 구조를 할당 한 DLL / 또는 라이브러리 (또는 라이브러리)도이를 해제하는 것입니다. 그렇기 때문에 많은 C 스타일 API가 이런 식으로 작성된 이유를 알 수 있습니다. 할당 된 배열 / 구조를 다시 전달하는 각 API에 대한 명시 적 무료 API입니다. STL의 또 다른 문제점은 호출자가 전달 된 복합 데이터 구조 (요소 추가 / 제거)를 수정할 수 있으며 허용되지 않을 수 있다는 것입니다. 그러나 시행하기는 어렵습니다.
davidbak

1
이와 같은 응용 프로그램을 분할 해야하는 경우 COM을 사용하지만 아마도 모든 구성 요소가 자체 C 및 C ++ 라이브러리를 가져올 때 코드 크기가 증가합니다 (동일하면 공유 할 수 있지만 필요할 때 분기 할 수 있음) . 전환하는 동안 예를 들어, 나는 이것이 불구하고, 영업 이익의 문제에 대한 적절한 조치임을 확신 아니에요.
사이먼 리히터

2
구체적인 예로서, 프로그램은 가능성이 높다 어딘가 다른 컴퓨터에 텍스트를 보낼 수 있습니다. 언젠가는 해당 텍스트를 나타내는 데 관련된 일부 문자에 대한 포인터가 있습니다. 당신은 절대로 그 포인터의 비트를 전송할 수 없으며 수신 측에서 정의 된 동작을 기대할 수 없습니다
Caleth

20

우리는 서버 응용 프로그램을 위해 노력하고 있습니다. 서버 응용 프로그램을 다른 부분 (DLL)으로 나누고 필요할 때 동적으로로드하고 나중에 언로드하는 것을 고려할 때조차도 점점 커지고 있습니다. 성능 문제

RAM이 저렴하므로 비활성 코드가 저렴합니다. 코드로드 및 언로드 (특히 언로드)는 취약한 프로세스이며 최신 데스크탑 / 서버 하드웨어의 프로그램 성능에 큰 영향을 미치지는 않습니다.

캐시는 비싸지 만 최근에 활성화 된 코드에만 영향을 미치며 사용되지 않는 메모리에 저장된 코드에는 영향을 미치지 않습니다.

일반적으로 프로그램은 코드 크기가 아니라 데이터 크기 또는 CPU 시간으로 인해 컴퓨터보다 더 많이 사용됩니다. 코드 크기가 너무 커져 주요 문제가 발생하는 경우 처음부터 왜 그런 일이 발생하는지 살펴보고 싶을 것입니다.

그러나 우리가 사용하는 함수는 입력 및 출력 매개 변수를 STL 객체로 전달하고 있으며이 StackOverflow URL에서 언급했듯이 이것은 매우 나쁜 생각입니다.

dll과 실행 파일이 모두 동일한 컴파일러로 빌드되고 동일한 C ++ 런타임 라이브러리에 동적으로 연결되어 있으면 괜찮습니다. 응용 프로그램과 관련 dll이 단일 단위로 빌드되고 배포되면 문제가되지 않습니다.

문제가 될 수있는 곳은 다른 사람들이 라이브러리를 만들거나 별도로 업데이트 할 수있는 경우입니다.

하나의 단일 PC가 더 이상 처리 할 수 ​​없을 정도로 커질 수있는 응용 프로그램을 구축하려는 경우 STL을 기술로 사용해서는 안된다고 결론을 내릴 수 있습니까?

실제로는 아닙니다.

여러 컴퓨터에 응용 프로그램을 배포하기 시작하면 해당 컴퓨터간에 데이터를 전달하는 방법에 대해 완전히 고려해야합니다. STL 유형 이상의 기본 유형 사용 여부에 대한 세부 사항은 노이즈에서 유실 될 수 있습니다.


2
비활성 코드는 처음에는 RAM에로드되지 않습니다. 대부분의 운영 체제는 실제로 필요한 경우에만 실행 파일에서 페이지를로드합니다.
Jules

1
@ 줄 : 죽은 코드가 라이브 코드와 혼합되면 (페이지 크기 = 4k 입도) 매핑되고로드됩니다. 캐시는 훨씬 더 세밀한 (64B) 세분성으로 작동하므로 사용하지 않는 함수가 크게 해를 끼치 지 않는 것이 여전히 그렇습니다. 그러나 각 페이지에는 부족한 런타임 리소스 인 RAM과 달리 TLB 항목이 필요합니다. (파일 기반 매핑은 일반적으로 적어도 Linux에서는 그렇지 않은 거대한 페이지를 사용하지 않습니다. x86-64에서 거대한 페이지 하나는 2MiB이므로 거대한 페이지에서 TLB를 놓치지 않고 더 많은 코드 나 데이터를 처리 할 수 ​​있습니다.
Peter Cordes

1
@PeterCordes가 참고하는 사항 : 따라서 릴리스 빌드 프로세스의 일부로 "PGO"를 사용해야합니다!
JDługosz

13

아니요, 결론은 다음과 같습니다. 프로그램이 여러 시스템에 분산되어 있어도 STL을 내부적으로 사용하면 모듈 간 / 프로세스 통신에서 STL을 강제로 사용할 이유가 없습니다.

사실, 내부 인터페이스와 내부 인터페이스를 비교했을 때 변경하기가 더 견고하고 어렵 기 때문에 외부 인터페이스 디자인을 시작부터 내부 구현과 분리해야한다고 주장합니다.


7

그 질문의 요점이 없습니다.

기본적으로 두 가지 유형의 DLL이 있습니다. 당신 자신과 다른 사람. "STL 문제"는 귀하와 이들이 동일한 컴파일러를 사용하지 않을 수 있다는 것입니다. 분명히, 그것은 당신의 DLL에 대한 문제가 아닙니다.


5

동일한 컴파일러 및 빌드 옵션을 사용하여 동일한 소스 트리에서 DLL을 동시에 빌드하면 정상적으로 작동합니다.

그러나 응용 프로그램을 여러 조각으로 나누는 "Windows 맛"방법은 일부는 COM 구성 요소 입니다. 이 크기는 작거나 (개별 컨트롤 또는 코덱) 크거나 (IE는 mshtml.dll에서 COM 컨트롤로 사용 가능)

필요할 때 동적으로로드하고 나중에 언로드

서버 응용 프로그램의 경우 아마도 효율성 이 끔찍할 것입니다. 오랜 시간 동안 여러 단계를 거쳐 이동하는 응용 프로그램이있을 때 실제로 실행 가능하므로 무언가 다시 필요하지 않을 때를 알 수 있습니다. 오버레이 메커니즘을 사용하는 DOS 게임을 상기시킵니다.

또한 가상 메모리 시스템이 제대로 작동하면 사용하지 않는 코드 페이지를 페이징하여 처리합니다.

하나의 PC로 더 이상 처리 할 수 ​​없을 정도로 커질 수 있습니다.

더 큰 PC를 구입하십시오.

올바른 최적화를 통해 랩톱이 hadoop 클러스터보다 성능이 우수 하다는 것을 잊지 마십시오 .

실제로 여러 시스템 필요한 경우 직렬화 비용이 발생하기 때문에 시스템 간의 경계에 대해 매우 신중하게 고려해야합니다. MPI와 같은 프레임 워크를 살펴보아야합니다.


1
"여러 단계를 거쳐 여러 단계를 거치는 애플리케이션이있을 때 실제로 실행 가능하기 때문에 다시는 필요하지 않을 때를 알 수 있습니다." DLL 파일을 캐시하면 기본 실행 파일에 직접 함수를 포함하는 것보다 더 많은 메모리가 필요합니다. 오버레이는 가상 메모리가없는 시스템 또는 가상 주소 공간이 제한 요소 인 경우에만 유용합니다 (이 응용 프로그램은 32 비트가 아니라 64 비트라고 가정합니다 ...).
Jules

3
"더 큰 PC 구매"+1. 이제 테라 바이트 단위 의 RAM이있는 시스템을 얻을 수 있습니다 . 단일 개발자의 시간당 요금 미만으로 Amazon에서 하나를 고용 할 수 있습니다. 메모리 사용량을 줄이기 위해 코드 최적화에 소요되는 개발자 시간은 얼마입니까?
Jules

2
"더 큰 PC 구입"에 직면 한 가장 큰 문제는 "앱이 얼마나 멀리 확장됩니까?"라는 질문과 관련이있었습니다. 내 대답은 "테스트에 얼마나 투자 할 의향이 있는가?"입니다. 적절한 장비를 대여하고 대규모 테스트를 설정하는 데 수천 달러가 소요될 것으로 예상하기 때문에 지금까지 확장이 가능할 것으로 기대하기 때문입니다. "단일 CPU PC로 할 수있는 일" 많은 구형 프로그래머들은 얼마나 많은 PC가 자랐는지 현실적인 아이디어를 가지고 있지 않습니다. 현대 PC의 비디오 카드만으로는 20 세기 표준의 슈퍼 컴퓨터입니다.
MSalters

COM 구성 요소? 아마 1990 년대에 지금?
Peter Mortensen

@MSalters-바로 ... 단일 PC에서 애플리케이션을 얼마나 확장 할 수 있는지에 대한 의문이있는 사람은 Amazon EC2 x1e.32xlarge 인스턴스 유형의 사양을 살펴 봐야합니다. 2.3GHz (3.1GHz로 버스트 가능), 340GB / s의 메모리 대역폭 (사양에 설명되어 있지 않은 메모리 종류에 따라 다름) 및 3.9TiB의 RAM 메인 RAM을 건드리지 않고 대부분의 응용 프로그램을 실행할 수있는 캐시가 충분합니다. GPU가 없어도 2000 년부터 500 노드 슈퍼 컴퓨터 클러스터만큼 강력합니다.
Jules

0

우리는 서버 응용 프로그램을 개발 중입니다. 서버 응용 프로그램을 처리하기 위해 필요에 따라 동적으로로드하고 나중에 언로드하는 것을 고려할 때조차도 점점 커지고 있습니다. 성능 문제.

첫 번째 부분은 의미가 있습니다 (성능상의 이유로 응용 프로그램을 다른 컴퓨터로 분할).

두 번째 부분 (라이브러리로드 및 언로드 라이브러리)은 이해하기에 추가 노력이므로 실제로는 개선되지 않습니다.

설명하는 문제는 전용 계산 기계로 더 잘 해결되지만 동일한 (주) 응용 프로그램에서 작동하지 않아야합니다.

고전적인 솔루션은 다음과 같습니다.

[user] [front-end] [machine1] [common resources]
                   [machine2]
                   [machine3]

프런트 엔드 컴퓨터와 계산 컴퓨터 사이에로드 밸런서 및 성능 모니터링과 같은 추가 항목이있을 수 있으며 전용 컴퓨터에서 특수 처리를 유지하는 것은 캐싱 및 처리량 최적화에 좋습니다.

이것은 DLL의 추가로드 / 언로드 나 STL과 관련이 없음을 의미하지 않습니다.

즉, 필요에 따라 STL을 내부적으로 사용하고 요소간에 데이터를 직렬화하십시오 (grpc 및 프로토콜 버퍼 및 이들이 해결하는 문제 종류 참조).

이것은 당신이 제공 한 제한된 정보로, 이것은 고전적인 xy 문제처럼 보입니다 (@Graham이 말했듯이).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.