gpt4 book ai didi

c++ - 与 cstdio 和 std::fstream 相比,为什么 SDL_RWops 在写入文件时表现如此糟糕?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:13:04 25 4
gpt4 key购买 nike

我目前正在将我的爱好项目从 std::fstream 迁移到 SDL_RWops(因为 SDL_RWops 是我唯一的简单选择用于在 Android 上加载 Assets )。

从文件读取完美,但写入文件却非常慢。

考虑以下测试用例:

C 标准 IO - 0.217193 秒

std::FILE *io = std::fopen("o.txt", "w");
for (int i = 0; i < 1024*1024*4; i++)
std::putc('0', io);
std::fclose(io);

C++ 流 - 0.278278 秒

std::ofstream io("o.txt");
for (int i = 0; i < 1024*1024*4; i++)
io << '0';
io.close();

SDL_RWops:- 17.9893 秒

SDL_RWops *io = SDL_RWFromFile("o.txt", "w");
for (int i = 0; i < 1024*1024*4; i++)
io->write(io, "0", 1, 1);
io->close(io);

所有测试用例均使用带有 -O3 的 g++ 5.3.0 (mingw-w64) x86 编译。我用过 SDL 2.0.4。

我也试过 -O0 得到类似的结果(慢 0.02 到 0.25 秒)。

看完这些结果后,我有一个明显的问题:
为什么SDL_RWops写入性能这么差?
我该怎么做才能让它表现得更好?


编辑:这是windows_file_write()(来自SDL)的代码,io->write应该指向的地方。它应该做缓冲输出,但我不确定它是如何工作的。

static size_t SDLCALL
windows_file_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
{
size_t total_bytes;
DWORD byte_written;
size_t nwritten;

total_bytes = size * num;

if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE || total_bytes <= 0 || !size)
return 0;

if (context->hidden.windowsio.buffer.left) {
SetFilePointer(context->hidden.windowsio.h,
-(LONG)context->hidden.windowsio.buffer.left, NULL,
FILE_CURRENT);
context->hidden.windowsio.buffer.left = 0;
}

/* if in append mode, we must go to the EOF before write */
if (context->hidden.windowsio.append) {
if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
INVALID_SET_FILE_POINTER) {
SDL_Error(SDL_EFWRITE);
return 0;
}
}

if (!WriteFile
(context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
SDL_Error(SDL_EFWRITE);
return 0;
}

nwritten = byte_written / size;
return nwritten;
}

最佳答案

简而言之:我设法改进了它。现在我得到了 0.316382 秒,这只比其他解决方案慢一点。
但这是我一生中做过的最肮脏的黑客攻击之一。如果有更好的解决方案,我将不胜感激。

它是如何完成的:我已经推出了 SDL_RWFromFile() 的自定义替换:我已经从 SDL_rwops.c 中复制粘贴了实现并删除了所有预处理器分支就好像只定义了 HAVE_STDIO_H 一样。该函数包含对 SDL_RWFromFP() 的调用,因此我也复制粘贴了 SDL_RWFromFP() 并对其应用了相同的修改。反过来,SDL_RWFromFP() 依赖于stdio_size()stdio_read()stdio_write()stdio_seek()stdio_close()(它们也是 SDL_rwops.c 的一部分),因此我也复制粘贴了它们。反过来,这些依赖于(再次!)struct SDL_RWops 中“隐藏” union 的某些字段,这些字段在使用预处理器的 Windows 上被禁用。我没有更改 header ,而是更改了复制粘贴的代码以使用“隐藏” union 的不同 成员,这些成员确实 存在于 Windows 上。 (这是安全的,因为除了我自己的代码和复制粘贴的代码之外,没有任何其他内容会触及该结构。)进行了一些其他调整以使代码以 C++ 而不是 C 的形式工作。

这是我得到的:

#if OnWindows

#define hidden_stdio_fp ((FILE * &)context->hidden.windowsio.h)
#define hidden_stdio_autoclose ((SDL_bool &)context->hidden.windowsio.append)

// ** Begin copied code **

static auto stdio_size = [](SDL_RWops * context) -> int64_t
{
int64_t pos, size;

pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
if (pos < 0) {
return -1;
}
size = SDL_RWseek(context, 0, RW_SEEK_END);

SDL_RWseek(context, pos, RW_SEEK_SET);
return size;
};

static auto stdio_seek = [](SDL_RWops * context, int64_t offset, int whence) -> int64_t
{
#ifdef HAVE_FSEEKO64
if (std::fseeko64(hidden_stdio_fp, (off64_t)offset, whence) == 0) {
return std::ftello64(hidden_stdio_fp);
}
#elif defined(HAVE_FSEEKO)
if (std::fseeko(hidden_stdio_fp, (off_t)offset, whence) == 0) {
return std::ftello(hidden_stdio_fp);
}
#elif defined(HAVE__FSEEKI64)
if (std::_fseeki64(hidden_stdio_fp, offset, whence) == 0) {
return std::_ftelli64(hidden_stdio_fp);
}
#else
if (std::fseek(hidden_stdio_fp, offset, whence) == 0) {
return std::ftell(hidden_stdio_fp);
}
#endif
return SDL_Error(SDL_EFSEEK);
};

static auto stdio_read = [](SDL_RWops * context, void *ptr, std::size_t size, std::size_t maxnum) -> std::size_t
{
std::size_t nread;

nread = std::fread(ptr, size, maxnum, hidden_stdio_fp);
if (nread == 0 && std::ferror(hidden_stdio_fp)) {
SDL_Error(SDL_EFREAD);
}
return nread;
};

static auto stdio_write = [](SDL_RWops * context, const void *ptr, std::size_t size, std::size_t num) -> std::size_t
{
std::size_t nwrote;

nwrote = std::fwrite(ptr, size, num, hidden_stdio_fp);
if (nwrote == 0 && std::ferror(hidden_stdio_fp)) {
SDL_Error(SDL_EFWRITE);
}
return nwrote;
};

static auto stdio_close = [](SDL_RWops * context) -> int
{
int status = 0;
if (context) {
if (hidden_stdio_autoclose) {
/* WARNING: Check the return value here! */
if (std::fclose(hidden_stdio_fp) != 0) {
status = SDL_Error(SDL_EFWRITE);
}
}
SDL_FreeRW(context);
}
return status;
};

static auto RWFromFP = [](FILE * fp, SDL_bool autoclose) -> SDL_RWops *
{
SDL_RWops *context = 0;

context = SDL_AllocRW();
if (context != 0) {
context->size = stdio_size;
context->seek = stdio_seek;
context->read = stdio_read;
context->write = stdio_write;
context->close = stdio_close;
hidden_stdio_fp = fp;
hidden_stdio_autoclose = autoclose;
context->type = SDL_RWOPS_STDFILE;
}
return context;
};

static auto SDL_RWFromFile = [](const char *file, const char *mode) -> SDL_RWops *
{
SDL_RWops *context = 0;
if (!file || !*file || !mode || !*mode) {
SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
return 0;
}

FILE *fp = std::fopen(file, mode);

if (fp == 0) {
SDL_SetError("Couldn't open %s", file);
} else {
context = RWFromFP(fp, (SDL_bool)1);
}

return context;
};

// ** End copied code **

#undef hidden_stdio_fp
#undef hidden_stdio_autoclose

#endif

关于c++ - 与 cstdio 和 std::fstream 相比,为什么 SDL_RWops 在写入文件时表现如此糟糕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37775277/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com