start page | rating of books | rating of authors | reviews | copyrights

Advanced Oracle PL/SQL Programming with Packages

Advanced Oracle PL/SQL Programming with PackagesSearch this book
Previous: 3.9 Considering Implementation Options Chapter 3
The PL/SQL Development Spiral
Next: 3.11 Don't Forget Backward Compatibility
 

3.10 Choosing the Best Performer

If there is a difference in execution time between the performance of my two string-repeaters, it will not be a big one. I will need to execute the functions many times to compare the cumulative difference. The best way to calculate the elapsed time of PL/SQL code execution is with the GET_TIME function of the DBMS_UTILITY builtin package. I have encapsulated GET_TIME inside the PLVtmr package (PL/Vision TiMeR) to make it easier to use. Example 3.9 shows the kind of script I used.[ 4 ]

[4] By the way, this code was for the most part generated for me with the PLVgen package to compare the performance of repeated , rep_rpad , and also the recursion-based implementation of repeated (see sidebar).

This SQL*Plus script (stored in the file timerep.sql on the disk) takes three arguments. The first, &1 , accepts the number of times to execute each function. The second, &2 , accepts a string that is to be duplicated. The third, &3 , accepts the number of repetitions of the string. I ran the script several times as shown below:

SQL> @timerep 100 abc 1 duprpad Elapsed: .77 seconds. Factored: .0077 seconds. duploop Elapsed: .66 seconds. Factored: .0066 seconds. recrep Elapsed: .71 seconds. Factored: .0071 seconds.  SQL> @timerep 100 abc 10 duprpad Elapsed: .71 seconds. Factored: .0071 seconds. duploop Elapsed: .99 seconds. Factored: .0099 seconds. recrep Elapsed: 1.54 seconds. Factored: .0154 seconds.

I ran each of these tests several times to allow the numbers to stabilize. The results are very interesting and certainly reinforce the need for a careful test plan. When repeating the string just once, the recursion-based implementation is superior. Upon reflection, this should not be a surprise. It handles a single repetition as a special case: an unmediated concatenation of two strings. The loop-based implementation comes in second, but all of the timings are very close. When we move to multiple repetitions of the string, however, the recrep function becomes extremely slow; again, I would expect that behavior because of the extra work performed by the PL/SQL runtime engine to manage a recursive program. The big news from this round, however, is that the RPAD implementation of repeated establishes itself clearly as the fastest technique.

Example 3.9: A Performance Comparison Script

DECLARE    a VARCHAR2(100) := '&2';    aa VARCHAR2(10000); BEGIN    PLVtmr.set_factor (&1);    PLVtmr.capture;    FOR rep IN 1 .. &1    LOOP       aa := rep_rpad (a, 'UL', &3);    END LOOP;    PLVtmr.show_elapsed ('duprpad');        PLVtmr.set_factor (&1);    PLVtmr.capture;    FOR rep IN 1 .. &1    LOOP       aa := repeated (a, 'UL', &3);    END LOOP;    PLVtmr.show_elapsed ('duploop');     PLVtmr.set_factor (&1);    PLVtmr.capture;    FOR rep IN 1 .. &1    LOOP       aa := recrep (a, 'UL', &3);    END LOOP;    PLVtmr.show_elapsed ('recrep');       END; /  

Of course, I also need to compare the performance for different kinds of strings. I ran the same timer script as follows to see how each function handled NULL values:

SQL> @timerep 200 null 10 duprpad Elapsed: 1.59 seconds. Factored: .00795 seconds. duploop Elapsed: 2.03 seconds. Factored: .01015 seconds. recrep Elapsed: 2.91 seconds. Factored: .01455 seconds.

In this scenario, the RPAD implementation was considerably faster than the loop and recursion techniques (though, once again, I found that if the number of repetitions was set to 1, the recrep function was faster). Finally, I greatly increased the number of string repetitions and then all became clear:

SQL> @timerep 100 abc 100 duprpad Elapsed: .77 seconds. Factored: .0077 seconds. duploop Elapsed: 4.28 seconds. Factored: .0428 seconds. recrep Elapsed: 5.22 seconds. Factored: .0522 seconds.

The conclusion I draw from my tests is that the RPAD technique offers a much more stable solution than that based on the FOR loop. Regardless of the number of repetitions, RPAD takes about the same amount of time. With the FOR loop and recursion approaches, as the repetitions increase, the performance degrades. That is not the sign of a healthy algorithm.

Given the results, it would make sense to implement the repeated function using the RPAD technique. You could possibly optimize further by using the FOR loop approach for small numbers of repetitions, and then switch to RPAD for larger repetitions. The gain with the FOR loop for minimal repetitions is, however, minimal -- it's probably not worth the trouble.

I was glad to see that the RPAD approach is faster. You should always use a builtin if it exists, rather than build your own. The FOR loop technique arose quite naturally from the way I expanded the scope of the twice function. It turned out, however, that it was not the path to the optimal solution. As for recursion, well, it is always an interesting phenomenon to watch and puzzle out, but it rarely offers the best implementation (except when it is the only implementation feasible).

Example 3.10: The Code for the Recursive Implementation of repeated

CREATE OR REPLACE FUNCTION recrep     (string_in IN VARCHAR2,      action_in IN VARCHAR2 := NULL,     num_in IN INTEGER := 1)    RETURN VARCHAR2 IS    v_action VARCHAR2(10) := UPPER (action_in);      initval VARCHAR2(32767);    nextval VARCHAR2(32767);     v_retval VARCHAR2(32767);     BEGIN    assert       (v_action IN ('UL', 'LU', 'N'),        'Please enter UL LU or N');    assert       (num_in >= 0, 'Duplication count must be at least 0.');     IF v_action = 'UL'    THEN       initval := UPPER (string_in);          nextval := LOWER (string_in);    ELSIF v_action = 'LU'    THEN       initval := LOWER (string_in);             nextval := UPPER (string_in);    ELSE       initval := string_in;             nextval := string_in;    END IF;      IF num_in = 1    THEN       RETURN initval || nextval;    ELSE       /* No more case conversions performed... */       RETURN (initval || repeated (nextval, 'N' , num_in-1));    END IF; END recrep; /


Previous: 3.9 Considering Implementation Options Advanced Oracle PL/SQL Programming with Packages Next: 3.11 Don't Forget Backward Compatibility
3.9 Considering Implementation Options Book Index 3.11 Don't Forget Backward Compatibility

The Oracle Library Navigation

Copyright (c) 2000 O'Reilly & Associates. All rights reserved.

Library Home Oracle PL/SQL Programming, 2nd. Ed. Guide to Oracle 8i Features Oracle Built-in Packages Advanced PL/SQL Programming with Packages Oracle Web Applications Oracle PL/SQL Language Pocket Reference Oracle PL/SQL Built-ins Pocket Reference