FizzBuzz по-сениорски

— Добрый день, я на интервью на позицию старшего разработчика.

— Здравствуйте, давайте начнем с небольшого теста, пока я ваше CV смотрю. Напишите программу, которая выводила бы числа от 1 до, скажем, миллиарда, притом если число кратно трем, то вместо числа выводится Fizz, если кратно пяти, то Buzz, а если и трем, и пяти, то FizzBuzz.

Серьезно, FizzBuzz? Задачка для начальной школы, на сениорскую позицию? Ну ладно.


Я достаю свой верный лаптоп, и пишу такой код:

#include <stdio.h>  #define LIMIT 1000000000  int main(void) {     for (int i = 1; i <= LIMIT; i++) {         if (0 == i % 3) {             if (0 == i % 5) {                 printf("FizzBuzz\n");             } else {                 printf("Fizz\n");             }         } else if (0 == i % 5) {             printf("Buzz\n");         } else {             printf("%d\n", i);         }     }      return 0; }

Запускаю программу, она себе бежит, но не так чтобы сильно быстро, через 3 с чем-то минуты, после первого миллиона, я ее прерываю и быстренько высчитываю, что весь процесс займет больше двух суток. Да, наверно надо было включить буферизацию, это бы несколько ускорило, но это не спасет, лучше просто перенаправить вывод в файл, что я и делаю, и через 41.5 секунду у меня есть красивенький файл на 7.5 гигов.

— Вам не кажется, что можно побыстрее? — спрашивает интервьюер.

— Да ладно, основное время занимает I/O, 7.5 гигов записать — не шутка, даже на SSD.

— А давайте перенаправим вывод в /dev/null.

— Без проблем.

Через минуту:

— Как это — 39.5 секунд? То есть весь I/O занимает 2 секунды, а все остальное время — мой код?

— Да, так получается. Это не самая медленная реализация, на каждой итерации два сравнения и один printf, я часто вижу вариант с тремя сравнениями и двумя printf’ами. Для джуниора, я бы сказал, это даже хорошо. А вот для сениора …

Это было больно, но, пожалуй, заслужено. Ладно, я тебе покажу, кто тут джуниор.

— Сейчас сделаю побыстрее.

— Попробуйте. Только объясняйте, что вы делаете.

