1. C++ / Говнокод #26389

    +2

    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
    // https://www.linux.org.ru/forum/development/15496357
    
    // Нужен нормальный способ сказать компилятору что type aliasing невозможен на некотором участке кода. Минимальный пример:
    
    template<typename T> struct f final {
    	void bad(T v) noexcept { while (b != e) *b++=v; }
    	void good(T v) noexcept {
    		auto tb(b), te(e);
    		while (tb != te) *tb++=v;
    		b=tb;
    		e=te;
    	}
    
    	T* b, * e;
    };
    template struct f<char>;
    /*
    Выхлоп gcc-8:
    
    $ g++ -xc++ -std=c++14 -pedantic-errors -Os -c -of.o f.cc
    $ objdump -Cd f.o
    f.o:     file format elf64-x86-64
    
    
    Disassembly of section .text._ZN1fIcE3badEc:
    
    0000000000000000 <f<char>::bad(char)>:
       0:	48 8b 07             	mov    (%rdi),%rax
       3:	48 3b 47 08          	cmp    0x8(%rdi),%rax
       7:	74 0c                	je     15 <f<char>::bad(char)+0x15>
       9:	48 8d 50 01          	lea    0x1(%rax),%rdx
       d:	48 89 17             	mov    %rdx,(%rdi)
      10:	40 88 30             	mov    %sil,(%rax)
      13:	eb eb                	jmp    0 <f<char>::bad(char)>
      15:	c3                   	retq   
    
    Disassembly of section .text._ZN1fIcE4goodEc:
    
    0000000000000000 <f<char>::good(char)>:
       0:	48 8b 07             	mov    (%rdi),%rax
       3:	48 8b 57 08          	mov    0x8(%rdi),%rdx
       7:	48 39 d0             	cmp    %rdx,%rax
       a:	74 09                	je     15 <f<char>::good(char)+0x15>
       c:	48 ff c0             	inc    %rax
       f:	40 88 70 ff          	mov    %sil,-0x1(%rax)
      13:	eb f2                	jmp    7 <f<char>::good(char)+0x7>
      15:	48 89 07             	mov    %rax,(%rdi)
      18:	48 89 47 08          	mov    %rax,0x8(%rdi)
      1c:	c3                   	retq
    */

    f<char>::bad(char)+0, f<char>::bad(char)+3 и f<char>::bad(char)+d - три раза за итерацию лезет в память. Разумеется, подобный код сливает в тестах производительности. Есть решение лучше, чем локальные переменные заводить каждый раз?

    Запостил: j123123, 28 Января 2020

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

    • Какой крестоотсос!
      Ответить
      • while (b != e) *b++=v;

        Н-н-н-нооо позвольте, где здесь С++, j123123?!это же чистая сишка.
        Ответить
        • Тут какие-то анскиллушные темплейты и структуры с функциями - в Си такого нет.
          Ответить
          • Это именно сишный паттерн, дрочить указатели в цикле while
            while (*s++ = *t++);
            Ответить
          • Да оно и в сишке точно так же компилится.
            Ответить
            • Если написать нормальную сишную функцию в этом говне:
              template<typename T> struct f final {
                  void very_good(T v) noexcept { my_f(b,e,v); }
              
                  void my_f(T* b, T* e, T v)
                  {
                      while (b != e) *b++=v;
                  }
              
                  T* b, * e;
              };
              template struct f<char>;


              то тогда GCC соптимизирует в memset:
              f<char>::very_good(char):
                      movq    8(%rdi), %rdx
                      movq    (%rdi), %rdi
                      cmpq    %rdi, %rdx
                      je      .L1
                      subq    %rdi, %rdx
                      movsbl  %sil, %esi
                      jmp     memset
              .L1:
                      ret


              Именно поэтому я за нормальные функции, а не всякое говно
              Ответить
              • Ну ты же локалок добавил и изменение b не закоммитил в мембер. Код не эквивалентный вообще.
                Ответить
                • Ну можно добавить return

                  https://godbolt.org/z/Ne28Mq

                  Но код good vs bad и так неэквивалентен.

                  Т.к. j123123 возможно привык жить в однопоточном мирке.
                  Ответить
                  • Ну можно проверить чтоб вот в диапазон где продрачивается цикл while (b != e) *b++=v; не попадает диапазон адресов самой этой структуры, и тогда заоптимизировать на основе этого.
                    Ответить
                • Ну тогда вот так
                  void my_f(T* b_, T* e, T v)
                  {
                      while (b_ != e) *b_++=v;
                      b = e;
                  }
                  Ответить
                  • Вы поймите, компилятор делает то что ему сказали.

                    В случае bad ему явно говорят: инкременти указатель в структуре на каждой итерации.

                    В случае good ему явно говорят: скопируй себе указатель, поитерируй и поменяй его в конце цикла.

                    Понимаю что в царском мире, где у процессора одно ядро, а потоки не нужны, оба случая кажутся эквивалентными.

                    Однако это не так, ибо в многопоточном коде различие СУЩЕСТВЕННОЕ.
                    Ответить
                    • Компилятору насрать на потоки и прерывания если ты не юзаешь атомики или volatile. Поменяй на что-нибудь кроме char и он перестанет хуйнёй страдать.

                      Он именно в своём единственном треде ссыт затереть b или e во время записи в *b.
                      Ответить
                      • Не совсем понял.
                        Ответить
                        • Если говношаблон
                          template<typename T> struct f final {
                          	void bad(T v) noexcept { while (b != e) *b++=v; }
                          	void good(T v) noexcept {
                          		auto tb(b), te(e);
                          		while (tb != te) *tb++=v;
                          		b=tb;
                          		e=te;
                          	}
                          
                          	T* b, * e;
                          };


                          специализировать типом int, крестоговнокомпилятор не будет предполагать что он перепишет хуйней "while (b != e) *b++=v;" хуйню "T* b, * e;" в этой говноструктуре
                          Ответить
                          • Да причём тут шаблон. В сишке с глобалками или полями структуры под b и e у тебя та же самая ерунда будет.
                            Ответить
                            • А я и не спорю. Шаблон тут оттого, что он в исходном говнокоде присутствует.
                              Ответить
    • Что такое type aliasing в данном случае?
      Ответить
      • Ну по-стандарту указатель на сhar может указывать на любую хуйню и переписывать ее. Если там char, крестоговняный компилятор предполагает что хуйня "*b++=v;" может переписать указатели "T* b, * e;" в самой структуре и поэтому дрочит память
        Ответить
        • А, это только при char и unsigned char. Понял.
          Ответить
          • Вообще char — это какой-то страшный тип. Он не signed char и не unsigned char. Он особенный.
            Ответить
            • Факт.

              6.5 Expressions
              7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 
              (The intent of this list is to specify those circumstances in which an object may or may not be aliased. )
              
              *    a type compatible with the effective type of the object,
              *    a qualified version of a type compatible with the effective type of the object,
              *    a type that is the signed or unsigned type corresponding to the effective type of the object,
              *    a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
              *    an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
              *    a character type. 
              Ответить
        • Пиздец.

          Но на ЛОРе правильно сказали. Это сишка.

          >Твой пример - это не код на с++, это си-код.
          
          >std::fill(b, e, v);

          Человек пытался писать на крестах как на сишке. Ну и получил. Ещё по-божески.
          Ответить
          • В Стандарте ;(
            Ответить
          • > std::fill

            // Specialization: for char types we can use memset

            (и это не шутка, там реально специализация через мемсет)
            Ответить
          • Мой критерий прост: если код компилируется крестокомпилятором - значит это код на C++. А используется или нет какая-то std::kokoko дрисня - дело десятое
            Ответить
            • Так борманд правильно говорит: мы можем такое же наговнячить в сишке.

              И оно будет так же криво оптимизироваться.

              Арифметика указателей, структуры, сhar. Кресты здесь причём?
              Ответить
              • Исходный код по ссылке на ЛОРе был на крестах - поэтому код был запощен как говнокод на крестах.

                Если б я этот код запостил как код на Си, мне б сказали что тут template<typename T> а в Си такого нет.
                Ответить
                • Но вы упорно продолжаете обсирать кресты, хотя в божественной сишке точно такой же изъян, если обращаться к структуре напрямую.

                  template тут не при чём.
                  Ответить
                  • https://govnokod.ru/23287#comment389547

                    > Тоесть, смотрите, как ведет себя настоящий, состоявшийся, знающий себе цену программист? А он ведет себя очень просто: плюсы - говно, но и си - говно, кричит он громко, ничуть не смущаясь подходящих к нему слева - одептов плюсов, а справа - почетателей битоебского низкоуровневого язычка типа Си
                    Ответить
                  • В сишке есть рестрикт.
                    Ответить
                    • А крестовый std::unique_ptr так не умеет?

                      Или там перепитушни много?
                      Ответить
    • Кстати, а где здесь type aliasing?
      Ответить
      • Нет, указатель адрес - он не может работать для регистра, ибо у него нет адреса - это питушизм. Это работает только потому, что конпелятор это умеет. В реальном мире стек давно сдох.


        Алиас явно и чётко показывает идею - входящий елемент - есть алиас исходящего, как ссылка в путих С++. А с указателем ты такую фигню не сделаешь - только указатель на указатель.


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


        Поэтому алиасы и указатели совершенно разные вещи, а бинды и кастыли со структурами - тоже разные вещи, ибо кастыли со структурами - питушня.
        Ответить
        • Поэтому я за ассемблер - там такой хуйни нет.
          Ответить
          • Я могу писать быстрее и лучше на сишке - мне удобней. Мне для этого не надо как рядовому питушку осиливать Сишку.

            Всё должно отражать всё - если твоей конпелятор написан как говно - он и оптимизировать будет как говно. И да, оптимизации не нужны я уже не помню где был тред, помоему на лоре.

            Вобще для моего мегаязыка оптимизации не нужны, ибо оптимизации нужны для питухов - там будет удобная ручная предсказуемость, как в ассемблере.

            В место питушских переменных - там буду переменные, который на самом деле по умолчанию безымянные регистры. Явный их биндинг на имена, явный ялиасинг. Никаких стеков и прочей ереси.

            Никаких автовекторизаций циклов( которые в 50% дают деградацию, допустим гцц(и все другие конпеляторы) по умолчанию вместо стекового фпу года гинерят ссе код, который медленее фпушного и теряют 10-30% производительности.

            Никаких разворачиваний циклов - нормальная автогинерациия кода в компилтайме, а не питушнян а шаблонах как в С++ и не ущербные макросы.

            Истинный компилтайм - для кода не будет существовать разницы между рантаймом и компилтаймом. Будет рантайм во время конпеляции - код будет сам гинерить то, что ему нужно.

            Ты пишешь char m[] = f(); в функции ты реализуешь заполнении массива, а конпелятор её исполняет. Нал оре был целый тред про constexp или как там его.

            Никаких оптимизаций циклов, никакой питушни - ты либо сам умеешь писать нормально циклы - либо иди в С++. Там за тебя в 20% случаев конпелятор заменит питушню на for(i) for(j) for(k) - на нормальный цикл на указателях, который работает в 3раза быстрее.

            И ещё масса всего - все эти оптимизации от бешенства с жиру - глянь на царский код, ему не нужны никакие оптимизации, кроме алиасинга( и то это из-за особенностей сишки).
            Ответить
            • > Любая ваша структура данных - это вариации на указателях

              Это справедливо только для классической питушни с адресным пространством, а есть еще всякие data-flow архитектуры, в которых адресуемой памяти вообще нихуя нет. В частности, для dataflow архитектур (которые очень часто реализуются поверх FPGA) применяются свои особые языки https://habrahabr.ru/post/122479/ вот статья со швабры например.
              Ответить
            • Царь Пишки.
              Ответить
    • человек-программист
      человек-компилятор
      человек-отладчик
      человек-дизассемблер
      человек-интерпретатор
      Ответить
      • https://i.redd.it/ftt8ve4b76q21.png
        Ответить
        • (насмешкою судьбы?) там нет упоминания о делфе. Ибо делфи - это серьезный язык, а не анимешная девочка с вонючей, неподмытой пиздёнкой.
          Ответить

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