2009-04-12 6 views
12

openstreetmap xml 파일을 사용하여 일반적으로 원래 크기의 약 10 % 인 이진 런타임 렌더링 형식으로 변환하는 변환기를 작성했습니다. 입력 파일 크기는 일반적으로 3GB 이상입니다. 입력 파일은 한번에 메모리에로드되지 않지만 점으로 스트림되어 polys가 수집 된 다음 bsp가 실행되고 파일이 출력됩니다. 최근에는 더 큰 파일의 경우 메모리가 부족하고 죽습니다 (문제의 파일은 1400 만 포인트와 1 백만 개의 다각형). 일반적으로 내 프로그램은 약 1GB에서 1.2GB의 RAM을 사용합니다. 나는 가상 메모리를 2에서 8GB로 늘리려고했지만 (XP에서는)이 변화가 아무런 효과가 없었다. 또한이 코드는 오픈 소스이므로 사용 가능한 램 (느리지 만)에 관계없이 작동하도록하고 싶습니다. Windows, Linux 및 Mac에서 실행됩니다.메모리 사용량이 많은 응용 프로그램에서 메모리가 부족하지 않도록하려면 어떻게해야합니까?

메모리가 부족하지 않도록 어떤 기술을 사용할 수 있습니까? 작은 하위 집합으로 데이터를 처리 한 다음 최종 결과를 병합 하시겠습니까? 내 자신의 가상 메모리 유형의 처리기를 사용하고 있습니까? 다른 아이디어?

답변

14

먼저 32 비트 시스템에서는 페이지 파일 설정에 관계없이 항상 4GB의 메모리로 제한됩니다. (그 중 2GB 만 Windows에서 처리 할 수 ​​있습니다 .Linux에서는 대개 3GB가 사용 가능합니다.)

첫 번째 확실한 해결 방법은 64 비트 OS로 전환하고 64 비트 응용 프로그램 이렇게하면 사용할 수있는 거대한 가상 메모리 공간이 생기며 운영 체제는 필요한대로 페이지 파일 안팎으로 데이터를 교환합니다.

두 번째로, 한 번에 더 작은 메모리 덩어리를 할당하는 것이 도움이 될 수 있습니다. 하나의 1GB 청크보다 4 개의 256MB 청크를 쉽게 찾을 수 있습니다.

셋째, 문제를 나눕니다. 한 번에 전체 데이터 세트를 처리하지 말고 한 번에 작은 섹션 만로드하고 처리하십시오.

+0

Windows는/LARGEADDRESSAWARE와 함께 3GB의 가상 공간을 가질 수 있습니다. –

+0

플래그는 OS가 제공 할 수있는 경우 프로세스가 최대 4GB *를 사용할 수있게합니다 *. 일반적으로 Windows는 여전히 각 프로세스에 2GB 만 제공하도록 설정되어 있습니다. 드라이버 불안정성의 위험이 있으므로 3GB를 제공하기 위해이를 변경할 수도 있습니다. PAE를 사용하면 더 많은 것을 얻을 수 있습니다. 그러나 64 비트는 아마도 더 나은 내기 일 것입니다. – jalf

+1

Imho, 세 번째 옵션이 가장 중요합니다. 메모리를 제어하는 ​​것 외에도 병렬 처리가 가능합니다. – xtofl

4

XML 처리에 대한 접근 방식이 이미 SAX 인 것처럼 들리지만 (모든 것을 한꺼번에 처리 할 수 ​​있으므로 XML을로드하는 것처럼)

솔루션은 거의 항상 알고리즘을 변경하여 문제를 더 작은 부분으로 줄입니다. 물리적으로 한 번에 많은 메모리를 할당하지 말고, 필요한 것을 읽고, 처리 한 다음 작성하십시오.

때때로 알고리즘에서 필요할 때 하드 드라이브를 사용하여 메모리를 확장 할 수 있습니다.

알고리즘을 분리 할 수없는 경우 memory mapped files과 같은 것을 원할 것입니다.

Windows 시스템을 사용하는 경우 최악의 경우 VirtualAlloc과 같은 것을 사용할 수 있습니다. 32 비트 시스템을 사용하는 경우 Physical Address Extension (PAE)과 같은 것을 사용할 수 있습니다.

