문제

나는 질문을 하고 내 자신의 대답을 따라가고 싶지만 다른 사람들이 어떤 대답을 가지고 있는지도 보고 싶습니다.

두 개의 별도 스레드에서 동시에 읽고 싶은 두 개의 큰 파일이 있습니다.한 스레드는 순차적으로 fileA를 읽고 다른 스레드는 순차적으로 fileB를 읽습니다.스레드 간에는 잠금이나 통신이 없으며 둘 다 최대한 빠른 속도로 순차적으로 읽고 읽은 데이터를 즉시 삭제합니다.

Windows에서 이 설정에 대한 경험은 매우 좋지 않습니다.두 스레드의 결합 처리량은 2~3MiB/초 정도입니다.드라이브는 두 파일 사이를 앞뒤로 탐색하는 데 대부분의 시간을 소비하는 것으로 보이며, 아마도 각 탐색 후에는 거의 읽지 않는 것 같습니다.

스레드 중 하나를 비활성화하고 단일 스레드의 성능을 일시적으로 살펴보면 훨씬 더 나은 대역폭(이 시스템의 경우 ~45MiB/초)을 얻을 수 있습니다.따라서 나쁜 2스레드 성능은 OS 디스크 스케줄러의 인공물임이 분명합니다.

동시 스레드 읽기 성능을 향상시키기 위해 할 수 있는 일이 있습니까? 아마도 다른 API를 사용하거나 어떤 방식으로든 OS 디스크 스케줄러 매개변수를 조정하면 될 것입니다.

일부 세부정보:

파일은 RAM이 2GiB인 시스템에서 각각 2GiB 정도입니다.이 질문의 목적에 따라 우리는 캐시되지 않고 완벽하게 조각 모음된 것으로 간주합니다.이를 확인하기 위해 조각 모음 도구를 사용하고 재부팅했습니다.

우리는 이러한 파일을 읽기 위해 특별한 API를 사용하지 않습니다.이 동작은 Win32의 CreateFile, C의 fopen, C++의 std::ifstream, Java의 FileInputStream 등과 같은 다양한 bog 표준 API에서 반복 가능합니다.

각 스레드는 루프에서 회전하여 읽기 함수를 호출합니다.우리는 각 반복마다 API에서 요청된 바이트 수를 1KiB에서 최대 128MiB 사이의 값으로 변경했습니다.이를 변경해도 효과가 없으므로 각 디스크 검색 후 OS가 물리적으로 읽는 양은 이 숫자에 의해 결정되지 않습니다.이것이 바로 예상되는 것입니다.

1스레드 성능과 2스레드 성능의 극적인 차이는 Windows 2000, Windows XP(32비트 및 64비트), Windows Server 2003, 그리고 하드웨어 RAID5 유무에 관계없이 반복 가능합니다.

도움이 되었습니까?

해결책

문제는 Windows I/O 예약 정책에 있는 것 같습니다.내가 찾아낸 바에 따르면 여기 OS에는 여러 가지 방법이 있습니다.디스크 요청을 예약합니다.Linux와 다른 업체는 서로 다른 정책 중에서 선택할 수 있지만 Vista 이전에는 Windows가 단일 정책에 잠겨 있었습니다.모든 요청이 64KB 블록으로 분할되는 FIFO 대기열.나는 이 정책이 귀하가 겪고 있는 문제의 원인이라고 생각합니다.스케줄러는 두 스레드의 요청을 혼합하여 디스크의 서로 다른 영역 간에 지속적인 검색을 수행합니다.
이제 좋은 소식은 여기 그리고 여기, Vista에서는 요청의 우선 순위를 설정하고 프로세스에 대한 최소 대역폭을 할당할 수 있는 보다 스마트한 디스크 스케줄러를 도입했습니다.
나쁜 소식은 이전 버전의 Windows에서는 디스크 정책이나 버퍼 크기를 변경할 수 있는 방법을 찾지 못했다는 것입니다.또한 프로세스의 디스크 I/O 우선순위를 높여 다른 프로세스에 대한 성능을 향상시키더라도 여전히 스레드가 서로 경쟁하는 문제가 있습니다.
내가 제안할 수 있는 것은 자체적으로 만든 디스크 액세스 정책을 도입하여 소프트웨어를 수정하는 것입니다.
예를 들어 스레드 B에서 다음과 같은 정책을 사용할 수 있습니다(스레드 A와 유사).

if THREAD A is reading from disk then wait for THREAD A to stop reading or wait for X ms
Read for X ms (or Y MB)
Stop reading and check status of thread A again  

상태 확인을 위해 세마포어를 사용하거나 perfmon 카운터를 사용하여 실제 디스크 대기열의 상태를 가져올 수 있습니다.X 및/또는 Y 값은 실제 전송 속도를 확인하고 천천히 수정하여 자동 조정될 수도 있습니다. 따라서 응용 프로그램이 다른 시스템 및/또는 OS에서 실행될 때 처리량을 최대화할 수 있습니다.캐시, 메모리 또는 RAID 수준이 어떤 방식으로든 영향을 미칠 수 있지만 자동 조정을 사용하면 모든 시나리오에서 항상 최고의 성능을 얻을 수 있습니다.

다른 팁

