Как стать автором
Обновить

Комментарии 37

И, после его запуска система становится виртуализированной или что происходит?
В систему встраивается гипервизор, как и любой другой, к примеру Hyper-V, обычно на Ring-1 уровня ядра.
Так он встраивает себя внизу под уже работающей ОС?
Как проявляется вообще его присутствие?
Насколько видно из описания проекта — никак не проявляется, автор выложил сам гипервизор, показал как его устанавливать, а дальше дело за теми, кто захочет его использовать и в каких целях.
Автору стоило бы дополнить его хотя бы минимальным примером допиливания, хотя бы например подменой cpuid.
т.е. ничего не происходит. Просто какой-то код работает на низком уровне и не падает. Отлично
Судя по исходникам, можно читать и писать память, в том числе ядра.
Т.е. делать свой дебагер с блэкджеком.
Или няшный вирус
Который нужно устанавливать
… но если есть ключик, то можно хоть и с апдейтом драйверов пропихнуть, никто не заметит.
Хотя по факту это ничего не меняет, производители дров и сейчас имеют полный доступ к системе.
Объясните чо с этим делать теперь
Это тот случай, когда говорят "если вы задаёте этот вопрос, то вам не нужен ответ".

Это — просто полуфабрикат. Поверх него можно сделать кучу интересных вещей. Но сам по себе… Как уже сказали: Ничего не происходит. Просто какой-то код работает на низком уровне и не падает. Отлично. — это достаточно полное описание происходящего. Причём последнее слово там к месте: подобную штуку самому, с нуля, по докам — написать достаточно сложно.
А почему малое количество строк на ассемблере преподносится как преимущество? Я всегда считал что asm используется для максимальной производительности. Все уже совсем не так?
Код на асме оч сложно портировать (к примеру, на другие архитектуры), к тому же сейчас компиляторы гораздо лучше оптимизируют, чем это сделал бы человек. Ну и человекопонятность кода.
Спасибо за ликбез!
А зачем там вообще ассемблер тогда?
Есть вещи, которые на языке высокого уровня (даже на C) просто не делаются. Добавлять в компилятор 10 новых сущностей ради того, чтобы отказаться от написания 10 строк кода на ассемблере никто не будет.
к тому же сейчас компиляторы гораздо лучше оптимизируют, чем это сделал бы человек
Увы, но это неправда. Такого — даже и близко нету.

Все компиляторы, даже самые современные оптимизируют между «ужас как плохо» и «это такой ужас, что у меня больше нет слов, одни эмоции».

Однако при этом иногда смотришь на код, который они выдают — и просто офигеваешь: это же просто гениально! Откуда это? А это значит — ваша задача хорошо «легла» на какой-то паттерн, который часто встречается и потому кто-то из разработчиков компилятора придумал как это место особо хорошо сделать. Разработал соответствующий ассемблерный кусок и вшил в компилятор. А так как разработчиков компилятора много (тысячи если считать всех, сотни — если только тех, кому за разработку денег платят), то случается это довольно часто. Но достаточно чуть-чуть изменить исходный код — и всё: пиши пропало.

На самом деле, конечно же, человек действует так же (особенно если не гнушаться тем чтобы посмотреть на то, как компилятор решает ту или иную проблему — очень полезно, особенно если вы мало работали с той или иной архитектурой), однако «сведение» задачи к известным паттернам человек делает намного, намного лучше.

Ну и вторая проблема: человек, в общем, выдаёт порядка 30-50 строк отлаженного кода в день. Неважно — на каком языке. Если язык компактный (Haskell какой-нибудь) это может быть чуть меньше, если очень «расхлябанный» (Java или ассемблер) — чуть больше, но в общем, разница от минимума до максимума для одного и того же человека — раза 2-3, не больше.

Однако одной строке на C соответствуют десятки строк ассемблера! То есть с гипервизором на C проще и быстрее работать. Ну и переносимость — хотя это не всегда важно.
Ну если задача одновременно "не легла" и является критичным местом, то переписать её на ассемблере — вполне реально. Писать же на ассемблере всё — оверкилл, куча времени и денег.

При этом те самые известные паттерны, встроенные в компилятор, писали программисты уровнем гораздо выше среднего. Количество программистов способных решить небанальную задачу на асме лучше компилятора невелико.
Количество программистов способных решить небанальную задачу на асме лучше компилятора невелико.
Как раз наоборот: количество программистов, неспособных решить небанальную задачу хуже компилятора невелико. И чем «небанальнее» задача — тем их меньше.

Простейший пример:
Пример на C
$ cat test.c
#define Old_O_DIRECTORY 00040000
#define Old_O_NOFOLLOW  00100000
#define Old_O_DIRECT    00200000
#define Old_O_LARGEFILE 00400000

#define O_DIRECTORY     00100000
#define O_NOFOLLOW      00040000
#define O_DIRECT        00400000
#define O_LARGEFILE     00200000

int bar(const char *pathname, int old_flags);

int foo(const char *pathname, int old_flags) {
  const int kUnstableFlags =
      Old_O_DIRECTORY | Old_O_NOFOLLOW | Old_O_DIRECT | Old_O_LARGEFILE;

  int new_flags = old_flags & ~kUnstableFlags;

  if (old_flags & Old_O_DIRECTORY) {
    new_flags |= O_DIRECTORY;
  }
  if (old_flags & Old_O_NOFOLLOW) {
    new_flags |= O_NOFOLLOW;
  }
  if (old_flags & Old_O_DIRECT) {
    new_flags |= O_DIRECT;
  }
  if (old_flags & Old_O_LARGEFILE) {
    new_flags |= O_LARGEFILE;
  }

  return bar(pathname, new_flags);
}
$  ./clang  -c -O3 -S test.c -o-
        .text
        .file   "test.c"
        .globl  foo
        .align  16, 0x90
        .type   foo,@function
foo:                                    # @foo
        .cfi_startproc
# BB#0:
        movl    %esi, %eax
        andl    $-245761, %eax          # imm = 0xFFFFFFFFFFFC3FFF
        leal    (%rsi,%rsi), %ecx
        movl    %ecx, %edx
        andl    $32768, %edx            # imm = 0x8000
        orl     %edx, %eax
        shrl    %esi
        movl    %esi, %edx
        andl    $16384, %edx            # imm = 0x4000
        orl     %eax, %edx
        andl    $131072, %ecx           # imm = 0x20000
        orl     %ecx, %edx
        andl    $65536, %esi            # imm = 0x10000
        orl     %edx, %esi
        jmp     bar                     # TAILCALL
.Lfunc_end0:
        .size   foo, .Lfunc_end0-foo
        .cfi_endproc

        .ident  "clang version 3.8.243773 "
        .section        ".note.GNU-stack","",@progbits

Для вас действительно проблема написать что-нибудь вот такое:
Пример на ASM
        .globl  foo
        .align  16, 0x90
        .type   foo,@function
foo:
        .cfi_startproc
        movl    %esi, %eax
        movl    %esi, %ecx
        andl    $0xfffc3fff, %esi
        andl    $0x00014fff, %eax
        andl    $0x00028fff, %ecx
        shl     %eax
        shr     %ecx
        orl     %eax, %esi
        orl     %ecx, %esi
        jmp     bar
.Lfunc_end0:
        .size   foo, .Lfunc_end0-foo
        .cfi_endproc

Да никогда в жизни не поверю!

Проблема в другом: смени я эти константы — и всё: компилятор породит что-то более-менее приличное всё равно, а человеку потребуется надо всем этим голову заново ломать! А если создать код на ассемблере, где эти константы менять можно — то да, версия на C будет лучше, потому что она не так «заточена» под конкретные условия.

Вот в этом и беда: человек может писать на ассеблере лучше, чем компилятор. Почти любой человек лучше почти любого компилятора. Проблема в другом. Проблема в том, что вы лишаетесь гибкости: как только задача чуть-чуть меняется вам всё приходится переделывать. Про перенос на другой процессор я уже вообще молчу, но даже небольшое изменение начальных условий превращается в муку.
Для по-настоящему сложных систем команд типа ARM со сдвигами и хитрой организацией констант компилятор действительно побеждает человека, и намного проще подсказать ему некоторые особенности, чам переписывать на ассемблере.
Да ладно вам. Возьмите тот же самый пример посмотрите что вам напорождает компилятор:

Вот вам ARM
$ ./clang --target=arm-eabi-elf -S -O3 test.c -o-
...
        .type   foo,%function
foo:                                    @ @foo
        .fnstart
@ BB#0:
        mov     r3, #32768
        mov     r2, #16384
        and     r12, r2, r1, lsr #1
        and     r3, r3, r1, lsl #1
        bic     r2, r1, #245760
        orr     r2, r3, r2
        mov     r3, #131072
        orr     r2, r2, r12
        and     r3, r3, r1, lsl #1
        orr     r2, r2, r3
        mov     r3, #65536
        and     r1, r3, r1, lsr #1
        orr     r1, r2, r1
        b       bar
.Lfunc_end0:
        .size   foo, .Lfunc_end0-foo
        .cantunwind
        .fnend


Сделать код лучше сможете сами или мне показать?

Проблема, собственно, та же, что в примере выше: компилятор не понимает логики программы — и потому генерит "лажу". Про "хитрую организацию констрант" — тоже мимо: во-первых она на ARM'е не такая и хитрая (вот Thumb'е — да, там есть странные варианты), а во-вторых — она далеко не всегда мешает, а уж использовать её "с пользой" компилятору удаётся реже, чем человеку обычно (хотя, как я уже говорил: нарвётесь на заранее кем-то придуманный код — можете быть приятно удивлены).

Насчёт же "проще подсказать некоторые особенности"… да, я знаю как переделать этот пример так, чтобы компилятор сгенерировал оптимальный (или почти оптимальный) код. Но можно ли это сказать, что это "подсказка некоторых особенностей"? Если там нужно, в том числе, ещё и "ни на что не влияющие" скобочки правильно расставить, чтобы "всё срослось"?

Это уже не компилятор генерит оптимальный код, это я его генерю — но через посредника. А он точно в этом случае нужен? Может проще как-нибудь напрямую?
Компилятор умеет LTO. Например, если O_DIRECT — это единственный флаг который может быть на входе, то код метода сокращается до двух инструкций. А вот человеку будет тяжело вспомнить в каких файлах какие флаги использовались, и что функция которая использует другие флаги нигде не вызывается и может быть исключена.
Это, как бы, то, с чего мы начинали:

Ну и вторая проблема: человек, в общем, выдаёт порядка 30-50 строк отлаженного кода в день. Неважно — на каком языке. Если язык компактный (Haskell какой-нибудь) это может быть чуть меньше, если очень «расхлябанный» (Java или ассемблер) — чуть больше, но в общем, разница от минимума до максимума для одного и того же человека — раза 2-3, не больше.

Компилятор просто сможет обработать большие объёмы кода быстрее, чем человек. Человеку зачастую требуется так много версии на "вылизывание" и "подгонку" метода под конкретные условия, что железяку соотвествующую успевают снять с производства. Так что если вы не под Вояджер программу пишите (где железо будет 40 лет неизменным ввиду физической невозможности его заменить), то вы просто не успеете воспользоваться совершенством "человеческого" кода. Но что он будет лучше — несомненно. LTO там или ГТО.
Назвать ваш пример небанальной задачей язык не поворачивается. Имелось ввиду что-то посложнее.
Попробуйте написать ту же нейронную сеть лучше компилятора. ;)
Да легко. Берёте вывод "clang -O3 -S" и смотрите где его улучшить. Уверяю вас — там будет куча мест, где это можно сделать. И чем сложнее будет задача — тем их будет больше.

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

Дурацкий вопрос: вы это сами пробовали проделывать или сейчас тут только руками махаете?

Я — пробовал. Долго — это да, тут и говорить не о чем. Но несложно. Современные компиляторы всё ещё генерят довольно-таки "рыхлый" код — и вышеуказанного примера вполне достаточно для того, чтобы примерно понять — когда и почему.

А смысл?
А вот это — нужно в каждом случае решать отдельно. Да, писать на ассемблере — долго и зачастую невыгодно. Просто времени и сил уходит на порядок больше, чем если писать то же самое на C и на два и более порядка — чем если писать на языках более высокого уровня.

Но это, согласитесь, совсем другой довод, чем тот, с которого мы начали. Да, от самых страшных вещей компиляторы научились избавляться (обратите внимание на то, что в описанном примере компилятор пидумал-таки как обойтись без условных переходов — а это, в общем-то, самое главное при использовании современным CPU), но говорить о том, что «компиляторы гораздо лучше оптимизируют, чем это сделал бы человек» — увы, несколько преждевременно. Это если сказать мягко.

P.S. Та же самая ситуация и «ниже», кстати: AMD пользуется HDL-компиляторами при извотовлении своих CPU, а Intel — много разводит «руками». Но происходит это не потому, что «компиляторы ща умные — вастче». А просто потому, что на «ручную» разводку у AMD ресурсов не хватает. Результат — всем известен, не так ли? С другой стороны если некому приличную архитектуру сделать, но никакая «ручная разводка» и «написание на ассемблере» не спасут. Пример — те же AMD и Intel, но уже в области разработки GPU :-)
Вопрос был в том, какой смысл брать вывод компилятора и улучшать его в качестве ассемблерной программы вместо того чтобы делать в ней ассемблерные вставки. С самого начала я об этом и говорил — неудачные куски можно переписать на асме в качестве вставок в С код.

Какую самую сложную задачу вы реализовали полностью на асме? Какой был её размер? Несколько быстрее это вышло, чем оптимизированный код на С?
Какую самую сложную задачу вы реализовали полностью на асме?
Интерпретатор байткода.
Какой был её размер?
Порядка 100'000 строк на асме. Аналог на C был примеро в 20'000 строк.
Несколько быстрее это вышло, чем оптимизированный код на С?
Сильно зависит от компилятора. GCC/Clang примерно вдвое удалось обогнать, а MSVC 2010 — вчетверо (сейчас не знаю, может он получше стал).

Почти все «трюки», которые применялись — были как раз подобного рода: человек может заметить, что он может одной инструкцией породить две, а то и три вещи, а компилятор на это неспособен.

Думаю что можно было бы «помочь компилятору» и сотворить такие структуры, которые он смог бы обратить в код близкий к написанному руками (примерно как в игрушечном примере выше). Одна беда: это ещё сложнее и дольше, чем писать сразу на ассемблере, а выигрыш, в общем, нулевой (на другую архитектуру трюки, скорее всего не лягут, а «провернуть фарш назад» и придумать как заставить компилятор породить-таки нужный тебе код на ассемблере — та ещё работёнка).

Но да, все задачи в таком режиме решать не будешь: разработка заняла примерно раз в 10 больше времени, чем вариант на C, а ускорение — всего лишь вдвое (а на языках более высокого уровня, чем C было бы ещё быстрее писать и оно бы ещё медленнее работало).

Чудес в мире не бывает, увы.
А если бы вы в тех 20000 строк на Си выбрали самые частоиспользуемые и неоптимизированные места и сделали там ассемблерные вставки (получив, например, 18000 строк Си + 10000 асм и потратив в 2 раза больше времени чем на чистый Си)? Позволю себе предположить, что ускорение было бы раза в 1.5, а то и больше.
О том и речь — чистый асм не имеет смысла. Слишком много в любом коде редкоиспользуемых, некритичных или нормально оптимизируемых компилятором мест. Смысла писать их на асме вместо Си — 0. Ассемблерные вставки не такая уж и редкость, а вот чистый асм можно сдавать в музей.
Хорошо, небольшой фикс — "компиляторы оптимизируют эффективней за единицу времени". :) Понятно, что человек может вылизать код до идеального состояния, но трудозатраты на это слишком велики. Тогда как компилятор в большинстве случаев выдаст относительно нормальный код, который может быть и не будет идеальным, но будет вполне рабочим и относительно эффективным, при этом за очень хорошие показатели времени.
Ну с этим никто и не спорит. Если бы человек мог порождать код за время сравнимое со временем работы компилятора — кто бы использовал компиляторы? Так что приходится выбирать.
На самом деле, очень интересно. Надо попробовать загрузить через этот супервизор 2 ОС одновременно. Всегда хотел возможность переключиться в полноценную виндовз, с полноценной GPU, при этом не выключая linux base os
Всегда хотел возможность переключиться в полноценную виндовз, с полноценной GPU, при этом не выключая linux base os
GPU passthrough же!
пробовал, стабильных решений не нашел
С 2014-го на основной рабочей станции именно так построено всё.
Arch Linux, KVM, LVM Thin Provisioning, GTX 1070. Да, видеокарта одна!
Нужна поддержка VT-d на проце, и IOMMU на плате. На видухе должна быть UEFI прошивка.

Win10, Win8 для разработки и отладки отлично работают. Для игр и прочих графических задач была Win10. Выдавала на 3-5% меньше от производительности хоста по процу и видео, если без всяких «фиксов» мелтдаунов и спектров, которые от производительнсти тыкву оставляют.

По диску чуть похуже. Самое лучшее, что смог выжать, это из 100к гостевых IOPS из 500к хостовых. Это из-за thin provisioning, без него процентов 10 потерь. Но он, сука, удобный!

С семёркой была беда какая-то. Она как-то криво с уефи работает, поэтому уефи прошивка видеокарты не хотела грузиться с пробросом. Просто чёрный экран. Про XP можно не говорить, он UEFI не умеет. Но для тестирования и без проброса хватает.

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

Чтобы вернуться в хост из гостя, когда видеокарта только одна, как у меня, отдельный USB-хост должен поддерживать операцию reset. Иначе ядро резетит всё подряд, что иногда вгоняет его в панику. Но у меня поддерживается резет, поэтому особо не парился.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий