Pull to refresh

Comments 59

Велосипед с беговой дорожкой — хорошая алегория виртуальной машины.
На данный момент:

enum OpCode {
      OPCODE_ILL = 0// ILLegal
      OPCODE_INC, // INCrement
      OPCODE_DEC, // DECrement
      OPCODE_ADD, // ADD
      OPCODE_SUB, // SUBtract
      OPCODE_MUL, // MULtiply
      OPCODE_JNZ, // Jump if Not Zero
      OPCODE_JG, // Jump if Greater
      OPCODE_JNG, // Jump if Not Greater
      OPCODE_JE, // Jump if Equal
      OPCODE_CPI1, // CoPy Immediate (1-byte) 
      OPCODE_CPI2, // CoPy Immediate (2-bytes)
      OPCODE_CPI4, // CoPy Immediate (4-bytes)
      OPCODE_CPI8, // CoPy Immediate (8-bytes)
      OPCODE_PUSH, // PUSH variable
      OPCODE_PUSHR, // PUSH Reference
      OPCODE_PUSHH, // PUSH exception Handler
      OPCODE_POP, // POP entity
      OPCODE_JMP, // unconditional JuMP
      OPCODE_CPB, // CoPy Bytes
      OPCODE_LDE, // LoaD array Element
      OPCODE_LDB, // LoaD structure Bytes
      OPCODE_LDR, // LoaD structure Reference
      OPCODE_STE, // STore array Element
      OPCODE_STB, // STore structure Bytes
      OPCODE_STR, // STore structure Reference
      OPCODE_CALL, // CALL procedure
      OPCODE_THROW, // THROW exception
      OPCODE_RET // RETurn
    };
