简体   繁体   English

Windows C++:在远程机器上写入大文件的最快方法是什么?

[英]Windows C++: Whats the fastest way to write a large file on a remote machine?

In our application we have to write large files (100 MBytes up to some GBytes) on remote machines (in the same LAN segement) frequently.在我们的应用程序中,我们必须经常在远程机器(在同一 LAN 网段中)上写入大文件(100 MB 到一些 GBytes)。 This is the bottleneck of our application.这是我们应用程序的瓶颈。 Remote machines may be native Windows but also Linux machines using SMB.远程机器可能是本地 Windows,也可能是使用 SMB 的 Linux 机器。

We found that creating files first locally and then copying them using the Windows API function CopyFile is MUCH faster than using CreateFile directly with a UNC Path (or drive letter) targeting the remote machine.我们发现首先在本地创建文件然后使用 Windows API 函数CopyFile复制它们比直接使用CreateFile和针对远程机器的 UNC 路径(或驱动器号)快得多。 But still we have to do 2 writes which seems far from optimum.但是我们仍然必须进行 2 次写入,这似乎远非最佳。

Inspired by the first comments on this question i implemented the usage of FILE_FLAG_OVERLAPPED for CreateFile as discussed here and here :受到关于这个问题的第一条评论的启发,我实现了对 CreateFile 的 FILE_FLAG_OVERLAPPED 的使用,如此此处所述

  HANDLE hToken;
    auto openResult = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
    if (!openResult)
    {
        gConsoleAndLog << "OpenProcessToken failed with err " << GetLastError() << std::endl;
    }

    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 3;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    tp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
    tp.Privileges[2].Attributes = SE_PRIVILEGE_ENABLED;
    if(! LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &tp.Privileges[0].Luid))
        gConsoleAndLog << "LookupPrivilegeValue SE_MANAGE_VOLUME_NAME failed with err " << GetLastError() << std::endl;

    if (! LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &tp.Privileges[1].Luid))
        gConsoleAndLog << "LookupPrivilegeValue SE_INCREASE_QUOTA_NAME failed with err " << GetLastError() << std::endl;

    if (! LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &tp.Privileges[2].Luid))
        gConsoleAndLog << "LookupPrivilegeValue SE_ASSIGNPRIMARYTOKEN_NAME failed with err " << GetLastError() << std::endl;

    auto adjustResult = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
    if (!adjustResult || GetLastError() != ERROR_SUCCESS)
    {
        gConsoleAndLog << "AdjustTokenPrivileges failed with err " << GetLastError() << std::endl;
    }
    else gConsoleAndLog << "AdjustTokenPrivileges SUCCESS" << std::endl;

In difference to the second post i can not set the privilege "SE_ASSIGNPRIMARYTOKEN_NAME" even when starting as administrator.与第二篇文章不同,即使以管理员身份启动,我也无法设置权限“SE_ASSIGNPRIMARYTOKEN_NAME”。 I dont know if that makes a difference.我不知道这是否有区别。

After opening the file with FILE_FLAG_NO_BUFFERING |用 FILE_FLAG_NO_BUFFERING 打开文件后 | FILE_FLAG_OVERLAPPED, the calculated size is pre allocated: FILE_FLAG_OVERLAPPED,计算出的大小是预先分配的:

   auto setFileErr = SetFilePointerEx(hFile, endPosition, NULL, FILE_BEGIN);
    if (setFileErr == INVALID_SET_FILE_POINTER)
    {
        CPrintWithOSError(NULL, 0, "SetFilePointerEx FAILED");
        return 1;
    }

    if (!SetEndOfFile(hFile))
    {
        CPrintWithOSError(NULL, 0, "SetEndOfFile FAILED");
        return 1;
    }

    if (!SetFileValidData(hFile, endPosition.QuadPart))
    {
        CPrintWithOSError(NULL, 0, "SetFileValidData FAILED");
        return 1;
    }

That works for local drives but SetFileValidData fails on remote drives.这适用于本地驱动器,但SetFileValidData在远程驱动器上失败。
The call fails with windows error调用失败并显示 Windows 错误

1314 a required privilege is not held by the client
  • How can this be fixed?如何解决这个问题?
  • What are other ways to do this?还有什么其他方法可以做到这一点?
  • Is there a way to increase file buffering for appending writes using the WinAPI?有没有办法增加文件缓冲以使用 WinAPI 追加写入?

If you have access to the tifflib source code, you should be able to solve this by buffering data to be written to the output file until the buffer is full or the file is closed.如果您有权访问 tifflib 源代码,您应该能够通过缓冲要写入输出文件的数据直到缓冲区已满或文件关闭来解决此问题。 A simple FILE * will do this, using setvbuf to set the buffer size to 1MB or so.一个简单的FILE *将执行此操作,使用setvbuf将缓冲区大小设置为 1MB 左右。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM