Резник Михаил Маркович : другие произведения.

Асинхронные компьютеры

Самиздат: [Регистрация] [Найти] [Рейтинги] [Обсуждения] [Новинки] [Обзоры] [Помощь|Техвопросы]
Ссылки:


 Ваша оценка:
  • Аннотация:
    Обычные компьютеры с последовательной последовательностью команд не могут обеспечить качественное повышение призводительности. Параллельные вычисления, как они реализуются сейчас, также не выход.Предлагается принципиально другая архитектура компьютеров и , соответственно, другая методология программирования.


Асинхронные компьютеры и асинхронное программирование.

Введение

  
   Современные компьютерные комплексы, предназначенные для организации больших вычислительных потоков, обладают рядом существенных недостатков. Практически все работают по схеме общего потока команд с различными данными, и это требует от алгоритмов очень жесткого условия - они должны быть независимы от данных, в противном случае нарушается общность потока команд. Таким образом, практически любые ветвления (в зависимости от данных) в программах недопустимы, что резко сужает применимость такого рода комплексов. Использование же кластеров с одной стороны требует специального программирования практически для каждой задачи, требующей реализации на таком комплексе, а с другой -- организация обмена данными между компьютерами может существенно понизить общую эффективность системы. Кроме того, сами процессоры, в силу своей универсальности, используют в таких задачах весьма небольшой процент своих возможностей. Многоядерность современных X86 процессоров, как показывает практика, дает довольно скромное повышение производительности, а иной раз и уменьшение ее по сравнению с одноядерным такой - же суммарной мощности. На мой взгляд, проблема состоит в том, что архитектура процессоров и вся логика процесса вычислений осталась от копирования последовательного способа вычислений шаг за шагом, как это делал человек на бумаге, что при современном техническом уровне стало тормозом в организации высокопроизводительных вычислений.
   Для решения этой задачи предлагается процедурный язык низкого уровня и логическая архитектура компьютера, которые обеспечивают насыщение вычислительного канала потоком команд для алгоритмов любой сложности, расширяемость как языка, так и технических средств с сохранением мобильности программ. Возможная реализация такого подхода, за счет разбиения процесса на более простые подзадачи, позволяет упростить техническую реализацию, создавать различные модификации в зависимости от сферы применения.
  