또한 프로그램에 대한 입력 제한 사항을 적용하고 32 비트 및 64 비트 시스템에 대해 다른 제한 사항을 적용 할 수도 있습니다.

4

어디에도 메모리가 누출되지 않았는지 확인 했습니까?

프로그램이 Linux로 이식 가능하므로 Valgrind에서 실행하여 프로그램을 실행하는 것이 좋습니다.

+0

예 누출 여부를 확인했지만 누락 여부를 확인했습니다. – KPexEA

0

txt를 바이너리 대화로하는 것처럼 들리므로 왜 메모리에 전체 데이터가 있어야합니까?
txt (xml)에서 원시를 읽고 binarystream에 저장할 수 없습니까?

2

Windows XP를 사용한다고 가정 할 때 메모리 제한 만 초과하고 위에 제안 된대로 코드를 다시 작성하고 싶지 않은 경우 boot.ini 파일에/3GB 스위치를 추가 한 다음 링커 스위치를 설정하여 추가 1GB의 메모리를 확보하는 것만으로도 충분합니다.

+0

3GB를 사용하는 것이 그렇게 간단하지 않습니다. 모든 포인터 조작 작업이 안전한지 확인해야합니다. 그렇지 않으면 메모리 사용량이 많아지면 충돌이 발생합니다. 자세한 내용은 http://blogs.msdn.com/oldnewthing/archive/2004/08/12/213468.aspx를 참조하십시오. – eran

3

메모리 문제가 BSP 트리를 메모리에 유지하는 것으로 의심되는 경우. BSP를 디스크에 보관하고 일부 청크 만 메모리에 보관하십시오. 이것은 구조가 다른 트리 구조보다 많은 것을 제공하므로 BSP에서 상당히 쉬워야하며 논리는 단순해야합니다. 효율적이고 기억하기 쉽도록 캐시 크기가 캐시 W/더티 플래그를 가질 수 있습니다. 캐시 크기는 사용 가능한 메모리를 호흡 공간보다 적게 설정합니다.

1

실제 메모리 (Windows의 워킹 세트라고 불리는)는 메모리 인 반면 사용중인 가상 메모리의 양은 예약 한 총량이라는 점에서 가상 메모리가 "RAM"과 다른 점을 알아야합니다 실제로 수정하거나 잠근 것입니다.

누군가 다른 사람이 지적했듯이 32 비트 Windows 플랫폼에서 가상 메모리 제한은 3 기가 바이트의 특수 플래그를 설정하지 않는 한 2 기가 바이트이고 코드와 사용하는 모든 라이브러리의 모든 포인터 서명되지 않은 포인터를 사용하십시오.

그래서 사용자를 강제로 64 비트로 설정하거나 가상 메모리를 모니터링하고 최대 블록 크기를 32 비트 운영 체제의 제한 내에서 편하게 사용할 수 있도록 조언하는 것이 좋습니다.

저는 Windows에서 32 비트 벽에 꽂았습니다. 그러나 Linux에서 이러한 제한 사항을 해결하는 데 경험이 없으므로 Windows 측면에 대해서만 이야기했습니다.

1

32 비트 XP에서 최대 프로그램 주소 공간은 2GB입니다. 그런 다음 DLL과 드라이버가 주소 공간에로드되어 조각화됩니다. 마지막으로 힙 조각화 문제가 있습니다.

가장 좋은 방법은 64 비트 시스템에서 64 비트 프로세스로 실행하고 실행하는 것입니다. 갑자기이 모든 문제들이 사라집니다. 더 나은 힙을 사용하여 힙 조각화 효과를 완화 할 수 있습니다. 그리고 VirtualAlloc을 사용하여 하나의 큰 연속 된 청크에서 메모리를 움켜 잡으십시오. 그런 다음 DLL/드라이버의 조각화를 막기 위해 시도 할 수 있습니다.

마지막으로 프로세스간에 BSP를 분리 할 수 ​​있습니다. 복잡하고 고통스럽고 솔직하게 디스크에 저장하는 것이 더 쉽습니다. 이론 상으로는 모든 것을 유지할 수 있다면 일련의 프로세스가 정보를 교환하도록함으로써 이론적으로 더 나은 성능을 얻을 수 있습니다 (OS보다 메모리보다 더 똑똑 할 수 있다고 가정 할 때). 파일 버퍼링을 처리 할 수 ​​있습니다 ... 큰 경우). 각 프로세스는 훨씬 적은 메모리가 필요하므로 2GB의 주소 공간 제한으로 실행하면 안됩니다. 물론 RAM을 통해 구울 수도 있고 훨씬 빨리 스왑 할 수도 있습니다.

작은 크기의 청크를 할당하여 주소 공간의 조각화 효과를 완화 할 수 있습니다. 이것은 다른 불쾌한 부작용을 갖지만, 성공적으로 할당하지 못하면 작고 작은 메모리 덩어리를 잡는 백 오프 정책을 따를 수 있습니다. 이 간단한 접근 방식은 그렇지 않은 경우 작동하는 프로그램을 자주 제공하지만 시간의 나머지 부분은 가능한 한 잘 수행됩니다.

소년, 64 비트 컴퓨팅이 다른 선택보다 훨씬 좋게 들리지는 않습니까?

1

어떻게 포인트를위한 메모리를 할당합니까? 한 번에 하나씩 포인트를 할당하고 있습니까 (예 : 포인트 = 새 포인트). 그러면 포인트의 크기에 따라 일부 메모리가 낭비 될 수 있습니다.예를 들어 windows 메모리는 16 바이트의 배수로 할당되므로 1 바이트를 할당하려고해도 OS는 실제로 16 바이트를 할당합니다.

이 경우 메모리 할당자를 사용하면 도움이 될 수 있습니다. STL 할당자를 사용하여 빠른 점검을 수행 할 수 있습니다. (Point 클래스에 대한 new 연산자를 오버로드하고 STL 할당자를 사용하여 'malloc'또는 기본 new 연산자 대신 메모리를 할당하십시오.)

+0

나는 힙 관리자를 사용하여 포인트와 폴리곤을 할당하기 때문에 필요한만큼의 공간 만 차지하고 내 힙은 (이 경우) 1MB 덩어리와 각 덩어리로부터의 요청을 모두 털어 내기 때문에 엿 들었다. – KPexEA

+0

또 다른 이유는 '메모리 (예 : 메모리가 작은 덩어리로 제공되지만 '1MB'를 요청하면 인접한 1MB 덩어리를 사용할 수 없습니다.) XML 구문 분석기가 '힙 관리자'를 사용합니까? XML 파서가 표준 메모리 할당을 사용하고 분열? –

0

메모리 크기에 독립적 인 경우 크기에 독립적 인 알고리즘이 필요합니다. RAM의 크기에 관계없이 메모리 사용량을 제어 할 수 없다면 경계에 부딪 힐 수 있습니다.

약간의 출력을 생성하는 데 사용할 수있는 최소한의 정보를 살펴보십시오. 그런 다음 입력을이 크기의 청크로 나누는 방법을 생각해보십시오.

이제 쉽게 들리겠습니까? (다행이 아니기 때문에 :))

1

최적의 방식으로 메모리를 할당하고 할당을 취소 할 수는 없습니다. 다른 사람들이 지적했듯이, 당신은 기억을 새기고 그것을 모를 수도 있습니다. 디버깅 및 메모리 할당 최적화에는 시간이 걸립니다.

메모리 사용을 최적화하는 데 시간을 낭비하지 않으려면 Conservative Garbage Collector을 사용해보십시오. malloc()/new 및 free() 플러그인 대체품입니다. 사실, free()는 아무 작업도하지 않으므로 프로그램에서 이러한 호출을 제거 할 수 있습니다. 이전에 제안 된대로 프로그램을 수작업으로 최적화하고 메모리 풀을 관리하면 CGC가 이미 수행 한 많은 작업을 끝내게됩니다.

1

출력뿐만 아니라 입력을 스트리밍해야합니다. 출력 형식이 스트림 지향이 아닌 경우 두 번째 전달을 고려하십시오. 예를 들어 출력 파일이 데이터의 check sum/size로 시작하는 경우 첫 번째 패스에 공백을 남겨두고 나중에 해당 공백을 찾고/쓰십시오.