내 답변에 몇 가지 추가 메모를 추가하고 싶습니다.우리가 테스트한 다른 모든 Microsoft 이외의 운영 체제에서는 이 문제가 발생하지 않습니다.Linux, FreeBSD 및 Mac OS X(다른 하드웨어의 마지막 OS)는 모두 한 스레드에서 두 스레드로 이동할 때 총 대역폭 측면에서 훨씬 더 우아하게 저하됩니다.예를 들어 Linux는 ~45MiB/초에서 ~42MiB/초로 성능이 저하되었습니다.이러한 다른 운영 체제는 각 검색 사이에 더 큰 파일 청크를 읽어야 하므로 디스크에서 검색을 기다리는 데 거의 모든 시간을 소비하지 않아야 합니다.

우리의 Windows용 솔루션은 다음을 통과하는 것입니다. FILE_FLAG_NO_BUFFERING 플래그를 지정하다 CreateFile 각 호출에서 대규모(~16MiB) 읽기를 사용합니다. ReadFile.이는 여러 가지 이유로 최적이 아닙니다.

  • 이와 같이 읽을 때 파일은 캐시되지 않으므로 일반적으로 캐싱이 제공하는 이점이 없습니다.
  • 이 플래그를 사용할 때의 제약 조건은 일반적인 읽기(페이지 경계에 대한 읽기 버퍼 정렬 등)보다 훨씬 더 복잡합니다.

(마지막으로 말씀드립니다.Windows에서 교체하는 것이 왜 그렇게 지옥 같은지 설명합니까?즉, Windows는 효율적으로 동시에 여러 파일에 대한 IO를 수행할 수 없으므로 다른 모든 IO 작업을 교체하는 동안 불균형적으로 느려질 수 있습니다.)


Will Dean에 대한 추가 세부 정보를 추가하려면 편집하세요.

물론 이러한 다양한 하드웨어 구성에 따라 원시 수치가 변경되었습니다(때로는 상당히).그러나 문제는 한 스레드에서 두 스레드로 이동할 때 Windows에서만 발생하는 지속적인 성능 저하입니다.테스트된 기계에 대한 요약은 다음과 같습니다.

  • 단일 드라이브로 Windows 2000, Windows XP(32비트) 및 Windows XP(64비트)를 실행하는 다양한 연령대의 여러 Dell 워크스테이션(Intel Xeon).
  • RAID 1+0이 포함된 Windows Server 2003(64비트)을 실행하는 Dell 1U 서버(Intel Xeon).
  • Windows XP(64비트), Windows Server 2003 및 하드웨어 RAID 5가 설치된 HP 워크스테이션(AMD Opteron).
  • 단일 드라이브로 Windows XP(32비트), FreeBSD(64비트) 및 Linux(64비트)를 실행하는 내 가정용 비브랜드 PC(AMD Athlon64)입니다.
  • Mac OS X를 실행하는 내 집 MacBook(Intel Core1), 단일 SATA 드라이브.
  • 내 집 쿨루 리눅스를 실행하는 PC.다른 시스템에 비해 성능이 크게 떨어지지만 멀티 스레드 디스크 읽기를 수행할 때 이 시스템도 RAID5를 사용하는 Windows 서버보다 성능이 뛰어날 수 있음을 보여주었습니다.

테스트 중에 이러한 모든 시스템의 CPU 사용량은 매우 낮았으며 바이러스 백신은 비활성화되었습니다.

이전에 언급하는 것을 잊었지만 우리는 일반 Win32도 시도했습니다. CreateFile API를 사용하여 FILE_FLAG_SEQUENTIAL_SCAN 플래그가 설정되었습니다.이 플래그는 문제를 해결하지 못했습니다.

꽤 다양한 Windows 버전에서 차이가 없고 단일 드라이브와 하드웨어 raid-5 간에는 차이가 없다는 것이 조금 이상해 보입니다.

그것은 단지 '직감'일 뿐이지만 이것이 정말로 단순한 탐색 문제인지 의심스럽습니다.OS X 및 Raid5 외에 이 모든 것이 동일한 컴퓨터에서 시도되었습니까? 다른 컴퓨터에서 시도해 보셨습니까?이 테스트 중에 CPU 사용량은 기본적으로 0입니까?

이 문제를 보여주는 가장 짧은 앱은 무엇입니까?- 여기서 한번 시도해 보고 싶습니다.

나는 일종의 메모리 스레드 안전 잠금을 만들 것입니다.각 스레드는 해제될 때까지 잠금을 기다릴 수 있습니다.잠금이 해제되면 잠금을 가져와 정의된 시간 길이 또는 정의된 양의 데이터 동안 파일을 읽은 다음 대기 중인 다른 스레드에 대해 잠금을 해제합니다.

당신은 사용합니까 IO완료 포트 Windows에서?C++를 통한 Windows에는 이 주제에 대한 심층적인 장이 있으며 운 좋게도 MSDN에서도 사용 가능.

Paul - 업데이트를 보았습니다.매우 흥미로운.

일부 상황에서는 사람들이 상당한 I/O 개선을 ​​보고하는 것처럼 보이기 때문에 Vista 또는 Win2008에서 시도해 보는 것은 흥미로울 것입니다.

다른 API에 대한 나의 유일한 제안은 파일을 메모리 매핑하는 것입니다. 시도해 보셨나요?불행하게도 파일당 2GB에서는 32비트 시스템에서 여러 개의 전체 파일을 매핑할 수 없습니다. 이는 이것이 그렇게 사소한 일이 아니라는 것을 의미합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top