Laufzeitoptimierung: Tuning-Challenge - Part I

Hinweise, Tips und Tricks, FAQs - keine Anfragen!!

Postby Willy1492 » Thu May 27, 2004 9:29 am

Ich habe mal Olafs letztes Beispiel als Referenz verwendet (damit die Testläufe nicht ganz so lange dauern) und mit meiner für große Tabellen optimierten Variante verglichen.
Neben dem Vergleich der Summen habe ich noch die jeweils besten Zeiten verglichen,
damit einzelne Ausreißer (s. z.B. die maximale Laufzeit bei 10 Einträgen, verglichen mit der Minimalen für 100) nicht das Ergebnis verfälschen:

Jetzt noch mal überarbeitet - ADD bei Ladenhuetern (und die damit obsoleten WHEN-OTHERS- bzw. ELSE-Anweisungen) weggelassen, und 1 Million Einträge geprüft.
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. 27.05.2004
  2. --------------------------------------------------------------------------------
  3. Anz. Eintraege in Tabelle:          10  Maximale Materialnummer:             3
  4. Dauerbrenner:                        0
  5. Saisonartikel:                       0
  6. Ladenhueter:                         3
  7.                         | Vergleich                             | Referenz
  8. Minimale Laufzeit:      |         33  ms                        |         32  ms
  9. Maximale Laufzeit:      |         76  ms                        |        172  ms
  10. Mittelwert Laufzeit:    |         43  ms                        |         68  ms
  11. Vergleichsroutine ist um Faktor                     1,03 langsamer als Referenzr
  12. Vergleichsroutine ist um Faktor                     1,59 schneller (Summe)
  13. --------------------------------------------------------------------------------
  14. Anz. Eintraege in Tabelle:         100  Maximale Materialnummer:             7
  15. Dauerbrenner:                        0
  16. Saisonartikel:                       2
  17. Ladenhueter:                         5
  18.                         | Vergleich                             | Referenz
  19. Minimale Laufzeit:      |        136  ms                        |        132  ms
  20. Maximale Laufzeit:      |        208  ms                        |        264  ms
  21. Mittelwert Laufzeit:    |        151  ms                        |        160  ms
  22. Vergleichsroutine ist um Faktor                     1,03 langsamer als Referenzr
  23. Vergleichsroutine ist um Faktor                     1,06 schneller (Summe)
  24. --------------------------------------------------------------------------------
  25. Anz. Eintraege in Tabelle:       1.000  Maximale Materialnummer:            35
  26. Dauerbrenner:                       10
  27. Saisonartikel:                      22
  28. Ladenhueter:                         3
  29.                         | Vergleich                             | Referenz
  30. Minimale Laufzeit:      |      1.045  ms                        |      1.059  ms
  31. Maximale Laufzeit:      |      1.105  ms                        |      1.442  ms
  32. Mittelwert Laufzeit:    |      1.061  ms                        |      1.143  ms
  33. Vergleichsroutine ist um Faktor                     1,01 schneller als Referenzr
  34. Vergleichsroutine ist um Faktor                     1,08 schneller (Summe)
  35. --------------------------------------------------------------------------------
  36. Anz. Eintraege in Tabelle:      10.000  Maximale Materialnummer:           252
  37. Dauerbrenner:                      161
  38. Saisonartikel:                      90
  39. Ladenhueter:                         1
  40.                         | Vergleich                             | Referenz
  41. Minimale Laufzeit:      |     11.328  ms                        |     12.517  ms
  42. Maximale Laufzeit:      |     15.673  ms                        |     13.763  ms
  43. Mittelwert Laufzeit:    |     12.475  ms                        |     13.133  ms
  44. Vergleichsroutine ist um Faktor                     1,10 schneller als Referenzr
  45. Vergleichsroutine ist um Faktor                     1,05 schneller (Summe)
  46. --------------------------------------------------------------------------------
  47. Anz. Eintraege in Tabelle:     100.000  Maximale Materialnummer:         2.002
  48. Dauerbrenner:                    1.646
  49. Saisonartikel:                     355
  50. Ladenhueter:                         1
  51.                         | Vergleich                             | Referenz
  52. Minimale Laufzeit:      |    137.600  ms                        |    217.561  ms
  53. Maximale Laufzeit:      |    146.181  ms                        |    268.567  ms
  54. Mittelwert Laufzeit:    |    141.888  ms                        |    229.991  ms
  55. Vergleichsroutine ist um Faktor                     1,58 schneller als Referenzr
  56. Vergleichsroutine ist um Faktor                     1,62 schneller (Summe)
  57. --------------------------------------------------------------------------------
  58. Anz. Eintraege in Tabelle:   1.000.000  Maximale Materialnummer:        16.669
  59. Dauerbrenner:                   15.319
  60. Saisonartikel:                   1.348
  61. Ladenhueter:                         2
  62.                         | Vergleich                             | Referenz
  63. Minimale Laufzeit:      |  2.845.146  ms                        |  3.067.513  ms
  64. Maximale Laufzeit:      |  2.898.925  ms                        |  3.122.678  ms
  65. Mittelwert Laufzeit:    |  2.857.928  ms                        |  3.092.441  ms
  66. Vergleichsroutine ist um Faktor                     1,08 schneller als Referenzr
  67. Vergleichsroutine ist um Faktor                     1,08 schneller (Summe)
  68. --------------------------------------------------------------------------------
  69. Gesamtlaufzeit in Mikrosekunden: 68.170.039
  70.  