0

64 비트 컴퓨터로 전환 할 필요가 없으며 다른 사람이 제안한 1000 가지 대부분을 필요로하지 않습니다. 필요한 것은보다 사려 깊은 알고리즘입니다. Windows에서 경우, 파일지도 (sample code)를 활용

  • :

    다음은이 상황을 돕기 위해 할 수있는 몇 가지 있습니다. 이렇게하면 실제로 메모리를 사용하지 않고 전체 파일을 읽는 것처럼 단일 버퍼 포인터를 통해 파일에 액세스 할 수 있습니다. 리눅스 커널의 최근 버전도 비슷한 메커니즘을 가지고있다.

  • 가능한 경우 파일을 순차적으로 스캔하여 메모리 상 DOM이 생성되지 않도록하십시오. 이렇게하면로드 시간과 메모리 요구 사항이 크게 줄어 듭니다.
  • 풀 메모리 사용! 노드, 점 및 기타 등의 작은 개체가 많이있을 것입니다. 풀링 된 메모리를 사용하여 도움을 받으십시오 (관리되지 않는 언어를 사용하고 있다고 가정합니다. 풀링 된 할당 및 메모리 풀 검색).
  • 관리 언어를 사용하는 경우이 특정 부분을 관리되지 않는 언어로 옮기고 메모리 및 파일 읽기를 제어하십시오. 관리 언어는 메모리 풋 프린트와 성능면에서 단순한 오버 헤드가 있습니다. (예, 이것은 "C++"태그가 붙어 있습니다.)
  • 한 번에 최소량의 데이터 만 읽고 처리하기 때문에 메모리 요구 사항이 낮아 지도록 내부 알고리즘을 설계하십시오.

마지막으로, 복잡한 작업에는 복잡한 측정이 필요하다는 점을 지적하십시오.8GB RAM이 장착 된 64 비트 시스템을 사용할 수 있다고 생각한다면, "메모리에 파일 읽기, 데이터 처리, 출력 쓰기"알고리즘을 사용하십시오.

0

거기에는 좋은 기술이 있습니다. 파일에 인스턴스를 저장하고 필요할 때 가져 오는 것이 좋습니다.

이 기술은 Doxygen과 같은 많은 오픈 소스 소프트웨어에서 많은 양의 메모리가 필요할 때 확장 가능하도록 사용됩니다.

0

이것은 오래된 질문이지만, 최근에 같은 일을했기 때문에 ....

간단한 대답은 없습니다. 이상적인 세상에서 거대한 주소 공간 (즉, 64 비트)을 가진 기계와 엄청난 양의 물리적 메모리를 사용할 것입니다. 엄청난 주소 공간만으로는 충분하지 않거나 그냥 쓰레기가 날 것입니다. 이 경우 XML 파일을 데이터베이스로 구문 분석하고 적절한 쿼리를 사용하여 필요한 것을 제거하십시오. 아마도 이것이 OSM 자체가하는 일일 것입니다 (저는 세계가 약 330GB라고 믿습니다).

현실에서는 아직 편의를 위해 XP 32 비트를 사용하고 있습니다.

공간과 속도 사이의 균형입니다. 얼마나 오래 걸릴 지 신경 쓰지 않는다면 어느 정도의 기억력을 발휘할 수 있습니다. STL 구조체를 사용하면 원하는 모든 것을 파싱 할 수 있지만 곧 메모리가 부족해질 것입니다. 스왑을 사용하는 할당자를 정의 할 수는 있지만 맵, 벡터, 집합 등이 실제로 무엇을하는지 모르기 때문에 비효율적입니다.

32 비트 머신에서 작은 풋 프린트로 모든 작업을 수행하는 유일한 방법은 내가하고 있었던 일과 작업을 청크로 분해 할 때 필요한 것을 매우 신중하게 생각하는 것이 었습니다. 메모리는 효율적입니다 (~ 100MB 이상을 사용하지 마십시오). 그러나 대단히 빠르지는 않지만 그다지 중요하지 않습니다. XML 데이터를 얼마나 자주 파싱해야합니까?

관련 문제