НовостиОбзорыСобытияIT@WorkРеклама
Идеи и практики автоматизации:

Блог

Нужно ли бороться за эффективность программного кода? В каких случаях и зачем? Как? 2.0

В заголовке поста указана версия 2.0, поскольку пост с таким названием уже был опубликован в понедельник, но несмотря состоявшуюся там полезную дискуссию, разговора по собственно этому вопросу там, на мой взгляд, не состоялось. А вопросы мы представляются важными, без ответа на них рассматривать конкретные методы повышения эффективности кода, по большому счету, вообще не имеет смысла.

А именно такое "методы повышения" будут в центре внимания вебинара "“Как с помощью инструментария разработчика сделать программный код эффективным”, подробнее о теме и зарегистрироваться – тут, который состоится в день "тройных чисел" – 12.12.12, но в 11:00.

К сожалению, по организационно-техническим причинам презентеры (слово хотя и новое, но мне видится оно лучше отражает суть дела, чем "спикеры") из Нижегородского исследовательского центра Intel – Галина Санжарлинская, Екатерина Антакова


[spoiler]
– не могут напрямую поучаствовать в наших блог-дискуссиях.
Но все же по почте им удалось прислать мне послание – они следят за нашими разговорами и обещают: "Все вопросы относительно технических возможностей повышения эффективности кода с помощью программных инструментов Intel, приведенные в комментариях, мы постараемся прокомментировать в ходе вебинара".

Что ж, это отрадно…

Мой тезис по теме остается прежним: эффективность кода должна быть оптимальной. А именно: уровень эффективности – некий компромисс между целовой бизнес-задачей и затратами на повышение эффективности.

Пример из уже довольно далекой программистcкой жизни.

В 1989-91 гг. в рамках собственной разработки прикладного ПО для IBM PC AТ на QuickBasic сделал набор библиотек для пользовательского интерфейса (окна, меню, и многое другое). Которые сначала использовал "для себя", а потом преобразовал в коммерческий продукт (Kolesov QB Tools – "Повоем на луну Basic-DOS".

Сначала все это работало в текстовом режиме экрана (поди, и не помнит никто, что это такое), но все же – графика. Кстати, вот запустил рекламную демо: работает!



Это то, что мы тогда видели на полном экрана  монитора (14 дюймов, 80*25 символов). Немного посмотрел – самому дурно стало – сколько же кода тогда было написало! А ведь это – только капля от написанного тогда).

Так вот. Поначалу вся графика была написана на самом QB. По началу, все это вполне устраивало (а уж по сравнению с командной строкой, казалось просто фантастикой). Но когда привыкли к "оконным инновациям", скорость рисовки стала несколько утомлять. Не сказать, что это мешало работе пользователя (а поначалу это был я сам), но все же то, что процесс рисования был заметен – несколько раздражало.

Встал вопрос – как ускорить.
Средства Интел тут не помогли бы. По двум причинам: 1) было понятно, что простая оптимизация компилятору тут не поможет 2) никаких средств от Интела не было и в помине.

Нужны были меры организационно-алгоритмически-технологического характера.
1) Поменять язык (это  и не помогло бы)
2) поменять алгоритмы (тоже не вариант – все уже было "вылизано")
3) делать что-то еще

Простой вариант – это использовать для рисовки функции BIOSа. В QB такие возможности были, что я и сделал.
Было получено отличное ускорение, скорость была "вполне" – глазом процесс рисовки стал не виден.

Но что-то там было все же не очень хорошо. Сейчас уже точно не помню, но кажется, выявились две проблемы
Через БИОС была доступны не все нужные для графики функции.

Возникли проблемы с совместимостью.
К тому времени моими программами пользовался уже не только я сам, и выяснилось, что на других ПК (сейчас не помню точно, но что-то было) все работало не всегда так, как хотелось бы.

Короче говоря, функции вывода графики были переписаны на Ассемблере. И на этом все закончилось – все стало работать еще быстрее (по сравнению с БИОС) и вполне устойчиво.

НО!

Объем кода на Ассемблере в начальном варианте QB Tools составлял не более 1-5% от общего кода. По мере расширения функционала эта доля быстро приближалась к нулю (как было 5-10 подпрограмм, так и осталась).
Трудоемкость написания на Ассемблере была раз в 10-100 больше чем на QB. Объем кода был небольшой, а повозиться пришлось. К тому же там нужно было вносить кое-какие расширения.

А дальше было вот что. В 1994 году я приобрел свой первые персональный ПК (НИИ не мог купить, его устраивали AT) – 486DX. Таких тогда мало в России еще было.

И выяснилось, что Ассемблеровский вариант графики (текстовой, по крайней мере) для 486DX не нужен. Я стал на свой ПК работать с QB-графикой (так было проще делать компоновку загрузочного модуля, все же смешанное программирование – это некоторые неудобства…). А клиентам продавал (да, уже пошли продажи!) вариант в ASM – они работали на AT и XT.
А через пару лет и им стал компоновать с QB – они тоже перешли на более мощные ПК.

Вот такая история о том, нужно бороть за эффективность код. Зачем и как…

========

P.S. Чтобы уж логично закончить мемуары, приведу тут два варианта процедур "сохранения-вывода фрагмента экрана на BAS и ASM

DECLARE SUB WNKstore (irow%, icol%, idr%, idc%, a$, kod%)
DEFINT I-N
'***********************************************************
'************ K o l e s o v   Q B   T o o l s **************
'*         БИБЛИОТЕКА подпpогpамм QB_WNK  v.2.87           *
'* поддержка оконного интерфейса в текстовом режиме экрана *
'***********************************************************
'*                Модуль WNK_BAS.BAS                       *
'*           Процедуры работы с видеопамятью               *
'*---------------------------------------------------------*
'*    Альтернативный вариант модуля WNK_ASM.BAS            *
'*            - вывод средствами BASIC                     *
'***********************************************************
'==========================================================
'======================    ВНИМАНИЕ: ======================
'    Процедура WNKstore использует обращение к подпрограммам,
'    написанным на ассемблере:
'      CopyScrBox  - вариант для QuickBASIC v.4.0
'      CopyScrBox7  - вариант для BASIC v.7.0
'    Необходимо исправить непосредственно текст процедуры WNKstore
' для использования одной из этих подпрограмм.
'
'  ============  СЕЙЧАС текст соответствует v.4.0 ==================
'
'    При работе в среде QB необходимо создать
' двоичную библиотеку .LIB, .QLB
' (см. текст модуля WNKVIDEO.ASM) и загружать:
'
'        QB.EXE /L WNKVIDEO
'============================================================
'   СОСТАВ ПРОЦЕДУР МОДУЛЯ:
'    WNKstore - сохранение/восстановление окна
'    WNKshadow - вывод "тени" под окно
'    WNKscreen0 - установка активной и видимой страницы
'   **********************
'     Общие формальные параметры:
'     ---------------------------
'   irow, icol    - координаты левого верхнего угла окна (строка, колонка)
'   idr, jdl      - размеры окна (строк, колонок)
'-------------------------------------------------------------------
' сохранение/восстановление окна:
'  Call WNKstore (irow, icol, idr, jdl, a$, kstore)
'   - " -
'   a$    -  симв. переменная для хранения изображения окна (рабочий буфер)
'   kstore = 0 - чтение "окна" (сохранение)
'          = 1 - восстановление
'-------------------------------------------------------------------
' вывод "тени" под окно:
'   CALL WNKshadow(irow, icol, idr, jdl, Kft, Kbt)
'    Kft,Kbt - цвета тени
'-------------------------------------------------------------------
' определение активной и видимой странцы:
'   CALL WNKscreen0 (Apage%, Vpage%)
'     Apage%, Vpage% - номера активной и видимой странцы
'                      видеопамяти (0-7)
'***********************************************************************
     DIM SHARED Offset%