GeSHi ©

Wie man sieht, bricht der Vorsprung meiner Version, der bei 100.000 Einträgen doch recht deutlich ist, bei einer Million Einträgen ganz schön ein.
Ob das mit einer Häufung der beim COLLECT auftretenden Hash-Kollisionen zusammenhängt?
Oder vielleicht eher mit dem READ ... BINARY SEARCH, das zunehmend ineffizienter wird.
Da könnte man noch etwas ändern - wenn auch mit negativem Einfluss auf kleinere itabs.

Mein Code (nach Einbau eines kleinen Hinweises von Stefan):
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. FORM vergleich USING    t_input     TYPE tyt_input
  2.                         p_max_matnr TYPE i
  3. *                       Uebergabe per Referenz ist schneller,
  4. *                       insbesondere das Kopieren der itab ist Aufwand
  5. *
  6. *                       und in dem Beispiel aendere ich die Parameter
  7. *                       auch nicht, dem Aufrufer kann es also egal sein,
  8. *                       dass ich die Werte nicht per VALUE uebergebe
  9.                CHANGING p_ergebnis  TYPE ty_ergebnis.
  10. *define _rt. " Test-Ausgabe Laufzeit von Einzelschritten
  11. *  format color &1.
  12. *  rt-from = rt-to.
  13. *  get run time field rt-to.
  14. *  rt-step = '&1'.
  15. *  rt-diff = rt-to - rt-from.
  16. *  write: / rt-tfill, rt-max, rt-step, rt-diff.
  17. *end-of-definition.
  18. *
  19. *  statics: begin of rt, max type i, tfill type i, step, from type i, to type i, diff type i, end of rt.
  20.   TYPES ty_char_mat(4) TYPE x.
  21.   DATA: tmp_mat TYPE i VALUE 1. " ty_char_mat VALUE 1.
  22.  
  23.   DATA: last_tabix TYPE sy-tabix VALUE 1,
  24.         tabix_diff TYPE i,
  25.         my_input TYPE ty_input.
  26.   TYPES: BEGIN OF ty_input2,
  27.            material  TYPE ty_char_mat,
  28. *        vom BUDAT des Originaltyps interessiert mich nur der Monat
  29.            m(2)     TYPE c,
  30.          END OF ty_input2.
  31.  
  32.   DATA: tmp_wa TYPE ty_input2,
  33.         tmp_input TYPE STANDARD TABLE OF ty_input2
  34.                    WITH NON-UNIQUE KEY material m.
  35.  
  36.   CLEAR p_ergebnis.
  37.   CHECK NOT t_input IS INITIAL.
  38.  
  39. * Da SORT in meinem ersten Beispiel am meisten Laufzeit verbrauchte,
  40. * jetzt ein Beispiel, das SORT nur auf eine kleinere Tabelle anwendet
  41. * COLLECT erfordert hier eine Typ-Konvertierung von MATERIAL
  42. *
  43. * Wenn es mehr als 2^22 Kombinationen Material und Monat gibt,
  44. * gibt es beim COLLECT allerdings einen Laufzeitfehler.
  45. * Gibt es mehr als 4 Millionen verschiedene Kombinationen?
  46.   LOOP AT t_input INTO my_input.
  47.     tmp_wa-material = my_input-material.
  48.     tmp_wa-m = my_input-budat+4(2).
  49.     COLLECT tmp_wa INTO tmp_input.
  50. *  _rt 1.
  51. * Statt SORT Ein weiteres COLLECT in eine neue itab lohnt erst ab
  52. * 10^6 Eintraegen (bzw. mehr als 2002 verschiedenen Materialien)
  53.  
  54.   SORT tmp_input BY material.
  55. *  _rt 2.
  56.  
  57.   DO p_max_matnr TIMES.
  58. *   Suche den ersten Index fuer das naechstfolgende Material
  59.     ADD 1 TO tmp_mat.
  60.     READ TABLE tmp_input " INTO tmp_wa
  61.          WITH KEY material = tmp_mat
  62.          BINARY SEARCH TRANSPORTING NO FIELDS.
  63.     CASE sy-subrc.
  64.       WHEN 0.
  65. *       Ermitteln der Anzahl Monate und passenden Zaehler erhoehen
  66.         tabix_diff = sy-tabix - last_tabix.
  67. *       je mehr Eintraege die itab hat, desto wahrscheinlicher sind
  68. *       12 Monate pro Material vorhanden, zumindest im derzeitigen
  69. *       Testszenario (Anzahl verschiedener Materialien / Anzahl Eintraege
  70. *
  71. *       Wenn ich die wahrscheinliche Haeufigkeit der Faelle kenne,
  72. *       kann ich das auch in der Reihenfolge der WHEN-Anweisungen beruecksichtigen
  73.         CASE tabix_diff.
  74.           WHEN 12.
  75. *            ein ADD wegzulassen, war der Tipp von Stefan.
  76. *            Er wollte aber die Anzahl Ladenhueter am Ende berechnen,
  77. *            ich wollte diesen wahrscheinlichsten Fall weglassen und
  78. *            so hoffentlich mehr sparen - hat aber nicht geklappt.
  79.  
  80.              ADD 1 TO p_ergebnis-dauerbrenner.
  81.           WHEN 11 OR 10.
  82.             ADD 1 TO p_ergebnis-saison.
  83. *          WHEN OTHERS.
  84. *            ADD 1 TO p_ergebnis-ladenhueter.
  85.         ENDCASE.
  86. *      WHEN 4.
  87. **       Materialnummern, die gar nicht vorkommen, werden auch in der Referenzloesung gezaehlt:
  88. *
  89. **       (Da verhaelt Olafs Loesung sich noch anders als die Referenz-Routine.
  90. **       Eine kleine Aenderung korrigiert das - und spart evtl. noch ein wenig Laufzeit.)
  91.  *       ADD 1 TO p_ergebnis-ladenhueter.
  92.       WHEN 8.
  93. *       kein naechstes Material mehr, letzten Eintrag lesen
  94.         READ TABLE tmp_input INTO tmp_wa
  95.              INDEX sy-tfill TRANSPORTING material.
  96.         CASE tmp_wa-material.
  97.           WHEN p_max_matnr.
  98.             tabix_diff = sy-tfill - last_tabix + 1.
  99.             CASE tabix_diff.
  100.               WHEN 12.
  101.                 ADD 1 TO p_ergebnis-dauerbrenner.
  102.               WHEN 11 OR 10.
  103.                 ADD 1 TO p_ergebnis-saison.
  104. *              WHEN OTHERS.
  105. *                ADD 1 TO p_ergebnis-ladenhueter.
  106.             ENDCASE.
  107. *          WHEN OTHERS.
  108. **           dieses Material und alle weiteren Materialien sind Ladenhueter
  109. *           p_ergebnis-ladenhueter = p_ergebnis-ladenhueter - tmp_wa-material + p_max_matnr.
  110.         ENDCASE.
  111.         EXIT.
  112.     ENDCASE.
  113. *   zuletzt gelesenen Eintrag fuer naechsten Schleifendurchlauf merken
  114.     last_tabix = sy-tabix.
  115.   ENDDO.
  116. *  p_ergebnis-dauerbrenner = p_max_matnr - p_ergebnis-ladenhueter - p_ergebnis-saison.
  117.   p_ergebnis-ladenhueter = p_max_matnr - p_ergebnis-dauerbrenner - p_ergebnis-saison.
  118. *  _rt 3.
  119. *  format color off.
  120. ENDFORM.                    " vergleich
GeSHi ©
Willy1492
....
....
 
Posts: 581
Joined: Tue Dec 03, 2002 4:44 pm

Postby Willy1492 » Thu May 27, 2004 9:48 am

Und hier noch der Vergleich meiner ersten Lösung (nach Einbau von Stefans Tipp in meine Lösung und in Olafs Lösung):
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. 27.05.2004
  2. --------------------------------------------------------------------------------
  3. Anz. Eintraege in Tabelle:          10  Maximale Materialnummer:             3
  4. Dauerbrenner:                        0
  5. Saisonartikel:                       0
  6. Ladenhueter:                         3
  7.                         | Vergleich                             | Referenz
  8. Minimale Laufzeit:      |         27  ms                        |         32  ms
  9. Maximale Laufzeit:      |         64  ms                        |        195  ms
  10. Mittelwert Laufzeit:    |         35  ms                        |         66  ms
  11. Vergleichsroutine ist um Faktor                     1,19 schneller als Referenzr
  12. Vergleichsroutine ist um Faktor                     1,86 schneller (Summe)
  13. --------------------------------------------------------------------------------
  14. Anz. Eintraege in Tabelle:         100  Maximale Materialnummer:             7
  15. Dauerbrenner:                        0
  16. Saisonartikel:                       2
  17. Ladenhueter:                         5
  18.                         | Vergleich                             | Referenz
  19. Minimale Laufzeit:      |         94  ms                        |        136  ms
  20. Maximale Laufzeit:      |        126  ms                        |        267  ms
  21. Mittelwert Laufzeit:    |        101  ms                        |        163  ms
  22. Vergleichsroutine ist um Faktor                     1,45 schneller als Referenzr
  23. Vergleichsroutine ist um Faktor                     1,61 schneller (Summe)
  24.  
  25. --------------------------------------------------------------------------------
  26. Anz. Eintraege in Tabelle:       1.000  Maximale Materialnummer:            35
  27. Dauerbrenner:                       10
  28. Saisonartikel:                      22
  29. Ladenhueter:                         3
  30.                         | Vergleich                             | Referenz
  31. Minimale Laufzeit:      |        853  ms                        |      1.075  ms
  32. Maximale Laufzeit:      |        890  ms                        |      1.304  ms
  33. Mittelwert Laufzeit:    |        864  ms                        |      1.167  ms
  34. Vergleichsroutine ist um Faktor                     1,26 schneller als Referenzr
  35. Vergleichsroutine ist um Faktor                     1,35 schneller (Summe)
  36. --------------------------------------------------------------------------------
  37. Anz. Eintraege in Tabelle:      10.000  Maximale Materialnummer:           252
  38. Dauerbrenner:                      161
  39. Saisonartikel:                      90
  40. Ladenhueter:                         1
  41.                         | Vergleich                             | Referenz
  42. Minimale Laufzeit:      |     10.825  ms                        |     12.545  ms
  43. Maximale Laufzeit:      |     12.363  ms                        |     18.124  ms
  44. Mittelwert Laufzeit:    |     11.528  ms                        |     14.022  ms
  45. Vergleichsroutine ist um Faktor                     1,16 schneller als Referenzr
  46. Vergleichsroutine ist um Faktor                     1,22 schneller (Summe)
  47. --------------------------------------------------------------------------------
  48. Anz. Eintraege in Tabelle:     100.000  Maximale Materialnummer:         2.002
  49. Dauerbrenner:                    1.646
  50. Saisonartikel:                     355
  51. Ladenhueter:                         1
  52.                         | Vergleich                             | Referenz
  53. Minimale Laufzeit:      |    197.305  ms                        |    224.252  ms
  54. Maximale Laufzeit:      |    203.405  ms                        |    229.539  ms
  55. Mittelwert Laufzeit:    |    199.877  ms                        |    227.151  ms
  56. Vergleichsroutine ist um Faktor                     1,14 schneller als Referenzr
  57. Vergleichsroutine ist um Faktor                     1,14 schneller (Summe)
  58. --------------------------------------------------------------------------------
  59. Gesamtlaufzeit in Mikrosekunden:  6.036.417
GeSHi ©

Hm - aus irgendeinem Grund ist Olafs Lösung durch meine Änderung etwas langsamer geworden. Aber weiter oben stehen ja die Werte für seine Originalversion.

Bei der Geschwindigkeit kann man dann auch mal eine Million Einträge prüfen (hier aber nur mit 3 Wiederholungen):
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. Anz. Eintraege in Tabelle:   1.000.000  Maximale Materialnummer:        16.669
  2. Dauerbrenner:                   15.319
  3. Saisonartikel:                   1.348
  4. Ladenhueter:                         2
  5.                         | Vergleich                             | Referenz
  6. Minimale Laufzeit:      |  2.842.756  ms                        |  3.053.653  ms
  7. Maximale Laufzeit:      |  2.848.541  ms                        |  3.177.316  ms
  8. Mittelwert Laufzeit:    |  2.846.117  ms                        |  3.121.882  ms
  9. Vergleichsroutine ist um Faktor                     1,07 schneller als Referenzr
  10. Vergleichsroutine ist um Faktor                     1,10 schneller (Summe)
GeSHi ©

Olafs Lösung kommt meiner immer näher.

Noch eine Anmerkung:
Obwohl (für große Tabellen) viel weniger ADD-Anweisungen weggelassen werden,
ist die Variante, die Ladenhueter am Ende zu berechnen, ein wenig schneller.
(Ich spare mir aber, jetzt den geänderten Quelltext zu posten - das sollte jeder selbst hinkriegen.)

Und mein Quelltext
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. FORM vergleich USING    t_input     TYPE tyt_input
  2.                         p_max_matnr TYPE i
  3. *                       Uebergabe per Referenz ist schneller,
  4. *                       insbesondere das Kopieren der itab ist Aufwand
  5. *
  6. *                       und in dem Beispiel aendere ich die Parameter
  7. *                       auch nicht, dem Aufrufer kann es also egal sein,
  8. *                       dass ich die Werte nicht per VALUE uebergebe
  9.                CHANGING p_ergebnis  TYPE ty_ergebnis.
  10. *define _rt. " Test-Ausgabe Laufzeit von Einzelschritten
  11. *  format color &1.
  12. *  rt-from = rt-to.
  13. *  get run time field rt-to.
  14. *  rt-step = '&1'.
  15. *  rt-diff = rt-to - rt-from.
  16. *  write: / rt-tfill, rt-max, rt-step, rt-diff.
  17. *end-of-definition.
  18. *
  19. *  statics: begin of rt, max type i, tfill type i, step, from type i, to type i, diff type i, end of rt.
  20. *  TYPES ty_char_mat(4) TYPE x.
  21.  
  22.   DATA: tmp_mat TYPE i VALUE 1. " ty_char_mat VALUE 1.
  23.   DATA: last_tabix TYPE sy-tabix VALUE 1,
  24.         tabix_diff TYPE i,
  25.         my_input TYPE ty_input.
  26.   TYPES: BEGIN OF ty_input2,
  27.            material  TYPE i,
  28. *        vom BUDAT des Originaltyps interessiert mich nur der Monat
  29.            y(4)     TYPE c,
  30.            m(2)     TYPE c,
  31.            d(2)     TYPE c, " d kann ich auch weglassen
  32.          END OF ty_input2.
  33.  
  34.   DATA: tmp_wa TYPE ty_input2,
  35.         tmp_input TYPE STANDARD TABLE OF ty_input2
  36.                    WITH NON-UNIQUE KEY material m.
  37.  
  38.   CLEAR p_ergebnis.
  39.   CHECK NOT t_input IS INITIAL.
  40.   tmp_input = t_input.
  41. *  _rt 1.
  42.   SORT tmp_input BY material m.
  43. *  _rt 2.
  44.   DELETE ADJACENT DUPLICATES FROM tmp_input COMPARING material m.
  45. *  _rt 3.
  46.   DO p_max_matnr TIMES.
  47. *   Suche den ersten Index fuer das naechstfolgende Material
  48.     ADD 1 TO tmp_mat.
  49.     READ TABLE tmp_input " INTO tmp_wa
  50.          WITH KEY material = tmp_mat
  51.          BINARY SEARCH TRANSPORTING NO FIELDS.
  52.     CASE sy-subrc.
  53.       WHEN 0.
  54. *       Ermitteln der Anzahl Monate und passenden Zaehler erhoehen
  55.         tabix_diff = sy-tabix - last_tabix.
  56. *       je mehr Eintraege die itab hat, desto wahrscheinlicher sind
  57. *       12 Monate pro Material vorhanden, zumindest im derzeitigen
  58. *       Testszenario (Anzahl verschiedener Materialien / Anzahl Eintraege
  59. *
  60. *       Wenn ich die wahrscheinliche Haeufigkeit der Faelle kenne,
  61. *       kann ich das auch in der Reihenfolge der WHEN-Anweisungen beruecksichtigen
  62.         CASE tabix_diff.
  63.           WHEN 12.
  64. *            ein ADD wegzulassen, war der Tipp von Stefan.
  65. *            Er wollte aber die Anzahl Ladenhueter am Ende berechnen,
  66. *            ich lasse lieber diesen wahrscheinlichsten Fall weg und
  67. *            spare so hoffentlich mehr
  68.  
  69. *            ADD 1 TO p_ergebnis-dauerbrenner.
  70.           WHEN 11 OR 10.
  71.             ADD 1 TO p_ergebnis-saison.
  72.           WHEN OTHERS.
  73.             ADD 1 TO p_ergebnis-ladenhueter.
  74.         ENDCASE.
  75.       WHEN 4.
  76. *       Materialnummern, die gar nicht vorkommen, werden auch in der Referenzloesung gezaehlt:
  77. *       (Da verhaelt Olafs Loesung sich noch anders als die Referenz-Routine.
  78. *       Eine kleine Aenderung korrigiert das - und spart evtl. noch ein wenig Laufzeit.)
  79.         ADD 1 TO p_ergebnis-ladenhueter.
  80.       WHEN 8.
  81. *       kein naechstes Material mehr, letzten Eintrag lesen
  82.         READ TABLE tmp_input INTO tmp_wa
  83.              INDEX sy-tfill TRANSPORTING material.
  84.         CASE tmp_wa-material.
  85.           WHEN p_max_matnr.
  86.             tabix_diff = sy-tfill - last_tabix + 1.
  87.             CASE tabix_diff.
  88.               WHEN 12.
  89. *                ADD 1 TO p_ergebnis-dauerbrenner.
  90.               WHEN 11 OR 10.
  91.                 ADD 1 TO p_ergebnis-saison.
  92.               WHEN OTHERS.
  93.                 ADD 1 TO p_ergebnis-ladenhueter.
  94.             ENDCASE.
  95.           WHEN OTHERS.
  96. *           dieses Material und alle weiteren Materialien sind Ladenhueter
  97.             p_ergebnis-ladenhueter = p_ergebnis-ladenhueter - tmp_wa-material + p_max_matnr.
  98.         ENDCASE.
  99.         EXIT.
  100.     ENDCASE.
  101. *   zuletzt gelesenen Eintrag fuer naechsten Schleifendurchlauf merken
  102.     last_tabix = sy-tabix.
  103.   ENDDO.
  104.   p_ergebnis-dauerbrenner = p_max_matnr - p_ergebnis-ladenhueter - p_ergebnis-saison.
  105. *  _rt 4.
  106. *  format color off.
  107. ENDFORM.                    " vergleich
GeSHi ©
Willy1492
....
....
 
Posts: 581
Joined: Tue Dec 03, 2002 4:44 pm

Postby Fabian1957 » Thu May 27, 2004 3:45 pm

Nur mal so der Vollständigkeithalber mein 'Quältext' (bin zum weiteren optimieren bisher noch nicht gekommen):

Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. FORM vergleich USING    value(t_input)     TYPE tyt_input
  2.                         value(p_max_matnr) TYPE i
  3.                CHANGING p_ergebnis         TYPE ty_ergebnis.
  4. * Idee - alle möglichen Werte durchgehen und sehen, wo die auftreten.
  5.   DATA: BEGIN OF monate,
  6.            jan(1) TYPE n,
  7.            feb(1) TYPE n,
  8.            mar(1) TYPE n,
  9.            apr(1) TYPE n,
  10.            mai(1) TYPE n,
  11.            jun(1) TYPE n,
  12.            jul(1) TYPE n,
  13.            aug(1) TYPE n,
  14.            sep(1) TYPE n,
  15.            okt(1) TYPE n,
  16.            nov(1) TYPE n,
  17.            dez(1) TYPE n,
  18.          END OF monate.
  19.  
  20.   DATA: wa_input         TYPE ty_input,
  21.         testmat          TYPE ty_material,
  22.         buchung(1)       TYPE n,
  23.         buchungen_monate TYPE i,
  24.         my_monat(2)      type n.
  25.  
  26. *  DATA: tmp_input TYPE SORTED TABLE OF ty_input
  27. *                   WITH NON-UNIQUE KEY material budat.
  28.  
  29.   data: tmp_input type standard table of ty_input.
  30.  
  31.   SORT t_input BY material budat+4(2).
  32.   tmp_input = t_input.
  33.   CLEAR p_ergebnis.
  34.   DO p_max_matnr TIMES.
  35.  
  36.     testmat = sy-index.
  37.     CLEAR: monate, my_monat, buchungen_monate.
  38.  
  39.     do 12 times.
  40.     my_monat = sy-index.
  41.     read table tmp_input with key material = testmat
  42.                                   budat+4(2) = my_monat
  43.                                   transporting no fields
  44.                                   binary search.
  45.  
  46.     if sy-subrc eq 0.
  47.      add 1 to buchungen_monate.
  48.     endif.
  49.     enddo.
  50.  
  51.     IF buchungen_monate = 12.
  52.       ADD 1 TO p_ergebnis-dauerbrenner.
  53.     ELSEIF buchungen_monate >= 10.
  54.       ADD 1 TO p_ergebnis-saison.
  55.     ELSE.
  56.       ADD 1 TO p_ergebnis-ladenhueter.
  57.     ENDIF.
  58.  
  59.   ENDDO.
  60.  
GeSHi ©


Hermann
Fabian1957
....
....
 
Posts: 535
Joined: Mon Dec 02, 2002 11:34 am

Postby Grace3566 » Thu May 27, 2004 4:23 pm

Hallo Hermann,
Du bist auf dem richtigen Weg. Ich habe Deine Routine nur minimal angepasst und die folgenden Ergebnisse erzielt (Deine geposteten Werte stimmten mit denen auf meinem System überein).

10 1,05 schneller (vorher 1,15 langsamer)
100 2,07 ( vorher 1,65 )
1.000 4,99 ( vorher 3,71 )
10.000 24,73 ( vorher 16,58 )
100.000 232,95 ( vorher 119,26 )

Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. FORM vergleich USING          t_input      TYPE tyt_input
  2.                               p_max_matnr  TYPE i
  3.                CHANGING p_ergebnis         TYPE ty_ergebnis.
  4. * Idee - alle möglichen Werte durchgehen und sehen, wo die auftreten.
  5.  
  6.   TYPES: BEGIN OF matmm,
  7.            material TYPE ty_material,
  8.            mm(2)    TYPE n,
  9.          END   OF matmm.
  10.  
  11.   DATA: wa_input         TYPE ty_input,
  12.         testmat          TYPE ty_material,
  13.         buchungen_monate TYPE i,
  14.         my_monat(2)      TYPE n.
  15.  
  16.   DATA: tmp_input TYPE HASHED TABLE OF matmm WITH UNIQUE KEY material mm,
  17.         l_input   LIKE LINE OF tmp_input.
  18.  
  19.   LOOP AT t_input INTO wa_input.
  20.     l_input-material = wa_input-material.
  21.     l_input-mm       = wa_input-budat+4(2).
  22.     COLLECT l_input INTO tmp_input.
  23.  
  24.   CLEAR p_ergebnis.
  25.   DO p_max_matnr TIMES.
  26.     testmat = sy-index.
  27.     CLEAR buchungen_monate.
  28.  
  29.     DO 12 TIMES.
  30.       my_monat = sy-index.
  31.       READ TABLE tmp_input WITH TABLE KEY material = testmat
  32.                                           mm       = my_monat
  33.                            TRANSPORTING NO FIELDS.
  34.       IF sy-subrc EQ 0.
  35.         ADD 1 TO buchungen_monate.
  36.       ENDIF.
  37.     ENDDO.
  38.  
  39.     IF buchungen_monate = 12.
  40.       ADD 1 TO p_ergebnis-dauerbrenner.
  41.     ELSEIF buchungen_monate >= 10.
  42.       ADD 1 TO p_ergebnis-saison.
  43.     ELSE.
  44.       ADD 1 TO p_ergebnis-ladenhueter.
  45.     ENDIF.
  46.   ENDDO.
  47. ENDFORM.                    "vergleich
GeSHi ©


VG Olaf
Grace3566
..
..
 
Posts: 62
Joined: Thu Oct 09, 2003 6:24 am

Postby Grace3566 » Thu May 27, 2004 4:35 pm

Hallo zusammen,
da nun zu allen bisherigen Ergebnissen auch die Codings vorliegen, will ich Euch das Coding zu meiner zweiten Variante nicht vorenthalten.

Interessant finde ich mal wieder wie viele Wege nach Rom führen.

Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1. form vergleich   using          t_input      type tyt_input
  2.                                 p_max_matnr  type i
  3.                  changing p_ergebnis         type ty_ergebnis.
  4.  
  5.   types: begin of ty_mon,
  6.            material  type ty_material,
  7.            monat(6),
  8.          end of ty_mon.
  9.  
  10.   types: begin of ty_col,
  11.            material type ty_material,
  12.            count type i,
  13.          end of ty_col.
  14.  
  15.   data: t_mon type hashed table of ty_mon
  16.                     with unique key table_line,
  17.         l_mon like line of t_mon.
  18.  
  19.   data: t_col type hashed table of ty_col
  20.                     with unique key material,
  21.         l_col like line of t_col.
  22.  
  23.   l_col-count = 1.
  24.   loop at t_input into l_mon.
  25.     insert l_mon into table t_mon.
  26.     check sy-subrc = 0.
  27.     l_col-material = l_mon-material.
  28.     collect l_col into t_col.
  29.  
  30.   clear p_ergebnis.
  31.  
  32.   loop at t_col into l_col.
  33.     if l_col-count = 12.
  34.       add 1 to p_ergebnis-dauerbrenner.
  35.     elseif l_col-count >= 10.
  36.       add 1 to p_ergebnis-saison.
  37.     else.
  38.       add 1 to p_ergebnis-ladenhueter.
  39.     endif.
  40. endform.                    " vergleich
GeSHi ©


VG Olaf
Grace3566
..
..
 
Posts: 62
Joined: Thu Oct 09, 2003 6:24 am

Postby Willy1492 » Mon May 31, 2004 11:03 am

Olaf P. hat geschrieben:Hallo zusammen,
da nun zu allen bisherigen Ergebnissen auch die Codings vorliegen, will ich Euch das Coding zu meiner zweiten Variante nicht vorenthalten.

Das duerfte wohl die für die meisten Fälle beste Lösung sein, noch dazu kompakter Code.
Was will man mehr.
Die Idee, die erste Hashed Table mit INSERT zu füllen und dann den SY-SUBRC auszuwerten, war genial.
(Und ab Release 6.40 kann die hashed table auch wesentlich mehr als 2^22 Einträge aufnehmen.
Zwar wird das COLLECT bzw. INSERT dann wegen zunehmehder Hash-Kollisionen immer langsamer, aber nicht so sehr, dass andere Alternativen wirklich besser sind.)

Allenfalls für Extremfälle kann man mit mehr Aufwand noch alternative Lösungen finden, die besser sind als Dein Besipiel.
Diese haben aber im Gegensatz zu Deinem Beispiel den Nachteil, dass (abgesehen von der schlechteren Wartbarkeit wegen des komplexeren Algorithmus) die Performance total in den Keller geht, wenn der Input gravierend von dem abweicht, wofür die Alternativ-Lösungen optimiert wurden.
Willy1492
....
....
 
Posts: 581
Joined: Tue Dec 03, 2002 4:44 pm

Postby Ilja583 » Tue Jun 01, 2004 12:19 am

Resumee:

Nach Auswertung der geposteten Routinen stelle ich fest, dass der beste Lösungsansatz wohl ein 2-Schritt-Verfahren zu sein scheint.

Im 1. Schritt wird die Tabelle auf eine sortierte und verdichtete Form gebracht ( pro Monat maximal 1 Einttrag ) und im 2. Schritt wird dann gezählt wieviele Einträge pro Material vorhanden sind.

Der pragmatische Ansatz wäre
Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1.   FIELD-SYMBOLS: <input> TYPE ty_input.
  2.   DATA: zaehler TYPE i.
  3.   CLEAR p_ergebnis.
  4. * 1. Schritt - sortierte, verdichtete Tabelle
  5.  
  6.   SORT t_input by material budat.
  7.   DELETE ADJACENT DUPLICATES FROM t_input COMPARING budat&#40;6&#41;.
  8.  
  9. * 2. Schritt - zählen, wie oft jedes Material gebucht wurde
  10.   LOOP AT t_input ASSIGNING <input>.
  11.  
  12.     AT NEW material.
  13.       CLEAR zaehler.
  14.     ENDAT.
  15.  
  16.     ADD 1 TO zaehler.
  17.  
  18.     AT END OF material.
  19.       IF zaehler = 12.
  20.         ADD 1 TO p_ergebnis-dauerbrenner.
  21.       ELSEIF zaehler >= 10.
  22.         ADD 1 TO p_ergebnis-saison.
  23.       ENDIF.
  24.     ENDAT.
  25.  
  26.   p_ergebnis-ladenhueter = p_max_matnr - p_ergebnis-dauerbrenner
  27.                                        - p_ergebnis-saison.
  28.  
GeSHi ©

Dies ist ein übersichtlicher und für spätere Entwickler leicht zu durchschauender Code und grob das, was ich wohl bei einem Kunden so stehen lassen würde.

Die weiteren Optimierungen beruhen darauf (wie Frank ja gepostet hatte), dass ein Großteil der Rechenzeit beim Sortieren der Tabelle draufgeht .
Eine überarbeitete Version hat Frank dann ja auch schon in seinem 1. (allgemeingültigem) Posting mitgeliefert, in dem der 1. Schritt nun so aussah.

Code: [Select all] [Expand/Collapse] [Download] (Untitled.txt)
  1.  
  2. * 1. Schritt überarbeitet
  3.  LOOP AT t_input INTO my_input.
  4.     tmp_wa-material = my_input-material.
  5.     tmp_wa-m = my_input-budat+4&#40;2&#41;.
  6.     COLLECT tmp_wa INTO tmp_input.
GeSHi ©


Den finalen Schliff hat dann Olaf P. in seinem letzten Posting gegeben, in dem er die Hauptarbeit des Zählens in den 1. Schritt integriert hat mit dem Tool, das uns SAP zur Hand gibt - eine Tabelle und dem Befehl COLLECT. Die Methode den SY-SUBRC des Collect, den er für den 1. Schritt sowieso braucht, auszuwerten und in einer 2. Tabelle mitzählen zu lassen wie häufig pro Material der Collect wirklich einen neuen Eintrag erzeugt hat, führt zu einem effizienten und elegantem Code.

Abschließende Bemerkungen:
1.) Weitere Effizienzsteigerungen lassen sich dadurch erzielen, dass man Aussagen über die statistische Verteilung in der Tabelle INPUT machen kann und die Routinen dementsprechend anpasst.

2.) Wenn man nicht über "SORT, DELETE-ADJACENT-DUPLICATES" geht, sondern die verbesserte Version eine sortierte, verdichtete Tabelle zu erstellen verwendet, wird die Origaltabelle (INPUT) ja nicht verändert. Und da ein nicht unerheblicher Teil der Laufzeit für die Wertübergabe der Inputtabelle draufgeht, könnte man stattdessen eine Referenzübergabe verwenden (war aber nicht erlaubt lt. Aufgabenstellung - somit nur als Anmerkung)

3.) Ich habe die oben gepostete Demolösung mal mit der letzten Version von Olaf P. ( die schnellste gepostete) verglichen. Olafs Version ist bei 100.000 Einträgen ca. um den Faktor 2 schneller als die pragmatische Lösung.
Das sind Welten - aber verglichen mit der Ersparnis der Demolösung gegenüber der ursprünglichen Referenzlösung (Faktor > 100 ) nicht so viel.
Da kommts dann wohl auf das Projekt und die genauen Umgebungsparameter (Kunden) an, die bestimmen ob der pragmatische Ansatz schon ausreicht oder noch daran rumgefeilt werden muss. Möglich ist viel, wie man sieht...

4.) Danke an alle, die hier gepostet haben.

5.) Ich hänge noch eine überarbeitete Version des Programms an, welches die geposteten Lösungen enthält und es zulässt die einzelnen Versionen (bzw eigene) gegeneinander auszutesten. (Und statt des Mittelwert der Laufzeit alternativ die minimale Laufzeit bei mehreren Durchläufen als Faktor zur Berechnung zulassen kann. Hatte mich Frank drauf aufmerksam gemacht und beim Betrachten und Vergleichen der verschiedenen Lösungen bin ich auch zu der Ansicht gelangt, dass dies wohl der bessere Ansatz ist)
Ilja583
.....
.....
 
