Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SFTP performance #100

Closed
lochnar187 opened this issue Oct 11, 2016 · 56 comments
Closed

SFTP performance #100

lochnar187 opened this issue Oct 11, 2016 · 56 comments
Assignees

Comments

@lochnar187
Copy link

I am seeing some performance issues and would like to ask others to take my test code and see if they have the same problem. Mostly, this is because I am running this against an SFTP server that I don't control or have any knowledge about, it's "black box" to me. So I want to make sure it's not that.

After noticing I wasn't getting the speeds I expected (sometimes as little as half) I wrote some code that gives a head to head comparison of speeds between SSH.NET and WinSCP. I used NuGet to get the latest versions of both SSH.NET and WinSCP. Testing file is an 834KB file.

Using VS 2015
Code build is targeted on .NET Framework 4.5.2
OS is Windows 10 (dev machine) and Windows Server 2012 R2 (test machine)

SSH.NET version, 2016.0.0
WinSCP version, 5.9.2

SFTP server reports it is: SSH-2.0-WS_FTP-SSH_7.6.3

Test Code:

using Renci.SshNet;
using System;
using System.Diagnostics;
using System.IO;
using WinSCP;

namespace SFTP_Compare {
    class Program {
        static string strServer     = "xxxx";
        static string strUser       = "xxxx";
        static string strPass       = "xxxx";
        static string strHostKey    = "ssh-rsa 1024 xxxxxx";
        static string strRmPath     = "/home/";
        static string strLcPath     = "C:\\Data\\";
        static string strFile       = "test_file.xyz";
        static int    intPort       = 22;
        static void TestWinSCP(int pintLoops) {
            long    lngByteKnt  = 0;
            double  dblCreated, dblConnected, dblOpenStream, dblDownloaded, dblEndTime;
            double  dblKBKntTotal = 0.0, dblCreatedTotal = 0.0, dblConnectedTotal = 0.0, dblOpenStreamTotal = 0.0, dblDownloadedTotal = 0.0, dblEndTimeTotal = 0.0;
            Stopwatch swTimer = new Stopwatch();

            Console.WriteLine();
            Console.WriteLine("WinSCP download test, {0} iteration(s).", pintLoops);
            Console.WriteLine("___________________________________________________________________");
            Console.WriteLine("|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |");

            for (int i = 0; i < pintLoops; i++) {
                try {
                    swTimer.Start();
                    SessionOptions soptSettings = new SessionOptions();
                    soptSettings.Protocol = Protocol.Sftp;
                    soptSettings.HostName = strServer;
                    soptSettings.UserName = strUser;
                    soptSettings.Password = strPass;
                    soptSettings.SshHostKeyFingerprint = strHostKey;
                    using (WinSCP.Session sesConnection = new WinSCP.Session()) {
                        swTimer.Stop();
                        dblCreated = swTimer.Elapsed.TotalMilliseconds;
                        swTimer.Restart();
                        sesConnection.Open(soptSettings);
                        swTimer.Stop();
                        dblConnected = swTimer.Elapsed.TotalMilliseconds;
                        swTimer.Restart();
                        dblOpenStream = 0;
                        TransferOptions toptStreamSettings = new TransferOptions();
                        toptStreamSettings.TransferMode = TransferMode.Binary;
                        sesConnection.GetFiles(strRmPath + strFile, strLcPath + strFile, false, toptStreamSettings);
                        swTimer.Stop();
                        dblDownloaded = swTimer.Elapsed.TotalMilliseconds;
                        FileInfo fiFile = new FileInfo(strLcPath + strFile);
                        lngByteKnt = fiFile.Length;
                        swTimer.Restart();
                    }
                    File.Delete(strLcPath + strFile); // Delete local copy for next run...WRG
                    swTimer.Stop();
                    dblEndTime = swTimer.Elapsed.TotalMilliseconds;

                    // Output test results...WRG
                    Console.WriteLine("|{0,4}   |{1,8:N1} ms |{2,10:N1} ms |{3,12:N1} ms |{4,8:N1} ms |", i + 1, dblCreated, dblConnected, dblDownloaded, dblEndTime);

                    // Save totals for later...WRG
                    dblKBKntTotal += (lngByteKnt / 1024);
                    dblCreatedTotal += dblCreated;
                    dblConnectedTotal += dblConnected;
                    dblOpenStreamTotal += dblOpenStream;
                    dblDownloadedTotal += (dblDownloaded);
                    dblEndTimeTotal += dblEndTime;

                } catch (Exception ex) {
                    Console.WriteLine(" *** Error on run {0}, Message: {1}", i + 1, ex.Message);
                }
            }
            Console.WriteLine("|=======|============|==============|================|============|");
            Console.WriteLine("|  avg  |{0,8:N1} ms |{1,10:N1} ms |{2,12:N1} ms |{3,8:N1} ms |", (dblCreatedTotal / pintLoops), (dblConnectedTotal / pintLoops), (dblDownloadedTotal / pintLoops), (dblEndTimeTotal / pintLoops));
            Console.WriteLine("|_______|____________|______________|________________|____________|");
            Console.WriteLine(" *** Average download speed for {0:N0} bytes: {1,5:N2} KB/s", (dblKBKntTotal * 1024), (dblKBKntTotal / (dblDownloadedTotal / 1000)));
            Console.WriteLine();
        }
        static void TestSSHNET(int pintLoops) {
            long    lngByteKnt  = 0;
            double  dblCreated, dblConnected, dblOpenStream, dblDownloaded, dblEndTime;
            double  dblKBKntTotal = 0.0, dblCreatedTotal = 0.0, dblConnectedTotal = 0.0, dblOpenStreamTotal = 0.0, dblDownloadedTotal = 0.0, dblEndTimeTotal = 0.0;
            Stopwatch swTimer = new Stopwatch();

            Console.WriteLine();
            Console.WriteLine("SSH.NET download test, {0} iteration(s).", pintLoops);
            Console.WriteLine("___________________________________________________________________");
            Console.WriteLine("|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |");

            for (int i = 0; i < pintLoops; i++) {
                try {
                    swTimer.Start();
                    using (SftpClient sftpClient = new SftpClient(strServer, intPort, strUser, strPass)) {
                        swTimer.Stop();
                        dblCreated = swTimer.Elapsed.TotalMilliseconds;
                        swTimer.Restart();
                        sftpClient.Connect();
                        swTimer.Stop();
                        dblConnected = swTimer.Elapsed.TotalMilliseconds;
                        swTimer.Restart();
                        using (Stream sFile = File.Open(strLcPath + strFile, FileMode.Create, FileAccess.Write)) {
                            dblOpenStream = 0;
                            //swTimer.Stop();
                            //dblOpenStream = swTimer.Elapsed.TotalMilliseconds;
                            //swTimer.Restart();
                            sftpClient.DownloadFile(strRmPath + strFile, sFile);
                            swTimer.Stop();
                            dblDownloaded = swTimer.Elapsed.TotalMilliseconds;
                            lngByteKnt = sFile.Length;
                            swTimer.Restart();
                        }
                    }
                    File.Delete(strLcPath + strFile); // Delete local copy for next run...WRG
                    swTimer.Stop();
                    dblEndTime = swTimer.Elapsed.TotalMilliseconds;

                    // Output test results...WRG
                    Console.WriteLine("|{0,4}   |{1,8:N1} ms |{2,10:N1} ms |{3,12:N1} ms |{4,8:N1} ms |", i+1, dblCreated, dblConnected, dblDownloaded, dblEndTime);

                    // Save totals for later...WRG
                    dblKBKntTotal += (lngByteKnt/1024);
                    dblCreatedTotal += dblCreated;
                    dblConnectedTotal += dblConnected;
                    dblOpenStreamTotal += dblOpenStream;
                    dblDownloadedTotal += (dblDownloaded);
                    dblEndTimeTotal += dblEndTime;

                } catch (Exception ex) {
                    Console.WriteLine(" *** Error on run {0}, Message: {1}", i+1, ex.Message);
                }
            }
            Console.WriteLine("|=======|============|==============|================|============|");
            Console.WriteLine("|  avg  |{0,8:N1} ms |{1,10:N1} ms |{2,12:N1} ms |{3,8:N1} ms |", (dblCreatedTotal / pintLoops), (dblConnectedTotal / pintLoops), (dblDownloadedTotal / pintLoops), (dblEndTimeTotal / pintLoops));
            Console.WriteLine("|_______|____________|______________|________________|____________|");
            Console.WriteLine(" *** Average download speed for {0:N0} bytes: {1,5:N2} KB/s", (dblKBKntTotal * 1024), (dblKBKntTotal / (dblDownloadedTotal / 1000)));
            Console.WriteLine();
        }

        static void Main(string[] args) {
            TestWinSCP(50);
            TestSSHNET(50);
        }
    }
}

Output from command prompt session on test machine:

Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:\Windows\system32>cd C:\Data

C:\Data>dir
 Volume in drive C has no label.
 Volume Serial Number is E6AE-D23F

 Directory of C:\Data

10/11/2016  01:03 PM    <DIR>          .
10/11/2016  01:03 PM    <DIR>          ..
10/06/2016  07:21 PM           408,064 Renci.SshNet.dll
10/06/2016  07:21 PM           961,463 Renci.SshNet.xml
10/11/2016  01:02 PM             9,216 SFTP_Compare.exe
10/06/2016  07:18 PM               189 SFTP_Compare.exe.config
10/11/2016  01:02 PM            15,872 SFTP_Compare.pdb
10/10/2016  03:03 PM            22,696 SFTP_Compare.vshost.exe
10/06/2016  07:18 PM               189 SFTP_Compare.vshost.exe.config
07/10/2015  07:01 AM               490 SFTP_Compare.vshost.exe.manifest
10/06/2016  07:22 PM        18,886,048 WinSCP.exe
10/08/2016  02:48 PM                75 winscp.ini
10/06/2016  07:22 PM           149,584 WinSCPnet.dll
              11 File(s)     20,453,886 bytes
               2 Dir(s)  16,536,723,456 bytes free

C:\Data>sftp_compare

WinSCP download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |     4.2 ms |   1,797.8 ms |       781.9 ms |   110.5 ms |
|   2   |   110.6 ms |   1,778.7 ms |       843.9 ms |   110.3 ms |
|   3   |   110.4 ms |   1,779.8 ms |       843.7 ms |   110.4 ms |
|   4   |   110.4 ms |   1,779.9 ms |       843.8 ms |   110.4 ms |
|   5   |   110.4 ms |   1,779.9 ms |       843.8 ms |   110.5 ms |
|   6   |   110.5 ms |   1,779.8 ms |       843.7 ms |   110.3 ms |
|   7   |   110.4 ms |   1,780.0 ms |       843.8 ms |   110.3 ms |
|   8   |   110.3 ms |   2,592.7 ms |       843.8 ms |   110.6 ms |
|   9   |   110.7 ms |   2,582.1 ms |       843.6 ms |   110.2 ms |
|  10   |   110.3 ms |   1,782.1 ms |     1,656.3 ms |   110.6 ms |
|  11   |   110.7 ms |   1,779.4 ms |       843.9 ms |   110.4 ms |
|  12   |   110.4 ms |   2,592.2 ms |       843.8 ms |   110.5 ms |
|  13   |   110.6 ms |   1,779.7 ms |       843.9 ms |   110.6 ms |
|  14   |   110.7 ms |   1,779.4 ms |     1,656.4 ms |   110.2 ms |
|  15   |   110.3 ms |   2,592.3 ms |       843.7 ms |   110.6 ms |
|  16   |   110.7 ms |   2,592.1 ms |       843.9 ms |   110.2 ms |
|  17   |   110.3 ms |   1,779.9 ms |       843.8 ms |   110.6 ms |
|  18   |   110.7 ms |   1,779.7 ms |       843.8 ms |   110.6 ms |
|  19   |   110.7 ms |   2,591.9 ms |       843.9 ms |   110.4 ms |
|  20   |   110.5 ms |   1,779.5 ms |       846.6 ms |   110.5 ms |
|  21   |   110.5 ms |   2,593.7 ms |       844.8 ms |   110.2 ms |
|  22   |   110.3 ms |   1,780.0 ms |       843.7 ms |   113.8 ms |
|  23   |   113.9 ms |   2,590.6 ms |       843.8 ms |   110.5 ms |
|  24   |   110.6 ms |   2,582.5 ms |       843.7 ms |   110.6 ms |
|  25   |   110.7 ms |   1,780.0 ms |       843.8 ms |   110.6 ms |
|  26   |   110.6 ms |   1,779.7 ms |       843.8 ms |   110.4 ms |
|  27   |   110.5 ms |   2,592.1 ms |       843.9 ms |   110.2 ms |
|  28   |   110.2 ms |   1,780.8 ms |     1,656.3 ms |   110.3 ms |
|  29   |   110.4 ms |   1,779.8 ms |       843.9 ms |   110.5 ms |
|  30   |   110.5 ms |   1,779.8 ms |       843.7 ms |   110.6 ms |
|  31   |   110.7 ms |   1,779.6 ms |       843.9 ms |   110.7 ms |
|  32   |   110.8 ms |   1,779.1 ms |       843.9 ms |   110.5 ms |
|  33   |   110.6 ms |   2,592.0 ms |       843.7 ms |   111.1 ms |
|  34   |   111.2 ms |   2,592.5 ms |       843.8 ms |   110.0 ms |
|  35   |   110.1 ms |   1,781.4 ms |       843.6 ms |   110.4 ms |
|  36   |   110.5 ms |   2,592.9 ms |       843.8 ms |   110.2 ms |
|  37   |   110.3 ms |   1,780.0 ms |       843.7 ms |   110.2 ms |
|  38   |   110.3 ms |   1,776.1 ms |       843.7 ms |   110.6 ms |
|  39   |   110.7 ms |   2,592.2 ms |       843.7 ms |   110.3 ms |
|  40   |   110.4 ms |   1,780.0 ms |       843.7 ms |   110.4 ms |
|  41   |   110.4 ms |   2,592.9 ms |       843.8 ms |   110.3 ms |
|  42   |   110.3 ms |   1,780.4 ms |       843.9 ms |   110.4 ms |
|  43   |   110.5 ms |   2,592.2 ms |       843.8 ms |   110.5 ms |
|  44   |   110.6 ms |   2,592.3 ms |       843.7 ms |   102.5 ms |
|  45   |   102.6 ms |   1,779.5 ms |       843.7 ms |   110.3 ms |
|  46   |   110.4 ms |   2,593.1 ms |     1,657.7 ms |   110.3 ms |
|  47   |   110.4 ms |   1,779.6 ms |       843.8 ms |   110.5 ms |
|  48   |   110.6 ms |   2,582.8 ms |       843.8 ms |   110.1 ms |
|  49   |   110.2 ms |   1,779.6 ms |       843.7 ms |   110.3 ms |
|  50   |   110.4 ms |   1,781.4 ms |       843.8 ms |   110.5 ms |
|=======|============|==============|================|============|
|  avg  |   108.3 ms |   2,088.4 ms |       907.7 ms |   110.3 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 42,700,800 bytes: 918.86 KB/s


SSH.NET download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |    11.3 ms |     974.3 ms |     1,337.5 ms |   190.6 ms |
|   2   |   190.6 ms |     815.4 ms |     1,323.7 ms |   195.5 ms |
|   3   |   195.5 ms |   1,365.0 ms |     1,452.2 ms |   190.0 ms |
|   4   |   190.0 ms |   1,577.7 ms |     1,516.7 ms |   202.2 ms |
|   5   |   202.3 ms |   1,197.2 ms |     1,562.5 ms |   192.6 ms |
|   6   |   192.7 ms |   1,194.1 ms |     1,584.5 ms |   189.3 ms |
|   7   |   189.3 ms |   1,311.7 ms |     1,609.4 ms |   198.8 ms |
|   8   |   198.8 ms |   1,477.7 ms |     1,427.2 ms |   188.1 ms |
|   9   |   188.2 ms |   1,417.4 ms |     1,561.7 ms |   201.3 ms |
|  10   |   201.3 ms |   1,401.8 ms |     1,926.7 ms |   194.3 ms |
|  11   |   194.3 ms |   1,401.4 ms |     1,599.1 ms |   190.2 ms |
|  12   |   190.3 ms |   1,528.9 ms |     1,607.8 ms |   197.8 ms |
|  13   |   197.9 ms |   1,338.8 ms |     1,770.6 ms |   196.8 ms |
|  14   |   196.9 ms |   1,833.7 ms |     1,683.3 ms |   201.9 ms |
|  15   |   201.9 ms |   1,414.5 ms |     1,405.0 ms |   195.4 ms |
|  16   |   195.4 ms |   1,197.0 ms |     1,465.8 ms |   216.8 ms |
|  17   |   216.9 ms |     854.8 ms |     1,282.3 ms |   192.2 ms |
|  18   |   192.2 ms |   1,121.3 ms |     1,356.2 ms |   200.7 ms |
|  19   |   200.7 ms |   1,166.9 ms |     1,321.3 ms |   193.4 ms |
|  20   |   193.4 ms |   1,147.5 ms |     1,341.7 ms |   202.1 ms |
|  21   |   202.1 ms |   1,165.9 ms |     1,549.1 ms |   202.5 ms |
|  22   |   202.5 ms |   1,157.9 ms |     1,469.9 ms |   202.1 ms |
|  23   |   202.2 ms |   1,420.7 ms |     1,295.2 ms |   189.1 ms |
|  24   |   189.2 ms |   1,191.0 ms |     1,428.3 ms |   193.2 ms |
|  25   |   193.2 ms |   1,251.3 ms |     2,123.6 ms |   190.8 ms |
|  26   |   190.8 ms |   2,066.9 ms |     1,852.7 ms |   190.1 ms |
|  27   |   190.1 ms |   1,295.0 ms |     1,322.2 ms |   198.5 ms |
|  28   |   198.5 ms |   1,256.5 ms |     1,364.1 ms |   190.4 ms |
|  29   |   190.4 ms |   1,338.1 ms |     1,509.6 ms |   188.2 ms |
|  30   |   188.3 ms |     863.8 ms |     1,268.3 ms |   200.8 ms |
|  31   |   200.8 ms |     830.0 ms |     1,263.6 ms |   199.9 ms |
|  32   |   199.9 ms |   1,245.4 ms |     1,709.0 ms |   199.5 ms |
|  33   |   199.5 ms |   1,316.6 ms |     1,409.2 ms |   200.2 ms |
|  34   |   200.3 ms |   1,367.3 ms |     1,335.6 ms |   201.4 ms |
|  35   |   201.4 ms |   1,309.9 ms |     1,434.1 ms |   195.3 ms |
|  36   |   195.3 ms |     855.0 ms |     1,299.7 ms |   188.2 ms |
|  37   |   188.2 ms |   1,400.1 ms |     1,395.0 ms |   188.3 ms |
|  38   |   188.3 ms |   1,339.3 ms |     1,466.6 ms |   199.9 ms |
|  39   |   199.9 ms |   1,604.9 ms |     1,511.3 ms |   256.2 ms |
|  40   |   256.3 ms |   1,414.4 ms |     1,353.6 ms |   186.9 ms |
|  41   |   186.9 ms |   1,393.3 ms |     1,414.8 ms |   196.7 ms |
|  42   |   196.7 ms |     889.2 ms |     1,269.1 ms |   196.9 ms |
|  43   |   197.0 ms |     809.1 ms |     1,397.1 ms |   199.6 ms |
|  44   |   199.6 ms |   1,305.2 ms |     1,567.5 ms |   188.3 ms |
|  45   |   188.3 ms |     882.4 ms |     1,376.4 ms |   201.8 ms |
|  46   |   201.9 ms |   1,276.5 ms |     1,633.0 ms |   218.6 ms |
|  47   |   218.7 ms |   1,211.7 ms |     1,462.9 ms |   197.9 ms |
|  48   |   197.9 ms |     783.3 ms |     1,288.4 ms |   193.7 ms |
|  49   |   193.8 ms |   1,333.6 ms |     1,439.8 ms |   201.3 ms |
|  50   |   201.3 ms |   1,171.2 ms |     1,374.2 ms |   191.5 ms |
|=======|============|==============|================|============|
|  avg  |   194.0 ms |   1,249.7 ms |     1,474.4 ms |   197.6 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 42,700,800 bytes: 565.66 KB/s


