|
Hi guys,
I had a bit of fun this morning with one of our commercial sites, which is making me revisit performance. We know from the benchmarks that there are GSP related bottlenecks, with taglibs, GString usage etc. This particular live server currently serves an average of 22 home pages per second. The same bench mark just to serve the contact form (not submit it) is avg 0.6 per second. For the latter, you can see the CPU hit 100% for several seconds and gradually come back down - for a single request. This didn't seem so bad to me until I think about just say 5 or 10 people doing it at an overlapping time. The result is the server grinds to a halt. It feels to me like there may be a relatively simple "production only" bug happening. Either groovy is recompiling the entire GSP every time, or there is some serious problem with taglib performance. I'm going to run some little tests to see if I can establish where the problem is, and it looks like I will need to fork 0.4.2 If anybody else could look at this I'd appreciate it. Normal the performance of the content is really acceptable, its just forms that are very slow. For my forms this means a bit more groovy script logic - but hardly much - and lots of taglib usage (and GStrings and closures). I'm hoping its a production-mode bug where its recompiling the GSP every time because it thinks it has changed due to a Tomcat classpath quirk. 950,000 leaflets were dropped through peoples' letter boxes this week, mentioning our competition form.... Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
> If anybody else could look at this I'd appreciate it. Normal the
> performance of the content is really acceptable, its just forms that > are very slow. For my forms this means a bit more groovy script logic > - but hardly much - and lots of taglib usage (and GStrings and > closures). While i'm not suggesting you're wrong, it seems a bit suspicious that the display of forms would be an issue. I mean, as far as gsp / groovy / grails is concerned it's just putting together some text. saying <input type=... vs <div class=... is really no different. Me thinks it's maybe what's going on in the marshalling of data for the form. -- - kate = masukomi http://weblog.masukomi.org/ --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Hmm. I'm wondering if rewriting the offending tags in Java while you
analyze the cause of the performance hit might help, if you're concerned about the impact to your live site. Why do you think this is a "production-only" issue? You might try seeing what happens if you run a page which calls your tags 100s of times. Me, I haven't had time to check on the performance hit from usage of my own taglib/plugin, but I would have to imagine that it would be far worse than what you're using, as my taglib invokes templates which invoke tags which invoke other templates which may also invoke tags; and it's all written in Groovy. To me, at first thought, this indicates that it might be worthwhile for me to rewrite my tags in Java sometime. I wrote this taglib largely to make it a lot easier to write quick little intranet, personal website, and other low-traffic apps. But I was also thinking that one day I'd like to use this for something production-quality, and that I'd need to perf-tune the taglib when that day came along. - Daiji Marc Palmer wrote: > Hi guys, > > I had a bit of fun this morning with one of our commercial sites, > which is making me revisit performance. We know from the benchmarks > that there are GSP related bottlenecks, with taglibs, GString usage etc. > > This particular live server currently serves an average of 22 home > pages per second. The same bench mark just to serve the contact form > (not submit it) is avg 0.6 per second. For the latter, you can see the > CPU hit 100% for several seconds and gradually come back down - for a > single request. > > This didn't seem so bad to me until I think about just say 5 or 10 > people doing it at an overlapping time. The result is the server > grinds to a halt. > > It feels to me like there may be a relatively simple "production only" > bug happening. Either groovy is recompiling the entire GSP every time, > or there is some serious problem with taglib performance. > > I'm going to run some little tests to see if I can establish where the > problem is, and it looks like I will need to fork 0.4.2 > > If anybody else could look at this I'd appreciate it. Normal the > performance of the content is really acceptable, its just forms that > are very slow. For my forms this means a bit more groovy script logic > - but hardly much - and lots of taglib usage (and GStrings and closures). > > I'm hoping its a production-mode bug where its recompiling the GSP > every time because it thinks it has changed due to a Tomcat classpath > quirk. > > 950,000 leaflets were dropped through peoples' letter boxes this week, > mentioning our competition form.... > > Marc > > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > > > ____________________________________________________________________________________ We won't tell. Get more on shows you hate to love (and love to hate): Yahoo! TV's Guilty Pleasures list. http://tv.yahoo.com/collections/265 --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Can we see the source for these forms? At least the lazy one (contact)?
On 4/4/07, Marc Palmer <[hidden email]> wrote:
Hi guys, |
|
In reply to this post by Marc Palmer Local
I think he's not suggesting that it's forms at fault, but rather the
taglib invocation (which most people have a lot less of in non-form views today). kate rhodes wrote: >> If anybody else could look at this I'd appreciate it. Normal the >> performance of the content is really acceptable, its just forms that >> are very slow. For my forms this means a bit more groovy script logic >> - but hardly much - and lots of taglib usage (and GStrings and >> closures). > > While i'm not suggesting you're wrong, it seems a bit suspicious that > the display of forms would be an issue. I mean, as far as gsp / groovy > / grails is concerned it's just putting together some text. saying > <input type=... vs <div class=... is really no different. > > Me thinks it's maybe what's going on in the marshalling of data for > the form. > ____________________________________________________________________________________ No need to miss a message. Get email on-the-go with Yahoo! Mail for Mobile. Get started. http://mobile.yahoo.com/mail --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
On 4 Apr 2007, at 15:08, D T wrote: > I think he's not suggesting that it's forms at fault, but rather the > taglib invocation (which most people have a lot less of in non-form > views today). > We already established with our benchmarks that GSP/taglibs/Gstrings are a peformance issue - i.e. directly rendered output from a controller is -much- faster. These forms use taglibs that call other tags, do a bit of closure magic etc for ModelTagLib. I'm adding some logging to my local grails 0.4.2 source to find out where in the request stage the problem is. ...but the amount of time being taken and % CPU used smacks of groovy recompile every time. Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Bizarrely, no sooner did you mention these problems than I started
getting them! And this after what seemed like someone had performance tuned my Grails app yesterday. Very annoying how it can be so variable. So my problem sounds very similar: when rendering a form (actually an Ajax snippet containing a form): CPU rocketed and stayed there! I finally located the cause: an infinite loop in one of my domain model getters. BUT: the getter is totally unrelated to the form contents yet was nonetheless being invoked during form rendering! How and why? I use g:select in a few places like: <g:select from="${domainObjectList}" optionKey="id" optionValue="name" value="${someObject.id}" "/> now when rendering the list of options in FormTagLib, we have this code: // create options from list if(from) { from.eachWithIndex { el,i -> def keyValue = null out << '<option ' // ... else { keyValue = el.properties[optionKey] out << 'value="' << keyValue << '" ' } // ... and a bit later if(optionValue instanceof Closure) { out << optionValue(el).toString().encodeAsHTML() } else { out << el.properties[optionValue].toString().encodeAsHTML() // ... Looks innocent enough, right? Well not quite. You see that <g:select from="${domainObjectList}" ... /> is a list of domain object instances. Now when you do "domainObject.properties[prop]" - as is done *twice* for each object in the from list - what actually happens is a call to Grails' SetPropertiesDynamicProperty#get() method, which returns a Map of properties keyed by name, therefore requiring a call to ALL of the getters! This is really inefficient for what should be a single property access, and is certainly not what people will want to or assume is happening. Changing it to el[whatever] works in my case. Will this work for all cases? Could there be other code like this? [...went away and double checked FormTagLib...] Correction: FormTagLib actually checks whether the object is a domain class for *one* of the above calls, so the SetPropertiesDynamicProperty#get() call only happens once for domain objects, but twice for most other object types. Marc Palmer wrote: > > On 4 Apr 2007, at 15:08, D T wrote: > >> I think he's not suggesting that it's forms at fault, but rather the >> taglib invocation (which most people have a lot less of in non-form >> views today). >> > > We already established with our benchmarks that GSP/taglibs/Gstrings > are a peformance issue - i.e. directly rendered output from a > controller is -much- faster. > > These forms use taglibs that call other tags, do a bit of closure > magic etc for ModelTagLib. > > I'm adding some logging to my local grails 0.4.2 source to find out > where in the request stage the problem is. > > ...but the amount of time being taken and % CPU used smacks of groovy > recompile every time. > > Marc > > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
On 4 Apr 2007, at 16:02, Marc Palmer wrote: > > On 4 Apr 2007, at 15:08, D T wrote: > >> I think he's not suggesting that it's forms at fault, but rather the >> taglib invocation (which most people have a lot less of in non-form >> views today). >> > > We already established with our benchmarks that GSP/taglibs/ > Gstrings are a peformance issue - i.e. directly rendered output > from a controller is -much- faster. > > These forms use taglibs that call other tags, do a bit of closure > magic etc for ModelTagLib. > > I'm adding some logging to my local grails 0.4.2 source to find out > where in the request stage the problem is. > > ...but the amount of time being taken and % CPU used smacks of > groovy recompile every time. OK well this I can replicate locally. It takes around 2s after first page load on my local Mac or on the lice server, in production mode with debug logging on. This tallies with the benchmarking that shows circa 0.6 req/s. I'm now breaking down the request time using the log. Action execution and processing is lightning fast, even with an interceptor that retrieves user info based on a cookie. The bottleneck looks like taglibs/GStrings currently. Some of our tags can be slow to execute, although it varies over time. Perhaps unsurprisingly writing out the list of countries in a select tag, and the years for a datePicker is taking some time (we are still talking nanos here though). However the results are difficult to make sense of, as parts that look slow in one run are fine in another. It's looking like concurrency is an issue. More when I work it out... Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Update... its taglibs, content size, or just ModelTagLib: Contact form with 17 g:modelXXX tags in: $ ab -c 5 -n 1000 http://localhost:8080/Code/contact [snip] Document Path: /Code/contact Document Length: 26904 bytes Concurrency Level: 5 Time taken for tests: 735.090 seconds Complete requests: 1000 Failed requests: 0 Broken pipe errors: 0 Total transferred: 27276772 bytes HTML transferred: 26904000 bytes Requests per second: 1.36 [#/sec] (mean) Time per request: 3675.45 [ms] (mean) Time per request: 735.09 [ms] (mean, across all concurrent requests) Transfer rate: 37.11 [Kbytes/sec] received Connnection Times (ms) min mean[+/-sd] median max Connect: 0 1 1.8 0 17 Processing: 876 3666 982.3 3580 7152 Waiting: 876 3666 982.3 3580 7152 Total: 876 3666 982.3 3581 7152 Ouch! Remove only the g:modelXXXX tags: $ ab -c 5 -n 1000 http://localhost:8080/Code/contact [snip] Document Path: /Code/contact Document Length: 6434 bytes Concurrency Level: 5 Time taken for tests: 44.597 seconds Complete requests: 1000 Failed requests: 0 Broken pipe errors: 0 Total transferred: 6828775 bytes HTML transferred: 6434000 bytes Requests per second: 22.42 [#/sec] (mean) Time per request: 222.99 [ms] (mean) Time per request: 44.60 [ms] (mean, across all concurrent requests) Transfer rate: 153.12 [Kbytes/sec] received Connnection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 62 222 104.1 221 791 Waiting: 62 222 104.1 221 791 Total: 62 222 104.1 221 791 Now that's better :) I just need find the slow bits now... Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Maurice Nicholson old
On 4 Apr 2007, at 17:48, Maurice Nicholson wrote: > Bizarrely, no sooner did you mention these problems than I started > getting them! And this after what seemed like someone had > performance tuned my Grails app yesterday. Very annoying how it can > be so variable. > > So my problem sounds very similar: when rendering a form (actually > an Ajax snippet containing a form): CPU rocketed and stayed there! > > I finally located the cause: an infinite loop in one of my domain > model getters. BUT: the getter is totally unrelated to the form > contents yet was nonetheless being invoked during form rendering! > > How and why? > > Looks innocent enough, right? Well not quite. You see that > <g:select from="${domainObjectList}" ... /> is a list of domain > object instances. Now when you do "domainObject.properties[prop]" - > as is done *twice* for each object in the from list - what actually > happens is a call to Grails' SetPropertiesDynamicProperty#get() > method, which returns a Map of properties keyed by name, therefore > requiring a call to ALL of the getters! > This is dailyWTF material. I think I may have seen it, commented on it in the source, or even perpetrated it. Fisheye "annotated" view will reveal all. However I think this is there for a reason - to NOT get an exception if the property does not exist. > This is really inefficient for what should be a single property > access, and is certainly not what people will want to or assume is > happening. Changing it to el[whatever] works in my case. Will this > work for all cases? Sure, there must be a better way. > Could there be other code like this? > Very possibly, remember we are pre-optimisation :) > [...went away and double checked FormTagLib...] > > Correction: FormTagLib actually checks whether the object is a > domain class for *one* of the above calls, so the > SetPropertiesDynamicProperty#get() call only happens once for > domain objects, but twice for most other object types. Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Yeah, it _could_ be a real Groovy performance gotcha and one to watch out for in future.
I can see how it's useful to prevent an exception, but in this case the "optionKey" and "optionValue" property names are provided by the user, so if those proterties don't exist it's a programmer error and they should know about it sooner than later.
|
|
Some graphs for your consideration.
These were done on our real app this afternoon. The form that's brought up in the Worst Case Scenario section are pretty much the most complex form you would ever expect to see on a web page (We're replicating a paper form) it's big, it's ugly, it spans 4 or 5 tables and loads a lot of objects. Also to note that this was running under J9 NOT the Sun JVM because the Sun JVM doesn't run under PPC Linux. But, as far as we can tell, once you get past the hurdle of actually making a grails app run under J9 (I'll post the details on that particular evil shortly) it seems to be fine. JMeter was running on a separate box with a gigabit connection to a PPC blade server on the same lan which was hosting our app and nothing else (2.5 gigs of ram i forget the processor but it's a new blade). This is a staging version of the app so no-one else was hitting it. I apologize but the following link will probably load quite slowly due to the many large uncompressed screenshots. http://www.masukomi.org/misc/grails/index.html -- - kate = masukomi http://weblog.masukomi.org/ --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Maurice Nicholson old
On 4 Apr 2007, at 22:02, Maurice Nicholson wrote: > > Yeah, it _could_ be a real Groovy performance gotcha and one to > watch out for > in future. > > I can see how it's useful to prevent an exception, but in this case > the > "optionKey" and "optionValue" property names are provided by the > user, so if > those proterties don't exist it's a programmer error and they > should know > about it sooner than later. Actually that is a potential issue but we need to look at the wider implications. I think your fix will die for collections of mixed objects where there may or may not be a property with that name. A change was made here in 0.4 for a reason if I recall. Anyway, I'm still VERY bored stripping down taglibs and perf testing to see what happens. Basically > 90% of the time is related to a single countrySelect (from my i18N taglib) box and a datePicker. These are just complex tags that do a lot of cout writing in little bits, and looping. I don't think closures are to blame, and I don't think it's GStrings. It's just... groovy - or problems in the "out" writer. I notice that this page is quite large in html form terms - over 15KB. I saw a hardcoded 8k buffer array in the GSP code earlier, so this could be a symptom of premature flushing, but I doubt it. The common thread here is g:select and g:datePicker. My country tag just wraps up g:select. g:modelDate wraps up datePicker. If I change the inner loop of g:select to just: out << el instead of all the other conditional stuff, it comes up to 14reqs/s... a bit better than 0.6 eh? Shame we lose all the functionality of the tag then! Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I have not debugged yet whether it is a naïve implementation of HTML encoding or potential ExpandoMetaClass overheads. Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
So this evening I ran a quick test using straight access to the tags,
just executing the same block of stuff on a single page hundreds of times. I ran this with localeSelect, which calls select with a fairly large set of options. It's also a slower tag than dateSelect, from my measurements. First I switched select to not call encodeAsHTML() upon the results of the optionValue closure and the page performed around 30% faster. I then switched select to call org.springframework.web.util.HtmlUtils.htmlEscape() directly. Bad news. It ran just as fast. It seems that Expando is Ex-Slow-Mo. So Marc, I'd suggest replacing all encodeAsHtml() calls with direct calls to the htmlEscape() for now. But something should probably be done about Expando's royal slowwwwwness. - Daiji Marc Palmer wrote: > > On 4 Apr 2007, at 22:02, Maurice Nicholson wrote: > >> >> Yeah, it _could_ be a real Groovy performance gotcha and one to watch >> out for >> in future. >> >> I can see how it's useful to prevent an exception, but in this case the >> "optionKey" and "optionValue" property names are provided by the >> user, so if >> those proterties don't exist it's a programmer error and they should >> know >> about it sooner than later. > > Actually that is a potential issue but we need to look at the wider > implications. I think your fix will die for collections of mixed > objects where there may or may not be a property with that name. A > change was made here in 0.4 for a reason if I recall. > > Anyway, I'm still VERY bored stripping down taglibs and perf testing > to see what happens. > > Basically > 90% of the time is related to a single countrySelect (from > my i18N taglib) box and a datePicker. These are just complex tags that > do a lot of cout writing in little bits, and looping. > > I don't think closures are to blame, and I don't think it's GStrings. > It's just... groovy - or problems in the "out" writer. I notice that > this page is quite large in html form terms - over 15KB. I saw a > hardcoded 8k buffer array in the GSP code earlier, so this could be a > symptom of premature flushing, but I doubt it. > > The common thread here is g:select and g:datePicker. My country tag > just wraps up g:select. g:modelDate wraps up datePicker. If I change > the inner loop of g:select to just: out << el instead of all the other > conditional stuff, it comes up to 14reqs/s... a bit better than 0.6 > eh? Shame we lose all the functionality of the tag then! > > Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I > have not debugged yet whether it is a naïve implementation of HTML > encoding or potential ExpandoMetaClass overheads. > > Marc > > > > > > > > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > > > ____________________________________________________________________________________ No need to miss a message. Get email on-the-go with Yahoo! Mail for Mobile. Get started. http://mobile.yahoo.com/mail --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Actually, on second thought, what I'd like to ascertain is whether it's
ExpandoMetaClass or the Codecs plugin that's the root cause. I don't know quite enough about that stuff to make a test of that very rapidly; maybe someone else who does can do it? Having said that, I took a quick peek at org.codehaus.groovy.grails.plugins.CodecsGrailsPlugin.groovy anyway and I have a few questions about how these codecs were implemented: 1) Why are these dynamic methods (encodeAs/decodeAs) written in Groovy (instead of Java)? 2) Why does the dynamic method check whether encodeMethod is defined at runtime (rather than when the plugin defines the dynamic method)- every single time that it's called? 3) Why is HTMLCodec written in Groovy (even if it is just a wrapper)? These questions apply to 0.4.x as well as the 0.5 codebase. Now it might be true that this plugin doesn't actually contribute to the slowdown witnessed by .encodeAsHTML(). So I don't want to jump to conclusions about that; and a proper analysis of the performance of Expando and the Codecs plugin is probably appropriate. - Daiji D T wrote: > So this evening I ran a quick test using straight access to the tags, > just executing the same block of stuff on a single page hundreds of times. > > I ran this with localeSelect, which calls select with a fairly large set > of options. It's also a slower tag than dateSelect, from my measurements. > > First I switched select to not call encodeAsHTML() upon the results of > the optionValue closure and the page performed around 30% faster. > > I then switched select to call > org.springframework.web.util.HtmlUtils.htmlEscape() directly. > > Bad news. It ran just as fast. > > It seems that Expando is Ex-Slow-Mo. > > So Marc, I'd suggest replacing all encodeAsHtml() calls with direct > calls to the htmlEscape() for now. But something should probably be > done about Expando's royal slowwwwwness. > > - Daiji > > Marc Palmer wrote: > >> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote: >> >> >>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch >>> out for >>> in future. >>> >>> I can see how it's useful to prevent an exception, but in this case the >>> "optionKey" and "optionValue" property names are provided by the >>> user, so if >>> those proterties don't exist it's a programmer error and they should >>> know >>> about it sooner than later. >>> >> Actually that is a potential issue but we need to look at the wider >> implications. I think your fix will die for collections of mixed >> objects where there may or may not be a property with that name. A >> change was made here in 0.4 for a reason if I recall. >> >> Anyway, I'm still VERY bored stripping down taglibs and perf testing >> to see what happens. >> >> Basically > 90% of the time is related to a single countrySelect (from >> my i18N taglib) box and a datePicker. These are just complex tags that >> do a lot of cout writing in little bits, and looping. >> >> I don't think closures are to blame, and I don't think it's GStrings. >> It's just... groovy - or problems in the "out" writer. I notice that >> this page is quite large in html form terms - over 15KB. I saw a >> hardcoded 8k buffer array in the GSP code earlier, so this could be a >> symptom of premature flushing, but I doubt it. >> >> The common thread here is g:select and g:datePicker. My country tag >> just wraps up g:select. g:modelDate wraps up datePicker. If I change >> the inner loop of g:select to just: out << el instead of all the other >> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6 >> eh? Shame we lose all the functionality of the tag then! >> >> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I >> have not debugged yet whether it is a naïve implementation of HTML >> encoding or potential ExpandoMetaClass overheads. >> >> Marc >> >> >> >> >> >> >> >> >> --------------------------------------------------------------------- >> To unsubscribe from this list please visit: >> >> http://xircles.codehaus.org/manage_email >> >> >> >> >> > > > > > ____________________________________________________________________________________ > No need to miss a message. Get email on-the-go > with Yahoo! Mail for Mobile. Get started. > http://mobile.yahoo.com/mail > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > > > > ____________________________________________________________________________________ Bored stiff? Loosen up... Download and play hundreds of games for free on Yahoo! Games. http://games.yahoo.com/games/front --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Ok, another efficiency point I just found with regard to these selects.
out.println or out << with mixed control logic is slower than out.println of a GString. I was able to speed up dateSelect a bit (20-30%) by changing every block of the style: out << "<option value=\"${m}\" " if(minute == i) out << "selected=\"selected\"" out << '>' << m << '</option>' out.println() to: out.println "<option value=\"${m}\" ${(i == minute) ? 'selected' : ''}>${m}</option}" which is uglier... but it makes sense because then you're having fewer Groovy method invocations. There's code like this all over FormTagLib. This isn't the multiple-times improvement you saw by eliminating select altogether, but it does speed up things by 30%. Calling taglibs indirectly also has overhead... but as far as I can tell, it's not as bad as the issues of out and encodeAsHtml() I found here. Anyway, I hope my findings here are useful for you (and others). Best, - Daiji D T wrote: > Actually, on second thought, what I'd like to ascertain is whether it's > ExpandoMetaClass or the Codecs plugin that's the root cause. I don't > know quite enough about that stuff to make a test of that very rapidly; > maybe someone else who does can do it? > > Having said that, I took a quick peek at > org.codehaus.groovy.grails.plugins.CodecsGrailsPlugin.groovy anyway and > I have a few questions about how these codecs were implemented: > > 1) Why are these dynamic methods (encodeAs/decodeAs) written in Groovy > (instead of Java)? > 2) Why does the dynamic method check whether encodeMethod is defined at > runtime (rather than when the plugin defines the dynamic method)- every > single time that it's called? > 3) Why is HTMLCodec written in Groovy (even if it is just a wrapper)? > > These questions apply to 0.4.x as well as the 0.5 codebase. > > Now it might be true that this plugin doesn't actually contribute to the > slowdown witnessed by .encodeAsHTML(). So I don't want to jump to > conclusions about that; and a proper analysis of the performance of > Expando and the Codecs plugin is probably appropriate. > > - Daiji > > D T wrote: > >> So this evening I ran a quick test using straight access to the tags, >> just executing the same block of stuff on a single page hundreds of times. >> >> I ran this with localeSelect, which calls select with a fairly large set >> of options. It's also a slower tag than dateSelect, from my measurements. >> >> First I switched select to not call encodeAsHTML() upon the results of >> the optionValue closure and the page performed around 30% faster. >> >> I then switched select to call >> org.springframework.web.util.HtmlUtils.htmlEscape() directly. >> >> Bad news. It ran just as fast. >> >> It seems that Expando is Ex-Slow-Mo. >> >> So Marc, I'd suggest replacing all encodeAsHtml() calls with direct >> calls to the htmlEscape() for now. But something should probably be >> done about Expando's royal slowwwwwness. >> >> - Daiji >> >> Marc Palmer wrote: >> >> >>> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote: >>> >>> >>> >>>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch >>>> out for >>>> in future. >>>> >>>> I can see how it's useful to prevent an exception, but in this case the >>>> "optionKey" and "optionValue" property names are provided by the >>>> user, so if >>>> those proterties don't exist it's a programmer error and they should >>>> know >>>> about it sooner than later. >>>> >>>> >>> Actually that is a potential issue but we need to look at the wider >>> implications. I think your fix will die for collections of mixed >>> objects where there may or may not be a property with that name. A >>> change was made here in 0.4 for a reason if I recall. >>> >>> Anyway, I'm still VERY bored stripping down taglibs and perf testing >>> to see what happens. >>> >>> Basically > 90% of the time is related to a single countrySelect (from >>> my i18N taglib) box and a datePicker. These are just complex tags that >>> do a lot of cout writing in little bits, and looping. >>> >>> I don't think closures are to blame, and I don't think it's GStrings. >>> It's just... groovy - or problems in the "out" writer. I notice that >>> this page is quite large in html form terms - over 15KB. I saw a >>> hardcoded 8k buffer array in the GSP code earlier, so this could be a >>> symptom of premature flushing, but I doubt it. >>> >>> The common thread here is g:select and g:datePicker. My country tag >>> just wraps up g:select. g:modelDate wraps up datePicker. If I change >>> the inner loop of g:select to just: out << el instead of all the other >>> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6 >>> eh? Shame we lose all the functionality of the tag then! >>> >>> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I >>> have not debugged yet whether it is a naïve implementation of HTML >>> encoding or potential ExpandoMetaClass overheads. >>> >>> Marc >>> >>> >>> >>> >>> >>> >>> >>> >>> --------------------------------------------------------------------- >>> To unsubscribe from this list please visit: >>> >>> http://xircles.codehaus.org/manage_email >>> >>> >>> >>> >>> >>> >> >> >> ____________________________________________________________________________________ >> No need to miss a message. Get email on-the-go >> with Yahoo! Mail for Mobile. Get started. >> http://mobile.yahoo.com/mail >> >> --------------------------------------------------------------------- >> To unsubscribe from this list please visit: >> >> http://xircles.codehaus.org/manage_email >> >> >> >> >> >> > > > > > ____________________________________________________________________________________ > Bored stiff? Loosen up... > Download and play hundreds of games for free on Yahoo! Games. > http://games.yahoo.com/games/front > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > > > > ____________________________________________________________________________________ Finding fabulous fares is fun. Let Yahoo! FareChase search your favorite travel sites to find flight and hotel bargains. http://farechase.yahoo.com/promo-generic-14795097 --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Marc Palmer wrote: > > On 4 Apr 2007, at 22:02, Maurice Nicholson wrote: > >> >> Yeah, it _could_ be a real Groovy performance gotcha and one to watch >> out for >> in future. >> >> I can see how it's useful to prevent an exception, but in this case the >> "optionKey" and "optionValue" property names are provided by the >> user, so if >> those proterties don't exist it's a programmer error and they should >> know >> about it sooner than later. > > Actually that is a potential issue but we need to look at the wider > implications. I think your fix will die for collections of mixed > objects where there may or may not be a property with that name. [snip] > Which the programmer should know about and is why I'm advocating the potential-exception-throwing property access here. Otherwise the output could be "undefined", as they like to say in C textbooks. > Anyway, I'm still VERY bored stripping down taglibs and perf testing > to see what happens. > > Basically > 90% of the time is related to a single countrySelect (from > my i18N taglib) box and a datePicker. These are just complex tags that > do a lot of cout writing in little bits, and looping. > > I don't think closures are to blame, and I don't think it's GStrings. > It's just... groovy - or problems in the "out" writer. I notice that > this page is quite large in html form terms - over 15KB. I saw a > hardcoded 8k buffer array in the GSP code earlier, so this could be a > symptom of premature flushing, but I doubt it. > > The common thread here is g:select and g:datePicker. My country tag > just wraps up g:select. g:modelDate wraps up datePicker. If I change > the inner loop of g:select to just: out << el instead of all the other > conditional stuff, it comes up to 14reqs/s... a bit better than 0.6 > eh? Shame we lose all the functionality of the tag then! > Have you tried my fix to see if that helps? In my case I had domain objects with several derived property getters - not normal instance attributes - which were computationally expensive. Of course I followed the getXxx naming convention for Java/Groovy bean convenience elsewhere, but I may rename some of these now so they aren't assumed to be "properties". > Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I > have not debugged yet whether it is a naïve implementation of HTML > encoding or potential ExpandoMetaClass overheads. > > Marc > > > > > > > > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
On 5 Apr 2007, at 10:47, Maurice Nicholson wrote: >> Anyway, I'm still VERY bored stripping down taglibs and perf >> testing to see what happens. >> >> Basically > 90% of the time is related to a single countrySelect >> (from my i18N taglib) box and a datePicker. These are just complex >> tags that do a lot of cout writing in little bits, and looping. >> >> I don't think closures are to blame, and I don't think it's >> GStrings. It's just... groovy - or problems in the "out" writer. I >> notice that this page is quite large in html form terms - over >> 15KB. I saw a hardcoded 8k buffer array in the GSP code earlier, >> so this could be a symptom of premature flushing, but I doubt it. >> >> The common thread here is g:select and g:datePicker. My country >> tag just wraps up g:select. g:modelDate wraps up datePicker. If I >> change the inner loop of g:select to just: out << el instead of >> all the other conditional stuff, it comes up to 14reqs/s... a bit >> better than 0.6 eh? Shame we lose all the functionality of the tag >> then! >> > > Have you tried my fix to see if that helps? > It doesn't. In my usage the el.properties[x] is never called as I am passing in an optionValue closure. > In my case I had domain objects with several derived property > getters - not normal instance attributes - which were > computationally expensive. Of course I followed the getXxx naming > convention for Java/Groovy bean convenience elsewhere, but I may > rename some of these now so they aren't assumed to be "properties". Yes, but please raise an issue for this - we need to sort out the SetPropertiesDynamicMethod to not be dailyWTF fodder. Marc --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Marc Palmer Local
Marc,
Lemme know if the changes I suggested help. Here's my patched-up version of FormTagLib to eliminate part of the issues I mentioned. If you rename FormTagLib.groovy in the core plugin and stick this somewhere you'll get the revised tags. - Daiji Marc Palmer wrote: > > On 5 Apr 2007, at 10:47, Maurice Nicholson wrote: >>> Anyway, I'm still VERY bored stripping down taglibs and perf testing >>> to see what happens. >>> >>> Basically > 90% of the time is related to a single countrySelect >>> (from my i18N taglib) box and a datePicker. These are just complex >>> tags that do a lot of cout writing in little bits, and looping. >>> >>> I don't think closures are to blame, and I don't think it's >>> GStrings. It's just... groovy - or problems in the "out" writer. I >>> notice that this page is quite large in html form terms - over 15KB. >>> I saw a hardcoded 8k buffer array in the GSP code earlier, so this >>> could be a symptom of premature flushing, but I doubt it. >>> >>> The common thread here is g:select and g:datePicker. My country tag >>> just wraps up g:select. g:modelDate wraps up datePicker. If I change >>> the inner loop of g:select to just: out << el instead of all the >>> other conditional stuff, it comes up to 14reqs/s... a bit better >>> than 0.6 eh? Shame we lose all the functionality of the tag then! >>> >> >> Have you tried my fix to see if that helps? >> > > It doesn't. In my usage the el.properties[x] is never called as I am > passing in an optionValue closure. > >> In my case I had domain objects with several derived property getters >> - not normal instance attributes - which were computationally >> expensive. Of course I followed the getXxx naming convention for >> Java/Groovy bean convenience elsewhere, but I may rename some of >> these now so they aren't assumed to be "properties". > > Yes, but please raise an issue for this - we need to sort out the > SetPropertiesDynamicMethod to not be dailyWTF fodder. > > Marc > > > --------------------------------------------------------------------- > To unsubscribe from this list please visit: > > http://xircles.codehaus.org/manage_email > > > > ____________________________________________________________________________________ Need Mail bonding? Go to the Yahoo! Mail Q&A for great tips from Yahoo! Answers users. http://answers.yahoo.com/dir/?link=list&sid=396546091 /* Copyright 2004-2005 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.springframework.validation.Errors; import org.springframework.context.NoSuchMessageException; import org.springframework.web.servlet.support.RequestContextUtils as RCU; import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU; import org.springframework.web.util.HtmlUtils /** * A tag lib that provides tags for working with form controls * * @author Graeme Rocher * @since 17-Jan-2006 */ class PatchedFormTagLib { def out // to facilitate testing /** * Creates a new text field */ def textField = { attrs -> attrs.type = "text" attrs.tagName = "textField" field(attrs) } /** * Creates a hidden field */ def hiddenField = { attrs -> attrs.type = "hidden" attrs.tagName = "hiddenField" field(attrs) } /** * Creates a submit button */ def submitButton = { attrs -> attrs.type = "submit" attrs.tagName = "submitButton" field(attrs) } /** * A general tag for creating fields */ def field = { attrs -> resolveAttributes( attrs) out << "<input type=\"${attrs.remove('type')}\" " outputAttributes(attrs) out << "/>" } /** * A general tag for creating textareas */ def textArea = { attrs -> resolveAttributes(attrs) // Pull out the value to use as content not attrib def value = attrs.remove( 'value') def escapeHtml = true if(attrs.escapeHtml) escapeHtml = Boolean.valueOf(attrs.remove('escapeHtml')) out << "<textarea " outputAttributes(attrs) out << ">" << (escapeHtml ? HtmlUtils.htmlEscape(value) : value) << "</textarea>" } /** * Check required attributes, set the id to name if no id supplied, extract bean values etc. */ void resolveAttributes(attrs) { if(!attrs.name && !attrs.field) { throwTagError("Tag [$tagName] is missing required attribute [name] or [field]") } attrs.remove('tagName') attrs.id = (!attrs.id ? attrs.name : attrs.id) def val = attrs.remove('bean') if(val) { if(attrs.name.indexOf('.')) attrs.name.split('\\.').each { val = val?."$it" } else { val = val[name] } attrs.value = val } attrs.value = (attrs.value ? attrs.value : "") } /** * Dump out attributes in HTML compliant fashion */ void outputAttributes(attrs) { attrs.each { k,v -> out << k << "=\"" << HtmlUtils.htmlEscape(v) << "\" " } } /** * General linking to controllers, actions etc. Examples: * * <g:form action="myaction">...</gr:form> * <g:form controller="myctrl" action="myaction">...</gr:form> */ def form = { attrs, body -> out << "<form action=\"" // create the link createLink(attrs) out << '\" ' // default to post if(!attrs['method']) { out << 'method="post" ' } // process remaining attributes outputAttributes(attrs) out << ">" // output the body body() // close tag out << "</form>" } /** * Creates a submit button that submits to an action in the controller specified by the form action * The value of the action attribute is translated into the action name, for example "Edit" becomes * "edit" or "List People" becomes "listPeople" * * <g:actionSubmit value="Edit" /> * */ def actionSubmit = { attrs -> out << '<input type="submit" name="_action" ' def value = attrs.remove('value') if(value) { out << "value=\"${value}\" " } // process remaining attributes outputAttributes(attrs) // close tag out << '/>' } /** * Creates a an image submit button that submits to an action in the controller specified by the form action * The value of the action attribute is translated into the action name, for example "Edit" becomes * "edit" or "List People" becomes "listPeople" * * <g:actionSubmitImage src="/images/submitButton.gif" action="Edit" /> * */ def actionSubmitImage = { attrs -> out << '<input type="image" name="_action" ' def value = attrs.remove('value') if(value) { out << "value=\"${value}\" " } def src = attrs.remove('src') if(src) { out << "src=\"${src}\" " } // process remaining attributes outputAttributes(attrs) // close tag out << '/>' } /** * A simple date picker that renders a date as selects * eg. <g:datePicker name="myDate" value="${new Date()}" /> */ def datePicker = { attrs -> def xdefault = attrs['default'] if (xdefault == null) { xdefault = new Date() } else if (xdefault != 'none') { xdefault = DateFormat.getInstance().parse(xdefault) } else { xdefault = null } def value = (attrs['value'] ? attrs['value'] : xdefault) def name = attrs['name'] def noSelection = attrs['noSelection'] if (noSelection != null) { noSelection = noSelection.entrySet().iterator().next() } def years = attrs['years'] // final PRECISION_RANKINGS = ["year":0, "month":10, "day":20, "hour":30, "minute":40] final PRECISION_RANKINGS = [year:0, month:10, day:20, hour:30, minute:40] def precision = (attrs['precision'] ? PRECISION_RANKINGS[attrs['precision']] : PRECISION_RANKINGS["minute"]) def day = 0 def month = 0 def year = 0 def hour = 0 def minute = 0 def dfs = new java.text.DateFormatSymbols(RCU.getLocale(request)) def c = null if(value instanceof Calendar) { c = value } else if (value != null) { c = new GregorianCalendar(); c.setTime(value) } if (c != null) { day = c.get(GregorianCalendar.DAY_OF_MONTH) month = c.get(GregorianCalendar.MONTH) year = c.get(GregorianCalendar.YEAR) hour = c.get(GregorianCalendar.HOUR_OF_DAY) minute = c.get(GregorianCalendar.MINUTE) } if (years == null) { def tempyear if (year == null) { // If no year, we need to get current year to setup a default range... ugly def tempc = new GregorianCalendar() tempc.setTime(new Date()) tempyear = tempc.get(GregorianCalendar.YEAR) } else { tempyear = year } years = (tempyear-100)..(tempyear+100) } out << "<input type=\"hidden\" name=\"${name}\" value=\"struct\" />" // create day select if (precision >= PRECISION_RANKINGS["day"]) { // if (precision >= 20) { out.println "<select name=\"${name}_day\">" if (noSelection) { renderNoSelectionOption( noSelection.key, noSelection.value, '') out.println() } for(i in 1..31) { out.println "<option value=\"${i}\" ${(i == day) ? 'selected' : ''}>${i}</option}" /* out.println "<option value=\"${i}\"" if (i == day) { out.println " selected=\"selected\"" } out.println ">${i}</option>" out << "<option value=\"${i}\"" if (i == day) { out << " selected=\"selected\"" } out << ">${i}</option>" */ } out.println '</select>' } // create month select if (precision >= PRECISION_RANKINGS["month"]) { // if (precision >= 10) { out.println "<select name=\"${name}_month\">" if (noSelection) { renderNoSelectionOption( noSelection.key, noSelection.value, '') out.println() } dfs.months.eachWithIndex { m,i -> if(m) { out.println "<option value=\"${i + 1}\" ${(i == month) ? 'selected' : ''}>${m}</option}" /* def monthIndex = i + 1 out << "<option value=\"${monthIndex}\"" if(month == i) out << " selected=\"selected\"" out << '>' out << m out.println '</option>' */ } } out.println '</select>' } // create year select if (precision >= PRECISION_RANKINGS["year"]) { // if (precision >= 0) { out.println "<select name=\"${name}_year\">" if (noSelection) { renderNoSelectionOption( noSelection.key, noSelection.value, '') out.println() } for(i in years) { // for(i in 1907..2107) { out.println "<option value=\"${i}\" ${(i == year) ? 'selected' : ''}>${i}</option>" /* out.println "<option value=\"${i}\"" if (i == year) { out.println " selected=\"selected\"" } out.println ">${i}</option>" out << "<option value=\"${i}\"" if (i == year) { out << " selected=\"selected\"" } out << ">${i}</option>" */ } out.println '</select>' } // do hour select if (precision >= PRECISION_RANKINGS["hour"]) { out.println "<select name=\"${name}_hour\">" if (noSelection) { renderNoSelectionOption( noSelection.key, noSelection.value, '') out.println() } for(i in 0..23) { // def h = '' + i // if(i < 10) h = '0' + h def h if(i < 10) { h = '0' + i } else { h = i } out.println "<option value=\"${h}\" ${(i == hour) ? 'selected' : ''}>${h}</option}" /* out << "<option value=\"${h}\" " if(hour == i) out << "selected=\"selected\"" out << '>' << h << '</option>' out.println() */ } out.println '</select> :' // If we're rendering the hour, but not the minutes, then display the minutes as 00 in read-only format if (precision < PRECISION_RANKINGS["minute"]) { // if (precision < 40) { out.println '00' } } // do minute select if (precision >= PRECISION_RANKINGS["minute"]) { // if (precision >= 40) { out.println "<select name=\"${name}_minute\">" if (noSelection) { renderNoSelectionOption( noSelection.key, noSelection.value, '') out.println() } for(i in 0..59) { def m if(i < 10) { m = '0' + i } else { m = i } out.println "<option value=\"${m}\" ${(i == minute) ? 'selected' : ''}>${m}</option}" /* out << "<option value=\"${m}\" " if(minute == i) out << "selected=\"selected\"" out << '>' << m << '</option>' out.println() */ } out.println '</select>' } } def renderNoSelectionOption = { noSelectionKey, noSelectionValue, value -> // If a label for the '--Please choose--' first item is supplied, write it out out.println "<option value=\"${(noSelectionKey == null ? "" : noSelectionKey)}\" ${(noSelectionKey == value) ? 'selected' : ''}>${HtmlUtils.htmlEscape(noSelectionValue)}</option}" /* out << '<option value="' << (noSelectionKey == null ? "" : noSelectionKey) << '"' if(noSelectionKey == value) { out << ' selected="selected" ' } out << '>' << HtmlUtils.htmlEscape(noSelectionValue) << '</option>' */ } /** * A helper tag for creating TimeZone selects * eg. <g:timeZoneSelect name="myTimeZone" value="${tz}" /> */ def timeZoneSelect = { attrs -> attrs['from'] = TimeZone.getAvailableIDs(); attrs['value'] = (attrs['value'] ? attrs['value'].ID : TimeZone.getDefault().ID ) // set the option value as a closure that formats the TimeZone for display attrs['optionValue'] = { TimeZone tz = TimeZone.getTimeZone(it); def shortName = tz.getDisplayName(tz.inDaylightTime(date),TimeZone.SHORT); def longName = tz.getDisplayName(tz.inDaylightTime(date),TimeZone.LONG); def offset = tz.rawOffset; def hour = offset / (60*60*1000); def min = Math.abs(offset / (60*1000)) % 60; return "${shortName}, ${longName} ${hour}:${min}" } // use generic select select( attrs ) } /** * A helper tag for creating locale selects * * eg. <g:localeSelect name="myLocale" value="${locale}" /> */ def localeSelect = {attrs -> attrs['from'] = Locale.getAvailableLocales() attrs['value'] = (attrs['value'] ? attrs['value'] : RCU.getLocale(request) ) // set the key as a closure that formats the locale attrs['optionKey'] = { "${it.language}_${it.country}" } // set the option value as a closure that formats the locale for display attrs['optionValue'] = { "${it.language}, ${it.country}, ${it.displayName}" } // use generic select select( attrs ) } /** * A helper tag for creating currency selects * * eg. <g:currencySelect name="myCurrency" value="${currency}" /> */ def currencySelect = { attrs, body -> if(!attrs['from']) { attrs['from'] = ['EUR', 'XCD','USD','XOF','NOK','AUD','XAF','NZD','MAD','DKK','GBP','CHF','XPF','ILS','ROL','TRL'] } try { def currency = (attrs['value'] ? attrs['value'] : Currency.getInstance( RCU.getLocale(request) )) attrs.value = currency.currencyCode } catch(IllegalArgumentException iae) { attrs.value = null } // invoke generic select select( attrs ) } /** * A helper tag for creating HTML selects * * Examples: * <g:select name="user.age" from="${18..65}" value="${age}" /> * <g:select name="user.company.id" from="${Company.list()}" value="${user?.company.id}" optionKey="id" /> */ def select = { attrs -> def from = attrs.remove('from') def keys = attrs.remove('keys') def optionKey = attrs.remove('optionKey') def optionValue = attrs.remove('optionValue') def value = attrs.remove('value') def noSelection = attrs.remove('noSelection') if (noSelection != null) { noSelection = noSelection.entrySet().iterator().next() } out << "<select name=\"${attrs.remove('name')}\" " // process remaining attributes outputAttributes(attrs) out << '>' out.println() if (noSelection) { renderNoSelectionOption(noSelection.key, noSelection.value, value) out.println() } // create options from list if(from) { from.eachWithIndex { el,i -> out << '<option ' if(keys) { out << 'value="' << keys[i] << '" ' if(keys[i] == value) { out << 'selected="selected" ' } } else if(optionKey) { def keyValue = null if(optionKey instanceof Closure) { keyValue = optionKey(el) out << 'value="' << keyValue << '" ' } else if(el !=null && optionKey == 'id' && grailsApplication.getGrailsDomainClass(el.getClass().name)) { keyValue = el.ident() out << 'value="' << keyValue << '" ' } else { keyValue = el.properties[optionKey] out << 'value="' << keyValue << '" ' } if(keyValue == value) { out << 'selected="selected" ' } } else { out << "value=\"${el}\" " if(el == value) { out << 'selected="selected" ' } } out << '>' if(optionValue) { if(optionValue instanceof Closure) { out << HtmlUtils.htmlEscape(optionValue(el).toString()) } else { out << HtmlUtils.htmlEscape(el.properties[optionValue].toString()) } } else { def s = el.toString() if(s) out << HtmlUtils.htmlEscape(s) } out << '</option>' out.println() } } // close tag out << '</select>' } /** * A helper tag for creating checkboxes **/ def checkBox = { attrs -> def value = attrs.remove('value') def name = attrs.remove('name') if(!value) value = false out << '<input type="hidden" ' out << "name=\"_${name}\" />" out << '<input type="checkbox" ' out << "name=\"${name}\" " if(value) { out << 'checked="checked" ' } out << "value=\"true\" " // process remaining attributes outputAttributes(attrs) // close the tag, with no body out << ' />' } /** * A helper tag for creating radio buttons */ def radio = { attrs -> def value = attrs.remove('value') def name = attrs.remove('name') def checked = (attrs.remove('checked') ? true : false) out << '<input type="radio" ' out << "name=\"${name}\" " if(checked) { out << 'checked="checked" ' } out << "value=\"${HtmlUtils.htmlEscape(value.toString())}\" " // process remaining attributes outputAttributes(attrs) // close the tag, with no body out << ' ></input>' } } --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
> It feels to me like there may be a relatively simple "production
> only" bug happening Can you confirm if this only happening in Tomcat and not in Jetty? Are you testing production in Tomcat. DT optimizations are writing to 'out' less times. I am wondering if the Writer is blocking. The reason I feel this is you say that you are getting the performance hit at different times, and different places. 2 seconds is a very long time compared .6 seconds even if the code is not optimized. The quick thing to check also is that you are running java with the server option -- just to check that it is not garbage collection, getting in the way of your tests. Or even setting the gc collection off for the test. Could you send me the headers that your page is generating, or tell me if the server is returning the CONTENT-LENGTH and the CHUNKED header.. The reason for looking at these headers is to see if the Server is Writing out at the same time that you are Writing to the writer. In the past I have seen many issues with Threads and Output streams. If it is the writer blocking there is code I can look at that I wrote in the past that should get around this problem. I was going to write this later for our site, but if this is causing issues now. I will write an implementation now, and post it to the mailing list. James On 4/5/07, D T <[hidden email]> wrote: Marc, |
| Powered by Nabble | Edit this page |