— Видите, что у нас тут есть паттерн — каждые 3*5, то есть 15 итераций цикла логика полностью повторяется. Тогда можно переделать цикл:

    for (i = 1; i < LIMIT - 15; i += 15) {         printf( "%d\n"          // 1                 "%d\n"          // 2                 "Fizz\n"        // 3                 "%d\n"          // 4                 "Buzz\n"        // 5                 "Fizz\n"        // 6                 "%d\n"          // 7                 "%d\n"          // 8                 "Fizz\n"        // 9                 "Buzz\n"        // 10                 "%d\n"          // 11                 "Fizz\n"        // 12                 "%d\n"          // 13                 "%d\n"          // 14                 "FizzBuzz\n",   // 15                 i, i+1, i+3, i+6, i+7, i+10, i+12, i+13);     }

— Если раньше на каждые 15 чисел у нас приходилось 15 сравнений переменной цикла и два if’а в теле цикла, то есть в общей сложности 45 сравнений, а каждое сравнение — это потенциальная проблема с branch prediction’ом, то теперь одно. Да и вызовов printf’а стало в 15 раз меньше. Одна только проблема — цикл не дойдет ровно до миллиарда, а только до 999999990 (макс число, кратное 15 и меньшее миллиарда), так что оставим старый цикл, но только для обработки хвоста, то есть последних 10 значений (это практически не влияет на производительность).

После всех изменений получился такой код.

— И что у нас со временем получается?

— Если вывод в файл, то 22.5 секунды, если в /dev/null – 20.2

Интервьюер доволен, похоже, чего-то такого он от меня и ожидал. Но … зря он про джуниора сказал.

— Я думаю, что это не предел.

— В самом деле? А что тут можно еще оптимизировать?

— Я уменьшил количество вызовов printf’а в 15 раз, но при этом сами эти printf’ы стали тяжелее. Да и вообще printf сам по себе тяжелый, из-за своей мощности — это ведь фактически виртуальная машина со своим языком, полным по Тьюрингу, на нем даже крестики-нолики писали. В данной ситуации используется лишь небольшая часть возможностей printf, так что можно его заменить на что-то свое, более легкое:

#define NUM cur += myitoa(num++, cur) #define FIZZ do { memcpy(cur, "Fizz\n", 5); cur += 5; num++; } while (0) #define BUZZ do { memcpy(cur, "Buzz\n", 5); cur += 5; num++; } while (0) #define FIZZBUZZ do { memcpy(cur, "FizzBuzz\n", 9); cur += 9; } while (0)  void print(int num) {     static char wrkbuf[CHUNK_SIZE];      char *cur = wrkbuf;     NUM;     NUM;     FIZZ;     NUM;     BUZZ;     FIZZ;     NUM;     NUM;     FIZZ;     BUZZ;     NUM;     FIZZ;     NUM;     NUM;     FIZZBUZZ;     fwrite(wrkbuf, cur - wrkbuf, 1, stdout); }

— Можно, конечно, использовать уже готовую itoa, но это нестандартная функция, не везде есть, да и она слишком универсальная, поскольку поддерживает разные системы счисления, а у нас только десятичная система — упрощаем все, что можно. Ну и, конечно, в главном цикле просто вызываем print(i) вместо длинного printf’а.

Получается такой код.

Я подхожу к доске и рисую табличку с результатами запусков:

Вариант

Вывод в файл

Вывод в /dev/null

Время (сек)

Относ наивной

Относ предыдущей

Время (сек)

Относ наивной

Относ предыдущей

наивная

41.429

1x

39.650

1x

оптимизация цикла

22.546

1.83x

1.83x

20.151

1.97x

1.97x

отказ от printf

12.563

3.30x

1.80x

8.771

4.52x

2.30x

— В принципе на вывод в файл можно особо не смотреть — там какое-то время съедается на I/O, и оно плавает, так что лучше ориентироваться на время без I/O.

Я стираю ту часть, где про вывод в файл.

— Итого ускорение в 4 с половиной раза.

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

— Я знаю, как можно еще ускорить.

— Серьезно?

— Абсолютно. Я до этого использовал чисто технические способы ускорения, а ведь можно еще и алгоритмически улучшить. Смотрите, что будет напечатано, когда мы вызываем, например, print(150000001) и следующий за ним print(150000016):

150000001\n150000002\nFizz\n150000004\nBuzz\nFizz\n150000007\n150000008\nFizz\nBuzz\n150000011\nFizz\n150000013\n150000014\nFizzBuzz\n 150000016\n150000017\nFizz\n150000019\nBuzz\nFizz\n150000022\n150000023\nFizz\nBuzz\n150000026\nFizz\n150000028\n150000029\nFizzBuzz\n        ^^         ^^               ^^                     ^^         ^^                     ^^               ^^         ^^ 

— Отличия всего в 16 байтах, а программа всю строку пересобирает с нуля. Можно просто менять байты на месте. Правда, заранее неизвестно, сколько десятичных разрядов надо поменять, так что это потребуется вычислить — сравнить два буфера, и определить, где они отличаются. Это, пожалуй, тяжеловатая задача, но у нас есть, — я делаю театральную паузу — векторные инструкции и интринсики для них!

Я не озвучиваю, но подразумеваю, что джуниор такого бы не придумал. В этот момент понимаю, что интервьюер тоже.

Открываю Intel’овскую страницу с интринсиками и нахожу там нужные векторные функции для работы с 16-байтными векторами. У меня тут максимум 10-байтные, но их можно добить нулями до 16, не проблема. И да, 16-байтные вектора — это SSE инструкции, никакой AVX-512 тут не нужен, мой 4-летний мобильный проц это точно потянет.

Получаю такой кусок с жирными и вкусными интринсиками:

unsigned int diff = 0xFFFF & ~_mm_movemask_epi8(_mm_cmpeq_epi8(                                   _mm_load_si128((__m128i const *)prev_first_number),                                   _mm_load_si128((__m128i const *)last_number))); unsigned int diff_pos = 16 - _tzcnt_u32(diff);   // number of changed digits

Быстрая проверка flags в /proc/cpuinfo – нужные для выбранных мной интринсиков SSE2 (еще со времен Pentium 4) и BMI1 (появился в Haswell’ах) в CPU есть, все должно работать.

Запускаю тот код, что получился, смотрю уже только вывод в /dev/null и обновляю табличку:

Вариант

Время (сек)

Относительно наивной

Относительно предыдущей

наивная

39.650

1x

оптимизация цикла

20.151

1.97x

1.97x

отказ от printf

8.771

4.52x

2.30x

переиспользование буфера

4.490

8.83x

1.95x

Еще почти в 2 раза ускорились! А по сравнению с начальным вариантов так вообще почти в 9. Жаль, до 10 раз не дотянул.

— Ну все, наверно теперь уже хватит. Это уже вполне по-сениорски.

Во взгляде интервьюера читается облегчение.

— Скорее всего, мы вплотную подошли к пределу того, что можно выжать из однопоточной программы, — говорю я, медленно подвисая к концу фразы. Как же я об этом раньше не подумал! — Я ведь еще многопоточность не попробовал!

Интервьюер, похоже, начинает чего-то опасаться, и старается как-то незаметно переползти на соседний стул, поближе к выходу из переговорки, но ему сильно мешают подлокотники. Пофиг, не надо было меня провоцировать!

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

for (int j = 0; j < THREAD_COUNT; j++) {         thread_pool[j].start_num = i;         thread_pool[j].count = NUMS_PER_THREAD;         thread_pool[j].buf = malloc(BUFFER_SIZE);         pthread_create(&thread_pool[j].id, NULL, worker, (void *)&thread_pool[j]);         i += NUMS_PER_THREAD;     }     int active_threads = THREAD_COUNT;     int max = LIMIT / 15 * 15;     for (int j = 0; active_threads; j = (j+1) % THREAD_COUNT) {         pthread_join(thread_pool[j].id, NULL);         fwrite(thread_pool[j].buf, thread_pool[j].buflen, 1, stdout);         if (max - i > NUMS_PER_THREAD) {             thread_pool[j].start_num = i;             pthread_create(&thread_pool[j].id, NULL, worker, (void *)&thread_pool[j]);             i += NUMS_PER_THREAD;         } else if (max > i) {             thread_pool[j].start_num = i;             thread_pool[j].count = max - i + 1;             pthread_create(&thread_pool[j].id, NULL, worker, (void *)&thread_pool[j]);             i += max - i + 1;         } else {             free(thread_pool[j].buf);             active_threads--;         }     } 

— У меня двухядерный проц с гипертредингом, так что четыре рабочих потока одновременно будет оптимально, пока главный поток занимается выводом, один рабочий поток не используется, так что в любой момент времени максимум 4 потока активны. Конечно, стоит поэксперементировать с размером куска, который дается на обработку — в идеале рабочий поток должен обрабатывать свой кусок ровно за то же время, что главный поток выводит данные, тогда никто никого не ждет, все работают на максимальной скорости.

Проведя несколько замеров, я остановился на кусках по 3 миллиона чисел — удобное число, кратное 15, и результат хороший.

Получился такой код.

Запускаю, и обновляю данный в табличке:

Вариант

Время (сек)

Относительно наивной

Относительно предыдущей

наивная

39.650

1x

оптимизация цикла

20.151

1.97x

1.97x

отказ от printf

8.771

4.52x

2.30x

переиспользование буфера

4.490

8.83x

1.95x

многопоточность

1.748

22.68x

2.57x

— Ну вот, я уменьшил время обработки в 22 с лишним раза. И код получился очень даже сениорский — умный алгоритм, многопоточность, интринсики опять же. Как считаете?

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

Я быстро закрыл лаптоп и покинул офис. Почему-то мне так и не перезвонили.


Все тесты делались на Dell Latitude 7480 с i7-7600U 2.8 Ghz, 16 Gb памяти, SSD и OpenSUSE Leap 15.1 с kernel’ом 4.12.14, каждый тест не менее 10 раз, выбиралось наименьшее значение. При компиляции использовались флаги -O3 -march=native -pthread

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

ссылка на оригинал статьи https://habr.com/ru/post/540136/

Делаем страницу на React с базой сотрудников при помощи Airtable и Quarkly

Слышали про такой инструмент, как Airtable, но не знали, с чего начать? Тогда приглашаем в мир визуального программирования построения БД!

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

Фронт будем делать при помощи Quarkly, а данные подтягивать из базы в Airtable. На выходе получим react-приложение, синхронизированное с базой данных.

Преамбула. Почему Airtable

Airtable — популярный no-code инструмент, в котором вы можете делать свои базы данных большого объема. Выглядят они как таблицы, но имеют гораздо более мощный функционал. В частности, для нашего урока выбор Airtable обусловлен легким способом передачи данных по API.

Если вы впервые слышите про Airtable, перед началом работы не будет лишним почитать официальное руководство на сайте компании. Также советуем не стесняться и задавать вопросы в чатике Airtable Chat & Community в телеграм.

Фронтендная часть работ будет сделана в Quarkly, и для этого мы будем использовать всего два компонента:

  • Карточка сотрудника. В ней будут фото, текстовые данные и две кнопки: отправить email и позвонить. Эти данные карточка будет получать от родительского компонента — обертки.
  • Обертка. Она будет принимать данные из Airtable, генерировать карточки и передавать в них данные.

Для тех, у кого нет времени вникать в пост в печатном формате, мы подготовили видео с субтитрами и таймкодами:

Часть 1. Делаем визуал в Quarkly

Создание карточки:

  1. Создадим новый проект в Quarkly, назовем его Airtable Example;
  2. Перейдем внутрь проекта;
  3. Добавим готовый блок с карточками сотрудников. Для этого кликаем на черную кнопку + посередине и выбираем блок из категории Team;

  4. Выбираем на панели слоев первую карточку (StackItem) и преобразуем её в компонент;

    Для этого нажмите на «троеточие» и выберите пункт Convert to Component. Назовем этот компонент EmployeeCard.

  5. Теперь мы можем свободно редактировать код этого react-компонента, но пока этого делать не будем и перейдем к созданию компонента-обертки.

Создание обертки:

  1. Подготовим обертку к преобразованию в компонент. Для этого сначала удалим оставшиеся три карточки, они нам не нужны;

  2. Теперь вытащим EmployeeCard из Stack. Затем преобразуем Stack в компонент, как мы уже делали ранее с EmployeeCard: правая панель, кнопка «троеточие» и Convert to Component. Компонент назовем EmployeeTable.

    На этом пока всё, подготовительный этап завершен. Оставим компоненты и займемся базой в Airtable.

Часть 2. Создаем базу данных в Airtable

Переходим на сайт Airtable и регистрируемся/авторизуемся.

  1. Кликаем на кнопку Add a base, чтобы создать новую базу. Из выпадающего меню выберите пункт Start with a template;

  2. Выбираем категорию HR & Recruiting и шаблон Employee directory. Далее кликаем на кнопку Use template;

  3. Переходим в созданный проект;

На данном этапе здесь мы ничего менять не будем, в текущем виде база уже нас устраивает.

Часть 3. Получаем доступ к API

Изначально Airtable интересен для нас именно за счет удобного API. При этом, что немаловажно, возможность забирать данные и отправлять их в нашу базу Airtable предоставляет бесплатно.

  1. Переходим на страницу выбора API проектов: https://airtable.com/api

  2. Выбираем наш проект Employee directory. В появившейся документации переходим на раздел AUTHENTICATION.

  3. Скопируйте две строчки, расположенные ниже заголовка EXAMPLE USING BEARER TOKEN (RECOMMENDED).

    У меня они выглядят так:

    $ curl https://api.airtable.com/v0/app2MdLITmRTBsrkg/Employee%20directory \
    -H "Authorization: Bearer YOUR_API_KEY"

  4. Теперь нам нужен YOUR_API_KEY. Это уникальный ключ доступа, который генерируется для каждого аккаунта. Найти его можно в настройках.

  5. На открывшейся странице перейдите в раздел API и нажмите на кнопку Generate API key;

  6. Скопируйте ключ. Сохраните его рядом со строчками из пункта 3. Они пригодятся нам далее.

Часть 4. Интегрируем базу Airtable в Quarkly

На этом этапе мы добавим в компонент EmployeeTable код, который будет забирать данные из API.

  1. Переходим в редактирование кода компонента. Для этого откроем вкладку Components и нажмем на кнопку <> у EmployeeTable (она появится при наведении курсора на компонент);

  2. Сейчас код компонента выглядит так:

  3. Заменим:
    import React from "react";

    на:

    import React, { useEffect, useState } from "react";

    Таким образом мы импортируем хуки useEffect и useState, которые помогут нам в дальнейшем;

  4. Ниже добавляем строчку, чтобы импортировать наш компонент EmployeeCard:
    import EmployeeCard from "./EmployeeCard";
  5. Заменим children (они нам пока не нужны) на override (пригодятся, чтобы выбирать элементы и стилизовать их на странице):
    const EmployeeTable = props => { 	const { 		children, 		rest 	} = useOverrides(props, overrides, defaultProps);

    на:

    const EmployeeTable = props => { 	const { 		override, 		rest 	} = useOverrides(props, overrides, defaultProps);
  6. Ниже добавим вызов хука useState, который будет следить за состоянием:
    const [employees, setEmployees] = useState([]);
  7. Далее добавим хук useEffect, который будет делать запросы к API Airtable и помещать полученные данные в наше состояние через функцию setEmployees.

    Добавляем сюда строчки, которые скопировали ранее. В fetch мы добавляем URL адрес нашей базы, добавляя параметр ?view=All%20employees. В headers мы добавляем параметры авторизации и непосредственно сам API ключ, который мы сгенерировали в 3 части этой статьи, подпункт 4.

    useEffect(() => { 			fetch("https://api.airtable.com/v0/appWw7KBKSc9bPjZE/Employee%20directory?view=All%20employees", { 				headers: { 					'Authorization': 'Bearer YOUR_API_KEY' 				} 			}) 			.then(response => response.json()) 			.then(data => setEmployees(data.records.map(({ fields }) => fields))); 	}, []);
  8. Теперь будем генерировать карточки из полученных данных, передавая им props с данными и override. Он нужен, чтобы выбирать и стилизовать элементы на странице.

    Меняем:

    return <Stack {...rest}> 		{children} 	</Stack>; };

    на:

    return <Stack {...rest}> 		{ 			employees.map(employee => <EmployeeCard  {...override("employeeCard")}  employee={employee} />) 		} 	</Stack>; };
  9. Нажмите Ctrl + S (или Cmd + S на Mac). Окончательный код выглядит так:
    import React, { useEffect, useState } from "react"; import { useOverrides, Stack } from "@quarkly/components"; import EmployeeCard from "./EmployeeCard"; const defaultProps = { 	"margin-top": "40px" }; const overrides = {};  const EmployeeTable = props => { 	const { 		override, 		rest 	} = useOverrides(props, overrides, defaultProps);  	const [employees, setEmployees] = useState([]);  	useEffect(() => { 			fetch("https://api.airtable.com/v0/appWw7KBKSc9bPjZE/Employee%20directory?view=All%20employees", { 				headers: { 					'Authorization': 'Bearer YOUR_API_KEY' 				} 			}) 			.then(response => response.json()) 			.then(data => setEmployees(data.records.map(({ fields }) => fields))); 	}, []); 	 	return <Stack {...rest}> 		{ 			employees.map(employee => <EmployeeCard  {...override("employeeCard")} employee={employee} />) 		} 	</Stack>; };  Object.assign(EmployeeTable, { 	...Stack, 	defaultProps, 	overrides }); export default EmployeeTable;

    Важно: не забудьте вставить свой уникальный API ключ вместо текста YOUR_API_KEY.

Готово! Теперь мы получаем данные от Airtable, помещаем их в employees и проходимся по нему методом map. На каждую запись в employees мы создаем <EmployeeCard/>, в который передаем как пропс конкретные данные.

Осталось научить EmpolyeeCard принимать эти данные и показывать их в нужном месте.

Часть 5. Учим EmpolyeeCard работать с БД

Здесь мы научим карточку сотрудника принимать данные и показывать их.

  1. Откроем код компонента. Для этого заходим во вкладку Components, ищем там EmployeeCard, наводим курсор и жмем на кнопку <>.
  2. Сейчас код компонента выглядит так:
    import React from "react"; import { useOverrides, Override, StackItem } from "@quarkly/components"; import { Box, Text } from "@quarkly/widgets"; const defaultProps = { 	"width": "25%", 	"lg-width": "50%", 	"sm-width": "100%" }; const overrides = { 	"box": { 		"kind": "Box", 		"props": { 			"height": "0", 			"margin": "0 0 20px 0", 			"padding-bottom": "100%", 			"background": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat" 		} 	}, 	"text": { 		"kind": "Text", 		"props": { 			"color": "--grey", 			"margin": "0", 			"children": "CEO" 		} 	}, 	"text1": { 		"kind": "Text", 		"props": { 			"as": "h3", 			"font": "--headline3", 			"margin": "5px 0 20px 0", 			"children": "Nathan K. Joe" 		} 	}, 	"text2": { 		"kind": "Text", 		"props": { 			"as": "p", 			"margin": "20px 0 5px 0", 			"children": "This space is 100% editable. Use it to introduce a team member, describe their work experience and role within the company. This is also a great place to highlight a team member's strong sides." 		} 	} };  const EmployeeCard = props => { 	const { 		override, 		children, 		rest 	} = useOverrides(props, overrides, defaultProps); 	return <StackItem {...rest}> 		<Override slot="StackItemContent" flex-direction="column" /> 		<Box {...override("box")} /> 		<Text {...override("text")} /> 		<Text {...override("text1")} /> 		<Text {...override("text2")} /> 		{children} 	</StackItem>; };  Object.assign(EmployeeCard, { ...StackItem, 	defaultProps, 	overrides }); export default EmployeeCard;
  3. Ищем строчку:
    } = useOverrides(props, overrides, defaultProps);

    и добавляем ниже:

    const { employee = {} } = rest;

    В объект employee помещаем наши данные.

  4. На примере фотографии сотрудника проверим, что всё работает, как нужно. Ищем строку и меняем:
    <Box {...override("box")} />

    на:

    <Box {...override("box")} background-image={`url(${employee.Photo && employee.Photo[0] && employee.Photo[0].url})`}/>

    Также ищем:

    "background": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat"

    и меняем на:

    "background-size": "cover", "background-position": "center", "background-image": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat"

    Должно получится так:

  5. Смотрим, какие поля у нас есть. Документация для API в Airtable сделана очень хорошо. Название полей можно посмотреть в https://airtable.com/api, выбрав свою базу.

    Далее ищем раздел EMPLOYEE DIRECTORY TABLE.

    Итак, у нас есть:

    Name
    Department
    Home address
    Email address
    DOB
    Start date
    Phone
    Reports to
    Title
    Status
    Photo
    Location

  6. Добавим Title. Для этого заменим:
    <Text {...override("text")} />

    на:

    <Text {...override("title")} children={employee.Title} />

    И не забудем отредактировать overrides этого компонента, чтобы мы могли его выбирать и редактировать на странице.

    Меняем:

    "text": { 	"kind": "Text", 	"props": { 		"color": "--grey", 		"margin": "0", 		"children": "CEO" 	} },

    на:

    "title": { 	"kind": "Text", 	"props": { 		"color": "--grey", 		"margin": "0", 		"children": "Title" 	} },

    Сохраняем и проверяем:

    Результат: в карточки добавилась строка с профессией.

  7. Повторим такие же действия для Name и Home address.

    Заменим:

    <Text {...override("text1")} /> <Text {...override("text2")} />

    на:

    <Text {...override("name")} children={employee.Name} /> <Text {...override("address")} children={employee['Home address']} />

    И поправим их overrides. Для этого заменим:

    "text1": { 	"kind": "Text", 	"props": { 		"as": "h3", 		"font": "--headline3", 		"margin": "5px 0 20px 0", 		"children": "Nathan K. Joe" 	} }, "text2": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "20px 0 5px 0", 		"children": "This space is 100% editable. Use it to introduce a team member, describe their work experience and role within the company. This is also a great place to highlight a team member's strong sides." 	} }

    на:

    "name": { 	"kind": "Text", 	"props": { 		"as": "h3", 		"font": "--headline3", 		"margin": "5px 0 5px 0", 		"children": "Name" 	} }, "address": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "10px 0 5px 0", 		"children": "Home address" 	} },

    Сохраняем и снова проверяем:

  8. Добавим ещё несколько Text по аналогии. Для простоты мы не будем брать Department и Reports to, потому что эти данные находятся в другой базе DEPARTMENTS TABLE.

    Добавляем:

    <Text {...override("address")} children={employee['Home address']} /> <Text {...override("Start date")} children={`Start date: ${employee['Start date']}`} /> <Text {...override("Status")} children={employee['Status']} /> <Text {...override("DOB")} children={`Birth date: ${employee['DOB']}`} />

    и

    "address": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "10px 0 5px 0", 		"children": "Home address" 	} }, "Start date": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "10px 0 5px 0", 		"children": "Start date" 	 	} }, "Status": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "10px 0 5px 0", 		"children": "Status" 	} }, "DOB": { 	"kind": "Text", 	"props": { 		"as": "p", 		"margin": "10px 0 5px 0", 		"children": "Birth date" 	} },

    Проверяем результат:

  9. Теперь добавим два компонента Link, в которых у нас будут Phone и Email:
    import { Box, Text } from "@quarkly/widgets";

    меняем на:

    import { Box, Text, Link } from "@quarkly/widgets";

    И добавляем следующие строки:

    <Link {...override("Email address")} children={employee['Email address']} href={`mailto:${employee['Email address']}`} /> <Link {...override("Phone")} children={employee['Phone']} href={`tel:${employee['Phone']}`}/>

    Не забыв про их overrides:

    "Email address": { 	"kind": "Link", 	"props": { 		"margin": "10px 0 5px 0", 		"color": "--primary", 		"text-decoration": "none", 		"children": "Email" 	} }, "Phone": { 	"kind": "Link", 	"props": { 		"margin": "10px 0 5px 0", 		"color": "--primary", 		"text-decoration": "none", 		"children": "Phone" 	} },

    Проверяем результат:

Финально наш код выглядит так:

import React from "react"; import { useOverrides, Override, StackItem } from "@quarkly/components"; import { Box, Text, Link } from "@quarkly/widgets"; const defaultProps = { 	"width": "25%", 	"lg-width": "50%", 	"sm-width": "100%" }; const overrides = { 	"box": { 		"kind": "Box", 		"props": { 			"height": "0", 			"margin": "0 0 20px 0", 			"padding-bottom": "100%", 			"background-size": "cover", 			"background-position": "center", 			"background-image": "url(https://images.unsplash.com/photo-1503443207922-dff7d543fd0e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=582&q=80) 50% 0/cover no-repeat" 		} 	}, 	"title": { 		"kind": "Text", 		"props": { 			"color": "--grey", 			"margin": "0", 			"children": "title" 		} 	}, 	"name": { 		"kind": "Text", 		"props": { 			"as": "h3", 			"font": "--headline3", 			"margin": "5px 0 5px 0", 			"children": "Name" 		} 	}, 	"address": { 		"kind": "Text", 		"props": { 			"as": "p", 			"margin": "10px 0 5px 0", 			"children": "Home address" 		} 	}, 	"Start date": { 		"kind": "Text", 		"props": { 			"as": "p", 			"margin": "10px 0 5px 0", 			"children": "Start date" 		} 	}, 	"Status": { 		"kind": "Text", 		"props": { 			"as": "p", 			"margin": "10px 0 5px 0", 			"children": "Status" 		} 	}, 	"DOB": { 		"kind": "Text", 		"props": { 			"as": "p", 			"margin": "10px 0 5px 0", 			"children": "Birth date" 		} 	}, 	"Email address": { 		"kind": "Link", 		"props": { 			"margin": "10px 0 5px 0", 			"color": "--primary", 			"text-decoration": "none", 			"children": "Email" 		} 	}, 	"Phone": { 		"kind": "Link", 		"props": { 			"margin": "10px 0 5px 0", 			"color": "--primary", 			"text-decoration": "none", 			"children": "Phone" 		} 	}, };  const EmployeeCard = props => { 	const { 		override, 		children, 		rest 	} = useOverrides(props, overrides, defaultProps); 	 		const { employee = {} } = rest;  	 	return <StackItem {...rest}> 		<Override slot="StackItemContent" flex-direction="column" /> 		<Box {...override("box")} background-image={`url(${employee.Photo[0].url})`}/> 		<Text {...override("title")} children={employee.Title} /> 		<Text {...override("name")} children={employee.Name} /> 		<Text {...override("address")} children={employee['Home address']} /> 		<Text {...override("Start date")} children={`Start date: ${employee['Start date']}`} /> 		<Text {...override("Status")} children={employee['Status']} /> 		<Text {...override("DOB")} children={`Birth date: ${employee['DOB']}`} /> 		<Link {...override("Email address")} children={employee['Email address']} href={`mailto:${employee['Email address']}`} /> 		<Link {...override("Phone")} children={employee['Phone']} href={`tel:${employee['Phone']}`}/> 		{children} 	</StackItem>; };  Object.assign(EmployeeCard, { ...StackItem, 	defaultProps, 	overrides }); export default EmployeeCard;

Делаем коммит в GitHub и публикуем на Netlify:

Ждем несколько минут и проверяем: https://keen-varahamihira-c54ae1.netlify.app/

Для проверки синхронизации меняем данные в базе:

Теперь они появятся в приложении:

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

Репозиторий на GitHub: https://github.com/quarkly-dev/Getting-data-from-Airtable-tutorial

Спасибо за внимание!

Если у вас остались вопросы — не стесняйтесь задавать их в комментариях. В следующем уроке рассмотрим ещё один пример работы с данными, покажем как интерактивно их визуализировать, дав возможность пользователю менять фильтры прямо в приложении.

ссылка на оригинал статьи https://habr.com/ru/company/quarkly/blog/540094/

Как Amazon тратил по $500 млн на разработку провальных игр и почему ничего не вышло

В 2012 году в структуре Amazon возникла собственная студия по производству компьютерных игр. По замыслу Джеффа Безоса, Amazon Game Studios должна была стать успешной и эффективной частью корпоративной экосистемы. Однако за прошедшие восемь лет добиться этого, увы, так и не удалось. Как же так вышло, что богатейшая компания, у которой получалось практически всё, не сумела завоевать рынок геймдева?

Ответы на этот вопрос нашел журналист Джейсон Шрайер (автор книги “Кровь, пот и пиксели”). Публикуем главные тезисы расследования.

Идея создать собственное подразделение по разработке игр исходила непосредственно от главы Amazon. Как часть подписки Amazon Prime, игры должны были продать подписку на сервис и привлечь геймеров к фильмам, сериалам и другим продуктам интернет-гиганта. А кроме того, продемонстрировать мощь облачных сервисов, способных, например, поддерживать онлайн-матчи на 10 000 пользователей.

Для компании, знаменитой своей корпоративной культурой, отлаженными бизнес-процессами и неограниченными финансовыми ресурсами (по данным собеседников Шрайера, годовой бюджет студии достигал $500 млн.), — цель вполне осуществимая.

image
Майк Фраццини

Главой направления назначили Майка Фраццини, опытного менеджера Amazon, который до этого прошел путь от управляющего “книжным” подразделением до руководителя дистрибьюции коробочных изданий игр. Поначалу студия делала мобильные игры для магазина Amazon Appstore, но в 2014 г., после неудачи с Amazon Fire Phone, приступила к производству игр для ПК и консолей.

Причина неудач №1: некомпетентный руководитель

Именно в фигуре Фраццини журналист видит ключевую причину проблем студии. С позиций корпоративной философии Amazon, если вы успешно можете управлять одним направлением, то справитесь и с любым другим. Фраццини очень амбициозен: каждая игра Amazon, как он заявлял сотрудникам, должна была стать “франшизой на миллиард долларов”, а коллектив студии пополнили “звёзды” геймдева — такие, как Ким Свифт (Portal), Клинт Хокинг (Far Cry 2), Ричард Хиллеман и Джон Смедли.

Однако большой управленческий опыт и преданность принципам Amazon не смогли заменить Фраццини недостаток профессиональных компетенций. Он не разбирается в видеоиграх и технологиях, не может отличить синематик-сцену от геймплейного видео, а большинство его комментариев и оценок — любительские. Вместо того, чтобы погрузиться в индустрию или прислушаться к окружающим профессионалам, он хватался за публикации об очередной коммерчески успешной игре и, вооружившись цифрами, пытался сделать “идеальную игру”.

Как результат — студия потратила огромные время и ресурсы на создание нескольких клонов популярных игр, которые громко анонсировали, а в итоге “консервировали” и отменяли.
Едва ли не единственным проектом, дождавшимся выхода, стал вдохновленный Overwatch шутер Crucible.

image
Crucible

В релизном интервью Фраццини с гордостью заявлял: «Одна из самых частых вещей, которые мы слышим о Crucible от игроков, — это то, что она уникальна”. Но игра получила разгромные отзывы от прессы и геймеров. Уже через неделю после выхода аудитория игры на Twitch упала до 1000 зрителей, а буквально через полгода, в октябре 2020 г., поддержка Crucible была прекращена.

Причина неудач №2: корпоративная культура

С одной стороны, работа в Amazon кажется раем: зарплаты вдвое выше среднерыночных, растущие в цене опционы, гибкие сроки разработки и практически отсутствие обычных для этой индустрии кранчей.

Но с другой стороны, оказывается, что корпоративная этика компании, с ее “14 принципами лидерства”, фокусе на измеримых показателях эффективности и бюрократией, уничтожают творчество и эксперименты, неотделимые от игровой разработки.

Вместо неформальных обсуждений сотрудники должны были обмениваться многостраничными документами. Вместо создания новаторских игр — работать над невоплотимыми гибридами всех существующих механик, якобы наиболее привлекательных для геймеров. Какой бы “звездой” ни был пришедший в компанию разработчик, он был обязан разделять корпоративные ценности и видение.

Да и финансового стимула довести игру до релиза у сотрудников тоже не было. Удивительно, но на фоне обычной для индустрии привязки зарплат и бонусов разработчиков к выходу игры и реакции аудитории, Amazon выплачивала вознаграждение работникам студии только за отработанное время. Поэтому, какую бы глупость ни спускали “сверху”, разработчики, держась за место, предпочитали не ввязываться в дискуссии и продолжали делать продукты, нужные руководству, но не интересные аудитории.

Причина неудач №3: неэффективный движок

Фраццини принял решение отказаться от Unity и Unreal Engine в пользу собственной технологии. С 2014 года студия разрабатывала движок под названием Lumberyard. Созданный на основе Crytek, он должен был стать стандартом для всех игр Amazon и иметь интеграцию с Amazon Web Services. Учитывая инфраструктурные мощности компании, со стороны это выглядело очень амбициозно и основательно.

Тем не менее, как пишет Шрайер, Lumberyard превратилась в страшилку для всего офиса. Работать на нем было сущим мучением. Код обрабатывался настолько долго, что программисты в ожидании смотрели кино и играли в игры, а для работы части функционала “требовались эзотерические команды”. По словам бывшего сотрудника, в офисе часто говорили: «Lumberyard убивает эту компанию».

Ситуация изменилась только в 2018 г., когда пришедший в студию Кристоф Хартманн (Bioshock, Mafia) получил разрешение перейти на Unreal Engine.

image
New World

Что дальше?

Несмотря на все неудачи, включая провал Crucible, Amazon продолжает делать игры. Ближайший проект — New World, который планировали выпустить в августе, но отложили на 2021 год. С ним тоже были проблемы: игра, посвященная выживанию в мире, очень похожем на Америку 1600-ых, выставляла коренное население как врагов и злодеев. Однако здесь, хоть и не сразу, руководство студии услышало критику и пересмотрело концепцию игры. По оценке самих сотрудников, у игры хорошие рыночные перспективы.

Если учитывать однозначный успех принадлежащих Amazon сервисов Twitch и Luna (облачная игровая платформа), у компании всё ещё есть большие шансы занять свое место на рынке разработки игр, заключает Шрайер. Правда, для этого может потребоваться руководитель другого типа, хорошо знакомый с индустрией. В качестве примера он приводит онлайн-кинотеатр Amazon Prime Video: там негативную ситуацию удалось переломить во многом благодаря приходу стороннего менеджера из Голливуда.

Разрабатывая игры, Amazon попыталась подчинить творческий процесс своей воле, и результаты этого — хороший урок для Apple Inc., Facebook Inc. и Google, чьи усилия до сих пор были столь же неэффективными. Успешные видеоигры — это сочетание искусства, развлечений, технологий и очень больших бюджетов. Крупные технологические компании разобрались пока только в последних двух.

ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/540142/

Почему теорию Максвелла так трудно понять?

Скромность не всегда добродетель

В 1865 году Джеймс Клерк Максвелл опубликовал свою статью “Динамическая теория электромагнитного поля» в «Философских трудах Королевского общества». Ему было тогда тридцать четыре года. Оглядываясь назад, мы можем заметить, что работа Максвелла была самым важным событием девятнадцатого века в истории физических наук. Если говорить в общем о естественных науках, то статья Максвелла была второй по значимости после «Происхождения видов» Дарвина. Но важность работ Максвелла не была очевидна для его современников. Более двадцати лет его теория электромагнетизма в основном игнорировалась. Физикам было трудно ее понять из-за обилия сложных уравнений. Математикам было трудно ее понять, потому что Максвелл использовал для объяснений физический язык. Этот труд был расценен как неясное предположение без должного количества экспериментальных доказательств. Физик Михаил Пупин в своей автобиографии «От иммигранта к изобретателю» описывает, как он путешествовал из Америки в Европу в 1883 году в поисках того, кто понимал Максвелла. Он отправился изучать теорию Максвелла, как рыцарь в поисках Святого Грааля.

Пупин сначала поступил в Кембридж с твердым намерением изучить теорию у самого Максвелла. Он не знал, что Максвелл умер четыре года назад. Узнав, что Максвелл умер, он остался в Кембридже и был назначен преподавателем колледжа. Но его наставник знал о теории Максвелла меньше, чем он сам, и был заинтересован только в том, чтобы научить Михаила решать математические задачи трипоса. Михаил Пупин был поражен, обнаружив, как он говорит, «как мало было физиков, которые уловили смысл теории, даже через двадцать лет после того, как она была сформулирована Максвеллом в 1865 году». В конце концов он бежал из Кембриджа в Берлин и поступил студентом к Герману фон Гельмгольцу. Гельмгольц понимал теорию и учил Пупина тому, что знал сам. Пупин вернулся в Нью-Йорк, стал профессором Колумбийского университета и обучал последующие поколения студентов, которые впоследствии распространили Евангелие Максвелла по всей Америке.

Открытка от Максвелла Питеру Тейту
Открытка от Максвелла Питеру Тейту

Как случилось, что теория Максвелла была так широко проигнорирована? В конце концов, Максвелл не был похож на своего современника Грегора Менделя, монаха, работавшего в безвестном монастырском саду в Богемии. Максвелл был известным профессором, директором Кавендишской лаборатории в Кембридже, ведущей фигурой в британском научном сообществе. Свидетельством его высокого положения можно считать то, что он был президентом секции А (математические и физические науки) Британской ассоциации содействия развитию науки, когда ассоциация провела свое ежегодное собрание в Ливерпуле в 1870 году. Он выступил с президентской речью в Ливерпуле, которая была опубликована во втором томе недавно основанного журнала «Nature». Стиль его выступления показывает нам, почему его теорию не воспринимали всерьез. Можно было ожидать, что он воспользуется возможностью, предоставленной президентской платформой, чтобы объявить миру о важности открытий, которые он сделал пять лет назад. Он не сделал ничего подобного. Он был абсурдно и раздражающе скромен.

Максвелл в первую очередь объявил тему своего выступления — обзор последних достижений, которые были сделаны на границе между математикой и физикой. Затем он с большим энтузиазмом рассказал о вихревой теории молекул, недавно предложенной сэром Уильямом Томсоном (впоследствии ставшим лордом Кельвином).

Теория, которую сэр Уильям основал на великолепных гидродинамических теоремах Гельмгольца, ищет свойства молекул в кольцевых вихрях однородной несжимаемой жидкости без трения. Гельмгольц показал, что в идеальной жидкости такое кружащееся кольцо, если оно однажды возникло, будет продолжать кружиться вечно, всегда будет состоять из той же самой части жидкости, которая была сначала закручена, и никогда не может быть разрезана надвое какой-либо естественной причиной. Эти кольцевые вихри способны к таким разнообразным связям и узловатым самоинволюциям, что свойства различных узловатых вихрей должны быть столь же различны, как и свойства различных видов молекул.

И так далее. Максвелл объяснил, как древняя теория о том, что материя состоит из атомов, столкнулась с логическим парадоксом. С одной стороны, атомы должны были быть твердыми, непроницаемыми и неразрушимыми. С другой стороны, данные спектроскопии и химии показали, что атомы имеют внутреннюю структуру и находятся под влиянием внешних сил. Этот парадокс в течение многих лет блокировал прогресс в понимании природы материи. Теперь, наконец, вихревая теория молекул разрешила парадокс. Вихри в эфире мягкие и имеют внутреннюю структуру, и тем не менее, согласно Гельмгольцу, они индивидуальны и неразрушимы. Оставалось только вывести факты спектроскопии и химии из законов взаимодействия вихрей, предсказанных гидродинамикой идеальной жидкости. Максвелл считал эту вихревую теорию материи замечательным примером плодотворного взаимодействия математики и физики.

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

Другая теория электричества, которую я предпочитаю, отрицает действие на расстоянии и приписывает электрическое действие напряжениям и давлениям во всепроникающей среде, причем эти напряжения одинаковы по характеру с теми, которые известны инженерам, и среда идентична той, в которой предполагается распространение света.

Фраза «Другая теория электричества, которую я предпочитаю», кажется, намеренно скрывает тот факт, что это была его собственная теория. Неудивительно, что вихри Кельвина произвели на его слушателей большее впечатление, чем уравнения Максвелла.

Мораль этой истории заключается в том, что скромность не всегда является добродетелью. Максвелл и Мендель оба были чрезмерно скромны. Скромность Менделя задержала прогресс биологии на пятьдесят лет. Скромность Максвелла замедлила прогресс физики на двадцать лет. Для прогресса науки будет лучше, если люди, делающие великие открытия, не будут слишком скромны, чтобы трубить в свои собственные трубы. Если бы у Максвелла было такое же эго, как у Галилея или Ньютона, он бы позаботился о том, чтобы его работы не игнорировались. Максвелл был таким же великим ученым, как Ньютон, и гораздо более приятным человеком. Но, к сожалению, он не начал президентскую речь в Ливерпуле словами, подобными тем, которые Ньютон использовал, чтобы представить третий том своей Principia Mathematica: «… исходя из тех же принципов, я теперь демонстрирую структуру системы мира». Ньютон не называл свой закон всемирного тяготения «очередной теорией тяготения, которую я предпочитаю».

Теория Максвелла и квантовая механика

Помимо скромности Максвелла, были и другие причины, по которым его теорию было трудно понять. Он заменил ньютоновскую вселенную материальных объектов, взаимодействующих друг с другом на расстоянии, вселенной полей, простирающихся через пространство и взаимодействующих только локально с материальными объектами. Понятие поля было трудно понять, потому что поля неосязаемы. Ученые того времени, включая самого Максвелла, пытались представить поля как механические структуры, состоящие из множества маленьких колесиков и вихрей, простирающихся в пространстве. Эти структуры должны были переносить механические напряжения, которые электрические и магнитные поля передавали между электрическими зарядами и токами. Чтобы поля удовлетворяли уравнениям Максвелла, система колес и вихрей должна была быть чрезвычайно сложной.

Если попытаться визуализировать теорию Максвелла с помощью таких механических моделей, она выглядит как возврат к астрономии Птолемея с планетами, движущимися по циклам и эпициклам в небе. Это не похоже на изящную астрономию Ньютона. Уравнения Максвелла, записанные в неуклюжих обозначениях, которыми пользовался Максвелл, были пугающе сложными, а его механические модели — еще хуже. Для современников теория Максвелла была лишь одной из многих теорий электричества и магнетизма. Ее было трудно осмыслить, и, казалось, не было явного преимущества перед другими теориями, которые описывали электрические и магнитные силы в ньютоновском стиле как дальнодействие между зарядами и магнитами. Неудивительно, что мало кто из современников Максвелла прилагал усилия, чтобы изучить его теорию.

Теория Максвелла становится простой и понятной только тогда, когда вы отказываетесь мыслить в терминах механических моделей. Вместо того чтобы думать о механических объектах как о первичных, а об электромагнитных напряжениях как о вторичных следствиях, вы должны думать об электромагнитном поле как о первичном, а о механических силах как о вторичном конструкте. Мысль о том, что первичными составляющими Вселенной являются поля, не сразу пришла в голову физикам поколения Максвелла. Поля — это абстрактное понятие, далекое от привычного мира вещей и сил. Уравнения поля Максвелла являются уравнениями в частных производных. Они не могут быть выражены простыми словами, такими как закон движения Ньютона: сила равна массе, умноженной на ускорение. Теория Максвелла должна была ждать следующего поколения физиков, Герца, Лоренца и Эйнштейна, чтобы раскрыть свою силу и прояснить свои концепции. Следующее поколение выросло с уравнениями Максвелла и чувствовало себя как дома во вселенной, построенной из полей. Первенство полей было так же естественно для Эйнштейна, как первенство механических структур — для Максвелла.

Современный взгляд на мир, возникший из теории Максвелла, — это мир с двумя слоями. Первый слой, слой фундаментальных составляющих мира, состоит из полей, удовлетворяющих простым линейным уравнениям. Второй слой, слой вещей, которые мы можем непосредственно потрогать и измерить, состоит из механических напряжений, энергий и сил. Эти два слоя связаны, потому что величины во втором слое являются квадратичными или билинейными комбинациями величин в первом слое. Чтобы вычислить энергии или напряжения, вы берете квадрат напряженности электрического поля или умножаете одну составляющую поля на другую. Двухслойная структура мира — основная причина, по которой теория Максвелла казалась загадочной и трудной. Объекты на первом уровне, объекты, которые действительно фундаментальны, являются абстракциями, не доступными непосредственно нашим чувствам. Объекты, которые мы можем чувствовать и осязать, находятся на втором слое, и их поведение лишь косвенно определяется уравнениями, действующими на первом слое. Двухслойная структура мира подразумевает, что основные процессы природы скрыты от нашего взгляда.

Теперь мы считаем само собой разумеющимся, что электрические и магнитные поля являются абстракциями, не сводимыми к механическим моделям. Чтобы убедиться в этом, достаточно взглянуть на единицы измерения электрического и магнитного полей. Условная единица напряженности электрического поля — квадратный корень из джоуля на кубический метр. Джоуль — это единица энергии, а метр — единица длины, но квадратный корень из джоуля — это не единица чего-то осязаемого. Мы не можем представить себе, как можно измерить непосредственно квадратный корень из джоуля. Единица измерения напряженности электрического поля — это математическая абстракция, выбранная таким образом, что квадрат напряженности поля равен плотности энергии, которую можно измерить с помощью реальных приборов. Единицей плотности энергии является джоуль на кубический метр, и поэтому мы говорим, что единицей напряженности поля является квадратный корень из джоуля на кубический метр. Это не означает, что напряженность электрического поля можно измерить с помощью квадратного корня калориметра. Это означает, что напряженность электрического поля — абстрактная величина, несоизмеримая с любыми величинами, которые мы можем измерить непосредственно.

Через шестьдесят лет после того, как Максвелл опубликовал свою теорию, Шредингер, Гейзенберг и Дирак изобрели квантовую механику. Квантовая механика была принята гораздо быстрее, чем теория Максвелла, потому что она сделала множество определенных предсказаний об атомных процессах и эксперименты показали, что все предсказания были правильными. Через год-два все поверили в квантовую механику как в практический инструмент для расчета основных процессов физики и химии. Природа, очевидно, подчинялась законам квантовой механики. Но значение квантовой механики оставалось спорным. Хотя квантовая механика была быстро принята, она не была быстро понята. Резкие расхождения во мнениях по поводу интерпретации квантовой механики сохраняются на протяжении семидесяти лет.

Прошло около тридцати лет после Максвелла, прежде чем его уравнения стали понятны всем. Для достижения согласованного понимания квантовой механики потребуется по меньшей мере вдвое больше времени. Мы все еще ведем страстные споры между сторонниками различных интерпретаций квантовой механики, Копенгагенской интерпретации, многомировой интерпретации, декогерентной интерпретации, интерпретаций скрытых переменных и многих других. Причина этих аргументов заключается в том, что различные интерпретаторы пытаются описать квантовый мир словами повседневного языка, а язык не подходит для этой цели. Повседневный язык описывает мир таким, каким его видят люди. Наше восприятие мира целиком связано с макроскопическими объектами, которые ведут себя в соответствии с правилами классической физики. Все понятия, которые появляются в нашем языке, являются классическими. Каждая из интерпретаций квантовой механики — это попытка описать квантовую механику на языке, в котором отсутствуют соответствующие понятия. Битвы между соперничающими интерпретациями продолжаются безостановочно, и конца им не видно.

Для понимания квантовой механики может оказаться полезным подчеркнуть сходство между квантовой механикой и теорией Максвелла. В двух отношениях теория Максвелла может дать ключ к тайнам квантовой механики.

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

Вторая связь между теорией Максвелла и квантовой механикой заключается в глубоком сходстве структуры. Подобно теории Максвелла, квантовая механика делит Вселенную на два слоя. Первый слой содержит волновые функции Шредингера, матрицы Гейзенберга и векторы состояний Дирака. Величины в первом слое подчиняются простым линейным уравнениям. Их поведение можно точно рассчитать. Но их нельзя наблюдать непосредственно. Второй слой содержит вероятности столкновений и превращений частиц, интенсивности и поляризации излучения, математические ожидания энергий и спинов частиц. Величины во втором слое могут быть непосредственно наблюдаемы, но не могут быть непосредственно вычислены. Они не подчиняются простым уравнениям. Это либо квадраты величин первого слоя, либо произведения одной величины первого слоя на другую. В квантовой механике, как и в теории Максвелла, Природа живет в абстрактном математическом мире первого слоя, но мы, люди, живем в конкретном механическом мире второго слоя. Мы можем описать Природу только абстрактным математическим языком, потому что наш вербальный язык находится дома только во втором слое.

Как и в случае с теорией Максвелла, абстрактное качество величин первого слоя проявляется в единицах, в которых они выражаются. Например, волновая функция Шредингера выражается в единице, которая является квадратным корнем из обратного кубического метра. Уже один этот факт ясно показывает, что волновая функция — это абстракция, навсегда скрытая от нашего взгляда. Никто никогда не измерит напрямую квадратный корень из кубического метра. Конечная важность теории Максвелла гораздо больше, чем ее непосредственные достижения в объяснении и объединении явлений электричества и магнетизма. Его конечная важность состоит в том, чтобы стать прототипом для всех великих триумфов физики двадцатого века. Это прототип теории относительности Эйнштейна, квантовой механики, теории обобщенной калибровочной инвариантности Янга-Миллса и единой теории полей и частиц.

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

ссылка на оригинал статьи https://habr.com/ru/post/539188/

Симуляция радара истребителя на 3″ осциллографической ЭЛТ


Все из вас уже наверняка повыбрасывали старые громоздкие ЭЛТ-мониторы, предпочтя им изящные ультратонкие ЖК-дисплеи. Многие уже и не думают, что еще можно повстречать столь древний девайс в современном мире… Но здесь речь пойдет об особенном ЭЛТ-дисплее – круглом! Его диаметр составляет всего 3 дюйма, и выглядит это чудо очень мило.

В прошлом году ко мне как-то обратился один любитель авиа-симуляторов, которому для воссоздания копии кабины истребителя потребовался экран радиолокатора. В целях максимальной реалистичности, предполагалось, что этот экран будет реализован на базе ЭЛТ. По силам ли Oscilloclok спроектировать и собрать подобный дисплей?

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

Комплектация

Ключевым компонентом в этом процессе стал новый прототип VGA-платы, преобразующей VGA-сигнал в аналоговый выход X и Y. В ней также были предусмотрены аналоговый выход контроля яркости луча и выход дискретного сигнала управления гашением.

Прототип VGA-платы от Oscilloclock

Выходы X и Y управляют платой отклонения Oscilloclock, а выход дискретного сигнала гашения управляет усилителем гашения на плате ЭЛТ.

Плата отклонения с реализацией сверхлинейного высоковольтного выхода

ЭЛТ-плата с реализацией улучшения частотной характеристики

Плата питания служит для питания нити накаливания катода, генерирует высокое анодное напряжение и напряжения для управляющей сетки.

Плата питания с улучшенной оптопарой

На первый взгляд все выглядело так просто, но не тут-то было. Как и в прочих проектах Oscilloclock, этот не обошелся без засиживания допоздна, болезненного дергания за волосы и лишения семьи внимания. Дальше я расскажу, что же меня так озадачило…

Недостаток яркости растрового сканирования

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

Чтобы понять почему, представьте, что вы граффитист, который рисует на стене круг. Обычно для этого вы бы просто делали круговое движение рукой. Это эффективно. В один или два захода можно получить жирный круг.

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

Круг граффити, нарисованный баллончиком (слева) и его вариант, полученный растровым сканированием (справа)

ЭЛТ подобна баллончику с краской – электронная пушка направляет поток электронов с определенной скоростью. Если луч будет перемещаться быстро, в заданную точку экрана за раз будет попадать ограниченное число частиц, что, естественно, ограничит яркость этой точки.

Компромисс между яркостью и отклонением

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

К сожалению, в большинстве доступных сегодня 3” ЭЛТ-дисплеев не реализована такая технология ускорения. На деле заказчик рассчитывал использовать универсальную трубку без PDA типа 3RP1A в силу ее широкой доступности. Но получаемое на ней растровое изображение было бы слишком тусклым. К тому же заказчику требовалась высокая яркость даже при условии использования зеленого акрилового фильтра.

Имеющиеся ограничения грозили реальными сложностями, но отступать я не привык.
Было решено сконфигурировать плату питания на подачу к пластинам отклонения 2кВ, что практически вдвое больше стандартно подаваемого большинством схем напряжения для этой ЭЛТ. Таким образом я рассчитывал сильно ускорить электроны, чего должно было хватить для получения ярких точек.

Но всегда есть какой-то подвох! Без PDA луч ускоряется до выхода из области отклонения. Из-за инерции электрона это означает, что луч не так легко преломляется, в связи с чем изображение уменьшается. Для корректировки необходим намного более мощный сигнал, чтобы отклонение луча стало достаточным для достижения им края экрана.

Сможет ли моя скромная плата отклонения обеспечить сигнал достаточной величины?

Сложности с отклонением

Обратимся к приведенной ниже спецификации 3RP1A. При работе на 2кВ с учетом максимальной границы диапазона пластинам отклонения оси X (Отклоняющие пластины 1-2) требуется колоссальный сигнал 198В для отклонения от центра всего на дюйм!

Значит для нашего экрана 3” необходимо 1.5*198 = ±300В (приблизительно), чтобы отклонить луч от центра к любому краю экрана. Это серьезная проблема. Стандартная плата отклонения Oscilloclock едва способна обеспечить ±200В до потери линейности.

В добавок к этому, выяснилось, что требуется добиться отклонения более 1.5” от центра! Взгляните на схему растрового изображения ниже, которое мне нужно было отобразить. Так оно выглядит при VGA сигнале 800х600. Обратите внимание на площадь мертвых зон, особенно слева и справа. Эта область определяется в VGA стандарте как импульс синхронизации, а также тайминги переднего фронта и заднего фронта, которые дают цепи дисплея время подготовиться к обработке каждой строки.

VGA-наложение, показывающее мертвые зоны

Следующая сложность (сколько их еще будет?) в том, что нам нужно отрисовывать круг в разрешении 800х600 с соотношением сторон 4:3 на круглом экране с соотношением 1:1. После преобразования всех разрешений в соотношение 1:1 и выражения их в дюймах для удобства, получаем следующее:

Соотношение сторон, приведенное к 1:1 и преобразованное в дюймы

Отсюда следует, что луч должен перемещаться максимум на +2.525” вправо и на 3.325” влево, обеспечивая перемещение фактического круга изображения радара на ±1.5” и достижение им края экрана. Но отклонение -3.325” по оси X (Отражающие пластины 1-2) при 198В/дюйм требует не менее -658В! Куда уж тут моей плате с напряжением ±200В…

Побежден — но не сломлен!

Спустя несколько часов мыслительных метаний мне в голову пришла прекрасная идея. Вот ряд сопутствующих ей фактов:

  1. На VGA-диаграмме большая часть мертвой зоны относится к направлению X. Для Y эта зона существенно меньше.
  2. Согласно спецификации ЭЛТ отклонение в “Отклоняющих пластинах 3-4” более чувствительно, чем в “Отклоняющих пластинах 1-2”. Причина в том, что 3-4 пластины расположены ближе к электронной пушке, и в этой области электроны движутся медленнее.
  3. Из-за повышенной чувствительности пластин 3-4 практически во всех осциллоскопах к ним подается вертикальный сигнал. И слепо следуя этой условности, я тоже планировал приспособить для этих пластин выход Y.
  4. Но в VGA-дисплее ни для вертикальных, ни для горизонтальных сигналов не характерна высокая частота. При этом оба этих вида сигналов имеют одинаковую амплитуду от 0 до 5В. А значит чувствительность отклонения значения не имеет.

Так что Вау! К черту условности – почему бы не подключить проблемный выход X, которому для преодоления мертвой зоны нужно намного большее отклонение, чем Y, к более чувствительным пластинам 3-4? А после просто повернуть трубку на 90 градусов!

Нужно еще раз все рассчитать…

Отклонение 3.325” по оси X при использовании более чувствительных пластин 3-4 при 140В/дюйм (в худшем случае) требует -466В! Это фантастическое улучшение по сравнению с -658В, но все еще намного больше, чем ±200В, которые может обеспечить моя плата…

Излечение платы отклонения

Вот незадача! Место для этой статьи закончилось. Но я попрошу вас, мой дорогой читатель, набраться немного терпения, потому что вскоре последует продолжение, в котором мы рассмотрим базовую схему платы отклонения, попытаемся понять ПОЧЕМУ она не может пропускать линейно более ±200В и в завершении узнаем, как я обошел это ограничение.

ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/539456/