C#과 비교한 C++ STL vector 성능
테스트를 위해 C#으로 다음과 같은 식의 예제를 만들 일이 있었습니다.
class Program
{
static void Main(string[] args)
{
string body = "...[10만 개의 0~9 숫자로 이뤄진 문자열]...";
int loopCount = 10000;
byte [] bodyContents = Encoding.UTF8.GetBytes(body);
for (int i = 0; i < 100; i++)
{
MemoryStream ms = new MemoryStream();
ms.Write(bodyContents, 0, bodyContents.Length);
ms.Flush();
}
}
}
보시는 바와 같이 간단합니다. 그런데, 이 예제를 Visual C++로도 만들어 보았는데,
#include "stdafx.h"
#include <string>
#include <vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<unsigned char> bodyBuf;
// bodyBuf = ...[10만 개의 0~9 숫자로 이뤄진 문자열]...
for (int i = 0; i < 100; i ++)
{
vector<unsigned char> dataBuf;
dataBuf.insert(dataBuf.end(), body.begin(), body.end());
}
return 0;
}
차이점이라면 C#의 MemoryStream을 단순히 vector<unsigned char>로 치환한 정도입니다. 이를 각각 x64, Release 빌드하고 성능 비교를 해보면 다음과 같습니다.
C# MemoryStream: 4밀리초
C++ vector.insert: 15밀리초
오히려 C#이 빠르죠. ^^ 물론, 약간 불공정한 면이 있습니다. MemoryStream의 Write는 아마도 내부적으로 memcpy를 사용할테니 공정하게 테스트를 하려면 C#에서도 List<byte>와 같은 자료 구조를 사용해야 합니다.
Stopwatch st = new Stopwatch();
st.Start();
for (int i = 0; i < 100; i++)
{
List<byte> list = new List<byte>();
for (int j = 0; j < bodyContents.Length; j++)
{
list.Add(bodyContents[j]);
}
//MemoryStream ms = new MemoryStream();
//ms.Write(bodyContents, 0, bodyContents.Length);
//ms.Flush();
}
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds); // 수행 시간: 46밀리초
예상대로 C++의 vector보다 느립니다. 반대로 MemoryStream과 같은 속도로 C++ 속도를 개선하는 것은 memcpy를 쓰면 됩니다.
for (int i = 0; i < 100; i ++)
{
vector<unsigned char> dataBuf;
dataBuf.reserve(bodyBuf.size());
memcpy(dataBuf.data(), bodyBuf.data(), bodyBuf.size());
}
// 수행시간: 6밀리초
결과는 다음과 같이 정리할 수 있는데, 다소 의외인 점이라면 C++에서 memcpy를 했는데도 C#의 MemoryStream과 비교하면 근소하게 느렸다는 것입니다.
=== 64비트 Release ===
C# MemoryStream: 4밀리초
C# List: 46밀리초
C++ memcpy: 6밀리초
C++ vector: 15밀리초
테스트할 가치는 크게 없지만, 혹시나 싶어 Debug 빌드로도 성능 측정을 해보았습니다.
=== 64비트 Debug ===
C# MemoryStream: 5밀리초
C# List: 112밀리초
C++ memcpy: 1 밀리초
C++ vector: 2760밀리초
재미있는 점은 C++ memcpy 결과가 Release 빌드보다 더욱 빨랐다는 것입니다. ^^( 이 결과가 너무 이상해서 몇 번을 해봤는데 결과가 같았습니다. 신기하군요. ^^) C#의 MemoryStream은 Debug/Release에 별로 영향을 받지 않는데, 내부적으로 호출되는 메모리 복사 코드는 결국 P/Invoke를 통한 C/C++ 코드의 실행이기 때문입니다.
32비트로 빌드한 것도 약간 흥미롭습니다.
=== 32비트 Release ===
C# MemoryStream: 4밀리초
C# List: 52밀리초
C++ memcpy: 16밀리초
C++ vector: 27밀리초
=== 32비트 Debug ===
C# MemoryStream: 5밀리초
C# List: 95밀리초
C++ memcpy: 12밀리초
C++ vector: 7879밀리초
32비트 Debug로는 C++ vector는 몹쓸 성능을 보입니다. ^^ 또한, 전체적인 성능은 x64가 x86보다 다소 낫다는 것도 알 수 있고!
그나저나, C++이 어느새 많이 발전했습니다. 이제는 C#과 같은 언어로 만든 프로그램을 C++로 포팅하는데 거의 유사하게 코드가 작성되는 경험을 했습니다. 특히 ^^ std::thread가 나온 것이 너무 반갑습니다. 그러면서 느낀 점이 있다면, ^^ 옛말(?)에 잘못 짠 어셈블리는 C++보다 느리다고 하더니... 이제는 대충 짠 C++은 C#보다 느리다는 말이 나올 듯합니다.
테스트 한 소스 코드는 첨부해 두었습니다. Visual Studio 2012에서 닷넷은 4.0으로, C++은 Platform Toolset을 v110으로 맞춘 기본 상태입니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]