Всем привет! Возможно, вы уже знаете о проекте LLaMA.cpp (на Хабре по этой теме был цикл новостей от @bugman). В самом репозитории никаких ссылок на модели не даётся, только указание на необходимые ресурсы. И ресурсы, надо сказать, немалые.

Для самой крупной модели (65B) указано требование ~40Гб оперативной памяти. Конечно, на компьютерах разработчиков встретить такой объем вполне возможно. Но далеко не у всех. Столкнувшись с проблемой нехватки памяти «здесь и сейчас», я вспомнил про уникальное по своей щедрости предложение от кого‑то, связанного с Эльбрусами (я не знаю кто ты, но я найду тебя и... поблагодарю).

На ресурсе https://elbrus.kurisa.ch/ можно запросить демо‑доступ к достаточно мощным серверам на Эльбрусе. Не знаю, какая на момент публикации очередь доступа, но доступ по SSH у меня уже был, поэтому я приступил к беспощадной эксплуатации.

Радует, что на сервере уже установлены инструменты make, поэтому собрать исполнительный файл llama.cpp/main не составило труда. Сконвертированные модели к этому времени скачались в /srv/home/kpmy/dev/llama.cpp/models (можно использовать их в ваших экспериментах, не скачивая заново).

По рекомендациям автора llama.cpp, запускаем приложение в режиме чата:

make
./main -m models/65B/ggml-model-q4_0.bin -n 256 --repeat_penalty 1.0 --color -i -r "User:" -p "User: Tell me about Elbrus."

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

В последней версии llama.cpp механизм работы с моделью был слегка изменён, теперь там mmap‑ится файл вместо полной загрузки в память, это ускорило старт программы.

На скриншоте видно нагрузку и пример работы в режиме «чата», правда, она нестабильная какая‑то, иногда начинает генерировать фразы за пользователя.

По производительности что‑то трудно сказать, так как «кажется», что на AMD Ryzen 5600G c 64Гб памяти под Windows эта модель крутится примерно с той же скоростью (это я узнал позже, когда раздобыл необходимый объем памяти). Но проверить это «аккуратнее» мне не так интересно, как выдумывать новые промпты к ИИ‑игрушке для «бедных».

Возможно, ребятам из Эльбрус будет интересно подкинуть свои патчи к исходной программе и как‑то повысить производительность. В любом случае, огромное спасибо им за бесплатные ресурсы, всё уже очень неплохо работает. Может быть, процессорные нейросетки это перспективное направление для данной архитектуры, специалисты тут могут подсказать.

Кстати, если кто‑то знает, можно ли похожим на llama.cpp простым средством генерировать картинки без GPU, пусть и за счёт потраченного процессорного времени, подсказывайте в комментариях.

Комментарии (17)


  1. bugman
    10.04.2023 14:52
    +2

    stablediffusion вполне работает на одном CPU без GPU


    1. kpmy Автор
      10.04.2023 14:52
      +2

      Оно оказалось не таким простым, как llama.cpp.

      А на Эльбрусе вообще не собралось :(


    1. entze
      10.04.2023 14:52

      Но там вопрос не к объему памяти, а в оптимизациях. Поэтому SD будет на телефоне работать быстрее.


      1. bugman
        10.04.2023 14:52
        +3

        Мне кажется, уважаемый автор спрашивал вообще о возможности запуска каких-либо text-to-image моделей, а не о том, какие из них лучше работают где. Понятно, что на условной 4090 оно будет работать еще лучше, да какой от этого автору прок?

        StableDiffusion вполне таки чувствителен к объёму свободной памяти. Я когда я кручу stable-diffusion-ui (https://github.com/AUTOMATIC1111/stable-diffusion-webui) на домашней машине, то наблюдаю, как с моделью 1.4, при совершенно стандартных-из-коробки-настройках для CPU ...

        cd <stable-diffusion-ui>
        source venv/bin/activate
        python launch.py --precision full --no-half --skip-torch-cuda-test
        

        ... на старте главный процесс отжирает 6 Гб + при инференсе еще до гигабайта.

        Как ориентир, генерация одного изображения 512x512 на простом промпте ("man on a moon") занимает ~ 4 минуты на 4 потоках Ryzen 5 3400G. Мои попытки использовать ROCm, чтобы задействовать возможности встроенного графического ядра в моём Ryzen ни к каким значимым успехам не привели, кроме повышенных объёмов потребляемой памяти. Но готов поверить, что плохо и мало копал.

        Попытки воспользоваться какими-то более продвинутыми функциями, типа img2img вообще крашатся по недостатку памяти.

        Надо отметить, что весь этот AI/ML софт переживает infancy своего lifecycle. Я регулярно поглядываю на свежие версии, но вижу совершенно необъяснимые косяки и вариацию по части используемой памяти и стабильности генерации. Я думаю где-то через пару лет эта тема устаканится, чтобы это можно было назвать production grade software. Пока это сугубо для энтузиастов-исследователей.


  1. shigorin
    10.04.2023 14:52
    +1

    Ну достаточно мощные сервера на эльбрусе — это на данный момент 4Э8СВ, куда до полутерабайта памяти помещается :)


    А вот 16С умеет до 4 Тб на процессор (соответственно до 16 Тб на систему), но мне и на 128 Гб довелось с жабой-то потолковать; впрочем, успешно.


    Насчёт оптимизации есть с кем состыковать, если кому понадобится; ну и эльбрус-вики не отменяли.


    1. Fulgur
      10.04.2023 14:52
      +4

      А вот 16С умеет до 4 Тб на процессор 

      Михаил, на официальном сайте МЦСТ (http://www.mcst.ru/elbrus-16s), для Эльбрус-16С заявлена поддержка максимум 1 Тб на процессор. Не могли бы пруфы подвести?


  1. rPman
    10.04.2023 14:52

    Про производительность
    запустите генерацию в режиме запроса без чата, ключ -p оно выведет ответ и завершит работу, показав подробную статистику по скорости, интересует два числа, токены в секунду и проходы (runs) в секунду, на i5-12600 с 6 ядрами (12 потоков) это 1сек на run (не помню точные числа), что даёт по 1-3 секунд на слово


    На скорость работы влияет размер сетки, наличие/отсутствии квантизации и выбранный алгоритм (их там два) во время конвертации весов. Ещё момент, количество потоков приложения не выставляй выше количества ядер, иначе нагрузку даст а скорость не вырастает, а что произойдет с Эльбрус и подавно вопрос.


    Скорость работы памяти не влияет, по крайней мере выключение профиля ddr4 3600mhz->2666mhz… Возможно при не использовании квантизации будет заметно, но с ней быстрее чем без нее.


    Ещё момент, код llama.cpp активно правится и периодически вылезают деградации скорости, был тред в гите где выкладывали тесты скорости по коммитам


    1. Fulgur
      10.04.2023 14:52
      +3

      У меня получились такие результаты, сначала для 7B модели (commit-hash a0caa34b162449b5c13b8d604573053300ff54a1):

      ./main -m /srv/home/kpmy/dev/llama.cpp/models/7B/ggml-model-q4_0.bin -p "Building a website can be done in 10 simple steps:" -n 512 -s 1678486056
      ...
      llama_print_timings:        load time = 12850.61 ms
      llama_print_timings:      sample time =  2100.08 ms /   512 runs   (    4.10 ms per run)
      llama_print_timings: prompt eval time = 55385.39 ms /   271 tokens (  204.37 ms per token)
      llama_print_timings:        eval time = 1018667.75 ms /   510 runs   ( 1997.39 ms per run)
      llama_print_timings:       total time = 1085869.75 ms

      А теперь для 65B модели:

      ./main -m /srv/home/kpmy/dev/llama.cpp/models/65B/ggml-model-q4_0.bin -p "Building a website can be done in 10 simple steps:" -n 512 -s 1678486056
      ...
      llama_print_timings:        load time = 26450.83 ms
      llama_print_timings:      sample time =  2600.10 ms /   512 runs   (    5.08 ms per run)
      llama_print_timings: prompt eval time = 414718.80 ms /   271 tokens ( 1530.33 ms per token)
      llama_print_timings:        eval time = 3368699.11 ms /   510 runs   ( 6605.29 ms per run)
      llama_print_timings:       total time = 3797947.29 ms

      В system-info видно это:

      system_info: n_threads = 32 / 32 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 1 | VSX = 0 |
      sampling: temp = 0.800000, top_k = 40, top_p = 0.950000, repeat_last_n = 64, repeat_penalty = 1.100000
      generate: n_ctx = 512, n_batch = 8, n_predict = 512, n_keep = 0


      1. rPman
        10.04.2023 14:52
        +1

        Спасибо, видно что машина примерно в 6..20 раз медленнее (почему такой разброс для 7B и 65B?)

        с 4b_0 квантизацией у меня
        для 7B модели
        llama_print_timings:      sample time =    10.58 ms /    32 runs   (    0.33 ms per run)
        llama_print_timings: prompt eval time =   304.90 ms /     5 tokens (   60.98 ms per token)
        llama_print_timings:        eval time =  2933.95 ms /    31 runs   (   94.64 ms per run)

        64B:
        llama_print_timings:      sample time =    11.85 ms /    32 runs   (    0.37 ms per run)
        llama_print_timings: prompt eval time =  2707.89 ms /     5 tokens (  541.58 ms per token)
        llama_print_timings:        eval time = 27549.84 ms /    31 runs   (  888.70 ms per run)

        Не поленитесь пожалуйста, запустите эту команду, она выдаст csv на экран с результатами по разному количеству задействованных потоков
        for a in {2..32};do printf "%s;" $a;./main -t $a -m /srv/home/kpmy/dev/llama.cpp/models/7B/ggml-model-q4_0.bin -p "Random joke:" -n 32  2>&1 |grep "llama_print_timings:        eval time" | cut -d "(" -f 2 | grep -o -e "[0-9\.]*" ;done
        

        тут используется ключ -n 32, ограничивающий вывод всего 32-я токенами (для бенчмарка более чем достаточно), можно поставить 16 или меньше, результат будет немного шумным но очень быстрым.

        вот какой результат получил у себя я на 6-тиядерном 12th Intel i5-12600:

        по X тут количество потоков, по Y — время на run (на токены точно так же выглядит).


        1. kpmy Автор
          10.04.2023 14:52
          +2

          kpmy@mamizou:~$ lscpu
          Архитектура:         e2k
          Порядок байт:        Little Endian
          CPU(s):              32
          On-line CPU(s) list: 0-31
          Thread(s) per core:  1
          Ядер на сокет:       8
          Сокетов:             4
          NUMA node(s):        4
          ID прроизводителя:   E8C
          Семейство ЦПУ:       4
          Модель:              2
          Имя модели:          E8C
          CPU MHz:             1300
          BogoMIPS:            2600.00
          L1d cache:           64K
          L1i cache:           128K
          L2 cache:            512K
          L3 cache:            16384K
          NUMA node0 CPU(s):   0-7
          NUMA node1 CPU(s):   8-15
          NUMA node2 CPU(s):   16-23
          NUMA node3 CPU(s):   24-31


          1. rPman
            10.04.2023 14:52
            +1

            Спасибо, очень странно, я ожидал что если конфигурация компьютера другая, то оптимум будет на других количествах потоков, а тут так же на половине.


            последний вопрос, llama.cpp собирался на машине, запущенной в режиме эмуляции x86 машины, но сам код как минимум собирается и для эплвского процессора и для малинки, т.е. есть шанс завести это нативно и посмотреть результат? я не говорю про оптимизации, просто интересно, 10х разница в скорости все же заметная.


            1. kpmy Автор
              10.04.2023 14:52

              Собирал на этой же машине, я так понимаю, она нативная, то есть этот линукс прям под e2k.


            1. mobi
              10.04.2023 14:52

              тут так же на половине

              Скорее всего потому, что Hyper-Threading есть и у Intel, и у Эльбруса.


              1. Fulgur
                10.04.2023 14:52

                Нет SMT у Эльбруса.


                1. mobi
                  10.04.2023 14:52

                  Тогда зависимость действительно не до конца понятна.


                  1. IvGrad2
                    10.04.2023 14:52
                    +2

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

                    Это ограничение в полной мере проявило себя на СХД, поэтому в ИНЭУМ разработали материнскую плату сразу с двумя микросхемами южного моста КПИ-2, по одной на два процессора. Например, 4Э8СВ-MSWTX на базе Эльбрус-8СВ. Это решило вопрос с пропускной способностью, который и возник в данном случае.

                    В случае Эльбрус-16С, "южный мост" внесён в процессор и проблем с пропускной способностью в многопроцессорных серверах уже не должно быть.


                    1. rPman
                      10.04.2023 14:52

                      есть возможность провести подобный бенчмарк на Эльбрус-16С?