[Zope-dev] ZPT Optimization Opportunity
Niels Mache, struktur AG
mache at struktur.de
Sun Sep 26 04:01:31 EDT 2004
Tres Seaver wrote:
> After seeing Chris McDonough's excellent paper at the Plone conference
> on optimizing content delivery using the new IStreamIterator iterface, I
> began experimenting with implementing such an optimization in
> ZopePageTemplates.
>
> I played around with having 'ZopePageTemplate._exec' request that it
> receive an iterator, instead of the usual big string. I hoped that such
> a change might enable greater concurrency and memory footprint, by
> avoiding creation of the big string at all; instead, medusa to push
> out the "chunk stream" represented by the StringIO buflist, while the
> appserver would be free to handle a new request without needing to
> malloc / copy the data.
>
> Here are the timings I have seen so far, using 'zopectl debug', with the
> following template:
>
> --------------- Template source --------------------------------
> <html>
> <body>
> <div tal:repeat="item python:[x for x in range(1000)]"
> tal:content="item">ITEM</div>
> </body>
> </html>
> ----------------------------------------------------------------
>
> --------------- Before the patch -------------------------------
>
>>>> Zope.debug('/test_iter', t=1)
>
> 250.2 milliseconds
>
>>>> Zope.debug('/test_iter', t=1)
>
> 106.7 milliseconds
>
>>>> Zope.debug('/test_iter', t=1)
>
> 106.5 milliseconds
>
>>>> Zope.debug('/test_iter', t=1)
>
> 124.6 milliseconds
> ----------------------------------------------------------------
>
> --------------- After the patch --------------------------------
> >>> Zope.debug('/test_iter', t=1)
> 249.2 milliseconds
> >>> Zope.debug('/test_iter', t=1)
> 107.2 milliseconds
> >>> Zope.debug('/test_iter', t=1)
> 125.0 milliseconds
> >>> Zope.debug('/test_iter', t=1)
> 162.1 milliseconds
> ----------------------------------------------------------------
>
> Given that the performance looks similar in this context, which doesn't
> benefit from the medusa / concurrency intent of the patch, it seems as
> though it might be a win (of *course* there aren't any tests for it!)
>
> I am attaching the patch I have so far for review and comment.
>
> Tres.
>
>
> ------------------------------------------------------------------------
>
> This patch causes ZopePageTemplates to return an IStreamIterator
> when called (published), potentially allowing medusa to return the
> same response payload (via the iterator) without needing to have the
> appserver thread join it into a big string.
>
> --- PageTemplate.py 2004-09-25 21:39:50.595938847 -0400
> +++ PageTemplate.py.new 2004-09-25 21:39:31.983326567 -0400
> @@ -83,7 +84,7 @@
> c['root'] = self
> return c
>
> - def pt_render(self, source=0, extra_context={}):
> + def pt_render(self, source=0, extra_context={}, iter_handler=None):
> """Render this Page Template"""
> if not self._v_cooked:
> self._cook()
> @@ -100,7 +101,10 @@
> getEngine().getContext(c),
> output,
> tal=not source, strictinsert=0)()
> - return output.getvalue()
> + if iter_handler is not None:
> + return iter_handler(output)
> + else:
> + return output.getvalue()
>
> def __call__(self, *args, **kwargs):
> if not kwargs.has_key('args'):
> --- ZopePageTemplate.py 2004-09-25 21:37:07.956803011 -0400
> +++ ZopePageTemplate.py.new 2004-09-25 21:37:23.024870003 -0400
> @@ -34,6 +34,8 @@
> from OFS.Cache import Cacheable
> from OFS.Traversable import Traversable
> from OFS.PropertyManager import PropertyManager
> +from ZPublisher.Iterators import IStreamIterator
> +
> from PageTemplate import PageTemplate
> from Expressions import SecureModuleImporter
> from PageTemplateFile import PageTemplateFile
> @@ -253,7 +255,8 @@
> # Execute the template in a new security context.
> security.addContext(self)
> try:
> - result = self.pt_render(extra_context=bound_names)
> + result = self.pt_render(extra_context=bound_names,
> + iter_handler=StringIOIterator)
> if keyset is not None:
> # Store the result in the cache.
> self.ZCacheable_set(result, keywords=keyset)
> @@ -331,6 +334,26 @@
> setattr(ZopePageTemplate, 'source.xml', ZopePageTemplate.source_dot_xml)
> setattr(ZopePageTemplate, 'source.html', ZopePageTemplate.source_dot_xml)
>
> +
> +class StringIOIterator:
> + """ Adapt a StringIO object to IStreamIterator.
> + """
> +
> + __implements__ = (IStreamIterator,)
> +
> + def __init__(self, stringio):
> + self._buflist = stringio.buflist
> + self._index = 0
> +
> + def next(self):
> +
> + if self._index >= len(self._buflist):
> + raise StopIteration
> +
> + data, self._index = self._buflist[self._index], self._index + 1
> +
> + return data
> +
> # Product registration and Add support
> manage_addPageTemplateForm = PageTemplateFile(
> 'www/ptAdd', globals(), __name__='manage_addPageTemplateForm')
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> Zope-Dev maillist - Zope-Dev at zope.org
> http://mail.zope.org/mailman/listinfo/zope-dev
> ** No cross posts or HTML encoding! **
> (Related lists -
> http://mail.zope.org/mailman/listinfo/zope-announce
> http://mail.zope.org/mailman/listinfo/zope )
More information about the Zope-Dev
mailing list