Posts: 1372
Joined: Wed Jan 08, 2003 3:00 pm

Postby Grace3566 » Tue Jun 01, 2004 7:01 am

Moin zusammen!

@Frank
Vielen Dank für das Kompliment. :)

@Stefan
M.E. fehlt beim "Delete Adjacent .. Comparing" noch die Materialnummer.

Es müsste auch noch ein bisschen schneller gehen, wenn man den "Clear zaehler" in die "End of Material"-Verarbeitung verschiebt und sich so die "At new"-Verarbeitung spart.

Bei meinen Tests hat sich auch gezeigt, dass der ASSIGNING langsamer als die Übertragung in eine Workarea ist. In der Praxis wird es schneller sein, da die Strukturen selten so schmal sind.

Ich finde es ist auch wichtig, dass Programme möglichst flexibel und robust sind. Konkret in Deinem Beispiel: Welche Routinen funktionieren noch, wenn die Materialnummern nicht numerisch oder fortlaufend sind?

@All
Hier ging es nur um die Performance und nichts anderes. In der Praxis würde ich eine übersichtlichere und wartbarere Routine einer schnelleren vorziehen, wenn die Differenz nur wenige Prozent beträgt. Ausnahme, das Programm läuft bspw. minütlich im Hintergrund und es kommt auf jede Sekunde an, die der Hintergrundprozess früher frei wird.

VG und eine schöne Woche
Olaf
Grace3566
..
..
 
Posts: 62
Joined: Thu Oct 09, 2003 6:24 am

Previous

Return to Tips + Tricks & FAQs

Who is online

Users browsing this forum: No registered users and 10 guests