
๋จธ๋ฆฌ๋ง
์ธ์์๋ ์ด๋ ๊ฒ ๊ฐ๋จํ๊ณ ๋งค์ฐ ์ ์ฉํ ์ ํธ๋ฆฌํฐ๊ฐ ์์ต๋๋ค. , ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ ๋งค์ฐ ์ค๋ซ๋์ ์ฐ๋ฆฌ ์์ฐ ๊ณผ์ ์ ๋ฟ๋ฆฌ ๋ฐํ ์์์ต๋๋ค(๋น๋ก ํด๋น ๋ฒ์ ์ ์ค์นํ ์๋ ์์์ง๋ง ํ์คํ ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฒ์ ์ ์๋์์ต๋๋ค). ์ฐ๋ฆฌ๋ ์ด๋ฅผ ์๋๋ ๋ชฉ์ , ์ฆ ๋ฐ์ด๋๋ฆฌ ํจ์น ๊ตฌ์ถ์ ์ฌ์ฉํฉ๋๋ค. ์ ์ฅ์์ ์๋ ๋ด์ฉ์ ๋ณด๋ฉด ์กฐ๊ธ ์ฌํ๊ฒ ๋ฉ๋๋ค. ์ฌ์ค ์ ์ฅ์๋ ์ค๋ ์ ์ ๋ฒ๋ ค์ก๊ณ ๋๋ถ๋ถ์ด ๋งค์ฐ ๊ตฌ์์
๋๋ค. (๋ด ์ ๋๋ฃ๊ฐ ๊ทธ๊ณณ์์ ์ฌ๋ฌ ๋ฒ ํธ์งํ ์ ์ด ์์ง๋ง ๊ทธ๊ฑด ์ค๋ ์ ์ผ์
๋๋ค.) . ์ผ๋ฐ์ ์ผ๋ก ๋๋ ์ด ๋ฌธ์ ๋ฅผ ๋ถํ์ํค๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค. ๋๋ ํฌํฌํ๊ณ , ์ฌ์ฉํ ๊ณํ์ด ์๋ ๊ฒ์ ๋ฒ๋ฆฌ๊ณ , ํ๋ก์ ํธ๋ฅผ ๋ค์์ผ๋ก ์ฎ๊ฒผ์ต๋๋ค. , ์ธ๋ผ์ธ๋ "ํซ" ๋ง์ดํฌ๋ก ํจ์, ์คํ์์ ํฐ ๋ฐฐ์ด(๋ฐ ์์งํ๊ฒ "ํญํ"์ผ๋ก ๋ง๋๋ ๊ฐ๋ณ ๊ธธ์ด ๋ฐฐ์ด)์ ์ ๊ฑฐํ๊ณ ํ๋กํ์ผ๋ฌ๋ฅผ ๋ค์ ํ ๋ฒ ์คํํ ๊ฒฐ๊ณผ ์ฝ 40%์ ์๊ฐ์ด ์์
์ ์๋น๋๋ค๋ ์ฌ์ค์ ์๊ฒ ๋์์ต๋๋ค. ...
๊ทธ๋ผ fwrite๋ ์ด๋ป๊ฒ ๋๋์?
์ด ์ฝ๋์์ fwrite(๋ด ํน์ ํ ์คํธ ์ฌ๋ก: ๊ฐ๊น์ด 300MB ํ์ผ ์ฌ์ด์ ํจ์น๋ฅผ ๊ตฌ์ถํ๊ณ ์ ๋ ฅ ๋ฐ์ดํฐ๊ฐ ์ ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์ ์์)๋ ์์ ๋ฒํผ ํฌ๊ธฐ๋ก ์๋ฐฑ๋ง ๋ฒ ํธ์ถ๋ฉ๋๋ค. ๋ถ๋ช ํ ์ด ์ผ์ด ๋๋ ค์ง ๊ฒ์ด๋ฏ๋ก ๋๋ ์ด๋ป๊ฒ๋ ์ด ๋ถ๋ช ์์ ์ํฅ์ ๋ฏธ์น๊ณ ์ถ์ต๋๋ค. ์์ง์ ๋ค์ํ ์ ํ์ ๋ฐ์ดํฐ ์์ค, ๋น๋๊ธฐ ์ ์ถ๋ ฅ์ ๊ตฌํํ๋ ค๋ ์๊ตฌ๊ฐ ์์ผ๋ฉฐ ๋ ๊ฐ๋จํ ์๋ฃจ์ ์ ์ฐพ๊ณ ์ถ์์ต๋๋ค. ๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅธ ๊ฒ์ ๋ฒํผ ํฌ๊ธฐ๋ฅผ ๋๋ฆฌ๋ ๊ฒ์ด์์ต๋๋ค.
setvbuf(file, nullptr, _IOFBF, 64* 1024)๊ทธ๋ฌ๋ ๊ฒฐ๊ณผ๋ ํฌ๊ฒ ํฅ์๋์ง ์์์ต๋๋ค(ํ์ฌ fwrite๊ฐ ์ ์ฒด ์๊ฐ์ ์ฝ 37%๋ฅผ ์ฐจ์งํจ). ์ด๋ ์ฌ์ ํ ๋์คํฌ์ ๋ฐ์ดํฐ๋ฅผ ์์ฃผ ์ฐ๋ ๋ฌธ์ ๊ฐ ์๋๋ผ๋ ์๋ฏธ์ ๋๋ค. fwrite์ "๋ด๋ถ"๋ฅผ ๋ณด๋ฉด FILE ์ ๊ธ/์ ๊ธ ํด์ ๊ตฌ์กฐ๊ฐ ๋ค์๊ณผ ๊ฐ์ ๋ด๋ถ์์ ๋ฐ์ํ๊ณ ์์์ ์ ์ ์์ต๋๋ค(์์ฌ ์ฝ๋, ๋ชจ๋ ๋ถ์์ Visual Studio 2017์์ ์ํ๋์์ต๋๋ค).
size_t fwrite (const void *buffer, size_t size, size_t count, FILE *stream)
{
size_t retval = 0;
_lock_str(stream); /* lock stream */
__try
{
retval = _fwrite_nolock(buffer, size, count, stream);
}
__finally
{
_unlock_str(stream); /* unlock stream */
}
return retval;
}
ํ๋กํ์ผ๋ฌ์ ๋ฐ๋ฅด๋ฉด _fwrite_nolock์ ์๊ฐ์ 6%๋ง ์ฐจ์งํ๋ฉฐ ๋๋จธ์ง๋ ์ค๋ฒํค๋์ ๋๋ค. ๋ด ํน๋ณํ ๊ฒฝ์ฐ์๋ ์ค๋ ๋ ์์ ์ฑ์ด ํ์คํ ๊ณผ์์ด๋ฏ๋ก fwrite ํธ์ถ์ ๋ค์์ผ๋ก ๋์ฒดํ์ฌ ์ค๋ ๋ ์์ ์ฑ์ ํฌ์ํ๊ฒ ์ต๋๋ค. - ๋ ผ์์ ๋ฅ์ํ ํ์๋ ์์ต๋๋ค. ์ ์ฒด: ์ด ๊ฐ๋จํ ์กฐ์์ผ๋ก ์ธํด ๊ฒฐ๊ณผ ๊ธฐ๋ก ๋น์ฉ์ด ํฌ๊ฒ ์ค์ด๋ค์์ผ๋ฉฐ ์๋ณธ ๋ฒ์ ์์๋ ์์ ์๊ฐ์ ๊ฑฐ์ ์ ๋ฐ์ ๋ฌํ์ต๋๋ค. ๊ทธ๊ฑด ๊ทธ๋ ๊ณ , POSIX ์ธ๊ณ์๋ ๋น์ทํ ๊ธฐ๋ฅ์ด ์์ต๋๋ค. . ์ผ๋ฐ์ ์ผ๋ก ๋งํ๋ฉด fread์๋ ๋์ผํ๊ฒ ์ ์ฉ๋ฉ๋๋ค. ๋ฐ๋ผ์ ํ ์์ #define์ ์ฌ์ฉํ๋ฉด ํ์ํ์ง ์์ ๊ฒฝ์ฐ ๋ถํ์ํ ์ ๊ธ ์์ด ์์ ํ ํฌ๋ก์ค ํ๋ซํผ ์๋ฃจ์ ์ ์ป์ ์ ์์ต๋๋ค(์ด๋ฐ ์ผ์ ๊ฝค ์์ฃผ ๋ฐ์ํฉ๋๋ค).
fwrite, _fwrite_nolock, setvbuf
์๋ ํ๋ก์ ํธ์์ ๋ฒ์ด๋ ํน์ ์ฌ๋ก๋ฅผ ํ ์คํธํด ๋ณด๊ฒ ์ต๋๋ค. ๋ฐ๋ก 512MB ํฌ๊ธฐ์ ํ์ผ์ 1๋ฐ์ดํธ์ฉ ์์ฃผ ์์ ๋ฉ์ด๋ฆฌ๋ก ๋๋์ด ์ฐ๋ ๊ฒ์ ๋๋ค. ํ ์คํธ ์์คํ ์ฌ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. AMD Ryzen 7 1700, 16GB RAM, 7200rpm HDD, 64MB ์บ์. Windows 10 1809๋ ๋ฒ์ ์์ ๋ฐ์ด๋๋ฆฌ๋ 32๋นํธ๋ก ๋น๋๋์๊ณ , ์ต์ ํ๊ฐ ํ์ฑํ๋์์ผ๋ฉฐ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์ ์ผ๋ก ๋งํฌ๋์์ต๋๋ค.
์คํ ์ํ:
#include <chrono>
#include <cstdio>
#include <inttypes.h>
#include <memory>
#ifdef _MSC_VER
#define fwrite_unlocked _fwrite_nolock
#endif
using namespace std::chrono;
int main()
{
std::unique_ptr<FILE, int(*)(FILE*)> file(fopen("test.bin", "wb"), fclose);
if (!file)
return 1;
constexpr size_t TEST_BUFFER_SIZE = 256 * 1024;
if (setvbuf(file.get(), nullptr, _IOFBF, TEST_BUFFER_SIZE) != 0)
return 2;
auto start = steady_clock::now();
const uint8_t b = 77;
constexpr size_t TEST_FILE_SIZE = 512 * 1024 * 1024;
for (size_t i = 0; i < TEST_FILE_SIZE; ++i)
fwrite_unlocked(&b, 1, sizeof(b), file.get());
auto end = steady_clock::now();
auto interval = duration_cast<microseconds>(end - start);
printf("Time: %lldn", interval.count());
return 0;
}
๋ณ์๋ TEST_BUFFER_SIZE๊ฐ ๋๋ฉฐ, ๋ช ๊ฐ์ง ๊ฒฝ์ฐ์ fwrite_unlocked๋ฅผ fwrite๋ก ๋์ฒดํฉ๋๋ค. ๋ฒํผ ํฌ๊ธฐ๋ฅผ ๋ช ์์ ์ผ๋ก ์ค์ ํ์ง ์๊ณ fwrite ์ฌ๋ก๋ถํฐ ์์ํ๊ฒ ์ต๋๋ค(setvbuf ๋ฐ ๊ด๋ จ ์ฝ๋ ์ฃผ์ ์ฒ๋ฆฌ). ์๊ฐ 27048906 ยตs, ์ฐ๊ธฐ ์๋ - 18.93 MB/s. ์ด์ ๋ฒํผ ํฌ๊ธฐ๋ฅผ 64KB(์๊ฐ - 25037111ฮผs, ์๋ - 20.44Mb/s)๋ก ์ค์ ํด ๋ณด๊ฒ ์ต๋๋ค. ์ด์ setvbuf๋ฅผ ํธ์ถํ์ง ์๊ณ _fwrite_nolock์ ์๋์ ํ ์คํธํด ๋ณด๊ฒ ์ต๋๋ค: 7262221 ยตs, ์๋ - 70.5 Mb/s!
๋ค์์ผ๋ก ๋ฒํผ ํฌ๊ธฐ(setvbuf)๋ฅผ ์คํํด ๋ณด๊ฒ ์ต๋๋ค.