END

SUB WNKscreen0 (Apage%, Vpage%)
'
'    Установка параметров режима SCREEN 0
'  (это нужно для установки адресов видеопамяти)
'    Apage% - номер активной старницы
'    Vpage% - номер видимой старницы
'------------------------------------------------
     SCREEN 0, , Apage%, Vpage%
     Offset% = 4096 * Apage%        ' смещение видеопамяти
END SUB

SUB WNKshadow (irow, icol, idr, idc, Kft, Kbt)
'     вывод "тени" под окно
'    Kft,Kbt - цвета тени
''''''''''''''''''''''''''''''''''''''''''''
'  код атрибутов цвета тени:
   Kct = (Kft AND 15) + (Kbt AND 7) * 16
' --------------------------------------------------
'  вариант с модулем WNK_ASM.ASM:
'   CALL ModifyAtr(irow + 1, icol + idc, idr, 2, Kct)
'   CALL ModifyAtr(irow + idr, icol + 2, 1, idc - 2, Kct)
' --------------------------------------------------
'  вариант на QB:
   DEF SEG = &HB800
   ik = irow * 160 + (icol + idc - 1) * 2 + 1 + Offset%
   FOR i = 1 TO idr: POKE ik, Kct: POKE ik + 2, Kct: ik = ik + 160: NEXT i
   ik = (irow + idr - 1) * 160 + (icol) * 2 + 3 + Offset%
   FOR i = 3 TO idc: POKE ik, Kct: ik = ik + 2: NEXT i
   DEF SEG
END SUB

SUB WNKstore (irow, icol, idr, idc, a$, kod)
'    ****  сохранение/восстановление картинки окна
'   irow, icol    - координаты левого верхнего угла окна (строка, колонка)
'1)   kod = 0 - чтение "окна" (сохранение)
'      Вход:   idr, idc      - размеры окна (строк, колонок)
'      Выход:  a$  -  симв. переменная для хранения изображения окна
'2)   Kod  = 1 - восстановление
'      Вход:  a$  -  симв. переменная для хранения изображения окна
'      Выход:   idr, idc      - размеры окна (строк, колонок)
'***********************************************************************
'
   IF kod = 0 THEN     ' сохранение окна
     a$ = CHR$(idr) + CHR$(idc) + SPACE$(idr * idc * 2)
   ELSE idr = ASC(a$): idc = ASC(MID$(a$, 2, 1))
   END IF
'-----------------------------------------------------
'    вариант с модулем WNK_ASM.ASM:
'  CALL CopyScrBox(SADD(a$)+2, irow, icol, idr, idc, kod)
'-----------------------------------------------------
'    вариант на QB:
   DEF SEG = &HB800
   k = 3: ik0 = (irow - 1) * 160 + (icol - 1) * 2 + Offset%
   FOR i = 1 TO idr
     ik = ik0
     FOR j = 1 TO idc
       IF kod = 0 THEN
         MID$(a$, k, 1) = CHR$(PEEK(ik))
         MID$(a$, k + 1, 1) = CHR$(PEEK(ik + 1))
       ELSE
         POKE ik, ASC(MID$(a$, k, 1)): POKE ik + 1, ASC(MID$(a$, k + 1, 1))
       END IF
       k = k + 2: ik = ik + 2
     NEXT j
     ik0 = ik0 + 160
   NEXT i
   DEF SEG
END SUB

SUB WnkStorePage (Page$, kod)
'
'  запоминание (Kod=0)/восстановление (=1) тек. активной страницы
    CALL WNKstore(1, 1, 25, 80, Page$, kod)
END SUB