А INT или SYSCALL нету? Как же ОС будет работать?
Пока много чего нету, но в данном случае это инструкции не нужны, так как не надо переключаться в режим ядра (система в одном адресном пространстве).
Я имею ввиду не переключение в режим ядра, а работу, например, с таймером. Без таймера не возможна работа ОС.
Тогда хороший вопрос. Пока не готов дать ответ.
[offtop]
Возможна. Небольшие RTOS (под PIC'и к примеру) зачастую используют кооперированную многозадачность. Таймер оказывается не нужен:

— потоки переключаются между собой из вежливости
— периферия вся на прерываниях, никакого polling'а
[offtop]
Таймер это и есть периферийная микросхема, которая весит на прерывании.
Конечно. Но это не делает утверждение «Без таймера не возможна работа ОС» верным. ОС может использовать разные scheduler'ы процессов, иногда сразу несколько. В малых (железных) проектах preemptive multitasking — штука вредная.

Иногда периферию слушают через polling по таймеру, но это редкость и не имеет отношения к ОС.
Какой язык будет использован для программирования в этой VM?
Планируется диалект лиспа.
> архитектура виртуальной машины должна быть регистровой, а не стековой
По моему стековый байткод можно прямо отобразить в регистровый и обратно. При трансляции в нативный код на лету, для стекового байткода можно уже на месте посмотреть количество доступных регистров и генерить код под них. В регистровой машине чуть сложней транслировать код под процессор с отличным набором регистров (хотя кого это волнует, когда современный JIT кеширует результаты). С другой стороны, регистровый байткод обычно меньше по размеру из-за отсутствия постоянных LOAD операций. От честного интерпретирования байткода (особенно складывания регистров на стек) придется позже отказаться, медленно.

> Поэтому, стоит отказаться от «жирных» абстракций, таких как классы, mark and sweep GC, и др.
Классы — это логика на уровне языка. На реальном железе это те же структуры как у вас, с таблицей виртуальных методов. Без уборщика мусора будет очень сложно работать с многопоточными приложениями. Тут будет очень сложно считать ссылки, каждое обращение придется оборачивать mutex'ом, дабы никто ненароком не удалил объект, пока мы с ним работаем. Или предлагается какой то новаторский подход в этом вопросе?

В целом: пока простейшая VM. Может стоило сначала описать принципиальные особенности вашей VM? Тогда можно взять готовую VM (к примеру Parrot) и сконцентрироваться на идее? Будет обидно потратить много сил на то, что по любому будет работать медленно если реализовано наивно (как у вас сейчас, что хорошо для прототипа), затем потратить еще больше сил на переписывание всего под JIT и тысячу железных трюков, так и не дойдя до собственно вашей идей (изюминкаtm). На том же RPython'е JIT и скорость работы вы получите даром.
>Будет обидно потратить много сил на то, что по любому будет работать медленно если реализовано наивно (как у вас сейчас, что хорошо для прототипа), затем потратить еще больше сил на переписывание всего под JIT и тысячу железных трюков, так и не дойдя до собственно вашей идей (изюминкаtm). На том же RPython'е JIT и скорость работы вы получите даром.

Я и так получаю JIT даром, поскольку использую LLVM.

Простейшая VM, поскольку пока мало что у спел рассказать, впрочем далее тоже не сложно, вопрос только в востребованности постинга — вижу это никому не интересно…
> поскольку пока мало что у спел рассказать
Пост, в котором раскрываются не интересные азы, не будет пользоваться успехом. Пост в духе «давайте подумаем», где раскрываются высокоуровневые требования уже более интересен. Тогда можно сравнить ваше виденье ВМ и то, что сейчас имеем в других проектах. Отсюда можно будет решить, что реализуется просто библиотеками (API), что уже есть в популярных ВМ, а что действительно внезапное откровение.

> Каждое обращение к внешней нефункциональной процедуре модуля будет оборачиваться синхронизацией
Это накладно. Но замечание было в другом. Представим P процессоров(4), которые крутят T потоков(16), которые в свою очередь исполняют почти бесконечное количество green threads. Учитывая, что у вас все приложения будут фактически сервисами, green threads впишутся идеально, так что ничего не будет подвисать. Но тогда не возможно будет реализовать прямое управление памятью и прямой подсчет ссылок для контроля жизнью объекта. Все сервисы будут работать в режиме создал объект, что либо сделал, выкинул. Не известно кем объект еще будет использоваться. Тут потребуется GC, да на столько умный, что бы не скидывать «не важные» объекты в своп, который по вашей идее, будет исполъзоваться принудительно для каждого приложения (принудительная персистентность). Либо у вас новаторская идея, которая позволяет работать во множестве потоков без сборщика мусора. Тогда хотелось бы ее услышать.

> Даже в текущем состоянии она умеет очень многое, чего должна уметь VM, отличаясь существенной простотой.
Простота и лаконичность — разные вещи. Софтина, реализующая новую интересную идею простым способом - это хорошо (к примеру pyramid среди питоновских веб фреймворков). Софтина, реализующая фичу X, которую уже многократно реализовали, это другое. Простота в вашем случае означает:
— менее эффективный код за счет прямого исполнения
— отсутствие машинных оптимизаций
— отсутствие уже стандартных вещей вроде разнопрофильной работы с памятью, интроспекция и т.д.
— пока отсутствие внятного дизайна, но возможно это лишь временное явление
— пока отсутствие декларации круга задач, под который создается новая ВМ
- и т.д.

Иными словами, в вашем случае «простой» означает «предельно простой proof-of-concept», урезанный под самое не могу, не для серьезного использования. Правильный подход для проверки идеи, но пока ни одной новой идеи мы не увидели.
Сборка мусора у меня уже фактически реализована на основе подсчёта ссылок (только для thread-local переменных). Понятно, что у такой сборки есть изъян (нет полной гарантии уничтожения из-за возможной цикличности ссылок), но мне и не нужна такая гарантия. Главным образом мне нужна сборка мусора для гарантии отсутствия висячих ссылок. В целом, даже такая сборка мусора предоставляет возможность достаточно комфортного кодирования. Для переменных, разделяемых потоками такой сборки нет по понятным причинам. Здесь у меня ничего нового.
> Для переменных, разделяемых потоками такой сборки нет по понятным причинам. Здесь у меня ничего нового.
Хм… Современные процессоры имеют несколько ядер. Вы предлагали разбивать софт на сервисы в другом топике. Значит оно уже будет работать в несколько потоков. Получается:

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

CPython находиться в такой же ситуации как и вы: не хотим сложного сборщика мусора, но хотим многопоточность. Решение было не эффективный GIL и отказ от многопоточности в пользу multiprocessing — отдельные однопоточные процессы, которые имеют свои копии данных в виртуальной памяти. Учитывая, что вы хотите крутить все в одном процессе, одной большой виртуальной памяти, такой трюк уже не сработает.

Вывод: решения нет. Значит нельзя эффективно и безопасно писать многопоточные сервисы. Альтернативы вы не предоставили, кроме как «возможность достаточно комфортного кодирования», которую я не вижу. Многопоточность + прямой подсчет ссылок не работает. Это опыт.
> Решение было не эффективный GIL и отказ от многопоточности в пользу multiprocessing — отдельные однопоточные процессы, которые имеют свои копии данных в виртуальной памяти.
У меня абсолютно то же решение. Модули не разделяют память, они только вызывают процедуры.
Причём будет нельзя передать указатель на не thread-local динамическую переменную между модулями (пока межмодульные вызовы не имплементированны, но планируется именно так).
А если есть общие данные, которые нельзя держать thread-local эффективно. В комменте ниже я привел пример с файловым индексом, который слишком большой, что бы его копировать для каждого потока. Его и в память то грузить можно только порциями, а потом потоки, как аквариумные рыбки на корм, налетают на отдельные ноды. При чем сам индекс все время меняется, это не мертвый снепшот.
— менее эффективный код за счет прямого исполнения
— отсутствие машинных оптимизаций
Как я уже сказал, эту проблему за меня решает LLVM
Не решает. LLVM оптимизирует твою машину, которая на прямую интерпретирует байткод. Трансляции байткода в машинный код не происходит, верно?
Неверно. Почитайте о LLVM.
По моему вы не правы. LLVM позволяет написать софтину, которая позже на лету себя оптимизирует. Прикладное приложение, с помощью загрузчика и run-time себя оптимизирует.

У вас ВМ, которая себя оптимизирует. И по прежнему на прямую исполняет байткод, разве не так? ;)

