小さな書き込みを実行するときに、SMB/CIFS共有のパフォーマンスの問題を修正するのに苦労しています。
まず、現在のネットワーク設定について説明します。
サーバー
クライアント(同じコンピューターのデュアルブート有線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++で記述された次のプログラム(GitHub here )を使用して、小規模な書き込みパフォーマンスをテストしています。
#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マウント構成:
//192.168.1.10/nas-main on /mnt/nas-main type cifs (rw,noexec,nodev)
Linuxでのプログラムの実行時間(ピークネットワーク出力は〜100Mbps):
$ time ./nas-write-test /mnt/nas-main/home/will/test.txt
real 0m0.965s
user 0m0.148s
sys 0m0.672s
単一のTCPパケットへの多数の行のチャンキングを示すPCAPスナップショット:
PowerShellで測定したWindowsでのランタイムのプログラム:
> 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
SMB書き込み要求ごとに1行表示するWindows上のPCAPスナップショット:
この同じプログラムは、Windowsでは約10分(約2.3Mbps)かかります。明らかに、Windows PCAPは非常に騒々しいSMB会話を示し、ペイロード効率が非常に低い。
小さな書き込みパフォーマンスを改善できるWindowsの設定はありますか?パケットキャプチャを見ると、Windowsは書き込みを適切にバッファリングせず、一度に1行ずつデータをすぐに送信しているようです。一方、Linuxでは、データは大量にバッファリングされるため、パフォーマンスははるかに優れています。 PCAPファイルが役立つかどうか教えてください。アップロードする方法を見つけることができます。
2016年10月27日更新:
@sehafocで述べたように、Sambaサーバーを減らしましたmax protocol
次のようにSMB1に設定:
max protocol=NT1
上記の設定では、まったく同じ動作が発生しました。
また、別のWindows 10マシンで共有を作成してSambaの変数を削除しました。また、Sambaサーバーと同じ動作を示しているため、これは一般にWindowsクライアントの書き込みキャッシュのバグであると考え始めています。
更新:10/06/17:
更新:10/12/17:
私はNFS共有もセットアップしましたが、Windowsもこのためにバッファリングなしで書き込みを行います。ですから、私が知る限り、これは間違いなく根本的なWindowsクライアントの問題であり、これは間違いなく残念です:-/
何か助けていただければ幸いです!
C++ endlは、 '\ n'の後にフラッシュが続くように定義されています。 flush()は高価な操作です。したがって、SMBだけでなく、ローカルスピニングを含む高価なフラッシュを伴うあらゆるストリームで発生するパフォーマンスの問題を正確に引き起こす可能性があるため、endlをデフォルトの行末として使用することは通常避けてください。 Rustまたは、途方もなく高い出力率の最新のNVMe)。
Endlを "\ n"に置き換えると、システムが意図したとおりにバッファリングできるようになり、上記のパフォーマンスが修正されます。一部のライブラリは "\ n"でフラッシュする可能性があることを除いて、その場合はより多くの頭痛の種があります( https://stackoverflow.com/questions/21129162/tell-endl-not-to-flush を参照してください) 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バイトの書き込みごとにアプリがフラッシュするのを見てきました... 1 MBのファイルを書き込むためにOS Grindの数百万のIOを表示するのは見事ではありません。
たとえば、クラッシュをデバッグする場合、クラッシュが発生する前にオフストリームをフラッシュする必要があるため、ログファイルですべての行をフラッシュする必要がある場合があります。一方、アプリケーションが終了する前に自動的にフラッシュされることが予想される詳細な情報ログを生成するだけの場合は、別のログファイルがすべての行をフラッシュする必要がない場合があります。特定の要件に合うように、より洗練されたフラッシュアルゴリズムを使用してクラスを派生できるので、どちらかである必要はありません。
あなたのケースを、データが完全にディスクに永続化され、オペレーティングシステムのバッファーで脆弱ではないことを確認する必要がある人々の対照的なケースと比較してください( https://stackoverflow.com/questions/7522479/how-do-i -ensure-data-is-written-to-disk-before-closing-fstream )。
記述されているように、outFile.flush()はすでにフラッシュされたofstreamをフラッシュするので不要です。簡潔にするために、endlを単独で使用するか、できればoutFile.flush()で「\ n」を使用してください。
SMBプロトコルを使用した読み取り/書き込みなどのリモートファイル操作のパフォーマンスは、サーバーとクライアントによって割り当てられたバッファのサイズによって影響を受ける可能性があります。バッファサイズは、固定量のデータを送信します。クライアントとサーバー間で要求と応答が送信されるたびに、かかる時間は少なくとも両側間の待ち時間に等しくなります。これは、広域ネットワーク(WAN)の場合は非常に重要です。 )。
SMBバッファー-MaxBufferSizeは、次のレジストリ設定で構成できます。
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf
データ・タイプ: REG_DWORD
範囲:1024〜65535(必要に応じて5000を超える値を選択してください)
BUT SMB SIGNINGは、許可される最大バッファサイズに影響します。したがって、SMB署名も無効にして、目標を達成する必要があります。次のレジストリを両方に作成する必要がありますサーバー側と、可能であればクライアント側でも。
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters
値の名前:EnableSecuritySignature
データ・タイプ: REG_DWORD
データ:0(無効)、1(有効)
コメントを残す十分な評判がありません(この回答の検証レベルを考えると、これはより良いと思います)。
LinuxレベルとWindowsレベルのトレースの大きな違いの1つは、LinuxではSMB1を、WindowsではSMB2を使用していることです。おそらく、バッチoplockメカニズムは、SMB2排他的リース実装よりもSMB1 sambaでパフォーマンスが向上します。どちらの場合も、これらはある程度のクライアント側のキャッシングを可能にするはずです。
1)SMBでウィンドウを試すために、Sambaで最大プロトコルレベルを低く設定してみてください2)排他的なoplockまたはリースが削除されていることを確認してください
お役に立てれば :)
興味深い現象。これが私が試すことです-これが本当に役立つかどうかはわかりません。それが私のマシンだったら、SMB perfcountersをよく見ます。そのうちの1つが原因を示します。
試すべきこと
ワーカースレッドを追加
SMB_RDRが1行あたり1つの書き込みI/O要求を実行する場合(ここでしないが発生するはずです)、それ may実行エンジンにいくつかのスレッドを追加するのに役立ちます。
「AdditionalCriticalWorkerThreads」を2、次に4に設定します。
HKLM\System\CurrentControlSet\Control\Session Manager\Executive\AdditionalCriticalWorkerThreads
デフォルトは0です。これは、重要なカーネルワーカースレッドが追加されないことを意味します。これは通常は大丈夫です。この値は、ファイルシステムキャッシュが先読みおよび後書き要求に使用するスレッドの数に影響します。この値を上げるとcanストレージサブシステムでより多くのキューI/Oが可能になります(これは、行ごとに書き込む場合に適しています)。しかし、それはより高価なCPUです。
キューの長さを追加
"AdditionalCriticalWorkerThreads"の値を増やすと、ファイルサーバーがconcurrent要求の処理に使用できるスレッドの数が増えます。
HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\MaxThreadsPerQueue
デフォルトは20です。SMB2ワークキューが非常に大きくなっている場合(perfcounter ‘Server Work Queues\Queue Length\SMB2 *’。should be <100)は、値を増やす必要がある可能性があることを示しています。