Professional Documents
Culture Documents
Applications Performance
An Oracle JD Edwards EnterpriseOne
Performance and Tuning Paper
November 2009
PURPOSE STATEMENT
This document is intended for information purposes only, and may not be incorporated
into any contract. It is not a commitment to deliver any material, code, or functionality,
and should not be relied upon in making purchasing decisions. The development,
release, and timing of any features or functionality described for Oracle’s products
remains at the sole discretion of Oracle. Updates and enhancements are provided in
accordance with Oracle’s Technical Support Policies at:
www.oracle.com/support/collateral/oracle-technical-support-policies.pdf
The topics are mostly application related, but there are some tools and
operating system material covered.
Studying the user’s manuals for circular saws, nail guns, and a host of other
tools does not prepare one to build a house. A list of the “top ten” household
electrical problems does not enable one to become an electrician. In the
former case, one needs to understand how to properly frame a house, how to
install plumbing, electricity, sheet rock, etc. In the latter case, the
fundamental principles of household wiring need to be studied and mastered.
Similarly, a “top ten” list of common performance problems does not prepare
one to analyze performance. Handing a tool to developers is NOT
tantamount to preparing them for performance analysis.
The purpose of the “top ten” lists is to give structure and specific direction
to the performance analysis process for people who already understand it.
It gives starting points to which the theoretical process can be practically
applied. It can allow one to short circuit a more lengthy analysis by
leveraging problems that others have already solved.
Yet a further analogy of the performance analysis process: A flu virus and a
bacterial infection have many of the same symptoms…but you would not
treat both with antibiotics. Rather - you conduct tests to determine which
sickness is actually present. THEN, you consult a list of medications to treat
that particular illness. The “medication” in this parable would be a SAR or a
code fix; the profiling data should point to the application of a code fix, not a
superficial set of symptoms.
While a detailed and rigorous treatment of the process is beyond the scope
of this paper, here is a brief summary of the salient truths of the performance
analysis process. Collectively they should form a reasonably complete picture
of performance analysis to the experienced EOne software developer.
Static Analysis of source code files is NOT performance analysis. While
certain classes of “low hanging fruit” opportunities can be preemptively
spotted in this manner, there is simply no way of accurately predicting
where time is spent without running the code and capturing a runtime
profile. Merely reading the code WILL NOT WORK.
Identify and define the problem – You can’t find a solution if you are
“If you don't know where you're going,
guessing at what the problem truly is. You will end up with a solution to a
chances are you will end up somewhere
non-existent problem.
else.”
- Yogi Berra Use a TOP DOWN analysis method – This means that you start at the
Application level, where the problem actually manifests itself. Then – you
work backwards from there, determining where the time is being spent.
Do not take wild guesses at the solution - … by applying SARs and ESUs
which may be time consuming and difficult to apply, but in which you have
only minimal confidence as a potential solution to the problem.
Profile the code – This answers the question “Where is the time being spent?”
Make proper use of logs – EOne debug logs can be generated on every
platform and provide the profiling data. The Performance Workbench tools
should be used.
There is a red paper on UBE performance on My Oracle Support (Doc ID
748333.1):
It is critical to generate debug This paper has detailed instructions on how to properly generate debug logs
logs properly which can be used for performance analysis and code profiling. It is critical
to follow these instructions precisely and accurately in order to generate
debug logs which produce suitable results and valid profiles.
Use Performance Workbench – It is available on My Oracle Support (Doc
ID 747328.1), and is a standard tool for EOne performance analysis with a
long track record of success in solving problems. Latest version available
here:
https://support.us.oracle.com/oip/faces/secure/km/DocumentDisplay.jspx?id=
747328.1
Always look at the jde.log first. Always use jde.logs – Look at them FIRST, before any other more complex
analysis, and before collecting large and time consuming debug logs. There
may be easy and obvious answers in the jde.log. In any case, any anomalies
in the jde.log should be explained or fixed before proceeding.
Do NOT Undersample – A two minute run of a four hour UBE will NOT
“The power of statistical analysis depends on
provide an accurate profile of the UBE. A code profile is a collection of
sample size: the larger the pile of data the analyst
has to work with, the more confidently he can draw
statistics. Performance analysis is at its heart a statistical exercise, and all
specific conclusions about it. A right handed statistics are only as good as the sample size.
hitter who has gone two for ten against left
Do not run E1 code on a fat client – Bad for many reasons. A lengthy
handed pitching cannot as reliably be predicted
to hit .200 against lefties as a hitter who has
section on this follows…
gone 200 for 1000.”
Your solutions should be driven by analyzing and finding where the time is
going, not by taking a solution from a top ten list. The “top ten” list merely
- Micheal Lewis, Moneyball
streamlines the analysis process. It does not replace it.
• From a blind reading of a “top ten” list, you would enter a SAR to get
Buffered INSERTs applied to the code. This may entail complex
recoding and/or refactoring, which would take time and testing.
You should use the principles not as guessing or rolling the dice, but more
like “Counting cards” in blackjack. If you have knowledge about a piece of
code based on First Principles and profiling, then the lists and anecdotes
(such as those in this paper) provide a means of systematic “guessing” at a
solution. You will be applying a solution which has worked in the past for
other situations which fit a similar statistical profile.
People continue to commonly capture UBE logs on the client for a number of
reasons:
• Buffered INSERTs
• JDB cache APIs: JDB_AddTableToDBCache()
• Transaction Processing
These factors alone are a showstopper for many UBEs…these are very
basic pieces of functionality which have a huge impact on UBE
performance.
EONe client side code is In a recent scan of EOne Tools source code, the IAMASERVER
fundamentally different preprocessor directive appears the following numbers of times:
than server side code.
• UBE system code: 68 places
The point is that the EOne client side code stream is different in many
and sundried ways from the server. Externally things may look the same,
but “under the hood” there are very different mechanisms involved.
2. All database I/O will now be issued across the network…rather than
close to the data, as on a server
• This disrupts the code profile by making I/O appear much slower
and less efficient than if the UBE were run on the server
• It takes much longer to run the desired data selection and/or use
case and get the desired profile …so both the I/O AND the code
itself is much slower.
• The UBE can consume all the resources on the client machine
6. Debug logs are cluttered with fat client / win32 / interactive GUI
code, since UBEs run as a thread.
• You will NOT get the neat, beginning to end profile containing
only UBE code.
Or not….
8. Since UBEs run as a thread in the fat client, they share resources
with interactive applications, such as JDB cache settings, OCM
mappings etc…
• This can lead to unintended side effects which are unique to the
client
9. The operating system is entirely different on a fat client than on any
of the server platforms.
But what about Named Event Rules? Does the Named Event Rules
(“NER”) mechanism provided by EOne provide a means to deliver the best of
both worlds: namely, the performance of compiled C code, while only
requiring knowledge of the user-friendly ER GUI interface?
The short answer is that NER mechanism works well for simple pieces of
Event Rules code, but for more complex logic, a hand-coded C function is
usually a better choice. Here are the upshots:
• Named Event Rules code (NER) is machine generated C-code
• Note that there is nothing inherently “slow” about NER code versus
hand-coded C business functions……they are both complied C
functions.
Many pieces of important The following are examples of key mechanisms not available or exposed via
EOne functionality which can the ER interface:
be leveraged in C Business
• JDE Cache APIs
Functions are not available in
ER. • Linked Lists
• Arrays
Additionally, many EOne system APIs have a more limited set of parameters
when called via the ER interface than are available in their “C” counterparts.
Finally, the machine generated NER C code accomplishes certain tasks less
efficiently than would be possible in hand-coded C.
• Population of large data structures is one example. The NER
mechanism churns out code which uses loops and APIs, rather than
efficient direct assignments. The latter would be possible in hand-
coded C Business Functions.
While NER C-code is easily generated from the ER, it cannot be fine tuned
for performance at the C code level.
You should be absolutely sure the performance problem is really and truly
being caused by a long running SELECT or SELECTs. IF THE TIME IS
NOT BEING SPENT IN SPECIFIC IDENTIFIABLE SELECT STATEMENTS
– THEN FORGET ADDING INDEXES….
An iSeries DBMON trace or the iSeries job logs may contain many index
IF THE TIME IS NOT BEING
SPENT IN SPECIFIC
recommendations….but many may only reduce a single 20ms query down to
IDENTIFIABLE SELECT 10ms, for example.
STATEMENTS – THEN FORGET
On the other hand – these are examples of reasons why you WOULD add a new
ADDING INDEXES….
index:
• Reducing a 50ms query down to 5ms, if said query gets called a million
times…..
• If the above cases had a significant impact on the runtime or response times
processes involved.
• If the newly added index gets used as per the second plan – and
reduces the runtime – then add that index
Then – document the new index very clearly, including the reason it
was added. Otherwise, some overzealous DBA may want to delete it two
years from now…to save disk space.
An index is really a re-ordered • One trouble with indexes is that they use valuable disk
copy of the base table. space, and become almost impossible to remove. This is
. because no one ever seems to remember why a given index was
added…so no one is certain if an obscure application or use
case actually needs the index. For large tables such as F0911,
the indexes can be a very significant fraction of the disk space
used by the database. You MUST think of each index as being a
re-ordered copy of the base table.
Indexes create a penalty in • Another major concern is that while indexes on a table help
INSERT, UPDATE, and the performance of SELECT statements, they create a
DELETE operatons.
penalty on INSERTs, UPDATEs, and DELETEs. When rows
. are added, altered, or deleted from a table, maintenance has to
be done on all these indexes to add, alter, or delete all
references to these rows in the indexes. For very large tables,
this can be significant.
The iSeries DBMON results and/or job log messages should NEVER be
used blindly to add indexes. Very broad heuristics are used to generate
these suggestions, and need to be “sanity checked” very closely.
Note that just because a SELECT statement takes a long time to process -
that does NOT mean it needs an index. There are several other reasons why
a SELECT is slow and/or inefficient
The bottom line: You should always verify the need for the index by cause
and effect …. and first principles: “WHERE IS THE CODE SPENDING ITS
TIME”
Loop Invariants
The crux of this issue is: why do something many times which need only
be done once?
Do not repeatedly recalculate • Do not repeatedly recalculate values which do not change
values which do not change
• Calculate the value ONCE and reuse the result
.
• Recalculate only when the value changes
• This issue impacts customized EOne financial reports in particular
The solution was to move the call to DTDateTitle to the “Init” section of the
UBE and simply save the results for all further use. This resulted in the
function being called ONCE for the entire UBE, and the runtime of the UBE
being reduced by over 40%.
The code profile also showed significant time spent in the Business Function
PAPeriodActivity. Drilling down into this function, we see its time in turn
is spent in CalculatePeriodFiscalYearOffsets:
Looking at the data selection for this report (extracted from the debug log), it
is clear that one ONE single company is involved:
“Memory Leak” has become a catch-all term for any memory related
problem. BUT – it is NOT always an accurate description of the true problem.
Too often, it is used by those who “know enough to be dangerous”
A true memory leak is when heap memory has gone out of scope and has
not been freed by the programmer. Heap memory is dynamically allocated at
runtime by the process, and it is what causes the memory footprint of the
process to rise and fall at runtime
In C-code: at the lowest code level, pointers to heap memory get initiated by
malloc(), and released by free(). The JDE wrappers for these functions are:
jdeAlloc() / jdeFree(). Failure to call the free() API when memory
allocated by malloc() is no longer needed by the program results in a true
“leak”.
But just because the process memory has been depleted does NOT mean
there is a true memory leak…instead, it may be an issue of excessive
Memory Consumption.
When memory which is built up and used by the program and meets the
following criteria:
Note that this does NOT mean the situation is acceptable…just that it’s not a
leak. In this case, code changes or re-factoring may indeed be necessary to
reduce the memory footprint. The most common source of memory
consumption in EOne application code is the use of jdeCache APIs, which
are most commonly used for storing the contents of a database table in
memory. Details are in the jdeCache section below.
Just because an EOne job – Call Object Kernel, UBE, etc - has increased its
memory footprint after running for a period of time does NOT mean that there
is a memory leak…or even a problem! ANY E1 process will store many
values in memory: environment handles, system data, commonly used
tables. This consumes memory. The tradeoff is performance for memory
footprint: It is more efficient to retrieve values from memory than to re-fetch
them from the database or to recalculate them.
Transactional MBFs also store large amounts of data in the form of cache and
other handles & pointers, e.g.: F4211FSBeginDoc(),
F4211FSEditDoc(), etc
A memory leak will yield a memory profile which never stops increasing
“Ski Slope”
Possible Memory Leak –
Memory keeps increasing
“Mesa”
Memory Consumption –
Memory usage increases after
startup, but levels off or varies
around a rough steady state
Time
“Tools such as Visual Quantify give Finding the cause of leaks and consumption at the EOne application code
you tons of data – but JDE debug level does NOT usually require cumbersome 3rd party tools. The output from
logs give you information …. The these tools is often not for the faint of heart. They typically require re-
latter is highly underrated as a compiling with special switches and options; they often can’t be run against
means of solving problems” EOne release code
- “The Performance Whisperer”
The results are also easily misinterpreted…. the output will show possible
sources of leaks. The data has to be read and interpreted properly. The
upshot is that memory tools require some degree of skill to use properly.
.
Performance Workbench records several metrics at the beginning of the
Summary file output to assist in finding the most common sources of memory
leaks and memory consumption:
• JDB_InitBhvr() not matched by JDB_FreeBhvr()
• jdeCache operations:
A large number of calls to jdeCacheAdd()is the single biggest indication
of memory consumption
jdeCacheInit() not matched by jdeCacheTerminate() or
jdeCacheTerminateAll()
If the source of the leak / consumption issue is in EOne Tools code, it can be
more subtle, and likely to require 3rd party tools to track down. These issues
will fly under the radar of debug logs, since low-level Tools diagnostics are
not available in the EOne logs.
However, serious and disruptive problems truly originating at the EOne tools
code level are much less common. Most true memory problems which are
disruptive will be found at the application code level.
And – there are a very finite number of places in the EOne application code
which consume or leak large amounts of memory.
Note that the failure point in the code for a leak or consumption problem is
almost certainly NOT related to the cause of the problem. The first piece of
code which fails to allocate memory is a “drive by” victim; it is the shrapnel,
not the bomb.
The memory leak or consumption silently does its damage “behind the
scenes”…until another unrelated area of the code unfortunate enough to
require memory is eventually impacted. See the example below:
• Missing
JDB_CloseTable()/JDB_FreeBhvr()/jdeCacheTermina
te()
The short answer is that the “out of memory” condition is usually driven by a
per-process limit; the process has exceeded limits inherent to the operating
system. There is a fundamental cap on the amount of memory a process is
capable of addressing on a given operating system.
The answer is usually NOT to add more memory to the machine. The
per process limit puts a cap on how much physical memory an individual
process may use… regardless of how much memory is physically on the
machine in question
export LDR_CNTRL=MAXDATA=0x10000000
start_process
unset LDR_CNTRL
• However, the space from the other quadrants that would be reallocated
to private data space is originally allocated to shared memory. This may
have unintended consequences for shared memory, which is used
heavily by EOne server processes.
For Solaris, the amount of per process heap memory is dynamic up to the
4GB 32-bit limit, so there are no parameters to adjust.
The iSeries uses a different memory model from the other platforms.
It employs a model knows as Single-level storage, and does not have the
4GB limit. There is a much larger limit on addressable memory.
Buffered Inserts
SQL INSERTs can be costly operations when performed thousands of times.
Each operation is a network roundtrip to the database
Buffered INSERTs allow For UBEs which perform bulk-loading operations, JDB middleware provides
many INSERT operations to the Buffered INSERT functionality to improve performance. Many INSERT
be delivered to the database operations can be issued to the database with a single operation. This can
in a single operation. have a very significant impact on UBEs which spent a large percentage of
their time INSERTing rows.
.
It can be a fairly simple change to implement:
• Via C-code in an EONe Business Function, there is the
JDB_SetRequestOption() API
o Call this API once at the beginning of the UBE for the table in
question, most likely in the “Init” section. Then, Buffered
Inserts will be activated for the duration of the UBE process.
• Via Event Rules: there is a check-box option for buffered inserts in
ER Table I/O via the Open Table operation: “Advanced Options” :
The buffer is flushed to the database when one of the following occurs:
• JDB_CloseTable() is called against the table in question
OR
• Tables which contain BLOB records will NOT use Buffered Inserts.
Buffered INSERTs will simply be a “no-op”, even if enabled.
• Tables which contain POST INSERT Trigger functions will NOT use
Buffered Inserts – including:
F0018, F00150, F03B11, F05100, F05138, F060116, F08001, F08101,
F08102, F32943, F32944, F32945, F38111, F4211, F4818, F49631
This benefit does come at a memory price: jdeCache APIs are the single
biggest source of significant memory consumption in EONe applications.
There is significant memory overhead involved in the use of these APIs well
above and beyond the data being stored.
These APIs hard-code the number of cursors per cache to 100. Most
EOne application code only requires two or three cursors; almost none
require more than ten.
EVERY piece of new code in EVERY piece of code using 8.96 and beyond should use these new
tools release in 8.96 and cache APIs. Write any and all new code using these new cache APIs.
beyond should use these new You should be sure to have an understanding of how many cursors are
cache APIs. used – or really needed - by each specific cache.
If you use the new APIs, but hardcode a ’50’ in the cursor parameter, this
only decreases the memory consumption by a factor of 2, leaving far too
much memory reduction on the table. The new APIs will not provide any
memory reduction benefits - unless this ability to specify a greatly
reduced number of cursors is actually used.
This is the single biggest thing that can be done to reduce memory
consumption – and improve performance – across a large number of
UBEs and interactive apps.
• Loop Invariants
• Buffered Inserts
These items must be incorporated into the First Principles analysis process,
not applied in a vacuum. NEVER make a fix (or enter a SAR) which will
involve a great deal of time and effort without having evidence that it will
impact the time being spent in the application in question.
The process is part Art, part Science, IS NOT “Plug and Chug”, IS NOT
reading data from a report, matching it up to a set of axioms, and tossing it
over a wall.
Oracle Corporation
World Headquarters
500 Oracle Parkway
Redwood Shores, CA 94065
U.S.A.
Worldwide Inquiries:
Phone: +1.650.506.7000
Fax: +1.650.506.7200
oracle.com