Другое дело если вы будете генерить безопасный IF, а кормить его LLVM. Или оно уже так работает?
> Другое дело если вы будете генерить безопасный IF, а кормить его LLVM. Или оно уже так работает?
да
Аплодирую! Мне надо лучше смотреть в код. Вопросы с оптимизацией кода решены.

P.S. просто смотрел изначально на в src/vm/instr.cpp, пока не дошел до эмитеров в mdata.cpp
> Тут потребуется GC, да на столько умный, что бы не скидывать «не важные» объекты в своп, который по вашей идее, будет исполъзоваться принудительно для каждого приложения (принудительная персистентность).

Чтобы не скидывать нужные объекты в своп, совсем не нужна сборка мусора. Здесь нужна подсистема управления памятью, которая отслеживает статистику использования страниц памяти.
И вообще не могу понять вашей критически-демотивирующей тональности (сводящейся к тому, что фактически это ничего нового, брось усилия). Помоему, это неверный подход.
> И вообще не могу понять вашей критически-демотивирующей тональности.
Всегда интересуюсь новыми идеями и с энтузиазмом их рассматриваю. Если нравятся, реализую. Но перед этим ищу слабые места, над которыми автор «не подумал». Теория и идея должна быть bullet-proof. Я вас не демотивирую, я хочу, что бы вы сначала просто текстом (у себя в блоге или тут) выложили идеи/дизайн вашем машины. А то начало было интересным, а потом вы нас начали кормить банальными решениями. Так боюсь до начинки не дойдем, станет не интересно.

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

Это с чего вы взяли невнятность дизайна? Из вводного поста? Декларация круга задач тоже есть — построение распределённой среды для клиентских приложений.
> Это с чего вы взяли невнятность дизайна?
Я не сказал, что дизайн не внятный. Его просто нет. Каюсь, может стоит просто вернуться в этот топик через две недели, когда вы выложите собственно зерно ваших идей, а не банальные детали ВМ, которые мы все видели и писали на втором курсе университета. Тогда извиняюсь если задел.

> построение распределённой среды для клиентских приложений.
Первый же вопрос пока без внятного ответа, как работать с памятью если: 1) у нас одно адресное пространство, 2) у нас много сервисов в многопоточной среде.
> Каюсь, может стоит просто вернуться в этот топик через две недели, когда вы выложите собственно зерно ваших идей, а не банальные детали ВМ, которые мы все видели и писали на втором курсе университета.
Что-то я сомневаюсь, чтобы на втором курсе писали JIT-виртуальные машины. Может это я такой глупый, а все писали… ))

> Первый же вопрос пока без внятного ответа, как работать с памятью если: 1) у нас одно адресное пространство, 2) у нас много сервисов в многопоточной среде.
1) у каждого хоста своё адресное пространство
2) о синхронизации читайте в предыдущем посте
Одни и те же вопросы в догонку объясняются в разных ветках… читающим будет смешно))

> у каждого хоста своё адресное пространство.
Вот тут проблема. Каждый хост имеет несколько потоков, ведь он крутиться на многоядерном процессоре. Явная синхронизация при подсчете ссылок сведет на нет производительность. Или какое другое решение?
Поскольку только один поток писатель может быть в модуле, механизм посчёта ссылок локализуется внутри модуля (никакого распределённого подсчёта ссылок). Отмечу также, что нет никаких хендлов, которые нужно предварительно открывать и закрывать. Потому нет необходимости в распределённой проверке валидности открытых хендлов.

Если этот ответ не удовлетворяет, дайте очень конкретный пример, тогда и обсудим.
> Без уборщика мусора будет очень сложно работать с многопоточными приложениями. Тут будет очень сложно считать ссылки, каждое обращение придется оборачивать mutex'ом, дабы никто ненароком не удалил объект, пока мы с ним работаем. Или предлагается какой то новаторский подход в этом вопросе?

Каждое обращение к внешней нефункциональной процедуре модуля будет оборачиваться синхронизацией

>… сконцентрироваться на идее

Идея данной VM в простоте. Даже в текущем состоянии она умеет очень многое, чего должна уметь VM, отличаясь существенной простотой. Главный критерий для меня — это простота.
а в чем приемущество от существующих на данный момент VM?
Пока ни в чём, т.к. пока она поддерживает то, что с лихвой могут другие виртуальные машины. Надеюсь в будущем это преимущество появится в виде прозрачности вызова удалённых модулей. Но я не вижу смысла в соревновательности, я её реализую для будущей архитектуры, т.е. она будет заточена под неё (если раньше не потеряю интерес).

По отстранённости реакции, вижу что не смог доступно описать материал в данном посте. Жаль, ведь идеи, на которых основывается мой дизайн, достаточно простые. Надеюсь, смогу это исправить в будущих постах.
прозрачность, это просмотреть ввиде лога все прошедшие команды?
> она поддерживает то, что с лихвой могут другие виртуальные машины.
Это не правда. Современная ВМ это транслятор в машинный код, с оптимизацией под архитектуру, управление памятью и полный доступ до всех плюшек host-OS. У вас, как мы уже выяснили выше, надежда на LLVM, который будет оптимизировать вашу машину. Не байткод, который вы пока интерпретируете. Переход на трансляцию — это все начать с нуля.

У вас пока нет потоков, семафоров или наоборот чего нибудь посвежее, например выявление side-effect free кода, транзакционной памяти и т.п. Это даже не 1% от всех задач, которые вам еще придется решить. Это как сравнить самокат и машину класса люкс, оба инструмента едут.

> если раньше не потеряю интерес.
Не теряйте, давайте лучше вместе подумаем над вашей ВМ. Начните с примеров приложений, пусть гипотетических, которые будут работать в вашей ВМ, используя все ее преимущества. Затем найдем минимальный набор фич ВМ, которые реализуют эти особенности. Затем под это найдем фичи вашей ОС.
Да почитайте же об LLVM. Никакой интерпретации нет.
Исполняется нативный код, который ещё может быть оптимизирован средствами LLVM (optimization passes).
Вопрос с оптимизацией решен. Но разве LLVM решает за вас остальные проблемы? Как на счет работы с памятью в многопоточном приложении, которые по вашей идее будут работать в одном адресном пространстве?
Давайте вы предложите конкретную проблему многопоточности, и мы её обсудим.
проблема многопточности, общая память на 4 процесса которые исполняются одновременно.
Чем вас не устраивает синхронизация, предложенная в предыдущей статье?
да синхронизация хорошо, но вот как тогда с производительностью?
У нас есть некоторая документная БД. Есть несколько сервисов, которые постоянно пишут в эту БД, пусть это будет аггрегатор новостей для группы пользователей. Запись очень плотная, 99,9% времени пишущие сервисы активны (к примеру слежение за курсом акций по миру).

Читают БД наоборот не равномерно. То во время перекура ожидаем огромную толпу, то где нибудь к 3 часам дня наоборот тишина, 2-3 тысячи запросов в минуту. Сервис публикуется сразу всем по сети. Естественно, что клиенты не заинтересованы в чистых документах, а уже в готовой инфе. Потому в системе работают не останавливающиеся map/reduce worker'ы, собирающие инфу в разного рода индексы.

Данные хранятся на нескольких уровнях, индексы и свежее на чем то быстром (свои SSD, raid массивы), на чем то среднем (кластер) и на чем то медленном (кассеты). При чем я как разработчик ничего не знаю об этом, это задача ОС/VFS сливать то, что используется редко в более объемное, но медленное хранилище. Это замечание к вашей будущей ОС, а не ВМ.

Потребуются green threads для работы с IO (файлы, сеть, БД). Я просто запускаю новую нить под каждый запрос не задумываясь как это повлияет на системный scheduler. Нет необходимости в thread pools и не блокирующем IO, что очень сильно упрощает работу.

Потоки хоть и green, но будут работать поверх пула реальных потоков, а значит исполнятся параллельно. Следовательно, если есть некий общий ресурс, индексы нашей БД в памяти (они большие, не будем же мы их грузить для каждого потока отдельно), который постоянно обновляется, то нужно разграничивать доступ. Без блокировок я могу пользоваться versioned index, когда деревья перестраиваются, но старые ноды при этом остаются. Это позволяет пишущему процессу обновить индекс, а читающий ничего не заметит, без блокировок (подсмотреть идею можно в CouchDB). С файлами все понятно, оставили ноды на диске, потом какой нибудь vacuum процесс это все подчистит. А как это дело в памяти?

Кратко: есть дерево индексов, на которое ссылается каждый поток. Оно большое и постоянно изменяется, старые ноды остаются в памяти, пока потоки их использующие их не отпустят. Потоки не знают кто еще использует эти ноды, вести прямой подсчет ссылок (взял ноду ++, отдал ноду --) будет крайне накладно из-за постоянной синхронизации, что сведет на нет параллельность выполнения (через 5 лет будем иметь десятки, если не сотни ядер в камне. критические секции станут bottleneck). Как чистить память?

Сборщик мусора имеет время «подумать». Ему нужно поддерживать свободную память в текущей генерации. Пока она есть, не важно сколько времени займет перебор всей памяти во время чистки, и какие трюки будем использовать. Правда и памяти на работу приходиться хапать сразу много, это не путь low memory profile утилит. Сборка мусора — интересный раздел computer science. 
> Потоки хоть и green, но будут работать поверх пула реальных потоков, а значит исполнятся параллельно.

Поскольку это ОС единого адресного пространства, потоки будут изначально легковесными (что и позволит запускать их тучами).

Очень перегруженный пример в малоизвестной мне предметной области, сложно на такое отвечать, потому возьму более абстрактную формулировку:

>А если есть общие данные, которые нельзя держать thread-local эффективно. В комменте ниже я привел пример с файловым индексом, который слишком большой, что бы его копировать для каждого потока. Его и в память то грузить можно только порциями, а потом потоки, как аквариумные рыбки на корм, налетают на отдельные ноды. При чем сам индекс все время меняется, это не мертвый снепшот.

Вкратце, если у нас ситуация, когда есть порции из огромного множества данных, и эти порции постоянно меняются потоками, то нужно эти самые порции разбить на отдельные модули. Тоже касается индекса и репликации — не стоит всё пихать в один модуль.
> Поскольку это ОС единого адресного пространства, потоки будут изначально легковесными.
По моему вы несколько не поняли идею green threads. Суть их в не ограниченном количестве без последствий для производительности. Это фактически кооперативная многозадачность поверх блокирующих ресурсов + по наследству принудительная многозадачность за счет пула реальных потоков.

Реальные потоки, какими бы они не были, усложняют работу scheduler'а. Чем их больше, тем больше лаги. Их количество влияет на работу. Их блокировка по прежнему влияет на рабоду scheduler'a. С реальными потоками не так то просто будет ответить за утверждение «что и позволит запускать их тучами». Алгоритмы scheduler'ов процессов/потоков — популярная тема для holy war'ов.

> Тоже касается индекса и репликации — не стоит всё пихать в один модуль.
Э… положить все в один модуль речи не было, как раз наоборот. Есть множество сервисов, которые используют некий общий ресурс, который в силу железных ограничений (память не резиновая) нельзя скопировать для каждого сервиса отдельно. Что бы убрать долгие блокировки на обновления мы оставляем старые ноды индекса (иначе чтение будет не возможным, 99.9% времени мы чего то пишем, следовательно лочим индекс). После того, как ноды больше не используются ни одним из сервисов, их можно выкинуть. Так вот когда их можно выкинуть?

Индекс, в силу своей природы (дерево, постоянная ре-балансировка) нельзя разбить на кусочки и положить в thread-local переменные каждого отдельного сервиса.
> Реальные потоки, какими бы они не были, усложняют работу scheduler'а. Чем их больше, тем больше лаги. Их количество влияет на работу. Их блокировка по прежнему влияет на рабоду scheduler'a. С реальными потоками не так то просто будет ответить за утверждение «что и позволит запускать их тучами». Алгоритмы scheduler'ов процессов/потоков — популярная тема для holy war'ов.

Вот именно поэтому проблема по-настоящему не решается с помощью middleware (VM, например, поверх linux), здесь нужна настоящая ОС единого адресного пространтства.

> Э… положить все в один модуль речи не было, как раз наоборот. Есть множество сервисов, которые используют некий общий ресурс, который в силу железных ограничений (память не резиновая) нельзя скопировать для каждого сервиса отдельно. Что бы убрать долгие блокировки на обновления мы оставляем старые ноды индекса (иначе чтение будет не возможным, 99.9% времени мы чего то пишем, следовательно лочим индекс). После того, как ноды больше не используются ни одним из сервисов, их можно выкинуть. Так вот когда их можно выкинуть?

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

Если я правильно понял проблему, то решаться она может следующим образом. База должна быть семантически структурирована на модули. Каждый такой модуль постоянно модифицируется писателями. Каждый такой модуль должен иметь пишущую внешнюю процедуру (пишущую, чтобы наравных конкурировать с другими писателями), позволяющую создавать кеш этого модуля, т.е. другой модуль, который будет использоваться читателями. id последнего кеш-модуля сохраняется в основном модуле. Один раз во временной интервал вызывается эта внешняя процедура кеширования, которая создаёт новый кеш-модуль и удаляет старый. Может возникнуть вопрос, как читатели будут знать какой кеш-модуль актуален на данный момент (его id). Это решается дополнительным модулем, задача которого делать редирект (т.е. возвращать id актуального кеша через внешнюю процедуру-читатель). Процедура кеширования основного модуля после создания нового кеша, обновит модуль перенаправления (через писатель, а он более приоритетен читателей, запрашивающие id актуальных кеш-модулей), затем удалит старый кеш-модуль.
> Вот именно поэтому проблема по-настоящему не решается с помощью middleware.
Ответ не верный =)

Есть 2 процессора с 4 ядрами каждый, итого можем крутить 8 параллельных задач. Знаем, что два потока на одном процессоре могут быстрей обмениваться данными, через кеш камня. К примеру мютексы будут железными, в сотню раз быстрей, да и много других плюшек. Также современный процессор может иметь состояния и переходить из одного в другое. В разных состояниях имеем какие то дополнительные инструкции и прочие вкусные вещи. Но переключение между состояниями, штука довольно дорогая.

Итог: задача ОС грамотно schedule задачи, учитывая железные особенности для большей производительности.

1) Допустим есть некоторые «железные потоки», их столько же, сколько в железе ядер. Такой поток всегда привязан к одному ядру, он либо работает, либо halt свое ядро и не потребляет энергию.

2) Ниже есть системные потоки, их может быть сколько угодно и они крутятся поверх «железных потоков». Количество системных потоков ограничено производительностью. Есть некоторая индексная структура, в которой описаны все системные потоки, их состояние блокировки, время ожидания, приоритет и т.д. Scheduler с помощью этой индексной структуры выбирает потоки на работу, что будет быстрей, чем перебирать все потоки-кандидаты. Чем больше системных потоков, тем больше индекс, тем больше требуется времени на поиск кандидата-потока, тем больше доля потерянного времени в одном кванте времени отданного потоку. В худшем случае 70% кванта времени будет уходить Scheduler'у, 30% потоку. Очевидно, что скорость работы системы будет не удовлетворительной, хотя утилизация процессора на все 100%. Все блокировки между системными потоками — системные, т.е. на уровне ядра ОС.

3) Народ подумал и решил, ведь большинство блокировок, это ожидание ресурса: сеть, файлы, queue, что угодно. Лишь малая часть блокировок реально системные (критические секции, они обычно мгновенные). Тогда почему бы не сделать кооперативную многозадачность. Если поток знает, что повиснет на IO, то отдав запрос он просто отдает управление любому другом потоку. Это уже все в user-space. Вместо обще-системного списка потоков, у меня теперь маленький список green threads внутри одного процесса. Искать в маленьком списке проще.

Первый уровень требует хорошего знания железа. Второй уровень хорошего дизайна ОС и scheduler'а. Третий уровень прикладной и в принципе не зависит от ОС, ВМ и т.д.
Ваш ответ относится к рапространённым ОС с блокирующими системными вызовами. Потому он предполагает сложный комплекс мер. Переписав ядро на ВСЕ неблокирующие вызовы этот изврат не понадобится, всё станет проще. И не надо будет корпоративной многозадачности, все потоки будут эгоистично потреблять ресурсы и блокироваться (но это не будет системной блокировкой). Это как настоящие потоки ОС, только с уменьшенными накладными расходами за счёт отсутствия необходимости переключаться в режим ядра.
Переключение в режим ядра не самая большая проблема. Да, это смена контекста, да это задержка, только камни под это оптимизируются. Сложность в scheduler'е, которому предстоит работать со *всеми* потоками в системе, а их много. И идея в том, что реализация удобных легковесных потоков не задача ОС, а задача прикладного run-time. Просто перечтите это утверждение снова, вы поймете)
> Необходимо обеспечить кэширование состояния базы с определённым временным шагом,
Получается в памяти держим индекс дважды, один раз для писателей (хотя им для ребалансировки не нужно все дерево целиком) и еще раз для читателей. При чем читатели обращаются через модуль редиректа (дополнительный модуль/сервис понижает отказоустойчивость системы — он может отвалиться). Это не эффективно, да и грузить индекс в кеш придется постоянно, помним что писатели все время пишут. Наше условие: объект доступен в индексе *сразу* как только писатель закончит (очень важно для всяких stock market).

Выходит общих данных попросту нет. Каждый модуль — это один поток с thread-local переменными. С большим индексом, как я уже описал выше, придется внедрять дублирующий кеш и устаревающими данными, дополнительный IO (а это *очень* дорого) для загрузки данных в кеш, дополнительная сериализация для пересылки thread-local копий данных между модулями. Один только вопрос остался: зачем? Какие преимущества мне даст система, если я таки решу пройти через все эти танцы с модулями, по сравнению с Linux + Python + C-extensions + Lustre?
Когда писатели не слишком активны, общие данные будут. Когда нужно отдать данные «на растерзание» писателям, то естественно читателям нужно давать реплику… это вообще универсальный принцип репликации и кэширования.
Писатели всегда активны. Читайте задачу внимательней. Современные сервисы в сети всегда что-то пишут (eventual consistency) и одновременно копаются в данных. Это уже стандарт. Следовательно проблема с дублированием данных в кеше все еще актуальна.
Sign up to leave a comment.

Articles