;**********************************************************
;************ K o l e s o v   Q B   T o o l s *************
;*         БИБЛИОТЕКА подпpогpамм QB_WNK  v.2.87          *
;**********************************************************
;*                Модуль WNKVIDEO.ASM                     *
;*--------------------------------------------------------*
;*    Подпрограммы прямого обращения к видеопамяти в      *  
;*        текстовом режиме экрана - SCREEN 0              * 
;*    для цветного монитора - CGA, EGA, VGA               *
;**********************************************************
;             Внешние процедуры:
;             ------------------
;  CopyScrBox - чтение/запись фрагмента экрана в символьную
;               переменную (переменная резервируется в вызывающей
;               программе) - вариант QB v.4.0
;  CopyScrBox7 -  - " - вариант QB v.7.0
;  ModifyAtr  - изменение атрибутов цвета фрагмента экрана
;
;  ActiveVideoPageSet0  - установка адреса активной видеостраницы
;
;  ************************************************
;             Внутренние процедуры:
;             ---------------------
;  GetScrParam - обработка входных параметров
;***************************************************************
;  ОБРАЩЕНИЕ:
;---------------------------------------------------------------
;   CALL CopyScrBox(offset%, irow%, icol%, idr%, idc%, kod%)
;          вариант QB v.4.5. 
;       kod = 0  - чтение фрагмента видеопамяти
;           = 1  - восстановление - " -
;       irow%, icol% - координаты левого верхнего угла
;                    (строка и столбец)
;       idr%, idc% - размеры фрагмента (по вертикали и горизонтали)
;       offset% = SADD(a$) - адрес символьной переменной (смещение)
;       ------------------
;   CALL CopyScrBox7(Ssegadd&, irow%, icol%, idr%, idc%, kod%)
;          вариант QB v.7.0. 
;       Ssegadd& = SSEGADD(a$) - адрес символьной переменной (полный)
;---------------------------------------------------------------
;   CALL ModifyAtr( irow%, icol%, idr%, idc%, atribut%)
;       atribut% - новый атрибут (цвет символа)
;---------------------------------------------------------------
;   CALL ActiveVideoPageSet0(Apage%) - установка адреса активной видеостраницы
;       Apage% - номер видеостаницы (0-7)
;***************************************************************
;         ПРИМЕРЫ ПРИМЕНЕНИЯ:
;***************************************************************
;    SCREEN 0        ' По умолчанию (после загрузки программы)
;                    ' - работа с 0-й видеостраницей
;    ...
;        'запоминание фрагмента
;    buffer$=SPACE$(idr%*idc%*2) ' резевируем переменную
;    CALL CopyScrBox(SADD(buffer$),irow%,icol%,idr%,idc%,0)
;    ...
;        'восстановление фрагмента
;    CALL CopyScrBox(SADD(buffer$),irow%,icol%,idr%,idc%,1)
;    ...
;    ' изменение атрибутов цвета:
;    '  Kfore, Kback - цвет символа и фона
;    atribut%=(Kfore AND 15) + (Kback AND 7) * 16
;    if Kfore>15 then atribut%=atribut%+128
;    CALL ModifyAtr( irow%, icol%, idr%, idc%, atribut%)
;  '
;  ' после переопределения активной видестраницы необходимой
;  ' обратиться к процедуре ActiveVideoPageSet0, например:
;    SCRREN 0, , 3, 2      '  3 - активная видеостраница, 2 - видимая
;    CALL ActiveVideoPageSet0(3)
;    ...
;  Создание библиотеки:
;  ====================
;         MASM.EXE  WNKVIDEO.ASM;
;         LIB.EXE  WNKVIDEO.LIB + WNKVIDEO.OBJ;
;4.5:
;         LINK /Q  WNKVIDEO.LIB,WNKVIDEO.QLB,,BQLB45.LIB;
;7.0:   
;         LINK /Q  WNKVIDEO.LIB,OUTVIDEO.QLB,,QBXQLB.LIB;
;
;   Для использования подпрограмм в среде QuickBASIC
;   загрузите:
;         QB.EXE /L WNKVIDEO.QLB
;********************************************************

.MODEL MEDIUM
.DATA
;      координаты фрагмента:  нумерация с НУЛЯ !
idr      db ?    ; кол-во строк
idc      dw ?    ;  кол-во столбцов
StrSeg   dw ?    ; адрес сегмента символьной строки (или откуда копируем - DS)
Aoffset  dw 0    ; смещение видеопамяти: по умолчанию 0-я страниница
.CODE
;
GetVideoSeg Proc     ; определение сегмента видеопамяти
        mov     ah,0Fh
        int     10h                     ;INT 10H fn. 0Fh - Get Video Mode
        mov     ax,0B800h    ;assume COLOR screen for now
        cmp     al,07h                  ;is it MONOCHROME mode?
        jne     arnd1
        mov     ax,0B000h    ;yes, set for mono screen seg
arnd1:  mov     es,ax        ; адрес сегмента видеопамяти
        ret
GetVideoSeg endp
;
CopyScreenBox Proc  near  ; обмен c видеопямятью   
        mov    StrSeg,ax    ; адрес сегмента символьной строки
        mov    si,[di]    ; смещение
        call GetVideoSeg    ; определение адреса сегмента
        mov    di,[bp]+6   ;  получаем код операции
        mov    dh,[di]
        mov    dl,0      ; обнуляем - признак операции CopyScrBox
        call PutVideo     ; перезапись в видео память
        ret
CopyScreenBox endp
;       перезапись в видеопамять
PutVideo proc near
;    определение координаты фрагмента
        mov    di,[bp]+14  ; Irow
        mov    al,[di]
        dec    al
        mov    bl,80
        mul    bl            ; AX - адрес строки
        mov    di,[bp]+12   ; Icol
        mov    bx,[di]
        dec    bx
        add    ax,bx        ; смещение в строке
        add    ax,ax
        add    al,dl        ; смещения для вывода атрибутов
        add    ax,Aoffset    ; адрес видеостраницы
;
     ;  установка счетчиков
        mov    di,[bp]+10   ; idr - длина по вертикали
        mov    cl,[di]
        mov    idr,cl
        mov    di,[bp]+8    ; idc - длина по горизонтали
        mov    cx,[di]
        mov    idc,cx
        xor    bl,bl        ; обнуление счетчика по вертикали
;     сейчас:
;  ax - смещение видеопамяти
;  es - адрес сегмента видеопямяти (приемник)
;  StrSeg - адрес сегмента строки (источник)
;  si - смещение строки (источник)
;  Для операции GetBox надо поменять адреса источника и приемника
        cmp    dl,0        ; вывод атрибутов?
        jne    next      ; да, CopyAtr
;         копирование экран - подготовка адресов
;         ds:si (откуда) ->  es:di (куда) 
        cmp    dh,0    ; 0 - Get   
        jne    next    ; PutBox: строка -> экран, ничего не меняем
;   Get:  экран -> строка   : все меняем местами        
        mov di,si       ; di - смещение в симв. строке
        mov cx,StrSeg   ; адрес сегмента строки
        mov StrSeg,es   ; адрес сегмента видеопамяти!
        mov es,cx       ; копируем в строку!  
;
;         пересылка фрагмента
next:      
        ; вывод одной строки
        mov    cx,idc      ; длина строки экрана
        cmp    dl,0        ; вывод атрибутов?
        je     CopyBox     ; нет, копирование экрана
;   вывод строки атрибутов 
        mov    di,ax       ; смещение видеопамяти в DI
next_str:
; вывод очередного байта
        mov   es:[di],dh   ; да, DH - код атрибута
        add di,2          ; увеличиваем адрес видеопамяти
        loop   next_str    ;не последний символ в сроке?
        jmp GO_ALL         ;переход к след. строке экрана
;
;     пересылка строки
CopyBox:
        cmp    dh,0    ; 0 - считать   
        je     GetBox  ; да
PutBox: mov di,ax   ; <>0 - восстановить: строка -> экран
        jmp GO_BOX    
GetBox:
        mov si,ax   ; экран строка
