"예, PHP는 C#보다 빠릅니다"에 대한 응답
14151 단어 phpperformancecsharp
여기에 사용된 벤치마크는 파일 시스템에서 파일을 4KiB 청크로 읽고 파일의 값
1
으로 바이트 수를 계산합니다. 먼저, 특히 디스크에서 파일을 읽는 것이 관련되어 있기 때문에 이 "벤치마크"가 매우 의미가 있다고 생각하지 않는다고 말하면서 시작하겠습니다. 파일 시스템 성능(캐시, 디스크 드라이브의 상태, 그 당시 커널이 얼마나 바쁜지)에 영향을 줄 수 있는 많은 것들이 있지만 그 중 어느 것도 테스트 자체에서 주소가 아닙니다.그럼에도 불구하고 결과는 우리가 이야기할 수 있는 몇 가지 흥미로운 성능 특성을 나타냅니다.
테스트를 위한 소스 코드는 여기에서 찾을 수 있습니다: https://github.com/dhhoang/csharp-php-file-read
작은 파일
이렇게 테스트 파일을 생성했습니다.
# for this test, we will use file_size of 4 MiB as specified in the original post
base64 /dev/urandom | head -c [file_size] > test.txt
PHP(8.0) 프로그램의 코드는 다음과 같습니다.
function test()
{
$file = fopen("/path/to/test.txt", 'r');
$counter = 0;
$timer = microtime(true);
while ( ! feof($file)) {
$buffer = fgets($file, 4096);
$counter += substr_count($buffer, '1');
}
$timer = microtime(true) - $timer;
fclose($file);
printf("counted %s 1s in %s milliseconds\n", number_format($counter), number_format($timer * 1000, 4));
}
test();
그리고 C#의 경우:
private static void Test()
{
using var file = File.OpenRead("/path/to/test.txt");
var counter = 0;
var buffer = new byte[4096];
var numRead = 0;
var sw = Stopwatch.StartNew();
while ((numRead = file.Read(buffer, 0, buffer.Length)) != 0)
{
counter += buffer.Take(numRead).Count((x) => x == '1');
}
sw.Stop();
Console.WriteLine($"Counted {counter} 1s in {sw.ElapsedMilliseconds} milliseconds");
}
Test();
t3-xlarge EC2 인스턴스에서 실행한 결과는 다음과 같습니다(참고: 코드는 10번 실행되고 런타임은 콜드 파일 캐시로 인한 이상을 제거한 후 평균)
Test-C# 53.2ms
Test-PHP 11.1ms
따라서 PHP 코드는 C# 코드보다 약 5배 빠릅니다!!! PHP가 실제로 C#보다 빠른 것 같습니까?
뭔가 확실히 여기에서 벗어났습니다. 파일을 읽을 때 .NET이 그렇게 느립니까? 아마 그렇지 않을 것입니다. 두 프로그램에서 "카운팅"부분을 제거한 간단한 테스트를 수행했는데 성능이 매우 유사해졌습니다. 블로그 작성자는 테스트에 "사용자 영역 코드가 거의"없고 주로 파일 읽기 성능을 테스트한다고 주장했습니다. 나는 이것이 잘못된 것으로 나타났습니다.
이제 두 프로그램을 자세히 보면
1
바이트를 세는 부분을 제외하고는 매우 유사합니다. PHP는 매우 최적화된 내장 함수substr_count
를 사용하는 반면 C# 코드는 LINQ를 사용합니다. LINQ는 C#에서 컬렉션을 사용하는 매우 편리한 방법이지만 속도도 상당히 느립니다. 구식 방식으로 바이트 수를 계산하려고 하면 어떻게 될까요?private static void Test_FileStream_NoLinq()
{
...
while ((numRead = file.Read(buffer, 0, buffer.Length)) != 0)
{
for (var c = 0; c < numRead; c++)
{
if (buffer[c] == '1')
{
counter++;
}
}
}
...
}
이제 결과는 다음과 같습니다(
Test-C#-NoLinq
참조).Test-C# 53.2ms
Test-PHP 11.1ms
Test-C#-NoLinq 6.5ms
따라서 이 시점에서 C#은 이미 이전보다 훨씬 빠르게 수행하고 있으며 PHP 프로그램보다 약 두 배 빠릅니다. 이것은 바이트 카운팅 프로세스가 총 실행 시간에 크게 기여함을 보여줍니다.
그래서 다음 질문은 우리가 더 잘할 수 있습니까? 바이트 버퍼로 작업할 때 개별 바이트를 반복하는 것은 매우 순진한 구현입니다. 보다 최적화된 방법은 SIMD와 같은 벡터화 기술을 활용하는 것입니다. 사실
substr_count
함수가 벡터화를 사용하지 않는다면 매우 놀랄 것입니다. 이를 테스트하기 위해 substr_count
를 사용하는 대신 문자열을 반복하는 또 다른 PHP 테스트 함수를 만들었습니다. 이는 C# Test_FileStream_NoLinq
함수와 비슷합니다.function test_manual_count()
{
...
while ( ! feof($file)) {
$buffer = fgets($file, 4096);
$length = strlen($buffer);
for ($i = 0; $i < $length; $i++) {
if($buffer[$i]=='1'){
$counter += 1;
}
}
}
...
}
결과(
Test-PHP-Manual-Count
참조):Test-C#-NoLinq 6.5ms
Test-PHP 11.1ms
Test-PHP-Manual-Count 135ms
그것은 고통스럽게 느리기 때문에 문자열에서 발생 횟수를 계산해야 할 때 항상
substr_count
를 사용하는 것이 좋습니다. 불행히도 C#은 동일한 기능을 가진 내장 메서드를 제공하지 않지만 많은 primitives for implementing vectorization 을 제공합니다. StackOverflow : VectorExtensions.OccurrencesOf(ReadonlySpan<byte>, byte)
에서 SIMD에 해당하는 기능의 구현을 찾았습니다. 이를 통해 카운터를 다시 작성할 수 있습니다.private static void Test_FileStream_Vectorized()
{
...
while ((numRead = file.Read(buffer, 0, buffer.Length)) != 0)
{
counter += buffer.AsSpan().Slice(0, numRead).OccurrencesOf((byte)'1');
}
...
}
결과(
Test-C#-Vectorization
참조):Test-C#-NoLinq 6.5ms
Test-C#-Vectorization 1.0ms
이는 수동 루프보다 6배 빠르고 PHP보다 약 10배 빠릅니다 😊.
대용량 파일
이 테스트에서는 3.2GBUbuntu ISO image를 사용하고 있습니다. 결과는 다음과 같습니다.
Test-PHP 3228.4ms
Test-PHP-Manual-Count 103966.7ms
Test-C#-NoLinq 5175.3ms
Test-C#-Vectorization 1104.7ms
여기에서 벡터화를 사용하면 두 언어 모두에서 작업이 훨씬 더 빨라지는 방법을 명확하게 볼 수 있습니다.
Reference
이 문제에 관하여("예, PHP는 C#보다 빠릅니다"에 대한 응답), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/goaty92/in-response-to-yes-php-is-faster-than-c-2a2g텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)