Presentation is loading. Please wait.

Presentation is loading. Please wait.

Stephen Ferguson Sr. Training Program Manager

Similar presentations


Presentation on theme: "Stephen Ferguson Sr. Training Program Manager"— Presentation transcript:

1 Stephen Ferguson Sr. Training Program Manager
Done-04: Designing for Performance: These are a Few of My Favorite Tricks Stephen Ferguson Sr. Training Program Manager

2 Agenda Overview Top 10 Tips Checking Performance Q & A
Done-04 Best Practices: Designing for Performance

3 What is GOOD performance?
A matter of perception Establish a baseline Critical operations Ignore occasional, little-used programs Good performance is a requirement Never an after thought The goal is good performance This presentation looks at some design techniques and some programming techniques to aid good performance. This presentation does not cover database or network performance tuning. The first thing to ask ourselves is what is GOOD performance? This is not an easy question to answer - performance is subjective, a matter of perception, and based on many factors. But we all know when an application performs badly. How many people think their application is too fast? Not many! The truth is we’re always trying to make applications run faster, and in this presentation we will look at a bunch of techniques and tools to make your application run faster, and to diagnose where there may be performance problems. It is best to establishing baselines on important aspects of the system and to remove the subjective aspects. Start by establishing a baseline in order to be able to quantify changes in performance from changes in load or application. The list of critical operations needs to be complete, but not have so many items on the list that it increases the amount of work needed to establish reliable baselines. Be realistic too – there is no point aiming to shave two minutes off a three hour batch job! Consider having multiple lists / baselines - baselines—one for daytime processing and one for end-of-day or batch processing. The baseline should include: • Tasks that are done many times throughout the day (creating orders, process control, data entry tasks). • Tasks that are important to users (such as Web site queries, customer support screens, and order entry tasks). And should not include: • Periodic tasks (such as monthly and weekly reports). • Little-used portions of the application. • Reporting, which generally falls into the above two categories and can often be scheduled outside of your primary operating hours. Performance tuning is not a one-off event, it is an ongoing activity repeated over time as things change. It is therefore important that applications be designed and programmed in a way that considers performance as a requirement in order that the need for ongoing tuning in the future is reduced. What is good performance? Using the available resources efficiently Environment: Network, Disk, Memory, CPU Affected by: Changing user counts, data, activity, traffic Application design Programming techniques Done-04 Best Practices: Designing for Performance

4 Cost of tuning an application
Phase Design Develop Test Deploy One more thought before proceeding – here’s the benefit of designing and developing for performance: Take the time to design your application efficiently and it will result in cost savings over the entire life of the application. It is far more expensive in terms of time, cost and resources to modify application code once the application is in production than if you take time to plan for performance at the design stage. Done-04 Best Practices: Designing for Performance

5 Agenda Overview Top 10 Tips Checking Performance Q & A
The tips are in no particular order of effectiveness – they are all effective. Done-04 Best Practices: Designing for Performance

6 Tip#1 Know The Rules Understand index selection rules Confirm
Indexing Performance Understand index selection rules Single index Multiple index Bracketing Confirm XREF INDEX-INFORMATION attribute Cost of indexes Bracketing – using indexes to retrieve a subset of records based on WHERE, OF, USING clauses. This presentation is not going to look at the index selection rules in detail as it would take the rest of the presentation to do so. But it is important that the rules are understood for index selection. Understand the rules – check the documentation, and the Knowledgebase Solutions on this subject (Solution ID and 21098). Make efficient use if index bracketing - having selected one or more indexes to satisfy a query, Progress tries immediately to isolate the smallest necessary index subset so as to return as few records as possible. Careful query design can increase the opportunities for bracketing, thereby preventing Progress from scanning entire indexes and examining all records. But indexes have a cost – they take up disk space, they have to be updated when created or deleted, possibly updated. And of course there is the cost of performing index rebuilds as required. Done-04 Best Practices: Designing for Performance

7 Tip#2 Know Your indexes Only maintain the indexes you need
Indexing Performance Only maintain the indexes you need Multi-component indexes are good Unique indexes faster than non-unique ROWID access is very fast Avoiding unneeded indexes Another danger is simply defining too many indexes on a table. You should define an index for a table when you have most or all of these requirements: *The index greatly reduces the amount of data to search to locate needed records. Avoid indexes on small numbers of distinct values. *The index is needed frequently. If only one occasionally used procedure needs some unusual selection, it is probably not worth defining an index just for that case. *Fast performance is essential for the procedure that uses the index. If you have a batch report that runs once a month that needs some special selection criteria, it probably isn’t worth defining an index just for that purpose. Maintaining an index every time you create or update a record is relatively expensive. Maintaining many indexes on the same table can be very expensive. Avoid defining indexes you don’t really need. Using multi-component indexes You can define an index on one or more fields in a table. Defining a multi-component index can be much more effective than defining multiple indexes on the same individual fields, but only when your application needs to access that combination of fields in the order in which they appear in the index. For example, if your application sometimes needs to select data based on the value of field A, and sometimes on A and B together, and sometimes on A, B, and C, then it makes sense to define a multi-component index with fields A, B, and C in that order. However, if your application sometimes needs to select data based just on field B, or on C, or on B and C together, without knowing the value of A, then this index will do you no good any more than you can easily locate a word in the dictionary by knowing the second or third letter in the word. Always evaluate the selection requirements of your application carefully as you design your database indexes. ROWID – fastest way of getting to a record. Done-04 Best Practices: Designing for Performance