GO_BOX:      
        push    ds
        mov ds,strseg     ; адрес сегмента источника
        rep movsw
        pop ds          ; восстановить DS
;
; обработка след. строки
GO_ALL:
        add    ax,160       ; адрес следующей строки
        inc    bl
        cmp    bl,idr   ; последняя строка?
        jne    next    ; нет
;
        ret
PutVideo endp
;
        public CopyScrBox  ; обмен с видео пямятью - v.4.5. 
CopyScrBox  proc
        push   bp
        mov    bp,sp
        mov    di,[bp]+16 ; адрес симв. переменной
        mov    ax,ds      ; адрес сегмента данных
        call CopyScreenBox   ; обмен данными
        pop bp
        ret 12
CopyScrBox  endp
;
        public CopyScrBox7  ; обмен с видео пямятью - v.7.0. 
CopyScrBox7  proc
        push   bp
        mov    bp,sp
        mov    di,[bp]+16 ; адрес симв. переменной
        mov    ax,[di+2]      ; адрес сегмента данных
        call CopyScreenBox   ; обмен данными
        pop bp
        ret 12
CopyScrBox7  endp
;
;
         public ModifyAtr  ; программа изменения атрибутов
ModifyAtr proc
         push  bp
         mov   bp,sp
         call GetVideoSeg    ; определение адреса сегмента
         mov    di,[bp]+6   ;  получаем код атрибута
         mov    dh,[di]
         mov    dl,1      ; 1 - признак операции ModifyAtr
        call PutVideo     ; перезапись в видео память
        pop    bp
        ret   10
   ModifyAtr   endp
;
        public ActiveVideoPageSet0
ActiveVideoPageSet0 proc       ; определение активной видеостраницы
        push   bp
        mov    bp,sp
        mov    di,[bp]+6
        mov    bx,[di]         ; номер видеостраницы
        mov    ax,4096
        mul    bx
        mov    Aoffset,ax
        pop    bp
        ret    2
ActiveVideoPageSet0 endp

        end
Фото:
Колесов Андрей
А что там поучительного?
Андрей Анненков
Разве не поучительно посмотреть, что читают современные программисты? У меня сложилось впечатление, что они стремятся как можно быстрее написать работающий (а не быстро работающий) код, и это правильно. Хотя Кнут все еще в почете )

Андрей, сорри, я сожалею о том, что влез сюда с комментариями, и больше в беседе не участвую. Тема никого, кроме нас с тобой, не интересует. Пока )
Колесов Андрей
А что тут удивительного? Разработка ПО всегда выполнялась в виде решения двух последовательных задач.
Первоочереденая, обязательная - написать работающий код
Вторая, по мере необходимости, - повысить его эффективность.

Если ты прочитаешь пост, написанный в начале этой ветки, то увидишь, что приведенный мной пример разработки 20летней давности как раз иллюстрирует ровно этот подход.

А после этого посмотри на заголовок поста: так как раз задает вопрос - "нужно ли бороться за повышение эффективности кода?" Точнее так: до каких пределов нужно бороться и какими методами.

А теперь посмотри (там же - в посте) - по поводу чего написан этот пост. Он написан по поводу того, что завтра (да, уже завтра, через 11 часов и 35 минут) состоится вебинар, в котором главными действующими лицами будут две замечательные сотрудницы Нижегородского исследовательского центра Интел. И посмотри название их доклада.

Они как раз исходят из базового тезиса - "повышать эффективность НУЖНО". А я их хочу как раз спросить - "нужно ли?"

Как ты уже, наверное, понял, мы в этом вопросе - союзники, а не противники, как тебе показалось.

Короче говоря: подключайся завтра (уже через 11 часов 22 минуты) к вебинару и давай вместе поспрашиваем барышень о том, как нужно вести разработку ПО.

Что касается "никто, кроме нас с тобой не интересует", то тут ты, думаю, ошибаешься. Я точно знаю еще одного, который пишет мне комментарии в письмах. Трое - это уже достаточно для многих серьезных дел  :)