Category
xpages stackoverflow
More and more XPage developers are turning to stackoverflow to seek community assistance with questions they have about developing XPage applications. This ongoing series will provide additional commentary and insights into the answers I have provided on the site that members of the community have found to be useful.
Part 1: Why Java is faster than SSJS
David Leedy asked,
"Is there a performance hit in SSJS when using @Functions?"
If I want to parse a text field in SSJS there are 2 main tools. The built in JavaScript code and the newly converted @Functions. Are the @Functions slower then using pure javascript? Or is there no real difference?
viewScope.put("length", tmpStr.length)
vs.
viewScope.put("length:, @Length(tmpStr))
The short version of the answer is: yes, but barely.
@Functions in SSJS are not true @Functions the way we're used to thinking of them. In Notes client apps and non-XPage Domino web apps, @Functions are part of Notes Formula, a limited scope, highly optimized macro language. Execution of these functions occurs very "close to the metal", so to speak, so event code written in this language will execute very rapidly (assuming no other factors are at play, such as network lag when executing functions that trigger network transactions, logical flaws in the code that cause unnecessary complexity, and so on).
In SSJS, however, @Functions are simply... more SSJS. They're implemented in Java, but in order for that Java to run, the XPage runtime first needs to find the correct code to execute.
When an XPage application is built (in the Eclipse project sense of the term "build"... in other words, compiled), a Java class is generated behind the scenes by Designer. This Java class is stored inside a design note within the application. Hence, when the end user accesses an XPage design element, Domino isn't looking at the XML; it's running the Java class that was generated by Designer. The XML is just an instruction set that tells Designer what Java to generate. This provides a runtime performance benefit compared to, for example, the way browsers work: HTML markup is downloaded from the request target, the markup is parsed, and then the browser decides what to do in real time based on the result of parsing that markup. The advance compilation step undertaken by Designer allows the XPage runtime engine to follow a far more efficient process.
Practically every XPage contains at least one instance of
Expression Language (EL). When viewing the XML source, you can easily spot instances of EL because it uses a recognizable syntax:
[load|dynamic]{[optional colon-separated prefix][expression]}
Here are a couple quick examples:
${database.title}
#{sessionScope.userTheme}
#{javascript:return tmpStr.length;}
#{javascript:return @Length(tmpStr);}
These expressions are occasionally also referred to as "bindings", because they are supported in two contexts: value bindings and method bindings. Method bindings are typically only used to define the code that should be invoked when an event is triggered; all other EL expressions are treated as value bindings. This simply means that the value of the component property that is bound to one (or more) of these expressions is not hard-coded. Some (usually, all) of the property value is calculated contextually at runtime.
Two portions of the EL syntax always have a direct impact on performance. The first is the initial character, which will either be a $ or a #. A $ indicates that the expression will only be evaluated once; expressions preceded by a # will be evaluated repeatedly. For Notes / Domino veterans, this distinction bears some loose conceptual similarities to the distinction between computed fields and computed-when-composed. In practice, each component property that can be assigned a value binding (and very few cannot) either does have a value binding or it has a "local value". Whenever any of these properties is accessed, the "getter" checks the local value first; if that value is not null, the value is immediately returned. Otherwise, it looks for a value binding associated with that property. If one exists, it returns the result of the evaluation of that value binding. Finally, if the property has neither a local value nor a value binding, null is returned.
Paul Withers posted a
detailed explanation of the difference between the actual inner workings of $ / # value bindings. To sum up that explanation:
- When a component is created, all properties defined on the XPage for that component are set immediately.
- If the value is hardcoded, that value is simply passed directly to the property.
- If, instead, it is a value binding, it follows one of two paths:
- If a # binding, a ValueBinding object is created and associated with the property.
- If a $ binding, the expression is evaluated immediately, and the resulting value is passed directly to the property.
That final bullet is why $ expressions are only evaluated once: by the time the component has been added to the tree, the property
no longer has a value binding. It has a local value that is the
result of evaluating a value binding. In case you're curious, this is also the reason why $ bindings are so sensitive to timing and sequence. # bindings aren't evaluated until the associated property is accessed; $ bindings are evaluted when the component is
created. Hence, you can't refer in the property of a component at the top of the page to properties of a component at the bottom of the page: the component to which the value binding refers doesn't even exist at the time the value binding is evaluated.
The second portion of the EL syntax that always impacts performance is the optional prefix. The prefix tells the EL resolver how to interpret the expression. If you ever hear an XPager referring to "standard EL", they're most likely referring to expressions with no prefix; SSJS expressions are still EL. But they use the optional "javascript" prefix to tell the resolver to use a custom process to interpret the expression. This is where the performance differential occurs.
Standard EL packs a lot of punch into a very finite syntax, but that syntax
is very finite. The string parsing required to determine what an expression "means" can be performed very efficiently. JavaScript, on the other hand, is an extremely complex language. Not only is it the only language in ubiquitous use today that supports the unusual pattern of closures, but like most programming languages, the same character can mean very different things based on its contextual position. One of the easiest examples of this to understand is the use of parentheses: they might indicate invocation of a function, or they might indicate custom order of operation; it's all about the context. If you're not in the habit of including
semicolons and curly braces in places where the JavaScript spec indicates they're technically optional, even
whitespace has meaning. There's even a scenario where whitespace can cause disasters even if all the semis and curlies are present, and the result is the difference between returning a fully defined object and returning a null pointer. Parsing all of this into something that runs as intended is a Herculean task, one typically only implemented by the various browser vendors.
To add insult to injury, regardless of the type of EL expression, its evaluation (or invocation) must all occur in Java. Again, that's fairly easy to do when parsing standard EL. For instance, variables are evaluated against the request scope, which essentially consists solely of doing an inexpensive get against a single Map. Once the pointer has been resolved, the XPage knows what Java object is involved, and its methods can be directly invoked. Babysitting variable scope in JavaScript is not nearly as straightforward. Similarly, all operations performed against each variable must be handled via equivalent Java operations.
XPages accomplish this language-to-language transition using what is known as an
abstract syntax tree (AST). This creates an in-memory, language-independent description of the structure of the code. It establishes a distinction between constructs like literals (hardcoded strings, numbers, etc.), identifiers (variables), operators (+, -, *, /, etc.), functions, and other concepts that might exist in any programming language. For instance, the notion of scope is far less tangible than the notion of an operator, but is crucial to keep track of (and, in this case, tricky, partly because Java does not yet have native support for closure). Once the JavaScript has been converted to this language-independent representation, each portion of the tree can be paired up with a corresponding Java construct, and the expression can finally be evaluated.
In a sense, this means that, every time SSJS is evaluated, the XPage runtime is essentially "guessing" what Java we want to invoke. Admittedly, it's a pretty good guess... frankly, I remain a bit astounded at how faithfully IBM adhered to the JavaScript specification, and doing so means that you're not often going to have results that differ wildly from what you'd experience running the same JavaScript expressions within a browser (aside from platform-specific differences, of course: Firefox doesn't define a global "database" variable, and SSJS doesn't natively know what "window" means). But it's expensive to maintain that faithful adherence, not just in terms of product cycle, but, again, every time a page evaluates SSJS.
This entire process is actually very easy to understand just by thinking in terms of spoken language. Imagine you've hired an employee, but every instruction you provide them and every question you ask them must be delivered through an interpreter, because you don't know their language, and they don't know yours. At all. Every time you say anything, even "yes" or "no", your employee does not understand until the interpreter has repeated what you said in their native language. If your interpreter is very skilled, they will be able to convey precisely what you mean, because they'll know which figures of speech have a specific cultural connotation and cannot simply be translated word-for-word. But every time you use some colloquialism that doesn't directly translate, extra care must be exercized to ensure your employee doesn't end up doing something drastically different from what you intended. And no matter how simple each unit of communication, it takes at least a little bit longer because it has to be repeated in a different language.
The moral of the story, then, is that when you write your code directly in Java, your XPages don't have to "guess" what Java should be executed... it just executes yours. Otherwise it's translating the code you wrote into a completely different language before it can run, and your user has to wait for that to happen. That wait is typically measured in milliseconds per expression, but add enough expressions and enough users, and they'll notice a difference.