在执行小写操作时,我一直在努力修复SMB / CIFS共享的性能问题。
首先,让我描述一下我目前的networking设置:
服务器
客户端(同一台计算机双启动有线Gig-E)
smb.conf文件
[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
我目前正在使用C ++编写的以下程序testing小写性能(在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; }
Linux安assembly置:
//192.168.1.10/nas-main on /mnt/nas-main type cifs (rw,noexec,nodev)
Linux上的程序运行时(峰值networking输出〜100Mbps):
$ time ./nas-write-test /mnt/nas-main/home/will/test.txt real 0m0.965s user 0m0.148s sys 0m0.672s
显示将多行分块成单个TCP数据包的PCAP快照:
在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
Windows上的PCAP快照每个SMB写入请求显示单行:
Windows上同样的程序需要大约10分钟(〜2.3Mbps)。 显然,Windows PCAP显示了一个非常嘈杂的SMB对话,净荷效率非常低。
Windows上有什么设置可以改善小写性能? 从数据包捕获看来,Windows不会正确地caching写入操作,并且一次只发送一行数据。 而在Linux上,数据被大量缓冲,因此具有非常优越的性能。 让我知道,如果PCAP文件会有所帮助,我可以find一种方法来上传它们。
更新10/27/16:
正如@sehafoc所提到的那样,我将Samba服务器的max protocol设置降为SMB1,具体如下:
max protocol=NT1
上述设置导致了完全相同的行为。
我还通过在另一台Windows 10计算机上创build共享来删除了Sambavariables,而且它也performance出与Samba服务器相同的行为,所以我开始相信这是一般的Windows客户端的写入caching错误。
更新date:10/06/17:
完整的Linux数据包捕获(14MB)
完整的Windows数据包捕获(375MB)
更新:10/12/17:
我也设置了一个NFS共享,Windows也没有写入缓冲。 所以,就我所知,这绝对是一个潜在的Windows客户端问题,这绝对是不幸的: – /
任何帮助将不胜感激!
C ++ endl被定义为输出'\ n'后跟一个flush。 flush()是一个昂贵的操作,所以你通常应该避免使用endl作为你的默认行尾,因为它可以创build你所看到的性能问题(不仅仅是SMB,而且包含本地转换生锈甚至是最新的NVMe,输出速度都很快)。
使用“\ n”replaceendl将通过允许系统根据预期进行缓冲来修复上述性能。 除了一些库可能冲刷“\ n”,在这种情况下,你有更多的麻烦(见https://stackoverflow.com/questions/21129162/tell-endl-not-to-flush的解决scheme覆盖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跟踪似乎表明,操作系统/networking缓冲区不被库flush()刷新。 系统依赖的行为将是更多的理由,以避免endl和过度冲洗。 如果您正在使用VC ++,则可以尝试切换到Windows GCC派生程序,以查看系统相关行为如何反应,或者使用Wine在Ubuntu上运行Windows可执行文件。
更一般地说,你需要考虑你的要求,以确定是否适当刷新每一行。 endl通常适用于显示等交互式stream(我们需要用户实际看到我们的输出,而不是以突发forms),但通常不适用于其他types的stream,包括那些冲突开销很大的文件。 我已经看到应用程序刷新每1和2,4和8字节的写入…看到操作系统研磨数以百万计的IO写一个1MB文件是不是很漂亮。
例如,如果您正在debugging崩溃,则日志文件可能需要刷新每一行,因为您需要在崩溃发生之前刷新该stream; 而另一个日志文件可能不需要清除每一行,如果它只是产生详细的信息日志logging,预计在应用程序终止之前自动刷新。 它不一定是/或者你可以用一个更复杂的冲洗algorithm来派生一个类来适应特定的需求。
比较你的情况与谁需要确保他们的数据完全坚持磁盘,而不是在操作系统缓冲区中脆弱的人的对比情况( https://stackoverflow.com/questions/7522479/how-do-i-ensure-data -is-written-to-disk-before-closing-fstream )。
请注意,正如所写,outFile.flush()是多余的,因为它刷新已经刷新的stream。 要迂腐,你应该单独使用endl,或者最好是带outFile.flush()的“\ n”,但不能同时使用。
我没有足够的声望留下评论(我认为考虑到这个答案的validation水平会更好)。
我注意到你的Linux和Windows级别跟踪有一个很大的差异,就是你在Linux上使用SMB1而在Windows上使用SMB2。 也许批处理oplock机制在SMB1 samba中执行比SMB2独占租用执行更好。 在这两种情况下,这些应该允许一些客户端caching。
1)也许尝试在Samba中设置一个较低的最大协议级别来尝试使用SMB1的窗口2)确认独占的oplock或租赁被取出
希望这可以帮助 :)
使用SMB协议的远程文件操作(如读/写)的性能可能受到服务器和客户端分配的缓冲区大小的影响。 缓冲区大小决定了发送固定数量数据所需的往返次数。 每当在客户端和服务器之间发送请求和响应时,所花费的时间量至less等于双方之间的等待时间,这在广域网(WAN)的情况下可能是非常重要的。
SMB缓冲区 – 可以通过以下registry设置来configurationMaxBufferSize:
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf
数据types: REG_DWORD
范围:1024至65535(根据5000以上的要求select值)
但SMB签名会影响允许的最大缓冲区大小。 因此,我们需要禁用SMB签名,以实现我们的目标。 以下registry需要在服务器端创build,如果可能的话还需要在客户端创build。
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters
值名称: EnableSecuritySignature
数据types: REG_DWORD
数据:0(禁用),1(启用)
有趣的现象。 这是我会尝试 – 我不知道这是否真的有帮助。 如果是我的机器,我会广泛地看SMB的性能performance。 其中之一将显示原因。
更多的事情要尝试
添加更多的工作线程
如果SMB_RDR每行写入一个I / O请求(这里不应该发生这种情况), 可能会有助于向执行引擎添加一些线程。
将“AdditionalCriticalWorkerThreads”设置为2,然后设置为4。
HKLM\System\CurrentControlSet\Control\Session Manager\Executive\AdditionalCriticalWorkerThreads
缺省值是0,这意味着不会添加额外的关键内核工作线程。 这是平常的事情。 此值影响文件系统caching用于预读和后写请求的线程数。 提高这个值可以允许在存储子系统中有更多的排队I / O(这是很好的,当你想逐行写入时),但是它更昂贵。
添加更多队列长度
增加“AdditionalCriticalWorkerThreads”值将引发文件服务器可用于服务并发请求的线程数。
HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\MaxThreadsPerQueue
缺省值是20.如果SMB2工作队列增长非常大(执行'服务器工作队列\队列长度\ SMB2 *'应该<100),则可能需要增加该值的指示。