1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
|
==========
Benchmarks
==========
pyftpdlib 0.7.0 vs. pyftpdlib 1.0.0
-----------------------------------
+-----------------------------------------+-----------------+----------------+------------+
| *benchmark type* | *0.7.0* | *1.0.0* | *speedup* |
+=========================================+=================+================+============+
| STOR (client -> server) | 528.63 MB/sec | 585.90 MB/sec | **+0.1x** |
+-----------------------------------------+-----------------+----------------+------------+
| RETR (server -> client) | 1702.07 MB/sec | 1652.72 MB/sec | -0.02x |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (connect, login) | 1.70 secs | 0.19 secs | **+8x** |
+-----------------------------------------+-----------------+----------------+------------+
| STOR (1 file with 300 idle clients) | 60.77 MB/sec | 585.59 MB/sec | **+8.6x** |
+-----------------------------------------+-----------------+----------------+------------+
| RETR (1 file with 300 idle clients) | 63.46 MB/sec | 1497.58 MB/sec | **+22.5x** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (RETR 10M file) | 4.68 secs | 3.41 secs | **+0.3x** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (STOR 10M file) | 10.13 secs | 8.78 secs | **+0.1x** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (QUIT) | 0.02 secs | 0.02 secs | 0x |
+-----------------------------------------+-----------------+----------------+------------+
pyftpdlib vs. proftpd 1.3.4
---------------------------
+-----------------------------------------+-----------------+----------------+------------+
| *benchmark type* | *pyftpdlib* | *proftpd* | *speedup* |
+=========================================+=================+================+============+
| STOR (client -> server) | 585.90 MB/sec | 600.49 MB/sec | -0.02x |
+-----------------------------------------+-----------------+----------------+------------+
| RETR (server -> client) | 1652.72 MB/sec | 1524.05 MB/sec | **+0.08** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (connect, login) | 0.19 secs | 9.98 secs | **+51x** |
+-----------------------------------------+-----------------+----------------+------------+
| STOR (1 file with 300 idle clients) | 585.59 MB/sec | 518.55 MB/sec | **+0.1x** |
+-----------------------------------------+-----------------+----------------+------------+
| RETR (1 file with 300 idle clients) | 1497.58 MB/sec | 1478.19 MB/sec | 0x |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (RETR 10M file) | 3.41 secs | 3.60 secs | **+0.05x** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (STOR 10M file) | 8.60 secs | 11.56 secs | **+0.3x** |
+-----------------------------------------+-----------------+----------------+------------+
| 300 concurrent clients (QUIT) | 0.03 secs | 0.39 secs | **+12x** |
+-----------------------------------------+-----------------+----------------+------------+
pyftpdlib vs. vsftpd 2.3.5
--------------------------
+-----------------------------------------+----------------+----------------+-------------+
| *benchmark type* | *pyftpdlib* | *vsftpd* | *speedup* |
+=========================================+================+================+=============+
| STOR (client -> server) | 585.90 MB/sec | 611.73 MB/sec | -0.04x |
+-----------------------------------------+----------------+----------------+-------------+
| RETR (server -> client) | 1652.72 MB/sec | 1512.92 MB/sec | **+0.09** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (connect, login) | 0.19 secs | 20.39 secs | **+106x** |
+-----------------------------------------+----------------+----------------+-------------+
| STOR (1 file with 300 idle clients) | 585.59 MB/sec | 610.23 MB/sec | -0.04x |
+-----------------------------------------+----------------+----------------+-------------+
| RETR (1 file with 300 idle clients) | 1497.58 MB/sec | 1493.01 MB/sec | 0x |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (RETR 10M file) | 3.41 secs | 3.67 secs | **+0.07x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (STOR 10M file) | 8.60 secs | 9.82 secs | **+0.07x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (QUIT) | 0.03 secs | 0.01 secs | +0.14x |
+-----------------------------------------+----------------+----------------+-------------+
pyftpdlib vs. Twisted 12.3
--------------------------
By using *sendfile()* (Twisted *does not* support sendfile()):
+-----------------------------------------+----------------+----------------+-------------+
| *benchmark type* | *pyftpdlib* | *twisted* | *speedup* |
+=========================================+================+================+=============+
| STOR (client -> server) | 585.90 MB/sec | 496.44 MB/sec | **+0.01x** |
+-----------------------------------------+----------------+----------------+-------------+
| RETR (server -> client) | 1652.72 MB/sec | 283.24 MB/sec | **+4.8x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (connect, login) | 0.19 secs | 0.19 secs | +0x |
+-----------------------------------------+----------------+----------------+-------------+
| STOR (1 file with 300 idle clients) | 585.59 MB/sec | 506.55 MB/sec | **+0.16x** |
+-----------------------------------------+----------------+----------------+-------------+
| RETR (1 file with 300 idle clients) | 1497.58 MB/sec | 280.63 MB/sec | **+4.3x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (RETR 10M file) | 3.41 secs | 11.40 secs | **+2.3x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (STOR 10M file) | 8.60 secs | 9.22 secs | **+0.07x** |
+-----------------------------------------+----------------+----------------+-------------+
| 300 concurrent clients (QUIT) | 0.03 secs | 0.09 secs | **+2x** |
+-----------------------------------------+----------------+----------------+-------------+
By using plain *send()*:
+-----------------------------------------+----------------+---------------+--------------+
| *benchmark type* | *tpdlib* | *twisted* | *speedup* |
+=========================================+================+===============+==============+
| RETR (server -> client) | 894.29 MB/sec | 283.24 MB/sec | **+2.1x** |
+-----------------------------------------+----------------+---------------+--------------+
| RETR (1 file with 300 idle clients) | 900.98 MB/sec | 280.63 MB/sec | **+2.1x** |
+-----------------------------------------+----------------+---------------+--------------+
Memory usage
------------
*Values on UNIX are calculated as (rss - shared).*
+------------------------------------------+-------------+-----------------+----------------+----------------+
| *benchmark type* | *pyftpdlib* | *proftpd 1.3.4* | *vsftpd 2.3.5* | *twisted 12.3* |
+==========================================+=============+=================+================+================+
| Starting with | 6.7M | 1.4M | 352.0K | 13.4M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| STOR (1 client) | 6.7M | 8.5M | 816.0K | 13.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| RETR (1 client) | 6.8M | 8.5M | 816.0K | 13.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| 300 concurrent clients (connect, login) | **8.8M** | 568.6M | 140.9M | 13.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| STOR (1 file with 300 idle clients) | **8.8M** | 570.6M | 141.4M | 13.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| RETR (1 file with 300 idle clients) | **8.8M** | 570.6M | 141.4M | 13.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| 300 concurrent clients (RETR 10.0M file) | **10.8M** | 568.6M | 140.9M | 24.5M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
| 300 concurrent clients (STOR 10.0M file) | **12.6** | 568.7M | 140.9M | 24.7M |
+------------------------------------------+-------------+-----------------+----------------+----------------+
Interpreting the results
------------------------
pyftpdlib, `proftpd`_ and `vsftpd`_ look pretty much equally fast. The huge
difference is noticeable in scalability though, because of the concurrency
model adopted. Proftpd and vsftpd spawn a new process for every connected
client, whereas pyftpdlib doesn't (see `the C10k problem`_). The difference
can be noticed on connect/login benchmarks and memory benchmarks.
The huge differences between 0.7.0 and 1.0.0 versions of pyftpdlib are due to
fix of `issue 203`_ . On Linux we now use `epoll()`_ which scales considerably
better than `select()`_. The fact that we're downloading a file with 300 idle
clients doesn't make any difference for `epoll()`. We might as well had 5000
idle clients and the result would have been the same. On Windows, where we
still use select(), 1.0.0 still wins hands down as the asyncore loop was
reimplemented from scratch in order to support fd un/registration and
modification. Benchmarks were conducted on Linux Ubuntu 12.04, Intel core duo -
3.1 Ghz box.
Setup
-----
The following setup was used before running every benchmark:
proftpd config
^^^^^^^^^^^^^^
::
# /etc/proftpd/proftpd.conf
MaxInstances 2000
...followed by:
::
$ sudo service proftpd restart
vsftpd config
^^^^^^^^^^^^^
::
# /etc/vsftpd.conf
local_enable=YES
write_enable=YES
max_clients=2000
max_per_ip=2000
...followed by:
::
$ sudo service vsftpd restart
twisted FTP server
^^^^^^^^^^^^^^^^^^
::
from twisted.protocols.ftp import FTPFactory, FTPRealm
from twisted.cred.portal import Portal
from twisted.cred.checkers import AllowAnonymousAccess, FilePasswordDB
from twisted.internet import reactor
import resource
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
open('pass.dat', 'w').write('user:some-passwd')
p = Portal(FTPRealm('./'),
[AllowAnonymousAccess(), FilePasswordDB("pass.dat")])
f = FTPFactory(p)
reactor.listenTCP(21, f)
reactor.run()
...followed by:
::
$ sudo python3 twist_ftpd.py
pyftpdlib
^^^^^^^^^
The following patch was applied first:
::
Index: pyftpdlib/servers.py
===================================================================
--- pyftpdlib/servers.py (revisione 1154)
+++ pyftpdlib/servers.py (copia locale)
@@ -494,3 +494,10 @@
def _map_len(self):
return len(multiprocessing.active_children())
+
+import resource
+soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
+resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
+FTPServer.max_cons = 0
...followed by:
::
$ sudo python3 demo/unix_daemon.py
The `benchmark script`_ was run as:
::
python3 scripts/ftpbench -u USERNAME -p PASSWORD -b all -n 300
...and for the memory test:
::
python3 scripts/ftpbench -u USERNAME -p PASSWORD -b all -n 300 -k FTP_SERVER_PID
.. _`benchmark script`: https://github.com/giampaolo/pyftpdlib/blob/master/scripts/ftpbench
.. _`epoll()`: https://linux.die.net/man/4/epoll
.. _`issue 203`: https://github.com/giampaolo/pyftpdlib/issues/203
.. _`proftpd`: http://www.proftpd.org/
.. _`select()`: https://linux.die.net/man/2/select
.. _`the C10k problem`: http://www.kegel.com/c10k.html
.. _`vsftpd`: https://security.appspot.com/vsftpd.html
|