Hi,<br><br>I just wanted mention a few workarounds I've come up with for the Python SMTP modules in regards to performance.<br><br>Before I started, I was getting about 15MB/s while sending e-mail from smtplib to smptd over a local connection. (i.e. both client/server running on the same machine). After the following changes, I'm seeing around<b> 220+MB/s</b> (increase of 14x)<br>
<br>The source can be found here:<br><a href="http://svn.python.org/view/python/trunk/Lib/smtplib.py?view=markup">http://svn.python.org/view/python/trunk/Lib/smtplib.py?view=markup</a><br><a href="http://svn.python.org/view/python/trunk/Lib/smtpd.py?view=markup">http://svn.python.org/view/python/trunk/Lib/smtpd.py?view=markup</a><br>
<br>When sending e-mail through <b>smtpdlib</b>, the following method is called.<br><br><pre><pre><span style="font-family: courier new,monospace;" class="PY_KEYWORD">def</span><a style="font-family: courier new,monospace;" name="quotedata"><span class="PY_IDENTIFIER"> quotedata</span></a><span style="font-family: courier new,monospace;">(data):</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    </span><span style="font-family: courier new,monospace;" class="PY_STRING">"""Quote data for email.<br><br>    Double leading '.', and change Unix newline '\\n', or Mac '\\r' into<br>
    Internet CRLF end-of-line.<br>    """</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    </span><span style="font-family: courier new,monospace;" class="PY_KEYWORD">return</span><span style="font-family: courier new,monospace;"> re.sub(r</span><span style="font-family: courier new,monospace;" class="PY_STRING">'(?m)^\.'</span><span style="font-family: courier new,monospace;">, </span><span style="font-family: courier new,monospace;" class="PY_STRING">'..'</span><span style="font-family: courier new,monospace;">,</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        re.sub(r</span><span style="font-family: courier new,monospace;" class="PY_STRING">'(?:\r\n|\n|\r(?!\n))'</span><span style="font-family: courier new,monospace;">, CRLF, data))</span><br>
</pre></pre><br>As you can see there are two regular expressions parsing the data. If you know that your message is formatted correctly to start with, then this step is unnecessary.<br><br>When receiving e-mail through <b>smtpd</b>, the SMTPChannel class inherits from <b>asynchat.async_chat</b>. The default recv buffer size for asynchat is 4K. This can be too much overhead for high data throughput. The buffer size can be increased with this code:<br>
<br><span style="font-family: courier new,monospace;">import asynchat</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">asynchat.async_chat.ac_in_buffer_size  = 1024*128</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">asynchat.async_chat.ac_out_buffer_size = 1024*128</span><br><br>The <b>smtpd.SMTP</b> class prcoesses data through the <b>smtpd.SMTPChannel</b> class. There are a lot of debug statements in the module, like so:<br>
<pre><pre><span style="font-family: courier new,monospace;" class="PY_KEYWORD">print</span><span style="font-family: courier new,monospace;"> >> DEBUGSTREAM, </span><span style="font-family: courier new,monospace;" class="PY_STRING">'Data:'</span><span style="font-family: courier new,monospace;">, repr(line)</span><br>
<br><span style="font-family: arial,helvetica,sans-serif;">By default, DEBUGSTREAM is a no-op, but that that doesn't prevent <span style="font-family: courier new,monospace;">repr(line)</span> from being called. When variable, <span style="font-family: courier new,monospace;">line</span>, contains a large email (multiple megabytes), <br>
this debug step will really kill performance. <br><br>Secondly, the method <b>found_terminator</b> will also perform expensive strings ops on the e-mail. Maybe its not possible to disable this step, in all cases,<br>but for testing performance, you can override the method like so:<br>
<br><span style="font-family: courier new,monospace;">class QuickSMTPChannel( smtpd.SMTPChannel, object):</span><span style="font-family: courier new,monospace;"></span><span style="font-family: courier new,monospace;"></span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">   def found_terminator(self):</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">      if (self._SMTPChannel__state == self.COMMAND or</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            self._SMTPChannel__state != self.DATA):</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">         super(QuickSMTPChannel,self).found_terminator()</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">      else:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">         data = smtpd.EMPTYSTRING.join(self._SMTPChannel__line)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">         self._SMTPChannel__line = []</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">         status = self._SMTPChannel__server.process_message(</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">               self._SMTPChannel__peer, self._SMTPChannel__mailfrom,</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">               self._SMTPChannel__rcpttos, data)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">         self._SMTPChannel__rcpttos = []</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">         self._SMTPChannel__mailfrom = None</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">         self._SMTPChannel__state = self.COMMAND</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">         self.set_terminator('\r\n')</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">         if not status:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">             self.push('250 Ok')</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">         else:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">             self.push(status</span><br></span><span style="font-family: arial,helvetica,sans-serif;"></span><br style="font-family: arial,helvetica,sans-serif;">
<span style="font-family: arial,helvetica,sans-serif;">Thanks,</span><br style="font-family: arial,helvetica,sans-serif;"><br style="font-family: arial,helvetica,sans-serif;"><span style="font-family: arial,helvetica,sans-serif;">- Casey</span><br>
<br><br style="font-family: arial,helvetica,sans-serif;"></pre></pre><br>