Первыми программистами могли быть... динозавры.

Попалась на глаза презабавнейшая статья "Диван vs Коллайдер", в "Компьютерре-онлайн" (прежде чем читать далее - лучше ознакомиться с ней).

Автор выводит свою "теорию" гибели динозавров, основываясь на, возможно, непостоянной гравитационной составляющей, присутствующей в нашем мире.

Идя "впереди мысли" автора статьи, можно предположить, что из-за огромной величины мозга динозавров - они могли:

а) Погибнуть, все, из-за ментальной войны между ними. Если вы когда-нибудь "натыкались" на слова "и волшебники вели ожесточенную войну друг с другом, пока, в результате, почти все не погибли в ней" - то вы поймете, о чем я...

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

Древние компьютеры вообще могли состоять из органических материалов (мы к этому сейчас только подходим), поэтому и не уцелели до наших дней. К тому же человечество сейчас вовсю озабочено энергосбережением (70% - 90% анонсов компьютерного "железа" сейчас пестрит упоминаем этого, прямо всеобщее помешательство какое-то, видать тайфуны достали таки США до "самого-самого"), а так же применением утилизируемых материалов и конструкций, содержащих их.

P.S. Лично у меня с детства была голова немного больше, чем у других. Вот я и намякиваю... ;) Что объем мозга динозавров мог приводить к каким-то "последствиям" этого, ведь не так много нужно мозга, чтобы обсчитывать кол-во лиан и деревьев на пути, куда-то должно было "деваться" остальное...

UPD 1. Смутно припоминаю, что за последние 7 лет - где-то мелькнула новость, на счет компьютеров динозавров. Какая-та часть его уцелела, вроде. Что-то такое говорилось, что определили возраст устройство, и, по видимому, это только часть устройства. И что-то говорилось на счет органического происхождения, возможного, всей конструкции. А эта часть не то окаменела, не то в пластах там чего-то... Возможно описание было такое - что-то, напоминающее дудку, с отверстиями. Возможно это отнесли к вспомогательному устройству (если честно - совершенно не помню, но что-то вот близкое или напоминающее это...)

UPD 2. Смотрю фильм "Мерлин и война драконов". Драконы всегда обладали магией... по сказаниям... и Илья Муромец, или кто там - с драконом воевал... Хм...

Parallel-Ax

Логическим продолжением цикла статей-обсуждений "Многопоточность - проще не бывает" - стала "консолидация" всего материала и описание его в Викизнаниях.

Ссылка на Parallel-Ax.

UPD 1. Смотри также "Parallel-Ax и Кей Хорстманн".

Многопоточность - проще не бывает 3.

(Начало статьи - смотрите в 1-ой части и во 2-ой части).

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

Значит проблемы нет? Раз уже есть "подходящие" фреймворки, которые могут задействовать сотни потоков одновременно.

Вы хотите сказать, что есть, например, такие высоконагруженные операции, как вывод графики, и как же тогда там? Что значит - "высоконагруженные операции графики" - вы ДЕКОМПОЗИРУЙТЕ задачу. На сотню отдельных составляющих. Каждая из которых будет крутиться в своем потоке. Нужно отрендерить "задний план", на картинке? - Пускай это сделает отдельный подузел. Если у нас есть 50 потоков для рендеринга заднего плана - декомпозируйте еще. Вплоть до обсчета 2x2 пикселя в левом верхнем углу одним из них.

Вы хотите сказать - а как же эффекты, как же - текстуры, накладываемые после, как же - шейдеры? - Блин, ну а как же организован конвейер на любом заводе? Есть 10-100 автоматов, каждый из которых - "существует в отдельном потоке" (в свою очередь каждый такой автомат - может состоять из еще - множества различных узлов его).

Какие-то операции, как делается сейчас - могут выполняться более-менее параллельно - например нам нужно 4 конвейера для выпуска одной легковой машины.

А если нужно преобразовать какой-то сигнал, то можно поделить его рабочую частоту - на кол-во доступных потоков, чтобы каждый "обсчитывал" свою часть.

Первое правило - декомпозиция, как метод борьбы со сложностью. Ну и запускайте каждый "декомпозированный" элемент - в отдельном потоке. Если у нас может быть, иногда, 25 потоков, для какой-то подсистемы, доступно, а может быть что и 450 потоков будет доступно (на каком-то там готовящемся к выходу процессоре) - пожалуйста - заложите в конструкцию, что если 25 потоков - то Система декомпозируется на 12 подсистем + еще 13 потоков можно "передать" на декомпозицию некоторым из них. Если у нас 450 потоков - тогда 12 подсистем (на деле, каждая из которых еще декомпозируется на сотни более мелких) - большинство из них получат свои потоки.

Тут уже и архитектура вырисовывается... новых фреймворков...

Многопоточность - проще не бывает 2.

1-ая часть статьи, "протестированная" в "узком" кругу, побудила сразу написать 2-ую часть статьи, которая будет состоять из "пояснений", а не обзоров других задач по многопоточности, как я планировал сначала...

Итак. В чем же проблема, связанная с многопоточностью.

1) Любой объект, у которого есть внутренние переменные, потенциально может хранить состояние данного объекта.

2) Если объект может сохранять свое состояние, то тогда он потенциально "потокоуязвим". - Представим объект Муж. Который принес зарплату. И у которого, незаметно для него, вынимает часть этих денег - объект Жена. Объект Муж утром пошел за покупками, а денег то - уже нет...

3) Именно поэтому функциональный язык программирования F# и "отлично подходит" для работы с многопоточностью - там нет постоянных объектов с внутренними переменными в них - есть только методы (содержащие локальные переменные "на стеке"). Нет сохраняемых данных - нет и проблем с синхронизаций. Вообще я почти не знаю F#, но уверен, что основное "преимущество" его, в вопросах многопоточности - именно это. - Но, согласитесь, что это - извращение в самом прямом смысле этого слова. - Они бы еще от компьютеров отказались, не только от классов, лишь бы "не было проблем с многопоточностью". :)


Решение проблемы.

1) Я предлагаю - не "барахтаться" в терминах "вот у нас есть функция, она, де, изменит данные, состояние объекта". - Я предлагаю не хранить деньги в карманах объекта Муж, а создать НАДО ВСЕМ ЭТИМ (с делегированием полномочий) объект СемейныйСовет. Который и будет давать добро или отказывать - на те или иные финансовые операции в семье.

2) В объекте СемейныйСовет будут свои внутренние данные (локальная база данных, образно говоря). Которая будет в поддержку "системе принятия решения".

3) "Фишка" в том, что у нас уже НЕТ разделяемых данных (карманов объекта Муж). - Объект СемейныйСовет накапливают всю информацию о текущих финансовых операциях и сам все решает - на что, куда и как. Именно в объекте СемейныйСовет ЕСТЬ все данные, необходимые для принятия решения (но не сами деньги).

4) СемейныйСовет - действует по "событийной" модели - дал добро на покупку пиццы, или отклонил покупку мороженого.

5) Еще раз. Потоки САМИ НЕ МОГУТ просто вот так брать данные из РАЗДЕЛЯЕМЫХ между несколькими потоками данных. Они сначала должны получить "добро". При ЗАПРОСЕ на ИЗМЕНЕНИЕ разделяемых данных - управляющий объект ДОЛЖЕН УЧЕСТЬ общую ситуацию в системе. А потоки - должны быть готовы, что может быть добро, а может быть и отказ (с соотв. обработкой таких ситуаций). Или же управляющий объект - ПОДСКАЖЕТ - что же делать. Вы видите, что мы разделяем, декомпозируем сложность. Управляющему объекту, в этом месте - мы дадим бизнес-правила, как же он должен поступать, как оценивать ситуацию.

Многопоточность - проще не бывает.

Понимаю, что проблемы века - только мне решать одному, давайте тогда решим их.

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

Давайте раскатаем их "в губу" и покажем преимущества ООП-подхода.

Задача 1.

1
Есть объект Поставщик и есть объект Потребитель.
2 В каждом из них - есть по одному полю данных (любой объект может состоять из двух вещей - данных и методов работы с этими данными).
3 Требуется организовать обмен данными между этими двумя объектами. Проще говоря - когда у Поставщика его поле становится равным 1 (т.е. у него что-то есть) - это значение нужно "перебросить" Потребителю. При этом, после "переброски" - у Поставщика значение этого поля становится равным 0.
4 При этом Потребитель не должен ждать, он должен "что-то свое делать".

Решения:

1.1 Когда у Поставщика появляются данные - он "шлет" сообщение Потребителю. Потребитель забирает данные, обнуляет соотв. поле у Поставщика. Паттерн "Observer" ("Наблюдатель"). Все. Все, задача уже решена.

1.2. То же самое решение, что и 1.1, отличие только в том, что используем, в добавок, паттерн "Command". Чтобы инкапсулировать передачу данных (тогда можем "прямо и пересылать, сколько нужно, какую-то величину, например"). Можно еще использовать, при этом, паттерн "Object Value" (Фаулер пишет, что по поводу "Object Value" - могут возникать разночтения - есть два разных паттерна, названных так).


Задача 2.

Та же самая задача, что и задача 1, отличие только в том, что, пускай еще будет 1 объект Поставщик. Итого - 2 объекта класса Поставщика и 1 объект класса Потребителя. Потребитель должен "забирать бабло" у обоих Поставщиков.

Решения:

Те же самые, что и к задаче 1.


Задача 3.

Та же самая задача, что и задача 2, отличие только в том, что есть еще один объект Потребителя. Итого - 2 объекта класса Поставщик и 2 объекта класса Потребитель. Ну и есть какое-то правило - как, когда и сколько - должны забирать данные объекты Потребители.

Решения:

Те же самые, что и к задаче 1 + нужно добавить диспетчеры, координаторы или еще что хотите - на любой вкус и цвет. Здесь нужно отметить, что есть ОЧЕНЬ много паттернов проектирования, относящиеся к системам, основанным на обмене сообщениями. Да хоть очереди сообщений тут внедряйте, да хоть - пускайте тестовые сообщения, - к ним еще дроссели прикрутите к системе, можете добавить пулы, можете добавить балансировку нагрузки - ну о-о-очень много вариантов реализации, на любой вкус и цвет...


Слышу голоса -- А где же потоки??? - Ладно, будут вам и потоки...

Задача 4.

Есть объект Склад. На который "складирует" объект Поставщик (выполняющийся в своем потоке) и с которого забирает данные объект Потребитель (выполняющийся в другом потоке).

Решения:

Те же самые, что и к задаче 3.


Задача 5.

То же самое, что и задача 4. Отличие только в том, что у всех-всех-всех объектов - есть по 10 полей данных (мука, кукуруза, сгущенка, хлеб и т.д.) Требуются какие-то скоординированно-согласованные действия.

Решения:

А как же в жизни то бывает? - Есть некий нач. склада. Который принимает квитанции о приходе, принимает квитанции об убытии. И "рулит" все правила. - Кому, сколько, когда. Другими словами - нужна целая подсистема, которая будет рассылать события и в которой и будут "прописаны" все правила.

А теперь - типичная "функционально-процедурная постановка" задачи про "сложности" с многопоточностью:

"Рассмотрим, например, класс, реализующий набор банковских счетов. В этом классе имеются две безопасные для потоков управления операции Deposit и Withdraw, предназначенные для зачисления денег на счет и снятия их со счета соответственно. Предположим, что требуется произвести композицию этих операций в безопасную для потоков операцию Transfer, которая переводит деньги с одного счета на другой. Промежуточное состояние, когда деньги сняты с одного счета и не зачислены на другой счет, не должно быть видимым для других потоков управления (т.е. перевод должен быть атомарным). Поскольку в операциях Deposit и Withdraw счет блокируется только на время их выполнения, для правильной реализации операции Transfer требуется понять дисциплину блокировок, используемую в данном классе, и изменить ее путем добавления метода для блокировки всех счетов или какого-нибудь одного счета. Последний подход позволяет выполнять параллельно операции переводов с разными счетами, но при этом появляется возможность синхронизационного тупика, если выполнение операции перевода со счета A на счет B перекрывается во времени с выполнением операции перевода со счета B на счет A. "

- Другими словами, авторы примера - объединяют 2 атомарные операции - в одну (снятие денег с одного счета + зачисление денег на другой счет = перевод денег), при этом, само собой - сложность увеличивается (в противоположность декомпозиции сложности, как методу решения задач). Само собой, если думать в 3D-измерении (а не "размазывать" все в 2D-измерении), то нужен какой-то НАДО ВСЕМ ЭТИМ - управляющий центр. У которого должна быть система правил. Авторы про это и намекают, вернее - задаются мыслью ("требуется понять дисциплину блокировок ... и изменить ее"), что чего б такого придумать, в данной ситуации.

Управляющий центр операции перевода денег. Центр. Объект. Координатор. В который закладываем систему правил. Возможно и как "конечный автомат".

Центру поступила команда -- "Перевести с одного банка 100 долларов на другой, но убедиться, и соблюсти. Что деньги перечислились. После этого уже - инкремировать один счет и декримировать - другой счет".

Ну и что может быть проще?

Центру поступил event (событие). Центр послал event одному банку, с номером ТЕКУЩЕГО трансфера и Центр послал, одновременно - второй event, второму банку, с номером все того же ТЕКУЩЕГО трансфера. Один банк прислал ответ - "Деньги получены!" - Хорошо, ОК, теперь Центр шлет обоим банкам еще по одному событию, чтобы реально уже списали/зачислили.

На деле механизм событий может быть оптимизирован ("размазан в 2D") - если точно известно, что эта операция - высоконагруженная, тогда чтобы симитировать события - можно воспользоваться несколькими регистрами. Если они принимают какие-то значения, значит "что-то состоялось". Если они в состоянии "не определены", значит операция еще "в процессе". При чем, как мне кажется - можно использовать именно группы таких регистров (переменных). - Все вместе они будут характеризовать состояние операции (грубо говоря - это что-то вроде мини-СУБД, к которой может обращаться Центр и "считать себе", анализировать, что ему надо).

А знаете как на деле происходит, если, например, вы расплачиваетесь кредиткой Visa Electron от Альфа-банка (не забыть сгрести от них бабло, за рекламу) - чтобы купить что-то в Интернете на одном из аукционов или просто какую-то книжку в Интернет-магазине?

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

- Так вот. Бизнес-требования банка, к данной операции - известны. В данном случае управляющий объект, Центр координации - расположен в банке. Это его правила - замораживать сумму, или нет. Вы только, образно говоря - запустили всю программу на выполнение (метод main()).

Какой-то банк - еще и письменное подтверждение ждать будет. Или если Вы перечисляете 1.000.000 долларов - то Вашего личного присутствия будет ждать. И подписи. А не просто "клик-клик" по Инету. А могут быть еще и всякие федеральные органы, регулирующие права "хозяйствующих субъектов". Банк еще может и аудиторам свой отчет слать. Ну и т.д.

Если у вас что-то не совсем сложилось в голове - как это все запрограммировать - то спрашивайте, не стесняйтесь. Лично мне так сразу понятно, как и что...

UPD 2. Вышло продолжение, 2-ая часть статьи.

Человеческий фактор и SQL.

Работа идет по плану, все трудятся (кто-то одноклассничает, не будем останавливаться на этом моменте, главное - все заняты).
Идут дискуссии, совещания, планерки... Незаметно в работе появляется такой этап (наблюдаемый мною на нескольких фирмах), когда или архитектор, или программист баз данных - начинает с сосредоточенным выражением лица подсчитывать... сколько байтиков займет такая вот структура SQL-таблицы...

А у меня сразу вопросы:

1) Разве известен объем дисковых накопителей, на этапе разработки enterprise-проекта, что можно уже "мерять" и "примерять"? Да пока разработка завершится (структура СУБД проектируется отнюдь не в последние дни, обычно - в первый, максимум - во второй месяц разработки) - доступное решение, по емкости, может быть в 1.5 раза выше, чем можно "наблюдать сейчас". Тем более во многие проекты стараются закладывать "переносимость", по СУБД. Т.е. вообще ничего по емкости не известно (в контексте самой задачи).
Кто-то хочет сказать, что уже "есть готовый сервер" под СУБД? А когда он наполнится, данными? Через год, сразу после завершения проекта? Разве? Через 2-3 года, "если проектировать безвольно, не учитывая". Что значит "не учитывая"? Кто ж проектирует, не оптимизируя? Вопрос в том - что значат эти подсчеты? Плавно переходим ко 2-му пункту.

2) Ну, хорошо, система проектируется из расчета - 1.500.000 пользователей. И? Что значат подсчеты? Подсчитывается, обычно, все по нескольким таблицам. На первом месяце разработки. А кто подсчитает ВСЕ ОСТАЛЬНОЕ? Когда бизнес-требования изменятся? Разработка ведь - носит (в наши времена) - в большинстве случаев - итеративный характер. Что можно считать НА ПЕРВОМ МЕСЯЦЕ разработки? Ну вот что?
Это все равно, что на первом месяце разработки - прикидывать, в уме (на листике) - закрома Родины через 4 года. Ну столько факторов, которые могут все увеличить в 2-3 раза, или уменьшить - что просто не передать словами. Цену на нефть кто-нибудь знает, какой она будет через 2 года? И сколько миллиардов инвестируется в отечественный автопром - через 3 года? Точную цифру назвать можете? А, что так? Слабо на листике подсчитать? Достаточно сказать, что колебания курса на рынке Форекс - уже сама по себе нетривиальная функция. На которую нужно опираться, в расчетах.
Так же и здесь. 1.500.000 пользователей в таблицах user_accounts, user_payments и 50 бизнес-требований, которыми вас "наградят" (начальство, аналитики, партнеры) через 3 месяца.

Я понимаю - задать ограничения на максимальную длину отдельных текстовых полей. Но при этом никто не смотрит "сколько байтиков займет". Можно (и нужно) проектировать эффективное приложение. По сотне самых разных факторов и показателей (выбор СУБД, например). Но считать число байт, по какой-то одной или 2-3 таблицам - ммм...