8 Tip#3 Know Your Data Know your data
Efficient data access Customer Know your data Join from smallest bracket to largest Not always parent to child Date ranges 1 0..* Order Know your data – Many Orders, but fewer placed today Access path analysis Join from smallest bracket to largest Not always parent to child Not always parent to child – eg when selecting within a date range (common activity within many transaction based applications). May be less orders (child table) within the date range than customers (parent table). Example: here we are selecting from parent (customer) to order (child), but in this case there are likely to be less orders with an OrderDate of TODAY than there are customers where the CreditLimit <> 0. Structuring your selection criteria in a join It’s important to put the tables into the proper sequence and to specify your selection criteria as early in the retrieval process as possible, as we shall see later – Progress does not optimize the query for you. There are further programming considerations regarding efficient JOINS later under programming. Done-04 Best Practices: Designing for Performance

9 Tip#3 Know Your Data (2) There are many customers
Efficient data access - example There are many customers Fewer orders placed today FOR EACH Order WHERE Order.orderDate = TODAY, FIRST Customer of Order WHERE Customer.CreditLimit <> 0: /* DISPLAY ... */ END. Structuring your selection criteria in a join When you need to retrieve data from multiple joined tables in a single query or FOR EACH statement, it is important to: Put the tables into the proper sequence and to specify your selection criteria as early in the retrieval process as possible. OpenEdge does not optimize complex joins in the same way that some other database managers do, rearranging the order of tables and fields. This is because Progress is designed to make it easy and effective to deal with individual records and multiple levels of selection, rearranging a join in a single statement is not typically an issue. If you join tables in a single statement, Progress retrieves the data in the order you specify. OpenEdge does not second-guess your selection and rearrange the retrieval for you. You have to take responsibility for structuring your selection efficiently. /* Less efficient selection: */ FOR EACH Customer WHERE Customer.CreditLimit NE 0, EACH Order OF Customer WHERE OrderDate = TODAY: Hardly any Customers have a CreditLimit of 0, so the Customer selection is going to return nearly all Customers. On the other hand, only a few Customers have placed Orders today. It would be much more efficient to identify the Orders first, and then get the Customer for each of those Orders: /* More efficient selection: */ FOR EACH Order WHERE OrderDate = TODAY, FIRST Customer OF Order WHERE CreditLimit EQ 0: It’s especially important to place the selection criteria for each table as high up in the statement (that is, as close to the front) as possible. Always define the selection for each table as part of the phrase for that table’s buffer. Done-04 Best Practices: Designing for Performance

10 Tip#3 Know Your Data (3) Avoid indexing logicals
Indexing Performance Avoid indexing logicals Word indexes on logicals sStatus = sStatus1 + “|” + sStatus2 + … FOR EACH <tablename> WHERE <tablename>.Status CONTAINS sStatus NO-LOCK: Using Word indexes is fast, and more flexible – allows multiple values. Very fast Avoiding indexes on logical values If your application needs to identify records that satisfy some Boolean condition (such as Active vs. Inactive, Male vs. Female, or Domestic vs. Foreign), it is not a good idea to do this by means of indexes on Logical fields that represent the two conditions. The same is true of other fields that have only a handful of values, whether they are character values, such as Foreign and Domestic, or integer values representing those meanings. An index bracket is the portion of the index that the OpenEdge RDBMS must search through to identify all the records that match your selection criteria. If this is half or a large fraction of all the records, then the index is not serving its purpose and data retrieval is not efficient. Instead, you should consider encoding these kinds of values in a word-indexed character field. Using word indexes for status indicators One powerful use of word indexes is not just to provide an index on all the words in a free text field, such as a status message or customer comments, but to create special character fields in which you store strings that identify other aspects of the record. For example, you can create a character field for a table in which you store various attributes of the record that otherwise would be individual logical fields with true/false values. It’s much more efficient to use the CONTAINS operator on a word-indexed field than to evaluate a number of different indexed fields. You can also store some combination of field names and field values in a word-indexed field to make it easier and faster to find a record based on a number of different search criteria, such as customers where you have some particular bits and pieces of name and address information. Done-04 Best Practices: Designing for Performance

11 Tip#4 Only Get The Data You Need
Efficient data access Keep transactions short Keep result sets accurate Don’t mix OLTP and Decision Support Keep transactions small (may not be efficient in bulk data loads, where larger transactions required): Reduce contention Reduce roll-back time! There is increased bi activity overhead for many small transactions. Don’t mix OLTP with “decision support”/reporting/querying activities. Store “history” elsewhere: Historical data (audit trails, transaction histories, etc) is better stored in a separate database for read-only access, reporting, etc. (Need to decide when to move it to the history database. Same for audit information. If enquiries (reports) and updates need to be together, can they be scheduled at different times? OLTP: Typically many concurrent updates, index light, normalized/reduced redundancy (efficient access and less time to backup if 24*7). E.g. booking systems, order entry. Decision support: Typically index heavy (for reporting and analyses), de-normalized for aggregate totals, e.g. reporting systems, data warehouses, history. Limit the amount of data sent over n/work – keep results sets small. Prompt user for sufficient input; Control use of wildcard; Mandate certain fields; Batch rows retrieved e.g. to a browse, one view port full at a time, and cache on the client if possible. Only show data that is needed, eg lookups. Do not pre-populate a browser with ‘default’ customer data if there is selection criteria to be added. Pre-populate based on already established criteria if available, but only a view-port full at a time. Consider holding on to such data once pulled from database – allow searching the current results and if not found, request back to database? Done-04 Best Practices: Designing for Performance

12 Tip#5 Move The Data Quickly
Efficient data access RAW-TRANSFER Beware unique key collisions BUFFER-COPY ASSIGN Use ProDatasets BY-REFERENCE From fastest to slowest, methods of copying records: RAW-TRANSFER Function - PROGRESS V8.1 provides the new RAW-TRANSFER statement. This statement allows the 4GL programmer to copy the contents of an entire Progress database record buffer into a field (or variable) whose datatype is RAW (and vice versa). It also allows the copying of one database buffer to another record buffer. Up to 5 times faster than BUFFER-COPY. Beware: unique key collisions when used in the same table. BUFFER-COPY - When copying fields from one record buffer to another, BUFFER-COPY does it more efficiently than ASSIGN (30+%) . Use the EXCEPT option when some fields are not to be copied, and the ASSIGN option when some fields are renamed during the copy operation. Even doing a BUFFER-COPY of a table with 100 fields is faster than an ASSIGN statement that copies a half dozen of those fields. In particular, the static BUFFER-COPY statement allows Progress to resolve the exact field list to copy and to identify the field mappings at compile time so that the operation can be made as fast as possible. Using the dynamic BUFFER-COPY method on a buffer handle requires Progress to evaluate the arguments during program execution, which is slower. As with all dynamic language, use the BUFFER-COPY method only when you truly don’t know the buffers or the fields to copy until run time. All this applies equally well to the BUFFER-COMPARE statement or method which compares two buffers and returns a list of differences. BUFFER-COMPARE is much faster than using a series of explicit comparison statements on individual fields. ASSIGN – slowest of all (but faster then many single assigned statements). ProDatasets: If you pass a ProDataSet BY-REFERENCE and the procedure call is local, Progress optimizes the call by having the called procedure refer to (point to) the instance of the ProDataSet already in the calling procedure. This saves all the overhead of copying the ProDataSet definition and data. If the call is remote, then Progress ignores the BY-REFERENCE keyword and passes the ProDataSet by value, as it must in that case. Because of the efficiency of passing the ProDataSet BY-REFERENCE, you should normally use this keyword in your parameter definitions in RUN statements for any case where the called procedure will always or sometimes be in the same session as the caller. Because Progress ignores the keyword on a remote call, you get the most efficient behavior in either case. In general you must consider that on a call BY-REFERENCE, Progress substitutes the ProDataSet handle in the calling procedure for the ProDataSet defined in the called procedure, and that is the cause of most of the side effects. Consider the cases described in the following sections. Done-04 Best Practices: Designing for Performance

13 Tip#6 Minimize Network Roundtrips
OrderWin.w blOrder.p 1 Run Find-order Find-order 2 Run Find-customer Find-customer 3 Run Find-Order-Lines Find-Order-Lines 4 Run Find-Items Find-Items Avoid multiple roundtrips – batch calls to the AppServer, especially when decisions can be made based on data values; make those decisions on the AppServer. Reduce network roundtrips - Avoid “conversational roundtrips” – with associated overhead, instead “batch” calls to an AppServer into a single call, especially where decisions are being made on basis of data values. Make the decisions on the AppServer, not on the client. Using field lists Progress allows you to specify a reduced list of fields to retrieve when you define a query or start a FOR EACH block. Under some circumstances, using a field list can reduce the amount of data transferred across the network in a client/server environment: Was primarily designed for use with DataServers, which provide a connection to non-OpenEdge databases, such as Oracle and SQL Server. These kinds of databases typically have fixed-length data values that can be much larger than their OpenEdge counterparts, where all data is stored in an efficient, variable-length form. Effective only in client/server environments, where the client application session has a direct connection to a database server on another system. This is not the recommended architecture for new applications. A truly distributed application uses an AppServer to run an OpenEdge session that accesses the database and returns data to the client using temp-tables. In this environment, you are completely in control of what fields you pass between client and server through your temp-table definitions. The field list mechanism plays no role in this. OpenEdge always retrieves the entire record if you lock the record with an EXCLUSIVE-LOCK. OpenEdge retrieves additional fields beyond those in the field list for its own purposes, including evaluating some of the selection criteria of the query or FOR EACH. The field list is not the same as a display list for a browse or a field list for the fields in a frame. Leave out a field in the FIELDS list of a query definition that is needed by any part of the application that uses the query, you will get a run-time error. This can cause serious maintenance problems if your query definitions must explicitly name every field that is used from that query anywhere on the client. The bottom line is that in a distributed application, you control the field list through the definition of temp-tables that pass data from server to client, and the FIELDS phrase on a query definition is not needed as part of that definition. 5 Process order Done-04 Best Practices: Designing for Performance

14 Tip#6 Minimize Network Roundtrips
OrderWin.w blOrder.p 1 Run assemble-order Assemble-order 2 Process order Find-order Find-customer Find-Order-Lines Find-Items Done-04 Best Practices: Designing for Performance

15 Tip#7 Use the AppServer™
Session managed model State-reset State-aware Stateless Session-free model State-free Think carefully about the mode Use The AppServer Truly modern applications - service orientated - should be AppServer based. With an Appserver application the network traffic is better managed by the application. Trade-offs are based around resources vs throughput, and of course programming complexity (where context has to be maintained): Resources and scalability: State-reset/State-aware: one to one relationship, largest resources consumed. State-less, scales very well with minimal hardware resources; lowest development costs (quickest to develop) and fastest server response time State-less using Bound/unbound (similar to locking an agent with WebSpeed) probably falls somewhere in the middle. Really depends on how much of the operation is bound…Don’t recommend coding a lot of business logic this way…Use state-reset or state-aware for long periods of ‘boundness’ Stateless: Higher development costs (program context db, write load.p and unload.p)…Slower server response times (client connected to broker, request then goes to AppServer) State-free: As Stateless, higher development costs, slower response times, but maximum use of resources. An AppServer running in state-reset and state-aware operating modes dedicates an Application Server process to each client connection, so the connected client is guaranteed of having each request handled immediately by the AppServer. Assuming that the general process load on the AppServer platform is light enough, the client can expect relatively fast response time from the AppServer. Throughput in a state-reset and state-aware connections tends to drop as client load increases, because the number of dedicated Application Server processes available to handle client connections is necessarily limited by the number of Application Server processes that can be started on the AppServer machine. A stateless or state-free AppServer passes requests from connected clients to any available Application Server process, so the number of clients that a single AppServer instance can handle using these operating modes is relatively high. A relatively small number of Application Server processes can support many client connections (unbound). A stateless or state-free AppServer can make maximum use of its available Application Server process resources to handle client requests, maximizing throughput for large numbers of clients, especially when they maintain short connections. State-free maximizes resource usage further still by use of truly asynchronous requests. Avoid multiple roundtrips – batch up calls to the AppServer, especially when decisions can be made based on data values; make those decisions on the appserver. FIELD-LISTS are useful, but only for client/server applications Using field lists Progress allows you to specify a reduced list of fields to retrieve when you define a query or start a FOR EACH block. Under some circumstances, using a field list can reduce the amount of data transferred across the network in a client/server environment, but there are limitations that mean that you should have limited use for it in most modern OpenEdge applications: The field list option was primarily designed for use with OpenEdge DataServers, which provide a connection to non-OpenEdge databases, such as Oracle and SQL Server. These kinds of databases typically have fixed-length data values that can be much larger than their OpenEdge counterparts, where all data is stored in an efficient, variable-length form. The field list has an effect only in a client/server environment, where your client application session has a direct connection to a database server on another system. This is not the recommended architecture for new applications. A truly distributed application uses an AppServer to run an OpenEdge session that accesses the database and returns data to the client using temp-tables. In this environment, you are completely in control of what fields you pass between client and server through your temp-table definitions. The field list mechanism plays no role in this. Even if you have a database connection in a client/server environment, Progress always retrieves the entire record if you lock the record with an EXCLUSIVE-LOCK. Progress retrieves additional fields beyond those in the field list for its own purposes, including evaluating some of the selection criteria of the query or FOR EACH. The field list is not the same as a display list for a browse or a field list for the fields in a frame. If you inadvertently leave out a field in the FIELDS list of a query definition that is needed by any part of the application that uses the query, you will get an error at run time. This can cause serious maintenance problems if your query definitions must explicitly name every field that is used from that query anywhere on the client. The bottom line here is that in a distributed application, you control the field list through the definition of temp-tables that pass data from server to client, and the FIELDS phrase on a query definition is not needed as part of that definition. Use AppServer Truly modern applications - service orientated - should be AppServer based. With an Appserver application the network traffic is better managed by the application. Trade-offs are based around resources vs throughput, and of course programming complexity (where context has to be maintained): Because an AppServer running in state-reset and state-aware operating modes dedicates an Application Server process to each client connection, the connected client is guaranteed of having each request handled immediately by the AppServer. Assuming that the general process load on the AppServer platform is light enough, this means that the client can expect relatively fast response time from the AppServer. However, throughput in a state-reset and state-aware connections tends to drop as client load increases, because the number of dedicated Application Server processes available to handle client connections is necessarily limited by the number of Application Server processes that can be started on the AppServer machine. Because a stateless or state-free AppServer passes requests from connected clients to any available Application Server process, the number of clients that a single AppServer instance can handle using this operating mode is relatively high. That is, a relatively small number of Application Server processes can support many client connections. If you never set connections to the bound state, a stateless or state-free AppServer can make maximum use of its available Application Server process resources to handle client requests, thus maximizing throughput for large numbers of clients, especially when they maintain short connections. State-free maximizes resource usage further still by use of truly asynchronous requests. See also presentation C1390-LV Programming with the OpenEdge Appserver for more detailed information on programming the AppServer and its’ operating modes. Done-04 Best Practices: Designing for Performance

16 Tip#7 Use the AppServerTM (2) Trade-offs - Resources
state-reset state-aware System Resources stateless (bound/unbound) Tradeoffs: Look at it from a resource consideration (hardware resources) versus throughput and the ability to scale (as the number of clients increases) State-reset/State-aware: one to one relationship, largest resources consumed. State-less, state-free, scale very well with minimal hardware resources. State-less using Bound/unbound (similar to locking an agent with WebSpeed) probably falls somewhere in the middle. Really depends on how much of the operation is bound. Not recommended to code a lot of business logic this way - use state-reset or state-aware instead for long periods of ‘boundness’. For state-free, binding an AppServer with persistent procedures is not recommended since this loses the advantages of state-free. Because an AppServer running in state-reset and state-aware operating modes dedicates an Application Server process to each client connection, the connected client is guaranteed of having each request handled immediately by the AppServer. Assuming that the general process load on the AppServer platform is light enough, this means that the client can expect relatively fast response time from the AppServer. However, throughput in a state-reset and state-aware connections tends to drop as client load increases, because the number of dedicated Application Server processes available to handle client connections is necessarily limited by the number of Application Server processes that can be started on the AppServer machine. Because a stateless AppServer passes requests from connected clients to any available Application Server process, the number of clients that a single AppServer instance can handle using this operating mode is relatively high. That is, a relatively small number of Application Server processes can support many client connections. If you never set connections to the bound state, a stateless AppServer can make maximum use of its available Application Server process resources to handle client requests. This operating mode thus maximizes throughput for large numbers of clients, especially when they maintain short connections. State-free extends this through support for parallel execution across multiple AppServer agents. stateless State-free Throughput Done-04 Best Practices: Designing for Performance

17 Tip#8 Use The Right Tool Specify NO-UNDO Group assignments with ASSIGN
Old Favorites Specify NO-UNDO Variables, parameters, TEMP-TABLES Group assignments with ASSIGN Pick the right block: DO is faster than REPEAT Unnecessary blocks are wasteful Procedures and UDFs are blocks too NO-UNDO - Without NO-UNDO, a before-image of the previous value is generated in what amounts to a separate record buffer for all NO-UNDO variables. Useful when you want the original value restored when you UNDO a block or transaction. If you don’t need this, the before-image generation is needless overhead. Use of NO-UNDO also causes the Progress local before-image (.lbi) file, which is maintained for each user connected to a database, to be smaller. It is good practice to use NO-UNDO since it is unusual for variables to require the UNDO support that they receive by default. With temp-tables the situation is not always so clear. There might be cases where you are adding records to a temp-table or changing records in a temp-table within a transaction, and you want to be able to undo those changes. If this is not the case, however, you’ll benefit significantly from defining the temp-table with the same NO-UNDO qualifier as for variables. This spares you from having Progress create a before-image of every temp-table change made within a transaction. Grouping assignments with the ASSIGN statement Significantly faster to assign 2 or more values in a row in a single statement using the ASSIGN keyword. Even if the values being assigned are unrelated, faster to do it in a single statement. OpenEdge adjusts index entries and does other work as part of each statement. Assign two fields that participate in the same index in two separate statements, the index block is rebuilt once after each statement—a much greater overhead than doing it in one statement. It might even cause a temporary (but fatal) unique index violation if you assign part of a composite key in one statement and the other part in another. Using DO instead of REPEAT The DO block, by default, does not provide many of the default services that the REPEAT block does (transaction management and default frame management). SO a DO block is much faster than a REPEAT block if you don’t need these services. Minimizing block nesting - All blocks in Progress incur some overhead; because OpenEdge provides many services to make programming easier there is a cost to all blocks, even simple DO blocks. For this reason, you should avoiding unnecessary block nesting wherever possible. The AppBuilder always puts a DO END block as a starting point for triggers, for instance. You should feel free to remove the block if your trigger requires only a single statement. And remember that there can be an additional benefit to combining multiple assignments into a single statement. If you turn several assignments into a single ASSIGN statement, a DO block that would otherwise have several statements can be reduced to just one statement with no block header. Minimizing nesting of procedure calls Procedures, whether internal or external, are blocks as well, and relatively expensive ones. Obviously, you should use procedures as necessary to structure your application properly. However, if you run a relatively small procedure or invoke a function many times in a performance-sensitive loop, you should consider moving the code directly into the procedure that calls it to execute it inline. If it’s executed many times, this can make a significant difference in performance. If a procedure of this type is invoked from multiple places, you can make it into an include file and include it each place where it would otherwise be run. In this way, the code remains reusable but it is compiled directly in place wherever it is used. This can make the code much faster. May be better to {include.i} common code rather than call it multiple times, even as internal procedures. Done-04 Best Practices: Designing for Performance

18 Tip#8 Use The Right Tool (2)
CASE is faster than nested IF Arrays are faster than comma-separated lists Avoid functions in WHERE clauses FOR EACH CUSTOMER WHERE (SUBSTR(name, 1, 1) = cName) NO-LOCK: Part of the optimization of the CASE statement is that it evaluates the expression only once, when the CASE statement is entered. By contrast, nested IF statements evaluate the expression in each IF clause, even if the expression is the same each time. Arrays instead of lists - Array element access is much faster than accessing an element of a list of comma-delimited strings. The memory location of each element of an array is distinct and, therefore, quickly accessed. Using the CAN-FIND function - CAN-FIND returns true or false simply by looking at the index entries if the selection criteria can be resolved strictly through a single index, without having to retrieve record values; it’s only asking whether a record can be found. It is not retrieving any particular field values. Thus, CAN-FIND is beneficial and efficient only when you use it to identify records through one or more fields in a single index. If CAN-FIND has to retrieve the database records themselves, then you have lost its performance advantage over the FIND statement. FUNCTIONS in where clauses – If a function or expression is used for the components of an index, an index or bracket will not be used. for each customer where (substring (name, 1, 1) = "A"): The index on name will not be used, instead primary index on Cust-num will be used. This expression will result in a full index scan to retrieve the rows. for each customer where (if rowid-customer <> ? Then rowid (customer) = rowid-customer else true): In this case row will not be retrieved directly using the rowid. Because Progress selects the index at compile time, it will not be able to evaluate the if statement. Therefore the primary index on cust-num will be selected, resulting in full index scan to retrieve the row. for each customer where (name matches "A*"): The index on name will not be used, instead primary index on cust-num will be used. This expression will result in a full index scan to retrieve the rows. Done-04 Best Practices: Designing for Performance

19 Tip#9 Tidy Up After Yourself
Dynamic programming Re-use dynamic objects Group objects with WIDGET POOLS DELETE object/handle when finished Using widget pools - Deleting individual dynamic objects is a big responsibility, and a serious nuisance as well. Widget pools are designed to help you make your use of dynamic objects much simpler and more reliable. A widget pool provides a means of treating objects you create as a set. When you delete the pool, all the objects you created in it go away together. The simplest way to group objects using widget pools is to associate all the objects in a single procedure with a widget pool. (The template procedures used by the AppBuilder for windows (and for visual SmartObjects, such as SmartWindows and SmartDataViewers) help you do this by having a CREATE WIDGET-POOL statement in the template’s definitions section). This means that, by default, all the dynamic objects you create that go into the window are deleted when the window is closed and its procedure deleted. This includes not just visual objects but dynamic buffers, queries, and so forth. A simple CREATE WIDGET-POOL statement creates an unnamed widget pool. This pool automatically goes away when its procedure terminates. This might not always be the behavior you want. In fact, by passing the handles to dynamic objects around, you can easily wind up with a handle whose scope exceeds the lifetime of the dynamic object it points to. In this case, the value of the handle can become invalid. Re-use dynamic objects – dynamic objects are generally slower than static ones, so look for ways to make efficiency savings when using them. This is because, at compile time, Progress cannot anticipate what the procedure will do at run time and so cannot set up the structures to support the procedure. More of the work is done at run time, when the interpreter looks at the values of the dynamic procedure elements, just in time to prepare and execute them. If you are going to create another dynamic object of the same type, especially inside a loop that is executed many times, it is much more efficient to reuse the same dynamic object rather than deleting it and re-creating it. This is true even if you change all the attributes of the object. Then you must simply remember to delete it after you are completely done with it. Note: can’t reuse a dynamic buffer in this way. When you use the CREATE BUFFER statement, you must name the table the buffer will be for. This name can be an expression so that the buffer name can be assigned dynamically at run time, but you can’t then reuse that same dynamic buffer object for a different buffer. Remember to DELETE the objects when finished though. Done-04 Best Practices: Designing for Performance

20 Tip#10 Set It Up Right Order of PROPATH Deploy r-code
Configuration Order of PROPATH Deploy r-code Never source code Use shared libraries -q -Bt -Mm Order of PROPATH – put the most traversed paths first. Use –q quick request - Use Quick Request (-q) to tell Progress to search PROPATH directories only on the first use of a procedure. Ordinarily in a Progress procedure, when the RUN statement is used to run a sub-procedure, Progress searches the PROPATH environment variable, looking for a procedure of the same name with a .r extension. If it finds a file with a .r extension (an r-code file), it checks to make sure the r-code file has not changed since that r-code file was created. This search is very useful in a development environment where procedures change regularly and you want to make sure you are always running the most current version of your application. However, in a production environment, you might want to bypass this search. With Quick Request (-q), after the initial search, if the procedure still resides in memory or in the local session-compiled file, Progress uses that version of the procedure rather than searching the directories again. However, Progress always checks whether Data Dictionary definitions related to a procedure were modified. If they were modified, Progress displays an error when it tries to retrieve the procedure. Procedure libraries - OpenEdge provides two types of r-code libraries: standard and memory-mapped. A standard library contains r-code procedures that execute in local memory. A memory-mapped library contains r-code procedures that execute in shared memory. When you execute r-code procedures from either a standard library in local memory or a memory-mapped library in shared memory, you get: • Faster access to r-code. • Fewer file open and close operations. • Less I/O to temporary files. When you execute r-code procedures from a memory-mapped library in shared memory, you get: • Even faster access to r-code. • Requires less memory because multiple users access the same r-code segments in shared memory. When loading and executing r-code procedures from operating system files in a directory, OpenEdge must open and close each file individually. When loading and executing r-code procedures from standard or memory-mapped libraries, OpenEdge opens only one file—the library itself—to access all of the r-code files in the library. -Bt – buffer size for temporary tables, in blocks, default is 10. -Mm - Use Message Buffer Size (-Mm) to specify the standard message buffer size, in bytes. This parameter is relevant only for network protocols. Progress uses message buffers to move records (messages) between servers and remote clients. Records (plus 40-byte headers) larger than the message buffer size are fragmented into multiple messages. If your database records are large, increase this parameter to avoid record fragmentation. However, if the network works more efficiently with small messages, reduce -Mm and fragment larger records. Done-04 Best Practices: Designing for Performance

21 Agenda Overview Top 10 Tips Checking Performance Q & A
SO now lets look at some ways that we can measure and compare performance Done-04 Best Practices: Designing for Performance

22 Checking Performance 1. QA feedback 4. Verify and test 2. Diagnose
Checking performance should be a part of the QA process, and should be done before deployment. Its important to test and QA performance levels in an environment similar to, or worse than, the intended deployment scenario. Use large databases, not a small test sample database; a poorly performing query may be perfectly acceptable on a few hundred records; it won’t be on a few hundred thousand. The same applied for the network. Do not test on a local area network if you are deploying to a WAN, the results will not be comparable. 3. Fix the problem Done-04 Best Practices: Designing for Performance

23 Checking Performance XREF ETIME Statistics With cross-reference (-yx)
Confirm index usage ETIME Measure timing Statistics With cross-reference (-yx) Find slow programs Profiler Analyze programs Different options available to the 4GL developer, which we shall discuss in the next slides… XREF - Confirm index usage Statistics With Cross-reference (-yx) - Find slow programs ETIME - Measure timing Profiler - Analyze programs Done-04 Best Practices: Designing for Performance

24 XREF Confirm index usage COMPILE x.p XREF x.xrf ...
x.p x.p 1 SEARCH sports.x f1f2f3idx WHOLE-INDEX x.p x.p 1 SORT-ACCESS sports.x f1 x.p x.p 1 SORT-ACCESS sports.x f2 x.p x.p 1 SORT-ACCESS sports.x f3 x.p x.p 4 SEARCH sports.x f1f2f3idx WHOLE-INDEX XREF specifies the file where the Application Compiler writes cross-reference information between source files and database objects.xref.  For each object reference, the .xref file contains one unformatted and blank-separated line containing the following: 1) Procedure name 2) Source filename 3) Line number 4) Reference type 5) Object identifier -- List of tags the XREF compile option generates: SEARCH: Indicates an index bracket or look up will be used.  The logical database name, table name, and index names are listed.  When multiple brackets and indexes are used for the same query, you will see one search line for each bracket. SEARCH ... WHOLE-INDEX: Indicates that a suitable bracket could not be constructed and an index scan over the entire table will be performed using the index noted. SORT-ACCESS: Indicates that the query result is to be ordered by a particular column value and no suitable index exists.  A sort of the query result on the noted column value is required. ACCESS: Indicates that the specified table and field value is used at this point in the program. CREATE: Indicates that a record is created at this location in the program. DELETE: Indicates that a record is deleted at this location in the program. UPDATE: Indicates that the specified field value of a table is updated at this location in the program. See the COMPILE statement in the Progress Language Reference for more information on the cross-reference file. Done-04 Best Practices: Designing for Performance