5๋ฒ์ ์คํ์ ํ๊ท ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ป์๋๋ฐ ๋๋ฌด ๊ฒ์๋ฌ์ ์ค์ฐจ๋ฅผ ๊ณ์ฐํ ์๊ฐ ์์์ต๋๋ค. ๋๋ก์๋ ์ผ๋ฐ HDD์ 93๋ฐ์ดํธ๋ฅผ ์ธ ๋ 1MB/s๊ฐ ๋งค์ฐ ์ข์ ๊ฒฐ๊ณผ์
๋๋ค. ์ต์ ์ ๋ฒํผ ํฌ๊ธฐ๋ฅผ ์ ํํ๊ณ (์ ๊ฒฝ์ฐ์๋ 256KB๊ฐ ๋ฑ ๋ง์ต๋๋ค) fwrite๋ฅผ _fwrite_nolock/fwrite_unlocked( ๋ฌผ๋ก ์ค๋ ๋ ์์ ์ฑ์ด ํ์ํ์ง ์์ ๊ฒฝ์ฐ).
๋น์ทํ ์กฐ๊ฑด์์ fread๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค. Linux๊ฐ ์ค์น๋ ํ๋์จ์ด ๋จธ์ ์ด ์๊ธฐ ๋๋ฌธ์(๋จ์ผ ๋ณด๋๋ ํฌํจ๋์ง ์์) ๊ฐ์ ๋จธ์ (Hyper-V, OpenSUSE 15, GCC 8.3.1)์์ ์ ํ๋ ์คํ์ ์ํํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค. ํจํด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๋์ผ: "๋ค์ดํค๋" fwrite 20 Mb/s, fwrite + 256 KB ๋ฒํผ๋ 23 Mb/s๋ฅผ ์์ฑ, fwrite_unlocked๋ ๋์ผํ ๋ฒํผ - 35 Mb/s(64๋นํธ ๋ฐ์ด๋๋ฆฌ, ์ด์
๋ธ๋ g++ -o2 -s -static-libgcc -static-libstdc++ fwrite_test.cpp -o fwrite_test).
์ฌํ
์ด ๊ธ์ ์ด ๋ชฉ์ ์ ๋ง์ ๊ฒฝ์ฐ์ ๊ฐ๋จํ๊ณ ํจ๊ณผ์ ์ธ ๊ธฐ์ ์ ์ค๋ช ํ๋ ๊ฒ์ด์์ต๋๋ค. (๋๋ ์ด์ ์ _fwrite_nolock/fwrite_unlocked ํจ์๋ฅผ ์ ํด๋ณธ ์ ์ด ์์ผ๋ฉฐ ๊ทธ๋ค์ง ์ธ๊ธฐ๊ฐ ์์ง๋ง ํ๋ ๊ฒ์ ๋๋ค.) ๋๋ ๊ทธ ์๋ฃ๊ฐ ์๋ก์ด ๊ฒ์ด๋ผ๊ณ ์ฃผ์ฅํ์ง ์์ง๋ง, ๊ทธ ๊ธฐ์ฌ๊ฐ ์ปค๋ฎค๋ํฐ์ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ถ์ฒ : habr.com
