1. Pascal / Говнокод #7977

    +102

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    89. 89
    90. 90
    91. 91
    92. 92
    const n = 50;
    type vec = array [1..n*4] of system.word;
    function vprod(const a, b: vec): Cardinal;
      var i: longInt;
      begin
        result := 0;
        for i := 1 to high(vec) do inc(result, a[i] * b[i]);
      end;
    function vprod_asm1(const a, b: vec): Cardinal; assembler;
      asm
         push ebx
         push ecx
         push edx
         push esi
         push edi
         xor ebx, ebx
         mov ecx, n*4
         mov esi, a
         mov edi, b
         xor eax, eax
         cld
      @@l:
         mov   ax, word ptr [esi]
         lea   esi, [esi+2]
         mul   word ptr [edi]
         lea   edi, [edi+2]
         shl   edx, 16
         mov   dx, ax
         add   ebx, edx
         dec   ecx
         jne   @@l
         mov   eax, ebx
         pop edi
         pop esi
         pop edx
         pop ecx
         pop ebx
      end;
    function vprod_asm2(const a, b: vec): Cardinal; assembler;
      asm
        push ebx
        push ecx
        push edx
        push esi
        push edi
        xor ebx, ebx
        mov ecx, n*4
        mov esi, a
        mov edi, b
        xor eax, eax
        cld
      @@l:
        lodsw
        movzx   edx, WORD PTR [edi]
        imul    edx
        lea     edi, [edi+2]
        add     ebx, eax
        loop  @@l
        mov   eax, ebx
        pop edi
        pop esi
        pop edx
        pop ecx
        pop ebx
      end;
    function vprod_mmx (const a, b: vec): Cardinal; assembler;
      var muls: record l, h: Cardinal end;
      asm
        push ebx
        push ecx
        push esi
        push edi
        mov ecx, n
        mov esi, a
        mov edi, b
        xor eax, eax
        lea     ebx, muls
      @@l:
        db $0F,$6F,$06  // movq    mm0, [esi]
        db $0F,$F5,$07  // pmaddwd mm0, [edi]
        lea     esi, [esi+8]
        db $0F,$7F,$03  // movq    [ebx], mm0
        lea     edi, [edi+8]
        add     eax, [ebx]
        add     eax, [ebx+4]
        loop    @@l
        db $0F,$77 // emms
        pop edi
        pop esi
        pop ecx
        pop ebx
      end;

    По просьбам трудящихся публикую модифицированную версию примера MMXTEST.PAS из комплекта компилятора TMT Pascal. Программа находит скалярное произведение двух векторов. Далее должен быть основной блок с фрагментами типа for i := 1 to 100000 do vprod(a, b); , которые я не стал публиковать ввиду ограничений. Функция vprod_asm1 — почти оригинальный код TMT, функция vprod_asm2 — мой оптимизированный вариант. Результаты запуска на двух машинах (таймер получал по RDTSC):
    AMD K6-2-333 МГц, FSB 66 МГц.

    Delphi7:
    Pascal = 0.550 sec.
    Asm x86 (original) = 1.034 sec.
    Asm x86 (optimized) = 0.490 sec.
    Asm MMX = 0.130 sec.
    С директивой $O- первый результат 0.853 sec.
    Замена loop на dec ecx + jne увеличивает результаты на 0,015 c.

    FPC:
    Pascal = 1.387 sec.
    Asm x86 (original) = 1.199 sec.
    Asm x86 (optimized) = 0.510 sec.
    Asm MMX = 0.124 sec.

    TMT:
    Pascal = 0.914 sec.
    Asm x86 (original) = 1.037 sec.
    Asm x86 (optimized) = 0.494 sec.
    Asm MMX = 0.126 sec.

    VP:
    Pascal = 0.520 sec.
    Asm x86 (original) = 1.033 sec.
    Asm x86 (optimized) = 0.493 sec.
    Asm MMX = 0.146 sec.
    С директивами $Q+,R+ первый результат 1.032 sec.
    С директивой $Speed- первый результат 0.731 sec.

    ------------------------------
    Celeron 1,86 ГГц, FSB 533 МГц.

    Delphi7:
    Pascal = 0.025 sec.
    Asm x86 (original) = 0.091 sec.
    Asm x86 (optimized) = 0.082 sec.
    Asm MMX = 0.044 sec.

    TMT:
    Pascal = 0.109 sec.
    Asm x86 (original) = 0.087 sec.
    Asm x86 (optimized) = 0.079 sec.
    Asm MMX = 0.042 sec.

    Запостил: inkanus-gray, 25 Сентября 2011

    Комментарии (26) RSS

    • Я думаю, мысль пояснять не стоит.
      Ответить
    • дельфи как дельфи.
      Ответить
    • На всякий случай кое-какие выводы:
      §1. Оригинальный ассемблерный вариант функции из примера на некоторых машинах работает медленнее паскалевского.
      §2. Код, сгенерированный Дельфи и Виртуал Паскалем, вдвое быстрее оригинального ассемблерного на некоторых машинах.
      §3. Код, сгенерированный Дельфи 7, на современных машинах быстрее примера в MMX.
      §4. Проверка целочисленного переполнения и границ диапазонов не влияет на быстродействие данного кода в Дельфи 7, в отличие от, например, VP, но даже его код получается не тормознее первой версии ассемблерного.
      Ответить
      • если код, сгенеренный дельфи, быстрее, чем ассемблерный - фиговый из вас ассемблерокодер...
        кстати, не так давно я уже встречал эту вонь про "дельфи быстрее ассемблера"
        Ответить
        • > если код, сгенеренный дельфи, быстрее, чем ассемблерный - фиговый из вас ассемблерокодер...
          Спасибо, капитан.
          Ответить
        • А ты лично пробовал обогнать компилятор дельфи на простом целочисленном коде? Не говори о том, чего не знаешь.
          Ответить
          • Я помню, что ты защищаешь компилятор дельфи
            Ответить
            • У него (Д7) есть серьёзный минус - отсутствие инлайна, тупо сделано то, что всякие синусы и даже сраные округления компилируются в вызов функции, но всякая фигня типа распихать целые числа по регистрам у него сделана отлично.
              Ответить
          • если с оптимизацией - сложно. была б она еще и без глюков
            Ответить
            • Расскажи хотя бы про один глюк оптимизации в Д7.
              Только не про
              i: word;
              for i := 0 to L.Count-1 do...
              Ответить
              • попадался код без хинтов и варнингов, без форов вообще, но результат был разный в зависимости от галочки оптимизации. До сути я не докопался (тогда времени не было), так что можно считать мое утверждение бездоказательным. если еще раз попадется обещаю запостить.
                ну или код корявый был:)
                Ответить
                • Я надеюсь, это не про просмотр счётчика цикла в отладчике?
                  Ответить
                  • нет конечно - про реализацию фора в курсе:)
                    когда найду - выложу.
                    Ответить
      • кончайте спамить.

        для математики есть (во-первых) компетентные компиляторы (например, *любой* С компилятор) и (во-вторых) кучи мат библиотек (таже STL's valarray хорошая стартовая точка).

        переизобретание велосипеда, всего лишь потому что с Дельфой прилагаются только квадратные колеса, наталкивает на мысли что вы пользуетесь неправильной тулзой для поставленой задачи.

        ЗЫ есть такой крутой пример: Quake1, один из самых быстрых SW 3D движков когда либо написаный, не содержит ни одной строчки асма.
        Ответить
        • > кончайте спамить.
          Я сначала не хотел, но в каком-то говнокоде мне предложили опубликовать результаты испытаний отдельным ГК.

          > например, *любой* С компилятор
          Конкретные примеры компиляторов с конкретными примерами ключей и сгенерированного кода имеются? Пока мне удалось заметить только мелочи типа add eax, 1 вместо inc eax в некоторых режимах, а также замену leave на явные операции с регистрами.

          > кучи мат библиотек
          Здесь пример слишком примитивный. Да и целью было не перемножить два вектора, а проиллюстрировать разные подходы.
          Ответить
          • > Конкретные примеры компиляторов с конкретными примерами ключей и сгенерированного кода имеются?

            олололо. сам попросил. aCC 6.06 c +O3 на Итанике 2:
            0000000000000010 <vprod(long*,long*)>:
              10:   [MMI]       alloc r31=ar.pfs,8,8,8
              16:               mov r11=r32
              1c:               mov.i r18=ar.lc
              20:   [MIB]       mov r10=r33
              26:               mov r8=0
              2c:               nop.b 0x0;;
              30:   [MMI]       adds r34=1024,r11
              36:               mov r15=r10
              3c:               mov r9=pr
              40:   [MMI]       mov r14=r11
              46:               cmp.ne.or.andcm p16,p17=42,r0
              4c:               cmp.eq.and p18,p0=42,r0;;
              50:   [MMI]       lfetch [r34],128
              56:               adds r35=1024,r10
              5c:               mov.i ar.lc=24
              60:   [MMI]       mov r17=r10
              66:               adds r16=8,r15
              6c:               mov r15=r11;;
              70:   [MII]       adds r14=8,r14
              76:               mov.i ar.ec=3
              7c:               nop.i 0x0;;
              80:   [MMF] (p16) ld8 r32=[r17],16
              86:         (p16) ld8 r19=[r16],64
              8c:         (p17) xmpy.l f47=f52,f33
              90:   [MII] (p18) add r37=r28,r39
              96:               nop.i 0x0
              9c:               nop.i 0x0;;
              a0:   [MMI] (p16) setf.sig f45=r32
              a6:         (p16) ld8 r33=[r15],16
              ac:         (p18) add r39=r23,r37
              b0:   [MMF] (p16) ld8 r20=[r14],64
              b6:         (p16) setf.sig f48=r19
              bc:         (p17) xmpy.l f52=f38,f35;;
              c0:   [MMF] (p16) setf.sig f46=r33
              c6:         (p16) setf.sig f50=r20
              cc:         (p17) xmpy.l f41=f49,f36
              d0:   [MMF] (p16) ld8 r32=[r15],8
              d6:         (p16) ld8 r19=[r17],8
              dc:         (p17) xmpy.l f38=f51,f34;;
              e0:   [MMI] (p16) setf.sig f49=r32
              e6:         (p16) setf.sig f51=r19
              ec:         (p18) add r19=r22,r39
              f0:   [MMI] (p16) ld8 r28=[r15],8
              f6:         (p16) ld8 r33=[r17],8
              fc:               nop.i 0x0;;
             100:   [MMI] (p17) getf.sig r37=f44
             106:         (p16) setf.sig f32=r33
             10c:               nop.i 0x0
             110:   [MMI] (p16) ld8 r27=[r15],8
             116:         (p16) ld8 r32=[r17],8
            Ответить
          • 11c:               nop.i 0x0;;
             120:   [MMI] (p18) getf.sig r20=f39
             126:         (p16) setf.sig f34=r32
             12c:               nop.i 0x0
             130:   [MMI] (p16) ld8 r26=[r15],8
             136:         (p16) ld8 r33=[r17],8
             13c:               nop.i 0x0;;
             140:   [MMI] (p18) getf.sig r21=f42
             146:         (p16) setf.sig f35=r33
             14c:               nop.i 0x0
             150:   [MMI] (p16) ld8 r32=[r15],8
             156:         (p16) ld8 r23=[r17],8
             15c:               nop.i 0x0;;
             160:   [MMI] (p17) getf.sig r22=f52
             166:         (p16) setf.sig f33=r23
             16c:               nop.i 0x0
             170:   [MMI] (p16) ld8 r33=[r15],8
             176:         (p16) ld8 r24=[r17],8
             17c:               nop.i 0x0;;
             180:   [MMF] (p17) getf.sig r23=f47
             186:         (p16) setf.sig f44=r33
             18c:         (p16) xmpy.l f42=f46,f45
             190:   [MMF]       nop.m 0x0
             196:               nop.m 0x0
             19c:         (p16) xmpy.l f36=f50,f48;;
             1a0:   [MMF] (p17) getf.sig r25=f43
             1a6:         (p16) setf.sig f46=r24
             1ac:         (p16) xmpy.l f39=f49,f51;;
             1b0:   [MMI] (p17) getf.sig r24=f37
             1b6:         (p16) setf.sig f51=r28
             1bc:               nop.i 0x0;;
             1c0:   [MMI] (p17) getf.sig r28=f40
             1c6:         (p16) setf.sig f37=r27
             1cc:         (p18) add r39=r21,r19;;
             1d0:   [MMI] (p16) lfetch [r35]
             1d6:         (p16) setf.sig f48=r26
             1dc:         (p18) add r36=r20,r39
             1e0:   [MII] (p16) setf.sig f50=r32
             1e6:         (p16) adds r33=128,r35;;
             1ec:         (p18) add r8=r38,r36;;
             1f0:   [MMI] (p17) add r36=r25,r8;;
             1f6:         (p17) add r38=r24,r36
             1fc:               nop.i 0x0
             200:   [MFB]       nop.m 0x0
             206:         (p16) xmpy.l f43=f44,f46
             20c:               br.ctop.dptk.many 80 <vprod(long*,long*)+0x70
             210:   [MII]       mov r33=r10
             216:               mov.i ar.lc=r18
             21c:               mov r32=r11;;
             220:   [MIB]       nop.m 0x0
             226:               mov pr=r9,0xfffffffffffffffe
             22c:               br.ret.sptk.many b0;;

            вот тебе и пайплайнинг, и префетч c бранчпредикшн, и все че только можно.
            Ответить
          • а вот и С код, подобно оригиналу использующий слегка кривые константы:
            #define MAX     50
            #define REAL_MAX (MAX*4)
            
            long
            vprod( long *a, long *b )
            {
                    int i;
                    long r = 0;
                    for (i=0; i<REAL_MAX; i++)
                    {
                            r += a[i]*b[i];
                    }
                    return r;
            }
            Ответить
        • * ФГМная привычка путать понятия «флуд» и «спам»;
          Ответить
    • несколько рекомендаций по оптимизации на ассемблере:
      1) уменьшить обращения к памяти (тут основные тормоза): mov [...], ... mov ..., [...] push ... pop ...
      2) eax, ecx, edx не нужно сохранять
      3) аргументы в функцию передаются по порядку: eax, edx, ecx, результат возвращается в eax. если метод класса, то первым параметром будет self
      4) FPC в начале и в конце процедуры добавляет push ebp / pop ebp, даже если это не требуется
      PS. оптимизация в старых версиях дельфи глючная.
      Ответить
    • вот этот вариант у меня чуть быстрее того, что с оптимизацией:
      asm
      push ebx
      push esi
      push edi
      xor ebx, ebx
      mov ecx, 2*n
      @@l:
      movzx edi, [eax]
      mov esi, [edx]
      imul edi, esi
      and edi, $FFFF
      add ebx, edi
      movzx edi, [eax + 2]
      shr esi, 16
      imul edi, esi
      add ebx, edi
      add eax, 4
      add edx, 4
      dec ecx
      jnz @@l
      mov eax, ebx
      pop edi
      pop esi
      pop ebx
      end;
      но он не будет работать для нечетного размера массива
      Ответить
      • >но он не будет работать для нечетного размера массива
        Отдельным ГК запостить не желаете? :)
        Ответить
    • показать все, что скрытоvanished
      Ответить

    Добавить комментарий