Зиро кост абстракции

Тема в разделе "WASM.LANGS", создана пользователем Rel, 5 апр 2021.

  1. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.241
    Мы не так давно с Thetrik обсуждали оптимизацию кода в высокоуровневых языках, где я показывал при каких условиях JIT-компилятор дотнета может опускать проверку на выход за границы массива. Так вот мне сегодня ютюб в предложку выплюнул интересный видос с примером того, как современные языки программирования сворачивают высокоуровневые генерик абстракции в достаточно эффективный машинный код. Если честно, это меня даже немного удивило, решил поделиться. Только не помню, где мы это обсуждали, да и это явно было из ряда офтопа, так что решил создать отдельную тему. В начале видоса обсуждается генерик абстракция на плюсах, растах и зигах, а в конце показывается машинный код. Довольно забавно:
     
    Artem_N и Thetrik нравится это.
  2. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.241
  3. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.329
    Плохо только, что работает это лишь в хвалебных рекламных обзорах и синтетических тестах. Когда это в реальном софте использовать начинают, ничто там не оптимизируется и 100 метровый ехе на 90% состоит из нагенерированного компилем мусора, от которого можно избавиться без каких-либо потерь в функциональности. Даже при
    Код (C):
    1.  
    2. typedef LPVOID HMODULE;
    3. typedef HMODULE HINSTANCE;
    4.  
    компиль не понимает, что vector<LPVOID>, vector<HMODULE> и vector<HINSTANCE> это одно и то же и сгенерит три абсолютно бинарно одинаковых реализации класса. Не говоря уже о чем-нибудь посложнее.
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.241
    О хоспаде, спецы - такие спецы:
    Код (C):
    1. #include <stdio.h>
    2. #include <vector>
    3.  
    4. typedef void*   LPVOID;
    5. typedef LPVOID  HMODULE;
    6. typedef HMODULE HINSTANCE;
    7.  
    8. using namespace std;
    9.  
    10. void test() {
    11.     vector<LPVOID>    one;
    12.     vector<HMODULE>   two;
    13.     vector<HINSTANCE> three;
    14.  
    15.     one.push_back(nullptr);
    16.    
    17.     two.push_back(nullptr);
    18.     two.push_back(nullptr);
    19.  
    20.     three.push_back(nullptr);
    21.     three.push_back(nullptr);
    22.     three.push_back(nullptr);
    23.  
    24.     printf("раз: %lu\n", one.size());
    25.     printf("два: %lu\n", two.size());
    26.     printf("три: %lu\n", three.size());
    27. }
    Код (ASM):
    1. test():
    2.         push    rbp
    3.         sub     rsp, 80
    4.         mov     rsi, rsp
    5.         lea     rdi, [rsp+8]
    6.         mov     QWORD PTR [rsp+8], 0
    7.         mov     QWORD PTR [rsp+16], 0
    8.         mov     QWORD PTR [rsp+24], 0
    9.         mov     QWORD PTR [rsp+32], 0
    10.         mov     QWORD PTR [rsp+40], 0
    11.         mov     QWORD PTR [rsp+48], 0
    12.         mov     QWORD PTR [rsp+56], 0
    13.         mov     QWORD PTR [rsp+64], 0
    14.         mov     QWORD PTR [rsp+72], 0
    15.         mov     QWORD PTR [rsp], 0
    16.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    17.         mov     rsi, rsp
    18.         lea     rdi, [rsp+32]
    19.         mov     QWORD PTR [rsp], 0
    20.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    21.         mov     rsi, rsp
    22.         lea     rdi, [rsp+32]
    23.         mov     QWORD PTR [rsp], 0
    24.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    25.         mov     rsi, rsp
    26.         lea     rdi, [rsp+56]
    27.         mov     QWORD PTR [rsp], 0
    28.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    29.         mov     rsi, rsp
    30.         lea     rdi, [rsp+56]
    31.         mov     QWORD PTR [rsp], 0
    32.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    33.         mov     rsi, rsp
    34.         lea     rdi, [rsp+56]
    35.         mov     QWORD PTR [rsp], 0
    36.         call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    37.         mov     rsi, QWORD PTR [rsp+16]
    38.         sub     rsi, QWORD PTR [rsp+8]
    39.         mov     edi, OFFSET FLAT:.LC1
    40.         xor     eax, eax
    41.         sar     rsi, 3
    42.         call    printf
    43.         mov     rsi, QWORD PTR [rsp+40]
    44.         sub     rsi, QWORD PTR [rsp+32]
    45.         mov     edi, OFFSET FLAT:.LC2
    46.         xor     eax, eax
    47.         sar     rsi, 3
    48.         call    printf
    49.         mov     rsi, QWORD PTR [rsp+64]
    50.         sub     rsi, QWORD PTR [rsp+56]
    51.         mov     edi, OFFSET FLAT:.LC3
    52.         xor     eax, eax
    53.         sar     rsi, 3
    54.         call    printf
    55.         lea     rdi, [rsp+56]
    56.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    57.         lea     rdi, [rsp+32]
    58.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    59.         lea     rdi, [rsp+8]
    60.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    61.         add     rsp, 80
    62.         pop     rbp
    63.         ret
    64.         mov     rbp, rax
    65.         lea     rdi, [rsp+56]
    66.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    67.         lea     rdi, [rsp+32]
    68.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    69.         lea     rdi, [rsp+8]
    70.         call    std::_Vector_base<void*, std::allocator<void*> >::~_Vector_base() [base object destructor]
    71.         mov     rdi, rbp
    72.         call    _Unwind_Resume
     
  5. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.329
    Rel,
    А теперь возьми иду и посмотри, как это в реальном софте выглядит, а не в хелловорлде из одной функции.
     
  6. ormoulu

    ormoulu Well-Known Member

    Публикаций:
    0
    Регистрация:
    24 янв 2011
    Сообщения:
    1.208
    Вот да кстати M$шный компиль в последнее время объединяет методы с одной реализацией в одну процедуру, чем несколько напрягает при реверсе классов.
     
  7. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.241
    Да это давно уже и в GCC/MinGW реализовано, просто для цешных спецов нужно как-то возбухать против нецешных компиляторов. На самом деле даже интересно, с чего компилятору этого не делать? Особенно если вспомнить, что многие штуки в вендовых хедерах даже не тайпдефами определены, а дефайнами препроцессора. То есть компилятор даже не узнает, что это типа разные типы, так как их разрулит препроцессор до базового типа. Но цешные спецы - такие спецы.
     
  8. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    Rel,

    Код (Text):
    1.  
    2. 6.        mov     QWORD PTR [rsp+8], 0
    3. 7.        mov     QWORD PTR [rsp+16], 0
    4. 8.        mov     QWORD PTR [rsp+24], 0
    5. 9.        mov     QWORD PTR [rsp+32], 0
    6. 10.        mov     QWORD PTR [rsp+40], 0
    7. 11.        mov     QWORD PTR [rsp+48], 0
    8. 12.        mov     QWORD PTR [rsp+56], 0
    9. 13.        mov     QWORD PTR [rsp+64], 0
    10. 14.        mov     QWORD PTR [rsp+72], 0
    11. 15.        mov     QWORD PTR [rsp], 0
    12. 16.        call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    13. 17.        mov     rsi, rsp
    14. 18.        lea     rdi, [rsp+32]
    15. 19.        mov     QWORD PTR [rsp], 0
    16. 20.        call    void*& std::vector<void*, std::allocator<void*> >::emplace_back<void*>(void*&&) [clone .isra.0]
    17. 21.        mov     rsi, rsp
    18. 22.        lea     rdi, [rsp+32]
    19. 23.        mov     QWORD PTR [rsp], 0
    - оптимизация походу к этому выхлопу не применима, регистр нельзя было обнулить или заполнить область строковыми инструкциями. Это не код это высер.
     
  9. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.241
    Да ты даже не понимаешь, что это и зачем, чего ты влез то сюда?
     
  10. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    Rel,

    Я понимаю что загрузка константы по профайлу медленна, чем загрузка регистра, тк для этого выборка нужна из памяти а не из регистров. Ну а по сути я хз чем вы тут занимаетесь :)
     
  11. Intro

    Intro Active Member

    Публикаций:
    0
    Регистрация:
    29 авг 2009
    Сообщения:
    561
    Indy_, не вижу не каких особых задержек. Больше вопросов к call'ам!