mev*_*ron 10 networking linux windows performance server-message-block
在执行小型写入时,我一直在努力解决 SMB/CIFS 共享的性能问题。
首先,让我描述一下我当前的网络设置:
服务器
客户端(同一台计算机双启动有线 Gig-E)
配置文件
[global]
printcap name=cups
winbind enum groups=yes
include=/var/tmp/nginx/smb.netbios.aliases.conf
socket options=TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=65536 SO_SNDBUF=65536
security=user
local master=no
realm=*
passdb backend=smbpasswd
printing=cups
max protocol=SMB3
winbind enum users=yes
load printers=yes
workgroup=WORKGROUP
Run Code Online (Sandbox Code Playgroud)
我目前正在使用以下用 C++ 编写的程序(在此处的GitHub 上)测试小型写入性能:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, char* argv[])
{
ofstream outFile(argv[1]);
for(int i = 0; i < 1000000; i++)
{
outFile << "Line #" << i << endl;
}
outFile.flush();
outFile.close();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Linux挂载配置:
//192.168.1.10/nas-main on /mnt/nas-main type cifs (rw,noexec,nodev)
Run Code Online (Sandbox Code Playgroud)
Linux 上的程序运行时(峰值网络输出约为 100Mbps):
$ time ./nas-write-test /mnt/nas-main/home/will/test.txt
real 0m0.965s
user 0m0.148s
sys 0m0.672s
Run Code Online (Sandbox Code Playgroud)
PCAP 快照显示将多行分块到单个 TCP 数据包中:
Windows 上的程序运行时间由 PowerShell 测量:
> Measure-Command {start-process .\nas-write-test.exe -argumentlist "Z:\home\will\test-win.txt" -wait}
Days : 0
Hours : 0
Minutes : 9
Seconds : 29
Milliseconds : 316
Ticks : 5693166949
TotalDays : 0.00658931359837963
TotalHours : 0.158143526361111
TotalMinutes : 9.48861158166667
TotalSeconds : 569.3166949
TotalMilliseconds : 569316.6949
Run Code Online (Sandbox Code Playgroud)
Windows 上的 PCAP 快照显示每个 SMB 写入请求的单行:
同样的程序在 Windows 上大约需要 10 分钟 (~2.3Mbps)。显然,Windows PCAP 显示了非常嘈杂的 SMB 对话,并且有效负载效率非常低。
Windows 上是否有任何设置可以提高小写性能?从数据包捕获来看,Windows 似乎没有正确缓冲写入并立即一次一行地发送数据。而在 Linux 上,数据被大量缓冲,因此性能要好得多。如果 PCAP 文件有帮助,请告诉我,我可以找到上传它们的方法。
2016 年 10 月 27 日更新:
正如@sehafoc 所提到的,我max protocol使用以下内容将 Samba 服务器设置减少到 SMB1:
max protocol=NT1
上述设置导致了完全相同的行为。
我还通过在另一台 Windows 10 机器上创建共享来删除 Samba 的变量,它也表现出与 Samba 服务器相同的行为,所以我开始相信这通常是 Windows 客户端的写入缓存错误。
更新:10/06/17:
更新:10/12/17:
我还设置了一个 NFS 共享,Windows 也不会为此进行缓冲写入。因此,据我所知,这绝对是一个潜在的 Windows 客户端问题,这绝对是不幸的:-/
任何帮助,将不胜感激!
C++ endl 被定义为输出“\n”,后跟刷新。lush() 是一项昂贵的操作,因此您通常应该避免使用 endl 作为默认的行尾,因为它可能会准确地产生您所看到的性能问题(不仅适用于 SMB,还适用于任何具有昂贵刷新的 ofstream,包括本地旋转) rust 甚至是最新的 NVMe,输出速率高得离谱)。
将 endl 替换为“\n”将通过允许系统按预期缓冲来修复上述性能。除了某些库可能会刷新“\n”,在这种情况下您会更头痛(请参阅/sf/ask/1479041371/以获取覆盖sync()方法的解决方案)。
现在让事情变得复杂的是,flush()仅针对库缓冲区内发生的情况进行定义。刷新对操作系统、磁盘和其他外部缓冲区的影响未定义。对于 Microsoft.NET,“当您调用 FileStream.Flush 方法时,操作系统 I/O 缓冲区也会被刷新。” ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) 这使得刷新对于 Visual Studio C++ 来说特别昂贵,因为它会将写入一直往返到正如您所看到的,位于远程服务器远端的物理介质。另一方面,GCC 说“最后提醒一下:通常涉及的缓冲区不仅仅是语言/库级别的缓冲区。内核缓冲区、磁盘缓冲区等也会产生影响。检查和更改这些缓冲区取决于系统”。(https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html)您的Ubuntu跟踪似乎表明操作系统/网络缓冲区没有被库flush()刷新。系统依赖行为更应该避免 endl 和过度刷新。如果您使用的是 VC++,您可以尝试切换到 Windows GCC 衍生版本来查看系统相关行为如何反应,或者使用 Wine 在 Ubuntu 上运行 Windows 可执行文件。
更一般地说,您需要考虑您的要求,以确定冲洗每条管线是否合适。endl 通常适用于交互式流,例如显示(我们需要用户实际看到我们的输出,而不是突发),但通常不适合其他类型的流,包括刷新开销可能很大的文件。我见过应用程序在每次 1 和 2 以及 4 和 8 字节写入时刷新...看到操作系统消耗数百万个 IO 来写入 1MB 文件,这并不好。
例如,如果您正在调试崩溃,则日志文件可能需要刷新每一行,因为您需要在崩溃发生之前刷新 ofstream;而另一个日志文件可能不需要刷新每一行,如果它只是生成预计在应用程序终止之前自动刷新的详细信息日志记录。它不必是非此即彼,因为您可以派生一个具有更复杂的刷新算法的类来满足特定要求。
将您的情况与需要确保其数据完全持久化到磁盘并且在操作系统缓冲区中不易受到攻击的人的对比情况进行比较(/sf/ask/526573561/ -在关闭 fstream 之前写入磁盘)。
请注意,如所写, outFile.flush() 是多余的,因为它刷新已经刷新的 ofstream。为了迂腐,您应该单独使用 endl 或最好将“\n”与 outFile.flush() 一起使用,但不能同时使用两者。
| 归档时间: |
|
| 查看次数: |
10612 次 |
| 最近记录: |