1. Си / Говнокод #25314

    −3

    1. 1
    2. 2
    3. 3
    4. 4
    void print_line(char *s){
        for(int i = 0; i < strlen(s); i++) putchar(s[i]);
        putchar('\n');
    }

    Почему C работает медленнее чем JavaScript ?

    Запостил: o8603054, 17 Января 2019

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

    • Поскольку char * не хранит длину строки и её нужно каждый раз получать, сканируя всю строку на предмет байта '\0', выполнение функции strlen требует времени O(n), где n –— длина строки.

      Цикл for в Си не кэширует условия (в отличие от Паскаля, например, где границы индекса определяются один раз), поэтому будет вызывать strlen на каждой итерации.

      Итого сложность получится O(n²).

      Вот такой вариант быстрее:
      void print_line(char *s){
          int length = strlen(s);
          for(int i = 0; i < length; i++) putchar(s[i]);
          putchar('\n');
      }
      Ответить
      • А еще лучше так
        void print_line(const char *s) {
            for(int i = 0; s[i] != 0; i++) putchar(s[i]);
            putchar('\n');
        }
        Ответить
        • А ещё лучше юзать укозатели. Но всё равно, мне не нравится вызов в суслике. Раз уж си и лоу-левел лучше уж сразу скопировать в видеопамять или куда там ещё.
          Ответить
          • >>в видеоп
            ага, чтобы потом в консоли работало, а в псевдотерминале нет
            Ответить
            • А ещё чтобы была защита от захвата вывода myprogram.exe > dump.txt
              Ответить
              • Вопрос к знатокам: сегмент-то будет yt 16битный. Как же будет работать биосный код?
                Ответить
                • Кокококой кококод?
                  Ответить
                  • ой нет
                    я ляпнул чуш

                    все хуже

                    вызов прерывания будет обработан ОСом и к биосу не попадет unless такой дикий ABI не реализован

                    так что код сукабляди может и не работаьть
                    Ответить
                    • Если приложение или ОС защищённого режима, то да. В VDM и в DPMI приходится реализовывать транслятор для каждой функции каждого прерывания (BIOS не сможет просто так разыменовать укококозатель на память защищённого режима).

                      Но именно это и позволяет досовскую программу запустить в окне (если бы это было не так, она бы пачкала своим выводом окна).
                      Ответить
                      • Да даже не в том дело биос не сможет разыментовать, тут ведь до биоса-то и не дойдет: обработчик прерываний ОС получит уведомление об INT, и не вызовет код биоса.

                        А даже если бы и вызвал, то в регистр сегментов надо загрузить адрес такой структуры где сказано что это сегмент 16битного кода, иначе процессор не сможет выполнить код биоса, ну тупо может быть invalid opcode.

                        А судя по "asm (" и "movw " (AT&T syntax) речь идет о каком-то unix-like.

                        Так что режим там сто пудово защищенный 32х битный.

                        Может быть это Xenix: там 16бит, но все равно защищенный (просто без виртуальной памяти)
                        Ответить
              • а если без шуток, то это надо через API tty делать: и ">" не сработает и поддержит все терминалы: и псевдотерминалы (ssh, xterm) и виртуальную консоль и даже физичеческий vt220 по гомпорту
                Ответить
          • void print_line(const char *s) {
                uint16_t sp = s;
                uint16_t len = strlen(s);
                asm (
                    "movw $0x1300, %%ax\n"
                    "movw %0, %%bp\n"
                    "movw %1, %%cx\n"
                    "movw $0, %%dx\n"
                    "movw $7, %%bx\n"
                    "int $0x10" :: "r"(s), "r"(len)
                );
            }
            Ответить
            • прерывание bios? серьезно?

              Кста, в линуксе можно сипать в видемемори напрямую, иксы так делают без DRI.
              Ответить
            • А через прерывание 0x80 даже работать будет.
              Ответить
              • В "Solaris" или в "BSD" будет?
                Ответить
                • думаю что нет
                  думаю что там сисколь другой
                  Ответить
              • этот ABI еще поддерживаеца?!
                Ответить
                • Да, причём даже в 64-битной проге его можно дёрнуть. Но ABI там 32-битное, поэтому указатель хуй передашь.
                  Ответить
                  • Линуксоиды тянут ABI с prehistoric times.

                    А BSD срёт на всех и перекомпилирует весь софт для каждой новой версии
                    Ответить
                • Запинал под 64 бита: https://ideone.com/nQgysb
                  Ответить
                  • Не совсем по-царски:
                    - const char* str = "Hello, world!\n";
                    + const char str[] = "Hello, world!\n";
                    - size_t len = strlen(str);
                    + size_t len = sizeof(str) - 1;

                    Теперь норм.
                    Ответить
                    • Не норм. Надо ещё mmap через прерывание звать. Но мне лень.
                      Ответить
                    • лишний байт!
                      char str[] = {'H', 'e', 'l','l'...};
                      size_t len = sizeof(str);
                      Ответить
                  • так-так-так

                    a(4) это EAX = 0x04 это sys_write
                    b(1) это EBX = 0x1 это fd = 1 (stdout)

                    ECX и EDX это буфер и размер соответственно

                    А теперь скажи мне, почему ты не сделал честно mov в эти регистры?
                    Разве это не было ли бы это более царски?
                    Ответить
                    • А нафиг делать мув если конпелятор сам способен всё разложить куда надо? Зачем ему палки в колёса вставлять?
                      Ответить
                      • чтобы быть более царским

                        а как сделать мув, кста?
                        movb $4, %eax сработает?
                        Ответить
                        • Да, только %%eax.
                          Ответить
                          • Действительно, один знак процента во встроенном асме захватит макроподстановка, поэтому нужно удвоить.
                            Ответить
                        • Царь, кстати, был против вставления палок в колёса кокококонпелятору. Он знал, как кокококонкретная версия «gcc» всё раскладывает, поэтому его программы были заточены под кококококонкретную версию «gcc».

                          > movb $4, %eax сработает?
                          Ты хочешь в 32-битный регистр положить 8-битное число? Серьёзно? Или пиши movd, чтобы кокококонстанта была 32-битной, или используй movs/movz, расширяющие число (но они умеют расширять только в два раза: 8 бит до 16, 16 до 32, 32 до 64 и так далее).

                          Можно, кокококонечно, написать movb $4, %al и получить в трёх старших октетах мусор.
                          Ответить
                          • >>Ты хочешь в 32-битный регистр положить 8-битное число? Серьёзно?
                            ой я дурак-дурак, конечно я хотел movd это же дабл ворд на интеле.

                            А можно положидь туда нуль а потом положить в нижнюю половинку (al), но это конечно говнецо

                            хих, прикольно, я давно на асме не писал
                            Ответить
                            • Я ошибся, не -d, а -l.

                              • movl $, %eax займёт 5 байт.

                              • xor eax, eax + mov al, 4 (пишу в синтаксисе «Интела») займёт 2 + 2 = 4 байта (ещё и во флаги насрёт).

                              • lea eax, [4] займёт 6 байт (когда в квадратных скобках смещение без регистра, нужна 32-битная константа).

                              • Можно исхитриться и написать lea eax, [eax + 4], тогда она займёт 3 байта (смещение может быть 8-битным или 32-битным), но нужно обнулить eax (самый короткий способ сделать это займёт 2 байта). Итого xor eax, eax + lea eax, [eax + 4] займут 5 байт.

                              • Погуглил про movsx и movzx (давно ими не пользовался). Оказывается, можно 8 бит расширить сразу до 32, но работают они только с регистрами или с памятью.
                              Тогда mov al, 4 + movzx eax, ax займут 2 + 2 = 4 байта.
                              Ответить
                              • Есть ещё однобайтовые инструкции CBW (расширить AL до AX) и CWDE (расширить AX до EAX).

                                mov al, 4 + cbw + cwde займут 4 байта.
                                Ответить
                                • Зато по тактам однозначно проебут тупому пятибайтному муву...

                                  Сначала эти мелкие инструкции засрут декодер, а потом ещё и выстроятся в очередь друг за другом.
                                  Ответить
                                  • Ещё идея: в 64-битном режиме можно использовать rip-адресацию. Тогда нужный байт при хорошем стечении обстоятельств можно вытянуть из какой-нибудь соседней инструкции.
                                    Ответить
                  • В «Cygwin» и в «MSYS» кокококомпилируется, но не работает. Слой «POSIX» они сделали, а слой «Linux» не стали.
                    Ответить
                    • в WSL должно сработать кмк

                      проверь
                      Ответить
                    • а не
                      WSL тоже не будет

                      Лососисис тунцов
                      Real x86-64 Linux may or may not handle int 0x80 even in 64-bit process. That depends CONFIG_IA32_EMULATION of kernel config.
                      WSL is not.

                      https://github.com/Microsoft/WSL/issues/3107
                      Ответить
                    • Попробуй труъ 64-битную версию, которую я скинул чуть ниже. Возможно, что они только 64-битное ABI запилили.
                      Ответить
                • А вот с кошерным ABI: https://ideone.com/gqtUbq
                  Ответить
                  • сложилось ощущение что старый ABI (досисколовый) поддерживаеца тока если CONFIG_IA32_EMULATION

                    верно?
                    Ответить
                    • Наверное. Но без него 32-битные проги не будут работать. Поэтому, скорее всего, он почти во всех дистрах включен.
                      Ответить
                      • раз его нету в WSL, я предполагаю что в ubuntu, debian и opensuse (последних) нет ни одной 32 битной проги
                        все пересобрано


                        Пойду у себя на слаке отключу его и пересоберу ядро и проверю
                        Ответить
                        • В убунте работают 32-битные проги. Но изкоробки вроде всё 64-битное.
                          Ответить
                          • ты меня не понял:
                            в ядре там разумеется есть CONFIG_IA32_EMULATION.

                            Но весь юзерленд собран конечно уже с x64.

                            Почему я так думаю?
                            Да потому что под WSL есть убунта. То-есть юзерленд убунты работает в системе где вместо Linux -- эмуляция его сисколов виндовым процессом (lxss или как оно там)

                            Эта эмуляция не умеет INT 0x80, о чем написан выше. Но убунта как-то там работает.

                            Это намекает на отсутствие в убунтячем узерленде 32битного кода с INT. И это логично, ведь весь код там собирается из сырцов и ничто не мешает его перебрать
                            Ответить
                            • По-дефолту юзерленд 64-битный, да. Но можно накатить 32-битных либ для совместимости (всякую проприетарщину запускать и т.п.)
                              Ответить
                              • А как работают всякие сисколлы в 32-битном режиме?

                                У меня от дебага виндового «Heaven's Gate» до сих пор ночные кошмары.
                                Ответить
                                • ложишь в регистры нужную инфу, и вызываешь прерывание 0x80.

                                  Запускается обработчик прервания, копирует к себе нужные данные и выполняет код
                                  Ответить
                                  • Там ещё вариант с sysenter, емнип, есть.
                                    Ответить
                                  • Т.е. ядро знает, что в юзер-моде у нас 32 бита, и само переводит всё на 32?
                                    Ответить
                                    • Да не, тупо по сработавшей точке входа определяет. Вон из 64-битного процесса работает и 64-битное ABI через syscall и старое 32-битное через int 0x80.
                                      Ответить
                                      • ну вообще говоря оно знает конечно, оно же может посмотреть какой сегмент сейчас загружен в регистр

                                        но наверное ей это не важно если оно знает что SYSCALL всегда x64 а INT и SYSENTER всегда x32 (??)
                                        Ответить
                              • Насколько вообще стабилен ABI лялиха? Судя по тому как Торвальдс орет когда кто-то его ломает, вероятно он очень стабилен?

                                Могу ли я запустить проприетраный софт 1999го года на CentOS 7?
                                Ответить
                                • Весьма стабилен. Я не припомню косяков из-за ABI ядра. Чего не скажешь о либах...
                                  Ответить
                                  • А была же история как гномовцы (GTKшники) поломали ABI и Торвальдс их обозвал мудаками, и тогда гномовец (с испанской какой-то фамилией) сказал что развиваться невозможно потому что заставляют тащить старое говно и вообще линукс проиграл битвы за десктоп
                                    Ответить
                                    • Да GTK'шники вечно ломают что-то в своём ABI... А что они в ядре забыли? Скинь ссылку почитать.
                                      Ответить
                                      • Они не забыли, ругался Торвальдс приводя пример того что это плохо.

                                        Я не могу найти ссыл чото, зато нашел ссыл с точностью до наоборот!


                                        ABI для модулей у линукса как раз не стабильно: моудли надо перебирать для каждого ядра.

                                        И вот Icaza пишет что дескать вот из этой нестабильности и мы нестабильны тоже и просрали битву за десктоп

                                        https://www.itwire.com/business-it-news/open-source/56418-torvalds-pours-scorn-on-de-icazas-desktop-claims
                                        
                                        https://tirania.org/blog/archive/2012/Aug-29.html
                                        Ответить
            • Питушня какая-то. Неужели нельзя включить O3/Release mode, чтобы ОП-код стал по скорости как жсный?
              Неужели даже просто до O(n) с неважно какой константой оптимизация не доводит?
              Ответить
              • можно переписать на паскале
                там размер страки палучить это O(1)

                и по этому я за "Pascal"
                Ответить
                • 1. Сложность получения длины строки.
                  "Pascal": O(1). "C": O(n).

                  2. Кэширование границ итератора цикла "for".
                  "Pascal": есть. "C": нет, цикл реализован как обычный while.
                  Ответить
              • а вдруг strlen запоминает строку в глобальную переменную, а putchar пишет в середину '\0', чтобы циклы быстрее завершались?
                Ответить
                • > putchar пишет в середину '\0'
                  Фу, середина - это всё ещё та же трудоёмкость. Квадратичное говно!
                  В оптимизированных версиях putchar '\0' ставят сразу в самое начало, и for (... strlen ...) даёт O(1), когда питушарское кэширование длины - O(n).
                  Ответить
                  • > Квадратичное говно!

                    Почему квадратичное? N×log(N)
                    Ответить
                  • > O(1)
                    size_t strlen(const char* s) {
                        static size_t cached_len;
                        static const char* cached_str;
                        if (cached_str == s)
                            return cached_len;
                        cached_len = real_strlen(str);
                        cached_str = str;
                        return cached_len;
                    }
                    Ответить
                    • а когда я потом в середину строки забабахаю \0, что будет?
                      Ответить
                      • Отвлечёшь его какой-нибудь другой строкой.
                        Ответить
                        • Надо постоянно помнить, что после изменения строки нужно сбросить кэш. Именно поэтому некоторые за иммутабельные строки... Хотя с другой стороны менять адрес при любом изменении строки –— это переголова.
                          Ответить
                          • нука сбросиьк мне кеш у локальной статической переменной
                            Ответить
                            • Тут речь была о кэше функции Борманда. Она идентифицирует переменные по их адресу и хранит кэш только для последней, для которой посчитала длину.
                              Ответить
                            • А, понял. Ты о том, что строка может храниться не в куче и не в области глобальных переменных, а в стеке, тогда бормандовская функция будет выдавать фигню, потому что стек меняется непредсказуемо?

                              И правда, Си предоставляет кучу способов прострелить себе колено.
                              Ответить
          • а нука напомни мне адресс видеопамяти для nvidia видюх. а то X11 меня бесит хочу свои окошки :)
            Ответить
            • lspci > your_cheek.txt
              Ответить
            • X11 не имеет отношения к окошкам. Этим занимается твой Window Manager.

              Адрес знает драйвер nvidia:
              Driver "nvidia"
              http://us.download.nvidia.com/solaris/1.0-9755/README/chapter-02.html
              Ответить
              • Вспомнил прикол из 90-х. Школьник, начитавшись журнала «Хакер», обращается к своему другу –— «компьютерному гению»:
                –— А можно перепрограммировать контроллер 13-го прерывания?
                Друг пытается понять, чего же от него хотят и что это за загадочный контроллер. Выдохнув, отвечает:
                —– Ну, допустим, можно. А тебе это зачем нужно?
                –— Винт отформатировать хочу! На низком уровне!
                Ответить
                • А кстати, Low Level Format работал на IDE? Мне казалось что там и команды-то такой не было уже, только в MFM. Вроде бы размер блока там был прибит гвоздями и равнялся 512 байтам чуть ли не до конца нулевых, когда появились большие блоки

                  Вот на флопаре он работал, но можно было и без биоса обойтись: обычный format.com это делал (и высокоуровневое и низкоуровневое), иначе как бы им можно было сменить размер дискеты?
                  Ответить
                  • 512 байт –— это только логический размер блока. На современных HDD интерфейса «ATA» физический блок простым смертным недоступен (он может весить несколько кило). В таких винчестерах по сути целый компьютер (ты помнишь про подключение в режиме терминала, муху цэцэ –— вот это всё). Как они транслируют логические адреса в физические, даже хуй не знает (точнее знают только инженеры производителя винчестеров).

                    В BIOS'ах конца 90-х была функция «Low Level Format», но на самом деле это была файка: она фактически тупо записывала нули во все логические блоки, т. е. это высокоуровневое форматирование, но без файловой системы.

                    На флопаре format.com выполнял не совсем низкоуровневое форматирование. Он не мог отформатировать сбойные дискеты или дискеты, которые до этого были неправильно отформатированы.

                    Настоящее низкоуровневое форматирование дискет выполняла программа «FDA» –— «floppy drive analyzer». Она умела читать межсекторные промежутки, записывать любые заголовки физических секторов. С помощью неё можно было отформатировать дискету не только для «IBM PC», но и для «ДВК», «Агата» –— вот этого всего зоопарка чудовищ. Вот эта программа могла творить чудеса.
                    Ответить
                    • У нас небольшой факап с терминологией (даже не у нас, а у всего мира)

                      Представим себе физическую цепочку MFM или Floppy.
                      [ISA]-->[контроллер]-->[диск]

                      Контроллер принимает некоторые команды от софта (драйвера или утилиты или софта в INT 13h).
                      Диск сам по себе это просто блин (или блины). Контроллер разбивает его на блоки: тупо пишет на дорожке границы блока. И сам потом по этим границам ориентируется.

                      Это и есть "Low Level Format".

                      Контоллер может принимать от софта команды для этой самой разметки. Это есть у контроллера флопарей или у контроллера MFM.

                      Именно это делает INT 13h AX=05h . В зависимости от плотности этих границ можно получать разную плотность дискет.
                      -----------------

                      Теперь цепочка IDE/ATA:

                      [PCI]-->[IDE Контроллер]-->Протокол_IDE-->[Контроллер_диска+сам_диск]

                      IDE Контроллер (его еще называют HBA -- HostBasedController) принимает от софта команды для навигации по диску (CHS или LBA, не важно). В этих коммандах размер блока равен 512

                      Переводит их в термины IDE/ATA и шлет контроллеру, встроенному в диск (у которого uart для терминала и муха цэцэ).

                      Контроллер_диска КАК ТО выполняет их на диске. Что там внутри -- похуй. Там может быть черти ябутся, и нету никаких блоков вообще. Важно что принимает он их в терминах 512-байтовых блоков ("блочное устройство", как говорят юниксоиды).

                      Сам диск разбит на КАКИЕ ТО блоки на заводе. Об этом знает его контроллер.
                      Так что проводить Low Level Format у него нельзя: не получится. Именно потому INT 13h лукавит.

                      Но это всё еще Low Level Format. Потому что он говорит: "У меня три блина, сто дорожек и размер блока 512 байт".

                      ---------------
                      Логическим (high level) форматированием я называю процесс создания ФАЙЛОВОЙ СИСТЕМЫ на диске посредством драйвера файловой системы.

                      Он создает кластера (которые могут быть больше блоков) и их таблицы (MFT, FAT итд).

                      Согласен?
                      Ответить
                      • >> Там может быть черти ябутся

                        Судя по каким баграм от иногда возникающей потери данных, так оно и есть.
                        Ответить
                        • "Все винчестеры можно разделить на две категории: те, которые уже сломались и те, которые скоро сломаются"
                          Ответить
                          • Именно поэтому я за перфокарты.
                            Ответить
                            • Люди до сих пор находят глиняные таблички шумерские.
                              А что останется от нас? Дискеты уже сгнили, например.

                              Именно потому я за глиняные таблички
                              Ответить
      • А вот такой ещё быстрее:
        puts(s);
        Ответить
    • Чот ты поздно, обычно это в сентябре проходят
      Ответить
      • Это восьмой гость провоцирует нас на обсуждение лоу-левел говна. Он часто так делает.
        Ответить
    • >strlen
      Потому что Шлёма Маляр
      http://wiki.c2.com/?ShlemielThePainter
      Ответить
    • Ахуеть, петухи на сях кодят.
      Ответить

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