25 ETIME Compare statements, routines
Returns elapsed time in milliseconds Find Order WHERE … ETIME(TRUE). DO iCount = 1 to 10000: FIND Order WHERE … END. iFindTime = ETIME. FOR FIRST Order WHERE … iForTime = ETIME. The ETIME function While the profiler allows you to gather performance information on large parts of an application, you can also gather processing information for routines or statements by using the ETIME function. Is a simpler and easier way to test of a specific part of your code. The ETIME function (for elapsed time). ETIME returns an integer value representing the number of milliseconds since the function was reset to zero. Because a millisecond is a substantial amount of time on a modern processor, you often have to repeat an action many times inside a loop to measure accurately just what its cost is. To reset the counter that ETIME uses, you invoke the function with an argument value of yes or true. Otherwise, you invoke it with no argument and no parentheses. If you forget to reset it to zero before you start, ETIME returns some enormous integer representing the number of milliseconds since your session started. This example shows you whether it is faster to use the FIND statement or a FOR FIRST block to find the Order with a Customer Number of 24 and an Order Date of 1/31/98. If you look at the indexes for the Order table in the Data Dictionary, you see that there is an index on the Order Date field, and another index that has the CustNum field as its primary component, followed by the OrderNum field. The FOR FIRST construct takes advantage of both of these indexes to resolve the request in the most efficient way possible. The FIND statement can use only one of the indexes. The code first resets ETIME, then does the same FIND in a loop times. It then saves the ETIME counter for this operation. Then it resets it again, does a FOR FIRST times, saves that value, and finally displays both values: The FOR FIRST was significantly faster. The actual difference is very dependent on the actual indexes and the number of records to search. Done-04 Best Practices: Designing for Performance