C:\Data>

I'd welcome any suggestions to improve performance.

Thanks

@drieseng
Copy link
Member

drieseng commented Oct 13, 2016

Below you find the results I get with a 843 Kb file hosted on a VM (OpenSSH_6.6.1p1).
This is with SSH.NET built from the develop branch.

WinSCP download test, 50 iteration(s).

___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |     3,4 ms |   1 422,9 ms |        56,0 ms |   101,7 ms |
|   2   |   101,7 ms |     910,2 ms |        50,9 ms |   101,2 ms |
|   3   |   101,3 ms |     910,2 ms |        50,9 ms |   101,7 ms |
|   4   |   101,8 ms |     913,8 ms |        50,8 ms |   101,3 ms |
|   5   |   101,3 ms |     912,3 ms |        50,9 ms |   101,2 ms |
|   6   |   101,2 ms |     909,3 ms |        50,9 ms |   102,6 ms |
|   7   |   102,7 ms |     912,3 ms |        50,8 ms |   102,1 ms |
|   8   |   102,2 ms |     914,6 ms |        51,8 ms |   102,8 ms |
|   9   |   103,0 ms |     914,4 ms |        50,8 ms |   101,4 ms |
|  10   |   101,5 ms |     911,8 ms |        50,6 ms |   103,0 ms |
|  11   |   103,2 ms |     914,2 ms |        51,7 ms |   101,3 ms |
|  12   |   101,4 ms |     913,8 ms |        50,9 ms |   102,4 ms |
|  13   |   102,5 ms |     912,7 ms |        50,8 ms |   100,9 ms |
|  14   |   101,0 ms |     909,6 ms |        50,6 ms |   101,1 ms |
|  15   |   101,2 ms |     909,3 ms |        50,8 ms |   100,9 ms |
|  16   |   100,9 ms |     911,3 ms |        50,8 ms |   102,3 ms |
|  17   |   102,5 ms |     913,7 ms |        50,4 ms |   100,9 ms |
|  18   |   100,9 ms |     910,7 ms |        51,5 ms |   101,5 ms |
|  19   |   101,5 ms |     911,8 ms |       101,2 ms |   101,4 ms |
|  20   |   101,5 ms |     915,2 ms |        51,6 ms |   100,8 ms |
|  21   |   100,9 ms |     910,4 ms |        50,5 ms |   101,9 ms |
|  22   |   102,0 ms |     915,1 ms |        50,8 ms |   101,8 ms |
|  23   |   101,9 ms |     911,3 ms |        51,6 ms |   101,7 ms |
|  24   |   101,9 ms |     914,8 ms |        51,4 ms |   101,0 ms |
|  25   |   101,1 ms |     912,4 ms |        50,9 ms |   100,7 ms |
|  26   |   100,8 ms |     910,8 ms |        50,8 ms |   101,4 ms |
|  27   |   101,5 ms |     916,5 ms |        50,8 ms |   101,1 ms |
|  28   |   101,1 ms |     910,1 ms |        50,7 ms |   102,6 ms |
|  29   |   102,7 ms |     913,5 ms |        51,3 ms |   101,2 ms |
|  30   |   101,2 ms |     912,3 ms |        50,8 ms |   101,5 ms |
|  31   |   101,5 ms |     914,6 ms |        51,6 ms |   101,1 ms |
|  32   |   101,1 ms |     909,6 ms |        50,8 ms |   100,9 ms |
|  33   |   100,9 ms |     911,7 ms |        50,9 ms |   101,3 ms |
|  34   |   101,4 ms |     913,5 ms |        51,7 ms |   101,3 ms |
|  35   |   101,4 ms |     912,7 ms |        50,4 ms |   101,0 ms |
|  36   |   101,0 ms |     911,5 ms |        50,8 ms |   101,6 ms |
|  37   |   101,7 ms |     911,4 ms |        51,8 ms |   101,5 ms |
|  38   |   101,6 ms |     910,7 ms |        50,8 ms |   101,3 ms |
|  39   |   101,4 ms |     912,2 ms |        50,7 ms |   101,7 ms |
|  40   |   101,8 ms |     911,5 ms |        50,3 ms |   102,0 ms |
|  41   |   102,1 ms |     915,6 ms |        50,8 ms |   101,5 ms |
|  42   |   101,5 ms |     914,5 ms |        51,2 ms |   101,5 ms |
|  43   |   101,6 ms |     912,8 ms |        50,7 ms |   101,0 ms |
|  44   |   101,1 ms |     912,5 ms |        50,9 ms |   101,2 ms |
|  45   |   101,3 ms |     914,5 ms |        51,4 ms |   100,9 ms |
|  46   |   101,0 ms |     911,5 ms |        50,5 ms |   100,8 ms |
|  47   |   100,8 ms |     911,7 ms |        50,8 ms |   102,3 ms |
|  48   |   102,5 ms |     914,8 ms |       101,9 ms |   101,5 ms |
|  49   |   101,6 ms |     913,2 ms |        51,3 ms |   101,0 ms |
|  50   |   101,0 ms |     907,6 ms |        50,6 ms |   101,0 ms |
|=======|============|==============|================|============|
|  avg  |    99,6 ms |     922,6 ms |        53,1 ms |   101,5 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 43 161 600 bytes: 15 881,22 KB/s


SSH.NET download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |     0,8 ms |     156,5 ms |        35,2 ms |     4,2 ms |
|   2   |     4,3 ms |     162,4 ms |        33,5 ms |     4,1 ms |
|   3   |     4,1 ms |     165,7 ms |        33,0 ms |     3,7 ms |
|   4   |     3,7 ms |     162,9 ms |        34,2 ms |     3,6 ms |
|   5   |     3,6 ms |     170,9 ms |        33,0 ms |     4,3 ms |
|   6   |     4,4 ms |     171,1 ms |        36,4 ms |     4,0 ms |
|   7   |     4,1 ms |     181,1 ms |        33,8 ms |     3,6 ms |
|   8   |     3,6 ms |     161,5 ms |        33,9 ms |     3,7 ms |
|   9   |     3,7 ms |     176,6 ms |        36,8 ms |     4,0 ms |
|  10   |     4,0 ms |     193,9 ms |        32,9 ms |     3,9 ms |
|  11   |     3,9 ms |     179,4 ms |        33,9 ms |     3,6 ms |
|  12   |     3,6 ms |     190,0 ms |        33,6 ms |     4,1 ms |
|  13   |     4,1 ms |     170,9 ms |        33,7 ms |     3,5 ms |
|  14   |     3,5 ms |     178,8 ms |        33,7 ms |     3,9 ms |
|  15   |     3,9 ms |     170,0 ms |        32,7 ms |     4,0 ms |
|  16   |     4,1 ms |     176,7 ms |        34,5 ms |     4,7 ms |
|  17   |     4,8 ms |     169,6 ms |        33,2 ms |     4,0 ms |
|  18   |     4,0 ms |     189,9 ms |        35,0 ms |     3,6 ms |
|  19   |     3,6 ms |     173,6 ms |        33,4 ms |     3,5 ms |
|  20   |     3,6 ms |     168,4 ms |        33,0 ms |     3,7 ms |
|  21   |     3,8 ms |     166,5 ms |        34,3 ms |     3,7 ms |
|  22   |     3,7 ms |     168,0 ms |        35,6 ms |     3,6 ms |
|  23   |     3,6 ms |     169,1 ms |        33,5 ms |     3,7 ms |
|  24   |     3,8 ms |     163,9 ms |        33,1 ms |     3,5 ms |
|  25   |     3,5 ms |     167,4 ms |        34,8 ms |     4,9 ms |
|  26   |     4,9 ms |     159,9 ms |        36,3 ms |     3,9 ms |
|  27   |     3,9 ms |     177,8 ms |        32,9 ms |     3,5 ms |
|  28   |     3,5 ms |     165,8 ms |        38,3 ms |     3,9 ms |
|  29   |     3,9 ms |     160,1 ms |        32,1 ms |     3,7 ms |
|  30   |     3,7 ms |     168,0 ms |        33,0 ms |     3,9 ms |
|  31   |     4,0 ms |     174,2 ms |        35,2 ms |     4,0 ms |
|  32   |     4,0 ms |     170,8 ms |        36,1 ms |     3,5 ms |
|  33   |     3,6 ms |     177,5 ms |        35,4 ms |     3,9 ms |
|  34   |     3,9 ms |     178,4 ms |        36,4 ms |     3,6 ms |
|  35   |     3,6 ms |     163,0 ms |        33,3 ms |     3,9 ms |
|  36   |     4,0 ms |     194,6 ms |        35,3 ms |     3,5 ms |
|  37   |     3,5 ms |     165,6 ms |        34,4 ms |     4,1 ms |
|  38   |     4,2 ms |     172,6 ms |        32,9 ms |     3,7 ms |
|  39   |     3,8 ms |     160,1 ms |        33,9 ms |     3,6 ms |
|  40   |     3,7 ms |     162,1 ms |        41,3 ms |     4,1 ms |
|  41   |     4,1 ms |     173,9 ms |        32,2 ms |     3,9 ms |
|  42   |     3,9 ms |     162,5 ms |        33,7 ms |     4,0 ms |
|  43   |     4,0 ms |     163,7 ms |        33,5 ms |     3,5 ms |
|  44   |     3,5 ms |     202,2 ms |        33,6 ms |     3,5 ms |
|  45   |     3,6 ms |     170,2 ms |        33,5 ms |     3,8 ms |
|  46   |     3,9 ms |     168,7 ms |        33,3 ms |     4,7 ms |
|  47   |     4,7 ms |     164,4 ms |        33,7 ms |     3,8 ms |
|  48   |     3,9 ms |     166,5 ms |        36,2 ms |     3,6 ms |
|  49   |     3,6 ms |     165,6 ms |        32,8 ms |     3,6 ms |
|  50   |     3,6 ms |     157,6 ms |        32,0 ms |     4,4 ms |
|=======|============|==============|================|============|
|  avg  |     3,8 ms |     171,0 ms |        34,2 ms |     3,9 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 43 161 600 bytes: 24 624,47 KB/s

@lochnar187
Copy link
Author

Thank you, I'll have to try it with the dev build.

@camcombs
Copy link

Following because I'm having timeout issues, also with a SFTP server I can't control.

@lochnar187
Copy link
Author

I pulled dev code today. Using the same test file and machines, I saw big improvements over the 2016.0.0 release. Looking good! I just wish I knew more about that server. It's got to have something to do with these transfer numbers.

Not to change the subject here but if anyone has suggestions on exploratory/diagnostic code to get more information about the server or transfer, please let me know.

WinSCP download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |     4.5 ms |   1,794.3 ms |       781.9 ms |   110.3 ms |
|   2   |   110.4 ms |   1,778.6 ms |       843.8 ms |   110.2 ms |
|   3   |   110.3 ms |   2,592.2 ms |       843.8 ms |   110.2 ms |
|   4   |   110.2 ms |   1,779.8 ms |       843.8 ms |   110.2 ms |
|   5   |   110.3 ms |   2,592.3 ms |       843.7 ms |   110.4 ms |
|   6   |   110.5 ms |   1,779.3 ms |       836.5 ms |   110.6 ms |
|   7   |   110.7 ms |   1,779.0 ms |       843.9 ms |   110.3 ms |
|   8   |   110.4 ms |   1,779.3 ms |       843.8 ms |   110.3 ms |
|   9   |   110.4 ms |   1,781.6 ms |       843.8 ms |   114.7 ms |
|  10   |   114.8 ms |   2,587.3 ms |     1,656.3 ms |   110.3 ms |
|  11   |   110.3 ms |   1,779.7 ms |       847.2 ms |   110.4 ms |
|  12   |   110.5 ms |   1,779.7 ms |       843.8 ms |   110.5 ms |
|  13   |   110.5 ms |   1,779.1 ms |       845.5 ms |   110.3 ms |
|  14   |   110.4 ms |   1,779.3 ms |       843.8 ms |   110.3 ms |
|  15   |   110.4 ms |   2,591.9 ms |       843.8 ms |   110.4 ms |
|  16   |   110.5 ms |   1,779.2 ms |       843.8 ms |   110.4 ms |
|  17   |   110.4 ms |   1,779.1 ms |       843.8 ms |   110.2 ms |
|  18   |   110.3 ms |   1,779.8 ms |       847.3 ms |   110.2 ms |
|  19   |   110.3 ms |   1,779.5 ms |       843.9 ms |   110.4 ms |
|  20   |   110.5 ms |   1,780.5 ms |       843.7 ms |   110.4 ms |
|  21   |   110.5 ms |   1,779.2 ms |       843.9 ms |   110.3 ms |
|  22   |   110.4 ms |   1,779.3 ms |       843.8 ms |   110.3 ms |
|  23   |   110.4 ms |   1,771.2 ms |       844.4 ms |   110.4 ms |
|  24   |   110.5 ms |   2,591.7 ms |       843.9 ms |   110.3 ms |
|  25   |   110.4 ms |   2,591.7 ms |       843.8 ms |   110.2 ms |
|  26   |   110.3 ms |   1,779.7 ms |       843.8 ms |   110.3 ms |
|  27   |   110.3 ms |   1,779.7 ms |       843.8 ms |   110.3 ms |
|  28   |   110.3 ms |   1,779.7 ms |       843.8 ms |   110.2 ms |
|  29   |   110.3 ms |   1,779.7 ms |       843.8 ms |   110.2 ms |
|  30   |   110.3 ms |   2,592.3 ms |       843.8 ms |   110.4 ms |
|  31   |   110.5 ms |   2,591.8 ms |       843.9 ms |   110.1 ms |
|  32   |   110.2 ms |   1,779.5 ms |       843.9 ms |   110.4 ms |
|  33   |   110.5 ms |   2,591.8 ms |     1,656.3 ms |   110.3 ms |
|  34   |   110.4 ms |   1,779.3 ms |     1,656.3 ms |   110.3 ms |
|  35   |   110.4 ms |   1,779.3 ms |       843.8 ms |   110.3 ms |
|  36   |   110.3 ms |   1,779.6 ms |       843.8 ms |   110.5 ms |
|  37   |   110.6 ms |   1,780.8 ms |       843.8 ms |   110.4 ms |
|  38   |   110.4 ms |   1,779.3 ms |       843.7 ms |   110.5 ms |
|  39   |   110.6 ms |   1,779.3 ms |       843.8 ms |   110.5 ms |
|  40   |   110.5 ms |   1,779.2 ms |       847.9 ms |   110.6 ms |
|  41   |   110.7 ms |   2,592.7 ms |       843.8 ms |   110.4 ms |
|  42   |   110.5 ms |   2,591.6 ms |       843.8 ms |   111.2 ms |
|  43   |   111.3 ms |   1,778.2 ms |       843.8 ms |   110.1 ms |
|  44   |   110.2 ms |   1,780.0 ms |       843.7 ms |   110.4 ms |
|  45   |   110.4 ms |   1,779.3 ms |     1,656.3 ms |   110.3 ms |
|  46   |   110.4 ms |   1,780.4 ms |       843.8 ms |   110.5 ms |
|  47   |   110.6 ms |   1,779.2 ms |       843.9 ms |   110.4 ms |
|  48   |   110.5 ms |   2,591.6 ms |       843.8 ms |   110.2 ms |
|  49   |   110.3 ms |   1,779.2 ms |       843.8 ms |   110.2 ms |
|  50   |   110.3 ms |   1,779.8 ms |       843.8 ms |   110.1 ms |
|=======|============|==============|================|============|
|  avg  |   108.4 ms |   1,974.6 ms |       907.7 ms |   110.4 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 42,700,800 bytes: 918.82 KB/s