Процедурный язык асинхронных вычислений.

  
   Введем несколько определений:
   Программа - совокупность процедур и сформированная область входных и выходных данных для одной из процедур, которая является начальной.
   Команда - вызов процедуры <код процедуры> <RIn> <ROn>
   Цепочка команд - конечная последовательность команд (блок).
   Процедура -код процедуры, совокупность цепочек команд (блоков ), входные данные (RI), выходные данные (RO) , рабочие данные (RW) и размер этих областей.
   Список кодов - последовательность из пар , включающих в себя код команды и ссылку на реализующую ее процедуру.
   Данные - конечная область из одинаковых пронумерованных ячеек памяти (регистров).
   Каждый регистр имеет, кроме некоторого объема памяти для информации, еще и указатель состояния , который показывает:
  -- наличие данных ("занято")
  -- подготовлен для записи ("захвачен")
  -- готов для записи ("свободен").
   В команде указывается область данных и номер регистра , с которого начинается выделенная область.
   Изначально все регистры в рабочей области процедуры "свободны", как и выходные данные.
   Процедура сама завершает свою работу как только вся выходная область будет заполнена, т.е. все данные в выходной области "заняты".
   Часть процедур "зашита" в комплексе и реализуется самим комплексом с помощью программных или технических средств, причем это может зависеть от конкретной реализации.
  
   Теперь опишем некий гипотетический комплекс, которые выполняет описанные выше процедуры. Он состоит из счетного множества командных процессоров, каждый из которых предназначен для выполнения одной процедуры. Для каждой программы отводится область процедур, в которую загружаются все процедуры программы и для процедуры загружаются список определяемых/переопределяемых команд. При инициации процедуры строится свой собственный список, который наследуется от вызывающей процедуры и дописывается в конец , а также получает рабочую область заданных размеров. Затем запускается первая из цепочек команд. Команды имеют в качестве параметров две области памяти. В качестве входных данных (RI) может быть как часть области входных данных, так и часть области рабочих данных(RW). Для выходных данных (RO) используется или часть области выходных данных вызывающей процедуры или часть области рабочих данных. Для определения выполняющейся процедуры для данной команды поиск ведется с конца списка, поэтому последние переопределения имеют приоритет. Вся цепочка команд идет в обработку сразу, в любом порядке. Команда начинает выполняться (т.е. передается другому командному процессору или техническими средствами комплекса) тогда, когда вся область входных данных для этой команды заполнена, а выходная свободна. При этом все регистры выходных данных получают статус "захвачен" Входные данные передаются по значению, т.е. подготовленная область копируется и эта копия передается процедуре, в то время как выходные данные передаются по ссылке, т.е. результаты попадают именно в то место, которое указано. Все команды, ожидающие данные, при изменении статуса входных данных на "занято" сразу же копируют их значение к себе в область параметров. Таким образом,можно освободить сформированную входную область сразу после передачи команды на выполнение, причем возможна ситуация, когда это делают несколько команд для одних и тех же регистров. В таком случае это не изменяет статус регистров и исключительная ситуация не фиксируется. После возвращения результата в регистр его статус меняется на "занято". Более формально можно описать работу командного процессора следующими шагами:
  
      -- Загрузка команд из инициируемой цепочки команд. Каждой команде выделяется область параметров и формируется область выходных данных, после чего они встают в очередь на получение данных и выполнение. Далее идет запускается шаг 2.
      -- Просмотр очереди команд. Команда со сформированной областью входных данных и свободной областью параметров передается на выполнение , у регистров выходной области меняется статус на "захвачен", команда удаляется из очереди и просмотр списка начинается снова.
      -- При изменении статуса любого регистра также происходит просмотр всех команд, где задействован этот регистр. Если регистр становится "занят", то для всех команд, которые ожидают этот регистр во входной области, его значение пересылается его значение в их входную область. Если становится занятой вся выходная область, то процедура, обслуживаемая этим процессором, заканчивает работу. После просмотра всех этих команд следует просмотр очереди команд( шаг 2).
  
   Теперь несколько конкретизируем описание набора команд. Ясно, что должна быть группа команд, позволяющих освобождать регистры рабочей области. Это либо отдельная команда, либо любая из команд с соответствующим признаком. Такая команда после передачи процедуры на выполнение освобождает все регистры входных данных этой команды, если они принадлежат рабочей области. Для входных (RI) и выходных (RO) данных такая команда недопустима.
   Также необходима команда активации цепочки команд, номер которой передается в регистре - параметре команды.
   Отдельно стоит вопрос об обмене данными с некоторым массивом внешней памяти. Предполагается, что результаты работы программы , как и входные данные, должны где - либо находиться. Действия с этой памятью, как то : выделение, освобождение и обмен данными должны обеспечиваться группой команд работы с внешней памятью.
   Вопрос работы с файловой системой и другими внешними устройствами вообще не стоит в рамках этой статьи, так - как по мнению автора, этот комплекс предназначен для организации именно вычислений, а не обменных операций, хотя для систем с очень большим объемом данных бывает актуально взаимодействие файлообменных операций и вычислений.
  

Управление комплексом.

  
   Как было указано выше, процедура может закончить работу, хотя ряд команд уже были запущены в работу и в свою очередь запустившие свои команды. Кроме того, могут возникать исключительные ситуации - обращение к не заказанным участкам памяти , неверный номер цепочки команд и многое другое, которые должны вызывать прекращение процедуры. И в этих случаях необходимо принудительно прекращать работу процедуры и подчистить весь "мусор", который останется за ней. Кроме того, очевидно, что реальная реализация может иметь лишь конечное количество командных процессоров, а команд на выполнение может (и должно) быть гораздо больше. Для распределения этого потока команд между физическими командными процессорами необходимо уметь свертывать процедуру, хранить поступающие к ней результаты , чтобы передать их при развертывании на другом физическом процессоре. Таким образом, каждая выполняемая команда должна нести с собой некий идентификатор, позволяющий привязать результаты выполнения команды к тому командному процессору, на котором будет выполняться та процедура, которая послала эту команду (который может быть совсем не тем, который посылал эту команду). Кроме того, сам процесс распределения ресурсов (конечного числа командных процессоров) между потоком команд различных программ и команд в рамках дерева процедур, порождаемых программой таким образом, чтобы с одной стороны, программа не ждала отправленные процедуры, которые были свернуты и ждали свою очередь на выполнение, достаточно сложен и опять -- таки требует специализированного ПО и соответствующего оборудования. Впрочем, вопросы планирования и квантования задач не является уж очень новой задачей и успешно решаются во всех многопрограммных операционных системах.
   Естественно, что обеспечить загрузку программы, начальных данных для нее и данных во внешней памяти, а также сохранение результатов из внешней памяти тоже должна какая - либо система, однако и эти, как и многие другие вопросы остаются вне рамок этой статьи.
  

Выполнение встроенных команд.

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

Программирование

  
   Эффективность описанной выше схемы работы зависит от того, насколько возможно загрузить комплекс потоком команд. Для этого , если рассматривать одну задачу , нужно запустить все вычисления, которые могут быть выполнены по готовности для них данных. Краеугольным приемом всех алгоритмов является цикл. Его эффективная реализация на таком комплексе является ключевой для оценки применимости. Для примера рассмотрим организацию цикла на примере расчета последовательности значений функции F(X1),F(X2), ...., F(XN) и запись их во внешнюю память. Рассмотрим алгоритм в привычном стиле программирования :
  
      -- Задание начального значения I равным 1
      -- Вычисление X по I
      -- Вычисление F по X
      -- Запись значения F в массив внешней памяти с индексом I
      -- Увеличение I на 1
      -- Проверка на конец цикла. В случае продолжения работы переход на шаг 2.
  
   Очевидно, что применять впрямую эту схему здесь невозможно. В самом деле, если все команды выполняются в любом порядке, то шаг 5 может выполнится раньше, чем шаг 2 или 4, что приведет явно к неверным результатам. Если же синхронизировать шаги так, чтобы они выполнялись строго друг за другом (а это возможно), то пропадает весь смысл использования такой схемы вычислений. Дело в том, что в теле цикла так или иначе используются переменные, регистры или ячейки памяти, в которых хранятся данные, необходимые для вычислений. Следовательно, нужно озаботиться их целостностью до тех пор, пока они не освободятся. Их нельзя переопределить для следующей итерации, а значит, нужно ждать окончания выполнения тела цикла. Выходом из этой ситуации является механизм порождения новой среды для тела цикла, что достигается применением рекурсии.
   Для реализации этой задачи рассмотрим рекурсивную функцию FST со следующими входными параметрами :
   A -- адрес массива во внешней памяти
   I -- индекс
   N - количество итераций
  
   и одним выходным
   C -- код возврата
   в которой используются следующие процедуры :
   X c одним входным параметром I и одним выходным -- значением Xi
   F с одним входным параметром X и одним значением -- вычисленным значением функции.
  
   В этой функции три блока команд:
  
   Блок 1
   Rx= X(I)
   Ft=F(Rx)
   Запоминание Ft в A c индексом I. Результат -- это 1 в Rc.
   Если I >N то инициация Блок 2 иначе инициация Блок 3
  
   Блок 2
   С=Rc
  
   Блок 3
   In=I+1
   Вызов процедуры FST со следующими входными параметрами A , In, N и выходным Rq.
   С=Rc+Rq
  
   Теперь подробно рассмотрим организацию вычислений.
   Очевидно, что первые три шага Блока 1 выполняются строго друг за другом, так как входные данные последующего есть результат предыдущего. Последний шаг выполняется сразу и не зависит от других. В случае, если это была последняя итерация, то идет заполнение выходного параметра (Блок 2), т.е. когда данные будут записаны в память, то произойдет окончание работы процедуры. Иначе же идет работа Блока 3 , где мы запускаем эту же функцию с увеличенным на 1 значением индекса I. Возвращение выходного значения произойдет, когда эта функция закончит работать и данные в Блоке 1 будут записаны в память. Это означает, что пока последняя итерация не выполнится, то все предыдущие ее будут ждать. Но это вовсе не означает, что все вычисления будут стоять. Напротив, расчет новых значений функции F будет запущены сразу, не ожидая окончания процедуры. Таким образом, вычислительный канал будет достаточно быстро загружен вычислением N значений функции F вместе с "погружением" рекурсии. По мере записи данных в память и отработке запущенных итераций пройдет "всплытие" рекурсии и, соответственно, завершение работы. Запуск этой процедуры с I равным 1 и даст требуемый результат.
  
   Если нам требуется посчитать сумму значений функции F(X1),F(X2), ...., F(XN) , то это легко достигается следующей модификацией выше описанной схемы :
   Рассмотрим рекурсивную функцию FSS со следующими входными параметрами :
   I -- индекс
   N - количество итераций
  
   и одним выходным - суммой значений функции.
   Процедуры X и F - те же, что и выше.
  
   Блок 1
   Rx= X(I)
   Ft=F(Rx)
   Если I >N то инициация Блок 2 иначе инициация Блок 3
  
   Блок 2
   С=Ft
  
   Блок 3
   In=I+1
   Вызов процедуры FSS со следующими входными параметрами In, N и выходным Rq.
   С=Ft+Rq
  
   Возвращаемое значение этой процедуры с I равным 1 дает требуемую сумму.
  

Приложение 1

Синтаксис процедурного языка.

  
   Здесь описывается одна из возможных моделей процедурного языка. Так -- как модель такого языка достаточно проста, то и синтаксис описывается просто.
  
   Программа представляет из себя совокупность процедур с уникальными именами.
  
   [PROG:<имя программы>:<имя процедуры входа >]
   [PROC:<имя процедуры 1>]
   ............
   [ENDPROC]
   ...........
   [PROC:< имя процедуры n>]
   ............
   [ENDPROC]
   ............
   [ENDPROG]
  
   Описание процедуры :
  
   [PROC:<имя процедуры>]
   [INPUT:< размер входной области>]
   [WORK:<размер рабочей области>]
   [OUTPUT:<размер выходной области>]
   [PROCLIST]
   <наименование процедуры 1>:<код процедуры 1>
   ...........
   <наименование процедуры n>:<код процедуры n>
   [ENDLIST]
   [DEFINE]
   [STRUCT:<имя структуры>]
   <имя структуры> :<имя поля>
   .............
   <имя структуры> :<имя поля>
   [ENDSTRUCT]
   .............
   [STRUCT:<имя структуры>]
   .............
   [ENDSTRUCT]
   <имя структуры>:<имя объекта>:<объект>
   .............
   <имя структуры>:<имя объекта>:<объект>
   [ENDDEF]
   [INIT]
   <имя объекта>:<список констант>
   .............
   <имя объекта>:<список констант>
   [ENDINIT]
   [BLOCK: <имя блока 1>]
   <команда 1>
   ..........
   <команда k1>
   [ENDBLOCK]
   ...............
   [BLOCK: <имя блока m>]
   <команда 1>
   ..........
   <команда km>
   [ENDBLOCK]
   [ENDPROC]
  
   Области
  
   У каждой процедуры имеется изначально четыре области регистров:
   Входные данные.
   Количество регистров определено в операторе [INPUT:< размер входной области>]. Обозначаются RI<номер регистра>, например RI1, RI594 и т.д. Изначально эти регистры заполнены данными и не могут быть освобождены в рамках текущей процедуры.
   Выходные данные.
   Количество регистров определено в операторе [OUTPUT:< размер выходной области>]. Обозначаются RO<номер регистра>, например RO1, RO594 и т.д. Изначально эти регистры не заполнены данными и не могут быть освобождены в рамках текущей процедуры.
   Рабочие данные.
   Количество регистров определено в операторе [WORK:< размер рабочей области>]. Обозначаются RW<номер регистра>, например RW1, RW594 и т.д. Изначально эти регистры не заполнены данными и могут быть освобождены в рамках текущей процедуры.
   Список командных цепочек (блоков).
   Количество регистров определено количеством операторов [BLOCK: <имя блока >].
   Обозначаются RB<номер регистра>, например RB1, RB594 и т.д. Изначально эти регистры заполнены данными и не могут быть освобождены в рамках текущей процедуры. Их содержимое используется для запуска соответствующей цепочки команд и соответствует порядку операторов [BLOCK: <имя блока >].
  
   Описание команды представлено следующими вариантами:
  
   <код процедуры> : <Входные данные>: <Выходные данные>
   или
   <код процедуры> : <Входные данные рабочей области> : <Выходные данные>:F
  
   Для группы команд ,как то: команда запуска секции , освобождения занятого регистра и другие,связанные только с обслуживанием текущей процедуры, отсутствуют выходные данные и их вид :
   <код процедуры> : <Входные данные>
   или
   <код процедуры> : <Входные данные>:F
  
   <Входные данные> - это номер регистра из любой из областей, кроме выходной.
  
   <Выходные данные> - это номер регистра рабочей области или номер регистра выходной области.
   Команды с F в конце освобождают входные данные рабочей области при запуске команды, если входные данные из другой области, то это синтаксическая ошибка.
   Однако работа с номерами регистров очень неудобна -- язык делается для человека, а не для машины. Поэтому, чтобы использовать мнемонику для работы с данными, в процедуре используется секция [DEFINE], в которой определяются именованные объекты, привязанные к конкретным подмножествам областей, состоящим из нескольких подряд стоящих регистров.
  
   Структуры и объекты.
  
  
   Имеется одна предопределенная структура -- регистр ( R ).
   Определение структуры :
   [STRUCT:<имя структуры>]
   <имя структуры 1> :<имя поля 1>
   ...............
   <имя структуры n> :<имя поля n>
   [ENDSTRUCT]
  
   Здесь <имя структуры k> - это имя уже ранее определённой структуры в разделе [DEFINE].
   После того, как все структуры определены, их можно привязать к конкретному месту в одной из областей с помощью выражения
   <имя структуры>:<имя объекта>:<поле>
   Здесь <имя структуры> - это имя ранее определённой структуры,
   <имя объекта> - это имя определяемого объекта
   <поле> - это либо поле ранее определенного объекта, либо регистр какой либо области.
   Поле объекта -- это <имя объекта>.<имя поля>, а для первого поля объекта эквивалентом является просто <имя объекта>.
   Рассмотрим несколько примеров.
  
   Просто регистры:
  
   RI5
   RW44
   RO12
   RB14
  
   Определение структур.
  
   [STRUCT:Input]
   R :I
   R:K
   R:G
   [ENDSTRUCT]
   [STRUCT:Output]
   R :L
   R:F
   R:T
   [ENDSTRUCT]
   [STRUCT:W]
   Input:A
   Output:B
   [ENDSTRUCT]
  
   Объекты.
  
   Привяжем ранее определенные структуры к конкретным регистрам, чтобы получить объекты:
  
   Input:Inp:RI1
   Output:Out1:RO1
   Input:WInp:RW10
   W:WS:RW24
  
   Тогда эквивалентами будут:
  
   WS.A.G и RW26
   WS.B.T и RW29
  
   Таким образом можно использовать объекты в командах процедур вместо или вместе с регистрами.
  
   Инициализация рабочей области
  
   В рабочей области часть регистров можно заполнить константами при запуске процедуры. Для этого в разделе [INIT] указывается регистр или эквивалентный ему объект рабочей области, начиная с которого константы из списка помещаются в регистры, начиная с указанного. В списке константы разделены запятыми. Сами константы представляются как десятичное число с порядком или без оного, например:
   189
   -16.356
   0.00025е-13
  
   Общие положения
  
   Именами программы, процедур, блоков, структур, полей могут быть любые идентификаторы -- буквенно-цифровые последовательности, начинающиеся с буквы. Код команды может включать в себя и специальные символы : *,/,-,+,%,#,?,&,~ и другие, кроме двоеточия, служащее разделителем, и точки с запятой, используемой для отделения оператора от последующего комментария. Другими словами, после любого оператора языка, в том числе и пустого, через ";" можно комментировать все операторы программы. Пробелы и знаки табуляции во всех операторах игнорируются и могут использоваться для повышения читабельности программы.
  
   Список команд
  
   Определим несколько команд, которые будем считать предопределенными:
  
   "Jump" инициация выполнения блока , который указан в единственном параметре.
   "+" сложение двух входных регистров в один результирующий.
   "-" разность двух входных регистров в один результирующий.
   "*" произведение двух входных регистров в один результирующий.
   "/" результат деления двух входных регистров в один результирующий.
   "?" условное присваивание : входная область состоит из трех регистров, первый регистр означает условие, и в случае положительного значения в результирующий регистр попадает значение второго входного регистра, в противном случае -- третьего.
   "Move" пересылка значения входного регистра в выходной.
   "Free" освобождение входного регистра.
   "Load" загрузка данных из внешней памяти , адрес которого во входном регистре, в выходной.
   "Stor" загрузка данных во внешнюю память , адрес которой в первом входном регистре, а значение -- во втором. Результат -- 1 по окончании процесса пересылки в выходном регистре.
   "Arg" получение данных, переданных программе в качестве параметров. Входной регистр несущественен, а в выходном содержится адрес поля внешней памяти , в котором и содержится передаваемая информация.
  

Работа со внешней памятью

  
   Единственной адресуемой единицей является регистр, поэтому если к заданному в регистре адресу внешней памяти добавить натуральное число m, то мы получаем адрес m+1 регистра во внешней памяти, отведенной для хранения регистров. Какова емкость регистра в битах, байтах или в словах -- это здесь не рассматривается. Считается, что емкость регистра достаточна для того, что обеспечить адресуемое пространство и обеспечить требуемую точность вычислений. Также не рассматривается форма представления данных в регистре.
  
  
  

Приложение 2

   Пример реализации функций
  
  
   [PROC:Head]
   [INPUT:2]
   [WORK:10]
   [OUTPUT:1]
   [PROCLIST]
   Stor_F:STF
   [ENDLIST]
   [DEFINE]
   [STRUCT: Input_F]
   R :N
   R :I
   R:A
   [ENDSTRUCT]
   [STRUCT:Input]
   R :N
   R :I
   R :A
   [ENDSTRUCT]
   [STRUCT:Output]
   R :Nout
   [ENDSTRUCT]
   [STRUCT:Work]
   R :C0
   R :C1
   R :Nt
   Input_F:Winp
   R: Nr
   [ENDSTRUCT]
   Input:Inp:RI1
   Output:Out:RO1
   Work:Work:RW1
   [ENDDEF]
   [INIT]
   Work : 0,1
   [ENDINIT]
   [BLOCK: Entry]
   Move : Inp.N : Winp.N
   Move : Work.C1 : Winp.I
   Move : Inp.A : Winp.A
   FST : Winp : Work.Nr
   [ENDBLOCK]
   [ENDPROC]
  
   [PROC:F]
   [INPUT:1]
   [WORK:7]
   [OUTPUT:1]
   [INIT]
   RW1 : 1
   [ENDINIT]
   [BLOCK:Head]
   Move : RI1:RW3 ; x
   Move : RI1:RW4 ; x
   * : RW4:RW2; x*x
   / : RW1:RO1 ;1/(x*x)
   [ENDBLOCK]
   [ENDPROC]
  
   [PROC:X]
   [INPUT:1]
   [WORK:7]
   [OUTPUT:1]
   [INIT]
   RW1 : 0,1,0.001
   [ENDINIT]
   [BLOCK:Head]
   Move : RI1:RW4 ; I
   Move : RW2:RW5 ;1
   - : RW4 : RW6 ; I-1
   Move : RW3 : RW7 ; 0.001
   * : RW6:RO1 ; (I-1)*0.001
   [ENDBLOCK]
   [ENDPROC]
  
   [PROC:Stor_F]
   [INPUT:3]
   [WORK:30]
   [OUTPUT:1]
   [PROCLIST]
   F:f
   X:x
   Stor_F:Fst
   [ENDLIST]
   [DEFINE]
   [STRUCT: Input_F]
   R :N
   R :I
   R:A
   [ENDSTRUCT]
   [STRUCT: Work]
   R:N
   R:I
   R :N_I
   R :RNext
   R :REnd
   R :Rp
   R :RC1
   R :RC2
   R :RCS
   R:X
   R:A_I
   R:E
   R:A_1
   R:F
   R:IW
   R:E1
  
   [ENDSTRUCT]
  
   Input_F:Inp:RI1
   Input_F:Inpw:RW2
   Work: W:RW5
  
   [ENDDEF]
   [INIT]
   RW1 : 1
   W.E:1
   W.E1:1
   [ENDINIT]
   [BLOCK:Head]
   x :I np.I: W.X
   f : W.X:W.F
   + : Inp.I:W.A_I
   - : W.A_I:W.A_1
   Stor :W.A_1: R :RC1
   Move : RB1:W,RNext
   Move : RB2:W,REnd
   Move : Inp.I:W.I
   Move : Inp.N:W.N
   - : W.N:W.N_I
   ? : W.N_I:W.Rp
   [ENDBLOCK]
   [BLOCK:Next]
   Move : Inp.N: Inpw.N
   Move : Inp.A: Inpw.A
   Move : Inp.I: W.Iw
   + : W.Iw:Inpw.I
   Fst :Inpw:W.Rc2
   + : W.RC1:I W.RCS
   Move : W.RCS:RO1
   [ENDBLOCK]
  
   [BLOCK:End]
   Move : W.RC1:RO1
   [ENDBLOCK]
  
   [ENDPROC]
  
 Ваша оценка:

Связаться с программистом сайта.

Новые книги авторов СИ, вышедшие из печати:
О.Болдырева "Крадуш. Чужие души" М.Николаев "Вторжение на Землю"

Как попасть в этoт список

Кожевенное мастерство | Сайт "Художники" | Доска об'явлений "Книги"