26 Find Slow Programs Procedure statistics
Statistics with cross-reference (-yx) Procedure statistics Number of calls Time spent executing Procedure swapping to SRT file Creates proc.mon file by default Use Statistics with Cross-reference (-yx) to collect procedure call statistics and write them to an output file. With this parameter, you can monitor information to answer the questions: • How many calls were made in a given period of time? What out for large numbers of calls; put these in internal procedures or include files • How long did a procedure spend executing? Watch out for long execution times; concentrate rewrite efforts here • How often was a procedure swapped to and from the temporary file (SRT file)? Watch out for re-reads; try using more –mmax client memory Progress places the default output file for the -yx parameter, proc.mon, in your current working directory. However, you can specify a different output file by using the PROCMON environment variable. Simply set PROCMON to point to the file you want to use. At the end of the session the statistics are written out to the proc.mon file; SHOW-STATS statement causes procedure call statistics to be written to the output file. The CLEAR option sets all counters and timers (such as Calls and Time) to 0. Fields output are: • Caller — The calling procedure; <top> indicates there was no calling procedure, start of session or from Procedure Editor. • Callee — The names of the called procedure. • Load Size — The size, in bytes, of each called procedure as Progress loads it into memory. Load size of 0, indicates an un-compiled source procedure or r-code previously loaded into memory. • Calls — The number of times the Caller procedure calls the Callee procedure. • Rd Bytes — The called procedure’s load size. However, if the procedure is swapped out of memory and later restored, Rd bytes equals the procedure’s Load Size added to the number of bytes read from the SRT file. Rd Bytes grows larger each time the procedure is swapped out of memory and restored from the SRT file. • Reread — The number of bytes Progress reads from the SRT file to restore a Caller procedure that was overwritten. Progress restores only what is necessary to continue executing the Caller procedure. In some cases, this is less than the amount swapped to the SRT file. • Time — The total execution time of the called procedure, in milliseconds. Done-04 Best Practices: Designing for Performance

27 Profiler Provides a ‘profile’ of procedure execution
Analyze programs Provides a ‘profile’ of procedure execution Analyze where most time spent Two parts Profiling capability built into 4GL -profile startup parameter PROFILER system handle Run profile.p Profiling Viewer tool Interpret and view the data TAKE NOTE: Unsupported Software package - do not call Progress Software Technical Support. Not adequately tested, reviewed, polished, documented, or run through a Beta cycle. What is Profiling? A profiler provides a "profile" of a particular execution. A profiler generally provides timing information and call-tree information; with that, an engineer can analyze where their program is spending most of its time and what part of the application is calling what other part of the application. Functionally, the profiling software comes in two parts. Part 1- the profiling capability itself -- this is the part built in to the 4GL Client that generates and outputs the necessary timing data from a run of a Progress client session. Part 1 was first shipped as an undocumented (and unsupported) feature in version 8.2A; in version 9.0B, the Part 1 feature was documented and is supported. Part 2 - The tool to import, interpret, and view the timing data from the application. See: %DLC%\src\samples\profiler\readme.doc for more details The process of analyzing application performance typically involves the gathering of data. While some tools (PROMON, for example) provide data from a specific moment in time, performance analysis can also be performed by accumulating data over a larger period of time in order to provide a wider perspective regarding overall performance. This method of analysis can help in determining application performance relating to larger tasks and processes, rather than individual points within a process. The Progress 4GL Application Profiler is a combination of 4GL functionality and an application that helps you capture and analyze performance information regarding a single user’s application session. By using the Application Profiler, you can view overall application performance for a specific application process or task. Can be useful after user complaints about performance! Check for: Query statements that take a long time to retrieve information. Unexpectedly large number of iterations of application code. The Profiler is available with your 4GL license and is installed by default on Windows systems in %DLC%\src\samples\profiler. Profiler ignores time spent waiting in User Interface statements (WAIT-FOR, PAUSE, or MESSAGE…ALERT-BOX) or while Progress tools are executing. The overhead of taking the measurements themselves is fairly minimal and the Profiler makes no attempt to factor it out. Note: running the Profiler does not have a major impact on your application. The only difference a user may ever see during profiling is an occasional slow-down while the Profiler creates compile listings to be loaded into the profiling tool. Done-04 Best Practices: Designing for Performance