SSH.NET download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |    10.6 ms |     854.5 ms |     1,237.7 ms |    36.2 ms |
|   2   |    36.2 ms |     790.2 ms |     1,234.4 ms |    30.9 ms |
|   3   |    30.9 ms |   1,274.8 ms |     1,370.2 ms |    30.4 ms |
|   4   |    30.4 ms |   1,172.6 ms |     1,360.5 ms |    31.0 ms |
|   5   |    31.1 ms |   1,214.2 ms |     1,373.9 ms |    30.6 ms |
|   6   |    30.6 ms |   1,183.1 ms |     1,338.5 ms |    31.3 ms |
|   7   |    31.3 ms |   1,135.0 ms |     1,357.8 ms |    29.9 ms |
|   8   |    29.9 ms |   1,249.5 ms |     1,413.4 ms |    30.7 ms |
|   9   |    30.7 ms |   1,166.6 ms |     1,351.1 ms |    31.7 ms |
|  10   |    31.7 ms |   1,206.5 ms |     1,276.2 ms |    29.8 ms |
|  11   |    29.8 ms |   1,190.5 ms |     1,348.7 ms |    31.6 ms |
|  12   |    31.6 ms |   1,403.9 ms |     1,385.1 ms |    29.9 ms |
|  13   |    29.9 ms |   1,133.3 ms |     1,367.0 ms |    30.5 ms |
|  14   |    30.5 ms |   1,260.8 ms |     1,349.0 ms |    30.1 ms |
|  15   |    30.2 ms |   1,171.7 ms |     1,394.7 ms |    30.9 ms |
|  16   |    30.9 ms |   1,164.9 ms |     1,360.2 ms |    32.2 ms |
|  17   |    32.3 ms |   1,267.7 ms |     1,346.9 ms |    30.1 ms |
|  18   |    30.1 ms |   1,216.1 ms |     1,380.3 ms |    30.3 ms |
|  19   |    30.4 ms |     778.4 ms |     1,247.8 ms |    30.7 ms |
|  20   |    30.7 ms |   1,321.7 ms |     1,370.9 ms |    29.8 ms |
|  21   |    29.8 ms |     791.9 ms |     1,225.3 ms |    30.4 ms |
|  22   |    30.4 ms |   1,096.9 ms |     1,223.9 ms |    30.3 ms |
|  23   |    30.3 ms |   1,450.5 ms |     1,286.6 ms |    30.6 ms |
|  24   |    30.7 ms |   1,296.2 ms |     1,351.7 ms |    30.6 ms |
|  25   |    30.7 ms |   1,051.9 ms |     1,301.3 ms |    31.8 ms |
|  26   |    31.9 ms |   1,252.0 ms |     1,342.4 ms |    30.7 ms |
|  27   |    30.7 ms |     798.5 ms |     1,241.6 ms |    30.5 ms |
|  28   |    30.5 ms |   1,257.4 ms |     1,413.2 ms |    29.8 ms |
|  29   |    29.8 ms |   1,198.3 ms |     1,317.6 ms |    41.7 ms |
|  30   |    41.7 ms |   1,175.5 ms |     1,470.8 ms |    30.6 ms |
|  31   |    30.6 ms |   1,207.0 ms |     1,355.9 ms |    31.6 ms |
|  32   |    31.7 ms |   1,184.3 ms |     1,366.4 ms |    29.9 ms |
|  33   |    29.9 ms |   1,413.0 ms |     1,689.5 ms |    30.4 ms |
|  34   |    30.5 ms |     963.7 ms |     1,604.3 ms |    31.3 ms |
|  35   |    31.4 ms |   6,153.2 ms |     2,574.9 ms |    30.1 ms |
|  36   |    30.1 ms |     997.3 ms |     1,421.5 ms |    29.9 ms |
|  37   |    30.0 ms |   3,241.6 ms |     1,688.5 ms |    30.4 ms |
|  38   |    30.5 ms |   1,308.0 ms |     1,646.8 ms |    42.8 ms |
|  39   |    42.8 ms |   1,216.4 ms |     1,637.1 ms |    29.9 ms |
|  40   |    29.9 ms |   1,562.0 ms |     1,438.5 ms |    31.5 ms |
|  41   |    31.5 ms |   1,407.7 ms |     1,806.8 ms |    30.6 ms |
|  42   |    30.7 ms |   1,360.6 ms |     1,512.1 ms |    30.1 ms |
|  43   |    30.1 ms |   1,659.3 ms |     1,527.7 ms |    30.7 ms |
|  44   |    30.7 ms |   1,623.3 ms |     1,753.2 ms |    30.3 ms |
|  45   |    30.3 ms |   1,312.4 ms |     1,426.0 ms |    31.3 ms |
|  46   |    31.3 ms |     790.7 ms |     1,278.9 ms |    29.7 ms |
|  47   |    29.8 ms |     791.9 ms |     1,380.5 ms |    30.4 ms |
|  48   |    30.4 ms |   1,412.9 ms |     1,393.2 ms |    31.1 ms |
|  49   |    31.1 ms |     821.7 ms |     1,307.3 ms |    30.7 ms |
|  50   |    30.7 ms |   1,171.7 ms |     1,419.0 ms |    29.3 ms |
|=======|============|==============|================|============|
|  avg  |    30.8 ms |   1,322.5 ms |     1,425.3 ms |    31.2 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 42,700,800 bytes: 585.12 KB/s

@ceastwood
Copy link

ceastwood commented Oct 20, 2016

I haven't done any thorough testing as above, but I was just evaluating the library and the performance issue is large in the current NuGet package (v2016.0.0). Once I get a chance in near the future I will revisit to test further but thought it might help if I added a comment.

The server is internal to the corporate network and always / easily provides near gigabit downloads when using a standalone sftp client (FileZilla). Connectivity is > 1Gbps

Using SSH.NET SftpClient to a memory stream will not go any faster than ~2.78MB/s. Also tried to provide a buffer the full size of the desired file to the memorystream constructor without luck.

Just downloaded the WinSCP NuGet package and it achieves ~70MB/s but there are various issues with that project as well.

Thanks,

@drieseng
Copy link
Member

@lochnar187 I'll do some tests with WS-FTP later this week, or early next week. I may need more information on your setup at the point.

@drieseng
Copy link
Member

@ceastwood Those numbers really trouble me. Can you provide more info on that SSH server (software, version, crypto used, ...). If not, I could provide a custom built version of SSH.NET that adds more debug output).

@lochnar187
Copy link
Author

@drieseng I'll give you all the info I have, sadly that's not much for the WS-FTP server as it's not mine. Just let me know what you need.

For the moment, here's a verbose log from FileZilla's connection to that server:

Status: Connecting to xxxxxxxxxxxxxxx...
Trace:  Going to execute "C:\Program Files\FileZilla FTP Client\fzsftp.exe"
Response:   fzSftp started
Trace:  CSftpControlSocket::ConnectParseResponse(fzSftp started)
Trace:  CSftpControlSocket::SendNextCommand()
Trace:  CSftpControlSocket::ConnectSend()
Command:    open "xxxxxxxxxxx@xxxxxxxxxxxxxxx" 22
Trace:  Looking up host "xxxxxxxxxxxxxxx"
Trace:  Connecting to xx.xx.xx.xx port 22
Trace:  Server version: SSH-2.0-WS_FTP-SSH_7.6.3
Trace:  Using SSH protocol version 2
Trace:  We claim version: SSH-2.0-PuTTY_Local:_Jun__1_2014_11:08:49
Trace:  Using Diffie-Hellman with standard group "group14"
Trace:  Doing Diffie-Hellman key exchange with hash SHA-1
Trace:  Host key fingerprint is:
Trace:  ssh-rsa 1024 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Trace:  Initialised AES-256 CBC client->server encryption
Trace:  Initialised HMAC-SHA1 client->server MAC algorithm
Trace:  Initialised AES-256 CBC server->client encryption
Trace:  Initialised HMAC-SHA1 server->client MAC algorithm
Command:    Pass: ********
Trace:  Sent password
Trace:  Access granted
Trace:  Opened channel for session
Trace:  Started a shell/command
Status: Connected to xxxxxxxxxxxxxxx
Trace:  CSftpControlSocket::ConnectParseResponse()
Trace:  CSftpControlSocket::ResetOperation(0)
Trace:  CControlSocket::ResetOperation(0)
Trace:  CFileZillaEnginePrivate::ResetOperation(0)
Status: Retrieving directory listing...
Trace:  CSftpControlSocket::SendNextCommand()
Trace:  CSftpControlSocket::ChangeDirSend()
Command:    pwd
Response:   Current directory is: "/xxxxxxxxxxx"
Trace:  CSftpControlSocket::ResetOperation(0)
Trace:  CControlSocket::ResetOperation(0)
Trace:  CSftpControlSocket::ParseSubcommandResult(0)
Trace:  CSftpControlSocket::ListSubcommandResult()
Trace:    state = 1
Trace:  CSftpControlSocket::SendNextCommand()
Trace:  CSftpControlSocket::ListSend()
Trace:    state = 2
Command:    ls
Status: Listing directory /xxxxxxxxxxx
Trace:  CSftpControlSocket::ListParseResponse()
Trace:  CSftpControlSocket::ResetOperation(0)
Trace:  CControlSocket::ResetOperation(0)
Status: Directory listing successful
Trace:  CFileZillaEnginePrivate::ResetOperation(0)

@spkane86
Copy link

spkane86 commented Oct 26, 2016

The reason for the poor performance is that requests aren't pipelining despite the code appearing built to do so.

Say the buffers are configured such that you can receive 5 SSH_FXP_READ responses (SSH_FXP_DATA) before you can read any more. The pattern I'm seeing is like this for downloads, in shorthand:

READ 0->
<- DATA 0
READ 1 ->
<- DATA 1
READ 2 ->
<- DATA 2
READ 3 ->
<- DATA 3
READ 4 ->
<- DATA 4
...

What you want to see is something more like this:

READ 0 ->
READ 1 ->
READ 2 ->
READ 3->
READ 4->
<- DATA 0
READ 5 ->
<- DATA 1
READ 6 ->
<- DATA 2
READ 7 ->
<- DATA 3
READ 8->
<- DATA 4
READ 9->
...

Does that make sense? Since SFTP has a 32KB max data size, with the pattern I'm seeing now, there is a round-trip-time inflicted for each 32KB in the file, which is why buffer sizes don't really matter much. It's only actually buffering 32KB before writing it right away.

From my cursory reading of the code, it may be due to the locking on _requests dictionary in SftpSession or perhaps the SftpFileStream itself. Another thought I had is that since response handling is event-driven, you're also likely to be stuck handling a response when you could be sending one out, especially when there is low latency on the connection. Disk I/O is more costly than sending a new request, and since you're handling responses right away and locking the _requests in the meantime, it can't get another request out by the time it's received a new response to handle from the previous request.

It seems to be an issue on writes as well. You want to send out as many writes as you can until you block waiting for WINDOW_ADJUST. That could be a similar thing, where WRITE's get built/sent while a lock is held, preventing further WRITE's from being built/sent simultaneously because you're already handling a STATUS.

Not really sure I'm anywhere close on the underlying cause in the code. But hey, it's pretty to look at, so maybe I'll look some more soon.

If it does have something to do with locking or event handling, I'm not sure the best solution for your code. When I've implemented pipelining on an SFTP client in the past, I had to kind of refactor the response handling so that it was closely in-tune with request sending, such that I had more direct control over when to send new request (i.e. immediately after receiving a response). In any case the most important thing is to completely fill the pipe at the start of the transfer, and then the existing paradigm should work relatively well.

That said, you really don't want to assume it won't start falling behind, by handling more responses than making new requests, but handling that is a lot more difficult than simply putting some blocker up on your response handling while you're busy filling the pipe at the start. I'd probably start there to see if it even does try to pipeline the requests in the first place. You might be surprised at the results.

@spkane86
Copy link

Just occurred to me that SftpSession.RequestRead is essentially synchronous. That would do it.

@drieseng
Copy link
Member

We may effectively be able to send multiple read requests at once; either by first checking what the size of the remote file is, and determine the optimal number of the requests to send in parallel, or by sending a given number of requests and ignoring those responses that return an EOF (and stop sending any further requests when any of these async requests return an EOF).

We would however need to write the responses to those requests in sequence, as such we would need to buffer more data and - as a consequence- consume more memory.

I currently don't have time to do a POC for this, but please don't hesitate to take a stab at this.

@drieseng
Copy link
Member

drieseng commented Jan 7, 2017

I've started working on this.

@drieseng
Copy link
Member

My (draft) read-ahead implementation of DownloadFile shows a "slight" increase from 31488 Kb/s
to 57142 Kb/s on a fast connection.

Ìncrease should be even higher on slower connection. I'll test this tomorrow.

@hifi
Copy link
Contributor

hifi commented Jan 18, 2017

What I can say from my part is that the raw channel overhead is non-existent as I can hit my gigabit local network limit with the exec channel meaning any throughput slowdown happens within the SFTP handling code.

I have some ideas I could try if I can reproduce the slowdown. I'm not too familiar with the SFTP subsystem of SSH, though.

@imapangolin
Copy link

I'm using the dev code and seeing a huge slow down as well. 28Meg file - 8 seconds with FileZilla but 2 minutes with SSH.NET. Increase buffer to 63K and I get it down to a minute. I don't know where the overhead issue is but could it be that some of these other tools are multi-threading the download?

@drieseng
Copy link
Member

I committed a preliminary version of my read-ahead changes in the develop branch.
Please hammer away at them, and let me know if it reduces download speed for you.

@imapangolin
Copy link

imapangolin commented Jan 22, 2017 via email

@lochnar187
Copy link
Author

@drieseng I'm stuck firefighting atm, I hope to get to this in a few days. Thank all for working on this issue!

@imapangolin
Copy link

imapangolin commented Jan 24, 2017

I did some debugging and the isCompleted is not being set at the end of the transfer. It's processing 32K chunks up until a short read at end of file but it doesn't set the isCompleted flag so it loops in the Monitor.Wait and never returns.

I did a little MORE debugging and after the short last chunk is read we never get back to another attempt but the isCompleted is not set. I'm going to try once more to see where we might be able to set this but so far I haven't been able to debug the right area to get there.

In the public byte[] Read()

This code...
while (!_queue.TryGetValue(_nextChunkIndex, out nextChunk) && !_isCompleted)
Monitor.Wait(_readLock);

Is falling into MonitorWait and it's never coming out - the while condition is true as theres no next chunk but isCompleted is not set true. I tried setting it on the short read, but that fails to write out the last chunk in that case.

The speed is phenomenal up to that point.

@imapangolin
Copy link

imapangolin commented Jan 24, 2017

I "fixed" this as follows. In SftpFileReader see code //tah

            while (!_isCompleted)
            {
                // we reach one chunk beyond the file size to get an EOF
                if (_readAheadOffset > _fileSize)
                {
                    _isCompleted = true;  // tah
                    break;
                }
                // TODO implement cancellation!?
                _semaphore.Wait();

.... I am NOT at all certain this is the best/correct way to fix this but it does work.

This doesn't work unfortunately. Because it is async it fails to write the complete file out..... so this has to be set elsewhere - I just do not know where.

@drieseng
Copy link
Member

drieseng commented Jan 24, 2017

The _isCompleted change that you added should not be necessary, but I could be wrong.
Right now, I stop reading ahead when we've read beyond the reported file size.
It may be safer to continue reading ahead until we get an EOF, but that would mean that we risk reading ahead a few times too much (which I'd like to avoid).

Please check if my latest commit fixes the problem for you.
If not, I'll add some extra CWL tracing.

@drieseng
Copy link
Member

drieseng commented Jan 24, 2017

Ignore my last comment, I'm looking into it.
It's my gf's anniversary, so I may not be able to commit the fix today.

@drieseng
Copy link
Member

I just committed a new version that should fix this issue.
Keep in mind that it's still very much WIP.

@imapangolin
Copy link

imapangolin commented Jan 25, 2017

I know you say it's a WIP but I'd call it a rousing success. WinSCP which was the fastest transfer I could find has been doing 29-31 seconds on my sample file. Consistently getting 15-16 using the changes you put into SSH.NET. I say go for it and put it in the next beta! THANK YOU! Happy Anniversary to your gf too!

@ceastwood
Copy link

@drieseng I will definitely help test this out when I get a chance over the next couple of days.

@drieseng
Copy link
Member

drieseng commented Feb 6, 2017

@lochnar187 Let's keep the discussion around the file/folder not found problem in issue #94.

@drieseng
Copy link
Member

drieseng commented Feb 6, 2017

I've now hardened the implementation so that a broken session or an unresponsive SSH server interrupts the read-ahead loop (and any blocking waits). This adds quite some complexity, which of course does not come for free.

To compensate for that additional cost (and the corresponding performance degradation), I've introduced some new optimizations. The net result is that - even after the "hardening" - performance has increased even a little further (compared to the draft).

I started working on SSH.NET after the 2013.4.7 release, so I used this as a baseline for my performance tests.

Downloading a 50 MB file 100 times takes 259850 ms on v2013.4.7, 159730 ms on v2016.0.0 and 90852 ms when using the develop branch.

If you look at the transfer speed, we went from 19703 KB/s for v2013.4.7, to 32054 KB/s for v2016.0.0 and finally settled at 56355 KB/s for that same 50 MB file when using the develop branch.

Note that I tested with different file sizes, to make sure there are no regressions for small files.
Let me know if you're interested in the full results.

@imapangolin
Copy link

imapangolin commented Feb 6, 2017 via email

@drieseng
Copy link
Member

drieseng commented Feb 6, 2017

Here's the % improvement in elapsed time of the develop branch versus v2016.0.0 for different file sizes.

image

@imapangolin
Copy link

I downloaded the latest dev build and I'm back in slow land. With the last version that didn't have the robust recovery I was getting 9 seconds on my download. With this version I get a full minute 5 seconds. This is the same throughput I get using the release version of SFTP with a 32K buffer. This version, sadly, isn't really improved for me.

@drieseng
Copy link
Member

drieseng commented Feb 7, 2017

We could see it as bad news, or consider it a challenge. I prefer the latter.
Let's try to see what made it regress for you.

Did you explicitly set a BufferSize ? In the version that you previously tested, the read-ahead used a hard-coded buffer size of 32 KB. Now we use whatever is configured (the default for SftpClient is 32 KB, which is a good default), with a small adjustment to take the protocol overhead into account.

Would it be possible for you to debug into ServiceFactory.CreateSftpFileReader(...)? When we're unable to determine the size of the file (SSH_FXP_LSTAT failed), we fall back to having maximum 3 pending reads.
Also what (exact) size of file(s) are you testing with?

When the file size is known, the maximum number of pending reads is based on the file size and the buffer size with a maximum of 10 pending reads.
In the version that you previously tested, the maximum number of pending reads was hard coded to 15.
With the new version, there will be a lot less read-aheads for small files (to avoid wasting round-trips to the server).
For large(r) files, there will be 10 read-aheads in the new version (vs. 15 before).

@drieseng
Copy link
Member

drieseng commented Feb 7, 2017

@imapangolin Also, you don't happen to have a server I can test against to reproduce this?

@imapangolin
Copy link

imapangolin commented Feb 7, 2017 via email

@imapangolin
Copy link

imapangolin commented Feb 7, 2017 via email

@drieseng
Copy link
Member

drieseng commented Feb 8, 2017

There's almost no reason to play around with the buffer size.
As I mentioned earlier, 32 KB is a good default.

On my system, with a local (and responsive) SSH server, there's almost no difference between 2 and 10 read-aheads. I guess 10 was the last value that I tested with; there was no million dollar usage study to get to that value :-)

Please do some tests with values higher than 10, and let me know if that changes much for you.

@imapangolin
Copy link

imapangolin commented Feb 10, 2017 via email

@drieseng
Copy link
Member

drieseng commented Feb 11, 2017

I did some further tests and attempts to improve transfer rate, but I failed to make a notable difference.
Putty is still quite a lot faster.

I'm gonna try to see if using the crypto classes from the .NET base class library makes a difference.
But to be honest, I'm not a crypto wizard and have no ambition to become one :)

@imapangolin
Copy link

imapangolin commented Feb 11, 2017 via email

@drieseng
Copy link
Member

Please try using version 2016.1.0-beta2, and let me know if performance is good now.

@drieseng drieseng added this to the 2016-1.0-beta2 milestone Aug 16, 2017
@lochnar187
Copy link
Author

Did a quick run with 2016-1.0-beta2, I'm seeing a major boost! The speed almost doubled. Here's the results, same test program as above.

C:\Data>dir
 Volume in drive C has no label.
 Volume Serial Number is E6AE-D23F

 Directory of C:\Data

08/16/2017  06:18 PM    <DIR>          .
08/16/2017  06:18 PM    <DIR>          ..
08/16/2017  06:07 PM           422,912 Renci.SshNet.dll
01/27/2017  06:26 PM         1,502,720 Renci.SshNet.pdb
08/16/2017  06:07 PM         1,008,622 Renci.SshNet.xml
01/27/2017  06:59 PM             9,216 SFTP_Compare.exe
10/06/2016  07:18 PM               189 SFTP_Compare.exe.config
01/27/2017  06:59 PM            15,872 SFTP_Compare.pdb
08/16/2017  06:09 PM            22,696 SFTP_Compare.vshost.exe
10/06/2016  07:18 PM               189 SFTP_Compare.vshost.exe.config
07/10/2015  07:01 AM               490 SFTP_Compare.vshost.exe.manifest
10/06/2016  07:22 PM        18,886,048 WinSCP.exe
10/08/2016  02:48 PM                75 winscp.ini
10/06/2016  07:22 PM           149,584 WinSCPnet.dll
              18 File(s)     26,195,039 bytes
               2 Dir(s)   9,880,752,128 bytes free

C:\Data>sftp_compare

WinSCP download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |     4.5 ms |   1,806.8 ms |       784.8 ms |   113.1 ms |
|   2   |   113.2 ms |   1,775.5 ms |       843.7 ms |   110.6 ms |
|   3   |   110.7 ms |   1,779.3 ms |       843.7 ms |   111.7 ms |
|   4   |   111.8 ms |   1,782.3 ms |       843.8 ms |   111.5 ms |
|   5   |   111.6 ms |   1,777.8 ms |       843.7 ms |   110.5 ms |
|   6   |   110.5 ms |   1,779.4 ms |       843.8 ms |   110.3 ms |
|   7   |   110.4 ms |   1,779.5 ms |       843.8 ms |   110.5 ms |
|   8   |   110.6 ms |   1,779.4 ms |       843.8 ms |   110.5 ms |
|   9   |   110.6 ms |   1,778.8 ms |       843.7 ms |   110.6 ms |
|  10   |   110.7 ms |   1,779.0 ms |       843.8 ms |   111.4 ms |
|  11   |   111.5 ms |   1,778.7 ms |       843.8 ms |   110.5 ms |
|  12   |   110.6 ms |   1,779.0 ms |       843.9 ms |   110.5 ms |
|  13   |   110.7 ms |   1,778.8 ms |       849.4 ms |   110.3 ms |
|  14   |   110.4 ms |   1,779.2 ms |       843.9 ms |   111.0 ms |
|  15   |   111.1 ms |   1,778.5 ms |       845.3 ms |   110.5 ms |
|  16   |   110.6 ms |   1,779.8 ms |       843.8 ms |   110.8 ms |
|  17   |   111.0 ms |   1,779.1 ms |       843.7 ms |   110.3 ms |
|  18   |   110.4 ms |   1,779.5 ms |       843.9 ms |   110.5 ms |
|  19   |   110.5 ms |   1,779.2 ms |       843.8 ms |   110.6 ms |
|  20   |   110.6 ms |   1,779.2 ms |       843.7 ms |   110.3 ms |
|  21   |   110.4 ms |     967.0 ms |       843.8 ms |   110.4 ms |
|  22   |   110.5 ms |     966.9 ms |       843.9 ms |   111.1 ms |
|  23   |   111.3 ms |   1,778.3 ms |       844.5 ms |   107.3 ms |
|  24   |   107.4 ms |   1,779.0 ms |       843.8 ms |   110.1 ms |
|  25   |   110.2 ms |   1,779.0 ms |       843.8 ms |   110.3 ms |
|  26   |   110.4 ms |   1,779.5 ms |       843.7 ms |   110.6 ms |
|  27   |   110.6 ms |   1,779.4 ms |       844.8 ms |   110.2 ms |
|  28   |   110.3 ms |   1,779.6 ms |       843.8 ms |   111.3 ms |
|  29   |   111.4 ms |   1,778.5 ms |     1,656.3 ms |   110.2 ms |
|  30   |   110.2 ms |   3,408.8 ms |     1,656.2 ms |   110.3 ms |
|  31   |   110.4 ms |   1,779.2 ms |       844.7 ms |   110.4 ms |
|  32   |   110.5 ms |   1,779.3 ms |       843.8 ms |   111.6 ms |
|  33   |   111.8 ms |   1,780.6 ms |     2,469.2 ms |   111.5 ms |
|  34   |   111.6 ms |   1,777.9 ms |       843.8 ms |   110.4 ms |
|  35   |   110.5 ms |   1,779.3 ms |       845.8 ms |   110.6 ms |
|  36   |   110.7 ms |   1,778.8 ms |       843.9 ms |   110.2 ms |
|  37   |   110.3 ms |   1,779.4 ms |       843.8 ms |   110.4 ms |
|  38   |   110.5 ms |   1,779.0 ms |       843.9 ms |   111.4 ms |
|  39   |   111.6 ms |   1,778.1 ms |       843.7 ms |   110.5 ms |
|  40   |   110.5 ms |   1,779.0 ms |       843.9 ms |   111.3 ms |
|  41   |   111.5 ms |   1,778.2 ms |       843.8 ms |   110.4 ms |
|  42   |   110.4 ms |   1,786.1 ms |       846.0 ms |   110.3 ms |
|  43   |   110.4 ms |   1,779.5 ms |       843.8 ms |   110.4 ms |
|  44   |   110.5 ms |   1,779.4 ms |     1,328.1 ms |   110.4 ms |
|  45   |   110.5 ms |   1,513.7 ms |       968.8 ms |   110.4 ms |
|  46   |   110.5 ms |   2,221.5 ms |       843.7 ms |   112.5 ms |
|  47   |   112.6 ms |   1,904.7 ms |       843.8 ms |   110.3 ms |
|  48   |   110.4 ms |   1,279.5 ms |       843.8 ms |   110.3 ms |
|  49   |   110.3 ms |   1,982.7 ms |       843.8 ms |   111.3 ms |
|  50   |   111.5 ms |   2,215.9 ms |       843.7 ms |   635.1 ms |
|=======|============|==============|================|============|
|  avg  |   108.6 ms |   1,788.7 ms |       920.1 ms |   121.2 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 40,448,000 bytes: 858.61 KB/s


SSH.NET download test, 50 iteration(s).
___________________________________________________________________
|  Run  |  Client Cr |  Connection  |    Download    |   Delete   |
|   1   |    10.7 ms |     927.5 ms |       628.9 ms |    37.0 ms |
|   2   |    37.0 ms |     922.5 ms |       793.7 ms |    30.2 ms |
|   3   |    30.2 ms |   1,028.0 ms |       676.0 ms |    30.1 ms |
|   4   |    30.1 ms |     878.7 ms |     1,258.9 ms |    30.0 ms |
|   5   |    30.0 ms |     928.2 ms |       766.1 ms |    30.8 ms |
|   6   |    30.8 ms |     878.3 ms |       682.4 ms |    30.3 ms |
|   7   |    30.4 ms |     997.2 ms |       707.5 ms |    45.3 ms |
|   8   |    45.3 ms |   1,011.9 ms |       667.9 ms |    30.0 ms |
|   9   |    30.1 ms |     996.0 ms |       649.9 ms |    33.7 ms |
|  10   |    33.7 ms |     833.0 ms |       869.0 ms |    57.9 ms |
|  11   |    58.0 ms |     890.6 ms |     1,246.2 ms |    30.6 ms |
|  12   |    30.7 ms |     915.5 ms |       670.6 ms |    34.2 ms |
|  13   |    34.2 ms |     940.2 ms |       804.1 ms |    30.5 ms |
|  14   |    30.5 ms |   1,051.0 ms |     1,122.1 ms |    30.3 ms |
|  15   |    30.4 ms |     864.3 ms |       617.7 ms |    29.7 ms |
|  16   |    29.7 ms |     857.0 ms |       638.0 ms |    30.0 ms |
|  17   |    30.0 ms |     991.7 ms |       766.8 ms |    30.1 ms |
|  18   |    30.1 ms |     903.6 ms |     1,156.0 ms |    30.8 ms |
|  19   |    30.8 ms |     959.0 ms |       842.0 ms |    30.2 ms |
|  20   |    30.2 ms |     945.7 ms |     1,065.2 ms |    29.7 ms |
|  21   |    29.7 ms |     948.2 ms |     1,136.4 ms |    31.1 ms |
|  22   |    31.1 ms |     902.3 ms |       613.0 ms |    30.4 ms |
|  23   |    30.4 ms |     842.2 ms |       821.2 ms |    29.9 ms |
|  24   |    30.0 ms |     887.7 ms |       647.8 ms |    29.5 ms |
|  25   |    29.5 ms |     852.7 ms |       632.1 ms |    30.0 ms |
|  26   |    30.0 ms |     906.8 ms |       644.7 ms |    30.1 ms |
|  27   |    30.1 ms |     906.1 ms |       596.0 ms |    30.4 ms |
|  28   |    30.4 ms |   1,014.1 ms |       674.0 ms |    29.6 ms |
|  29   |    29.7 ms |     895.5 ms |     1,076.0 ms |    30.6 ms |
|  30   |    30.6 ms |     953.3 ms |       716.5 ms |    31.1 ms |
|  31   |    31.1 ms |     929.5 ms |       643.4 ms |    30.4 ms |
|  32   |    30.5 ms |     970.7 ms |       731.7 ms |    30.7 ms |
|  33   |    30.8 ms |     926.8 ms |       621.8 ms |    30.3 ms |
|  34   |    30.4 ms |     847.4 ms |       670.3 ms |    30.3 ms |
|  35   |    30.4 ms |     895.4 ms |       707.8 ms |    31.4 ms |
|  36   |    31.4 ms |   1,060.1 ms |       653.8 ms |    30.3 ms |
|  37   |    30.4 ms |     898.9 ms |       632.5 ms |    30.2 ms |
|  38   |    30.2 ms |     891.6 ms |       658.6 ms |    30.5 ms |
|  39   |    30.5 ms |     847.9 ms |       602.4 ms |    29.8 ms |
|  40   |    29.9 ms |     905.8 ms |       624.0 ms |    29.5 ms |
|  41   |    29.5 ms |     975.0 ms |       605.6 ms |    30.0 ms |
|  42   |    30.1 ms |     877.6 ms |       674.4 ms |    29.6 ms |
|  43   |    29.6 ms |   1,000.7 ms |       760.5 ms |    31.7 ms |
|  44   |    31.7 ms |     895.9 ms |       693.0 ms |    30.1 ms |
|  45   |    30.1 ms |     886.6 ms |       649.7 ms |    30.2 ms |
|  46   |    30.2 ms |     875.3 ms |       701.4 ms |    29.9 ms |
|  47   |    29.9 ms |   1,052.4 ms |       621.3 ms |    30.3 ms |
|  48   |    30.4 ms |     856.2 ms |       653.4 ms |    30.5 ms |
|  49   |    30.5 ms |     922.2 ms |       687.3 ms |    30.3 ms |
|  50   |    30.3 ms |     918.8 ms |       700.7 ms |    30.9 ms |
|=======|============|==============|================|============|
|  avg  |    31.0 ms |     925.3 ms |       749.6 ms |    31.4 ms |
|_______|____________|______________|________________|____________|
 *** Average download speed for 40,448,000 bytes: 1,053.89 KB/s

Keep up the great work and thank you.

@drieseng
Copy link
Member

Thanks for the feedback!

@drieseng
Copy link
Member

drieseng commented Aug 29, 2017

@lochnar187
I've discovered a performance regression when the read-ahead mechanism is used with Sun SSH (issue #292).
The fix for that issue should also improve performance a little for other SFTP servers.
I might call upon you to perform another test when beta3 is available.

@imapangolin
Copy link

imapangolin commented Aug 29, 2017 via email

@lochnar187
Copy link
Author

@drieseng I'd be happy to do that! Let me know when.

@drieseng
Copy link
Member

drieseng commented Sep 16, 2017

@lochnar187 @imapangolin I've committed the fix for issue #292.

I had to revert sending SSH_FXP_OPEN and SSH_FXP_LSTAT requests in parallel since Sun SSH does not handle this well. This may result in a minor performance regression, but this should only be measurable for small files.

On the other hand, we now no longer send unnecessary SSH_FXP_READ requests once we've reached the reported file size. This should have a positive effect on all downloads.

Can you perform some tests with the develop branch?
Also feel free to play around with the maximum number of pending reads (which is determined in ServiceFactory.CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize).

@lochnar187
Copy link
Author

@drieseng I gave it another run with the dev branch code and I'm seeing another 15-20% increase in speed over the last run (Aug 16). Mostly, this was due to less variation in today's runs with each cycle coming in with 600 to 750 ms while the last run saw wider variance. Very nice :)

I'm not done yet, I want to do a new bit of code to play with pending read count and see what that does. I'll post more in a while. Thank you.

@NoahLerner
Copy link

NoahLerner commented Jan 26, 2021

Hi @drieseng great work on this project!
I'm debugging the latest code because SSH.NET isn't performing as well as WinSCP (700 kb/s vs 1.6 mb/s < x) and I discovered the read ahead mechanism seems underperformant for me... I already confirmed that my max pending reads is 10.

All I did was add this simple debug statement

if (_queue.Count > 1)
{
    Debug.WriteLine("Queue count is : " + _queue.Count);
}

right here

And my results showed that over the course of my "large" download (290 mb file) over a high latency and high bandwidth network, I only ever reached a _queue count of 2 a couple of times.

Do I understand correctly that my queue is not being filled and as such I am downloading the entire file almost without any readaheads? Should I open a new issue for this? Thank you very much in advance.

UPDATE:
After playing around more with the code I'm thinking that the queue not ever being filled is a symptom of lock contention. Every time a successful SSH_FXP_READ is sent and the queue written to, we immediately Pulse and activate the Read thread. The read thread locks down the queue and then loops around to Monitor.Wait which then reactivates the Read-Ahead thread. All this back and forth seems to essentially be creating synchronicity between the threads.

@jhardin-accumula
Copy link

We're also still seeing very poor performance on large file downloads in 2020.0.1 - 250kB transfers quickly (a few seconds), but 61MB transfers very slowly (7-8 minutes, 100-200kB/sec throughput during the transfer). Setting a large (256kB) buffer for large files doesn't help. The same file downloads very quickly in WinSCP (7-8 MB/sec).

@helviett
Copy link

@jhardin-accumula
I’ve got the same issue, but I also can’t get WinSCP download large files faster than ssh.net. Can you please share your code? Perhaps I need to tune session parameters.

I write extension for vscode and visual studio. Npm package for sftp downloads 70 mb file in a minute while it takes 10 minute or more to download the same file using SSH.NET.

@jhardin-accumula
Copy link

@jhardin-accumula
I’ve got the same issue, but I also can’t get WinSCP download large files faster than ssh.net.
Can you please share your code? Perhaps I need to tune session parameters.

I write extension for vscode and visual studio. Npm package for sftp downloads 70 mb file
in a minute while it takes 10 minute or more to download the same file using SSH.NET.

Actually, my quick comparison for the above may have been misleading - I'm doing C# dev in a MacOS-hosted Parallels VM and I was actually comparing SSH.NET in the VM to sftp from the Mac environment... Running WinSCP in the VM gives similar mediocre network performance, and the network throughput reported by Task Manager in the VM is much higher than the network throughput for the VM environment reported by the MacOS host. I'm not sure SSH.NET is the problem for me. The production install at AWS is showing > 400kB/s throughput from the same SFTP server.

Modified SSH.NET parameters: timeout = 10m, keepalive = 30s (to allow for connection idle delay from processing the file after download), and if the file being retrieved is >1MB the buffer size is set to 256kB (even though Noah's comment suggests that's pointless... I'm anticipating a fix :) ).

@zybexXL
Copy link
Contributor

zybexXL commented Aug 30, 2021

I've added two PRs today that improve SFTP performance (by a LOT, in my case), and simultaneously reduce CPU usage: #865 and #866. I'm not sure if the SCP file transfers also benefit, I didn't test/check that.

According to my benchmarks, both FileUpload and FileDownload have massive speed gains and are now comparable to Filezilla.

These changes are for SftpClient.UploadFile/DownloadFile and variants. The Stream/CopyTo versions will also benefit a little on the CPU-usage side, but since they are synchronous and don't queue requests, I don't think they'll benefit that much performance-wise. However, if you are using (like I was) the Stream versions because they support Resume, you might want to check out #864.

I'd be very interested to know if these changes also help you guys :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests