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
Improve mail sending by reduce MIME encoding process #1824
Comments
I get what you're trying to do here, however, the implementation you wrote has some fairly major issues - it represents the only time that PHPMailer would attempt to write to disk, preventing it from working in read-only environments. It would be better to keep the cache in memory, or better, make it pluggable with a PSR-6 instance. That said, it's not clear whether this brings any significant performance improvement since instead of reading a file once, it would need to read it twice and write it once. Do you have benchmarks to back it up? Your usage example in this ticket is already sub-optimal because it creates a new instance inside the loop, and there can be no benefit at all in the example file because it only sends one message - it also strikes me as unnecessary to add a complete example purely to add one line - if we're going to have a cache like this, it should probably be automatic and not require user code changes. Overall, what I'd really like to see to improve attachment handling is to remove the need to store attachments in memory at all - have them encoded dynamically using a generator, which would be faster and much more memory efficient. I suspect this would require a BC break and a PHP version bump, which I'm considering for future versions anyway. |
Hi Synchro, Thanks for your review. This solution could be running at Read-Only file system because using PHP array type to keep the MIME encoding result in memory.
Performance improvement:
I think using PHP array type to provide the key-value cache to reduce MIME encoding will reduce CPU usage. |
You have some very strange results there that are hard to explain - using a single PHPMailer instance means you can use keepalive which eliminates TLS handshakes and multiple SMTP commands, so you have to be doing something odd for that to be slower. Can you share your benchmark code? |
Hi Synchro, Code: https://github.com/changyy/PHPMailer/blob/master/examples/smtp_reduce_mime_encoding.phps Those processes are only on building a mail content.
|
I've tried this locally, changing three things
So the cache seems to make it go slower? |
Hi Synchro, This result is very interesting. :D My simple cache design is to speed up the processing Would you mind to take a try to get the same result with a larger image attachment? Image like 50KB, 100KB size. The EDM is usually with 100 KB~400 KB size. In my work, I need to send 100,000 emails with same Maybe I need to study PHPMailer more deeper to change my mind. :) Thanks for your review. |
$ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0600 0.1133 0.0553 0.0974 5.7kb 0.0700 0.1526 0.0598 0.1359 50kb 0.0786 0.1687 0.0592 0.1386 100kb 0.0897 0.1782 0.0619 0.1439 200kb 0.1159 0.2045 0.0662 0.1470 500kb 0.1930 0.2837 0.0707 0.1546
$ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0579 0.1069 0.0515 0.1021 5.7kb 0.0682 0.1526 0.0561 0.1862 50kb 0.0829 0.1543 0.0571 0.1555 100kb 0.0938 0.1646 0.0587 0.1469 200kb 0.1282 0.1895 0.0803 0.1567 500kb 0.1888 0.2793 0.0742 0.1686 Method1: Send mails with creating a new PHPMailer instance. Method2: Send mails with only one PHPMailer instance. Method3: Send mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore. Method4: Send mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore.
$ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0579 0.1069 0.0515 0.1021 5.7kb 0.0682 0.1526 0.0561 0.1862 50kb 0.0829 0.1543 0.0571 0.1555 100kb 0.0938 0.1646 0.0587 0.1469 200kb 0.1282 0.1895 0.0803 0.1567 500kb 0.1888 0.2793 0.0742 0.1686 Method1: Send mails with creating a new PHPMailer instance. Method2: Send mails with only one PHPMailer instance. Method3: Send mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore. Method4: Send mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore.
I found an improved way: Skip file_exists checking when using ReduceMIMEEncodeCacheStore designed. You may take a try by using this version code, https://github.com/changyy/PHPMailer/blob/master/src/PHPMailer.php, to run your benchmark. I also run the benchmark on multiple file size: https://github.com/changyy/PHPMailer/blob/master/examples/benchmark_reduce_mime_encoding.phps
Method1: Send mails with creating a new PHPMailer instance. I think the ReduceMIMEEncodeCacheStore solution will be faster when the total size of the attachments exceeds 100KB. |
I really recommend you use a PSR-6 or PSR-16 interface for this and allow users to inject a cache handler, even something simple like this, or any of these adapters. It will eliminate nearly all of your code and allow users to choose a cache implementation (e.g. disk / array / memcache / redis) without PHPMailer having to be concerned about the details. It might also allow the cache to be used for other things, perhaps punycoded addresses, for example. I do think it's strange that you're getting slower results when using a single instance - doing that runs much less code and uses less memory, so I don't know why it would be slower - an xdebug profile would show what's happening. Using the external array by reference is effectively equivalent to having an instance property that's reused across multiple sends (i.e. what a single instance would do). One minor thing - |
$ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0646 0.1114 0.0561 0.1054 5.7kb 0.0757 0.1610 0.0624 0.1452 50kb 0.0846 0.1856 0.0623 0.1469 100kb 0.0989 0.1862 0.0740 0.1496 200kb 0.0636 0.1117 0.0563 0.1058 500kb 0.2195 0.3179 0.0834 0.1745 Method1: Send mails with creating a new PHPMailer instance. Method2: Send mails with only one PHPMailer instance. Method3: Send mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore. Method4: Send mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore.
$ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0646 0.1114 0.0561 0.1054 5.7kb 0.0757 0.1610 0.0624 0.1452 50kb 0.0846 0.1856 0.0623 0.1469 100kb 0.0989 0.1862 0.0740 0.1496 200kb 0.0636 0.1117 0.0563 0.1058 500kb 0.2195 0.3179 0.0834 0.1745 Method1: Send mails with creating a new PHPMailer instance. Method2: Send mails with only one PHPMailer instance. Method3: Send mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore. Method4: Send mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore.
Using psr-16-simple-cache on MIMECache $ php composer require psr/simple-cache $ php benchmark_reduce_mime_encoding.phps ... Size Method1 Method2 Method3 Method4 1.8kb 0.0645 0.1205 0.0573 0.1064 5.7kb 0.0814 0.1740 0.0605 0.1509 50kb 0.0872 0.1698 0.0622 0.1473 100kb 0.1030 0.1839 0.0678 0.1496 200kb 0.1302 0.2208 0.0677 0.1567 500kb 0.2261 0.3194 0.0878 0.1730
Hi Synchro, I have implemented it on PSR-16 SimpleCache interface and change the field name from ReduceMIMEEncodeCacheStore to MIMECache. Something changed:
The benchmark:
|
Problem description
When you want to send small changes mail content to a lot of receivers one by one, the same attachments would be MIME encoded repeatedly.
It would cost CPU time/credit on AWS, so I try to propose a solution.
Code to reproduce
Look at sample code: https://github.com/PHPMailer/PHPMailer/blob/master/examples/smtp.phps
Use a while loop to reproduce situation:
Debug output
None
Solution:
Providing a MIME Encoding Cache Store to handle it.
The text was updated successfully, but these errors were encountered: