In terms of the cache though, this what Struts and other MVCs would use to store compiled expressions for parameter setting (in the ParameterInterceptor and other places). So, I would assume the trade off could be well worth it considering the relatively low number of expressions in an application. Using a Map with locking per key would also reduce contention.

The only other way I could think of is to compile the expressions into classes and then load them into the ClassLoader and let that thing do the synchronization. As long as the name of the class could be uniquely constructed from the expression, this could also work.

As for the parsing strategy, which I always call linear parsing because I never go backwards, but sliding window is much cooler, that's how I had started writing it originally and then in favor of time and simplicity, changed it to a StringTokenizer. I'll look at changing it back and see what the difference is. Thanks for the help.

-bp


On Oct 12, 2008, at 9:23 PM, Chris Brock wrote:


Well, that's one way of implementing a cache. Even if you did do that, the
performance gain from compiling the expression is on the order of a
magnitude (or greater), so any contention in the hash lookup would be far worth it. However, there are other more efficient strategies that have been employed through the use of external code generation, and tying compiled
expressions directly to the instance of a JSP tag.

But we're talking about web-based stuff here. MVEL is used for a broad range of stuff, like actual scripting in Smooks and JBoss Drools. It's used for straight-up data binding in jBPM and JBoss ESB. It's used for some
custom UI stuff in Mule Galaxy, etc.

To you P.S.: MVEL works directly on the expression by using a sliding window
algorithm. Think of it like this.

0 { f }
1 { o }
2 { o }
3 { . }
4 { b }
5 { a }
6 { r }
7 { [ }
8 { 0 }
9 { ] }
MVEL takes the string as an array. It holds a start position, and a cursor
position.

When it starts parsing, it starts scanning until it finds the first
non-identifier character, which is '.'. At this point it does a capture. At this point: start=0; cursor=3; This represents the boundary of the first token. We then process the token by converting it to a String, marking the start position as cursor+1 and repeat. When we hit the next non- identifier which is '[' MVEL knows this is a index accessor and acts appropriately,
following the same principle.

MVEL does this inline with actual evaluation.  It works out to be
significantly more efficient than StringTokenizer and also allows you to
incrementally add complexity over time.

Unfortunately, it becomes increasing more difficult to maintain a design like this, especially as it evolves into more sophisticated constructs.

You're welcome to contact me directly in e-mail to discuss this, as I don't
want to pollute the Struts mailing list with non-Struts related talk.


Brian Pontarelli wrote:

Not sure I follow. If I compile an expression, how can I reuse the
compiled version? I would assume it would need to be in a cache where
the key is the expression String and the value is the compiled
version. Correct me if I'm wrong.

-bp

P.S. based on your knowledge, I'm wondering if JCatapult would perform
better if the expression wasn't divided into its pieces but rather
evaluated character by character until a boundary is hit, at which
time that portion is evaluated? I wouldn't think this would increase
performance drastically, but you would know much better than I.



On Oct 12, 2008, at 8:38 PM, Chris Brock wrote:


" I'd also be interested to hear a
good discussion about caching compiled MVEL expressions and whether or
not thread contention for the cache is an issue at all"

There is no contention in the cache.  MVEL returns self-contained,
stateless, evaluation trees (or bytecode via the JIT) that do not
require
synchronization or contention in multi-threaded scenarios.  The
payload
returned by the compiler is essentially stateless code, and there is
no
"cache" that is used such as reflection cache or otherwise as there
is in
things like Commons EL, or JEXL.  This is actually, from an
architectural
perspective what makes MVEL stand apart from these technologies.


Brian Pontarelli wrote:

Sure.  But OGNL will return similar results with 50 tests.  Yet
people have
run into performance problems. The issue is that you're not looking
at
performance in terms of resource contention, and in terms of
aggregate
resource usage.

I'd say that for web application expressions OGNL and MVEL are about
equal then. In fact, I've never wanted to replace OGNL for
performance
reasons. It was for primarily other reasons.


Say you have a page which contains 20 expressions.  And your pages
are
getting hit 15 times a second (a reality in some high traffic
sites).
That's 300 expressions running every second.  Now, in insolation
that's
probably chump change.  But as resource contention rises in these
situation,
the overall efficiency drops and resource usage is exaggerated as a
result.

I've worked with this level of traffic and higher and it is still not
an issue to be setting 20 values for 1ms per request.



You might in term start to find that what is only 0ms in an isolate closed-loop test (which is not a very good way to benchmark in Java,
by the
way) could very well be something that contributes to a significant
amount
of CPU time in systems with high load.

Probably not in this case though and the scale between 1 iteration
and
50 is decent testament to that. It the CPU was pinned it would be
more
linear.



Take these real benchmarks (from MVEL 1.2--which is old):

Test Name            : Deep Property
Expression           : foo.bar.name
Iterations           : 50000
Interpreted Results  :
(OGNL)               : 1955.20ms avg.  (mem delta: -790kb)
[1936,1949,1943,1994,1954]
(MVEL)               : 114.80ms avg.  (mem delta: -112kb)
[119,113,110,117,115]
Compiled Results     :
(OGNL Compiled)      : 92.80ms avg.  (mem delta: -580kb)
[92,92,92,92,96]
(MVEL Compiled)      : 1.80ms avg.  (mem delta: -18kb) [1,2,2,2,2]

Here's what I got for 50K on my box using MVEL and JCatapult side by
side:

MVEL 808ms
JCatapult 1200ms

MVEL had a hit for the first method call, but it was only 40ms.
Otherwise, they performed exactly the same for anything up to 50
iterations. MVEL often poked above 1ms for single iterations, while
JCatapult never did, but that's negligible for both. JCatapult is
definitely slower as the iterations go up.

I tossed in a thread test with 50 threads each running 50K iterations
and the averages were:

MVEL 8000ms
JCatapult 23000ms

However, under one test condition, MVEL never returned and caused a
load of 50 on my box. It was quite distressing, but it looked like
MVEL got into a bunch of infinite loops or something. I let it run at
a load of 50 for a while and then I had to kill it, but none of the
threads had finished yet.

I also did a 50 thread and 50 iteration test and the averages were
roughly:

MVEL 30ms
JCatapult 120ms

Except for the case above, MVEL definitely out-performs JCatapult.



... 50,000 iterations on MVEL interpreted in 114.80ms.  This is a
1000x more
iterations than your benchmark.  If I divide 114.8ms / 1000 ... I
get 0.1ms
(or what would otherwise be rounded down to 0ms). In OGNL's case, it
did 50
iterations in 1.95ms (or what would be measured as 1ms -- as these
time
measurements always round down because of the fact
currentTimeMillis()
returns the result in MS).

Although JCatapult is slower, I'd be careful with such math because
it
isn't always as linear as this.





You can talk about "good enough" all you want, but faster is always
better
when it comes to scale. :)

I know a lot about scale and this is not the only truth. In fact, for
what we are talking about, good enough should be just fine. Most
scale
problems occur because of bottlenecks and I doubt that our case of
web
applications and setting parameters is a bottleneck.

However, I'm definitely welcome to suggestions on improvements for my
quite simple expression evaluator. I'd also be interested to hear a
good discussion about caching compiled MVEL expressions and whether
or
not thread contention for the cache is an issue at all.
Unfortunately,
because JCatapult uses my concept of dynamic attributes quite
heavily,
it might be difficult to swap in MVEL without some tweaks to the type
conversion API. But I could look into it.


-bp



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



--
View this message in context:
http://www.nabble.com/MVEL--tp19867360p19948098.html
Sent from the Struts - Dev mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



--
View this message in context: 
http://www.nabble.com/MVEL--tp19867360p19948361.html
Sent from the Struts - Dev mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to