28 Profiler Architecture
Application Profiler Prof.db Profile.out Done-04 Best Practices: Designing for Performance

29 Profiler Steps Create an Application Profiler database
Collect profiling data Load output profiling data into the Profiler database Analyze the performance of an application using the Application Profiler utilities 1.Create an Application Profiler database. 2.Collect profiling data (using session startup parameters, profile.p or the PROFILER system handle). 3.Load the output profiler data into the Profiler database. 4.Analyze the performance of an application using the Application Profiler utilities. Done-04 Best Practices: Designing for Performance

30 Profiler Demo Done-04 Best Practices: Designing for Performance
Allows you to identify and ‘drill-down’ on the statements that are taking longest to run. Great for spotting inefficient record selection criteria by identifying which statement are taking longest to run, or are called most often and so may need refining for efficiency. The (unsupported) Profiler tool allows you to Add, Delete and View profiled sessions: Add – add a new session from the prof.out file, gathered while the application was running. Delete – delete the selected session View – View the selected session. See next slide. Within the Session Summary window we can see where most time was spent within the profiled application. Double-click the selected row in the browse to see more detail….. Drilling down we can see the total time pent on each line of the procedure. Done-04 Best Practices: Designing for Performance

31 In Summary Performance is a requirement Create a performance baseline
Measure Performance Done-04 Best Practices: Designing for Performance

32 Questions? Done-04 Best Practices: Designing for Performance

33 Thank you for your time! Done-04 Best Practices: Designing for Performance

34 Done-04 Best Practices: Designing for Performance


Download ppt "Stephen Ferguson Sr. Training Program Manager"

Similar presentations


Ads by Google