Сам себе игровая консоль: как я сделал свой «тетрис» с нуля. Что происходит, когда программист встречается с железом?

image

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

Как я к этому вообще пришел?

Проекты разработки самодельных игровых приставок стали очень популярны к нашему времени. Если раньше embedded-разработка была достаточно дорогой и доступной лишь для избранных, то сейчас на рынке можно найти все что хочешь — и мощные микроконтроллеры с кучей периферии за 300 рублей, и готовые дисплейные модули по 250 рублей, и макетные платы с удобными dupont коннекторами за весьма скромные деньги.

image

Собрать свой гаджет в пределах одной-двух тысяч рублей стало вполне реальным. Люди собирают себе самые разные устройства, а игровые приставки — одна из самых популярных тем. Однако, для многих людей, которые только начинают знакомится с миром embedded-электроники, собрать консоль в своем корпусе с Raspberry Pi на борту и RetroPie в качестве оболочки — за счастье.

image

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

image

Вот так, с любовью программировать игры, я и пришел к мысли сделать свою консоль, так как вижу её именно я. Только без чужих библиотек и наработок, но не прям уж bare metal. Сел я и начал думать, на чём же мы будем строить наш игровой девайс!

Из чего будем делать?

Как я уже говорил выше, в наше время выбор железа для создания своих девайсов большой — тут и мощные микроконтроллеры/одноплатники, по производительности сравнимые с телефонами 2005-2006 годов, и различная периферия — аж глаза разбегаются. Однако проектировать будущую консоль нужно исходя из некоторых требований.

Характеристики моего девайса следующие:

  1. Процессор: двухядерный ARM микроконтроллер RP2040 на частоте 133мгц, построенный на архитектуре Cortex-M3. Сам процессор распаян на плате Raspberry Pi Pico.
  2. ОЗУ: 260 килобайт SRAM, встроена в процессор. Немного, но если грамотно распоряжаться ресурсами — то хватит.
  3. ПЗУ: 2Мб SPI Flash-памяти, также распаяны на плате.
  4. Дисплей: 1.8″ TFT-матрица с разрешением 128×160. Выбор разрешения обусловлен производительностью будущей консоли — процессор банально не сможет заполнять матрицу с относительно высоким разрешением.
  5. Ввод: 6 кнопок, 4 из которых — направление, 2 — действий. В будущем могут добавиться еще несколько.
  6. Звук: динамик. Пока не знаю, с чего рулить будем — возможно, возьмем «железный» ШИМ-контроллер процессора, а возможно прикрутим внешний ЦАП с i2s.
  7. Питание: 3.7в аккумулятор BL-4C. Да, да, тот самый с Nokia и современных кнопочников! Аккумулятора, емкостью в 800мАч должно хватать хотя-бы на 4-5 часов игры. При этом зарядка АКБ обеспечивается модулем TP4056.

Весьма неплохо для самоделки, согласны? Как я уже говорил раннее, эти характеристики примерно соответствуют мобильным телефонам 2004-2006 годов — Nokia 6600, Sony Ericsson K510i, Samsung D800. Отличие лишь в ОЗУ (в телефонах её 2-4 мегабайта) и периферийных модулях типа контроллера дисплея.

image
На фото E398 — мобилка 2004 года выпуска, но она здесь не просто так. 🙂

Важную пометку нужно сделать касательно дисплеев: эти 1.8″ матрицы бывают приходят с «синевой» — это не железная проблема и не совсем брак. Сам контроллер в дисплея в них сильно греется (хотя токоограничивающий резистор стоит) и негативно влияет на клей, из-за чего матрицы отклеивается от подсветки и слои поляризации начинают «синить» картинку. Лечится проклееванием подложки матрицы суперклеем.

image

RPi Pico я решил выбрать, поскольку информации про них достаточно мало, характеристики хорошие и пока что никто особо ничего на них не делал, тем более в рунете. А ещё у них очень удобное и простое SDK, практически bare-metal. ESP32, например, работает на FreeRTOS и имеет кучу библиотек, здесь же API простое и понятное.

image

Закупаем все необходимое и начинаем творить!

Графика

В первую очередь нам нужно подключить дисплей и что-нибудь на него вывести. Заодно и SPI погоняем на незнакомом чипсете, благо работа с ним очень простая — задаем конфигурацию пинам (gpio_set_function), настраиваем SPI-контроллер и можно посылать данные.

image

SPI у RP2040 работает на частоте вплоть до ~60мгц — это достойная скорость передачи, в том числе и для быстрого вывода графики. На самом деле, SPI даже предпочтительнее чем параллельный 8080-интерфейс для использования в микроконтроллерах: дело не только в количестве занимаемых пинов, но и в возможности использования DMA!

В подобных проектах всегда нужно делать так, чтобы дисплей можно было при необходимости поменять, а желательно вообще научить работать его с несколькими контроллерами: разные дисплеи одной диагонали могут использовать разные контроллеры. В моём случае, это ST7735. Для разрешений 240×320 используются ILI9325, ILI9341, ST7789. Команды инициализации дисплея честно позаимствованы, но именно в этом нет ничего зазорного — сама система команд относительно стандартизирована, отличается лишь первичная настройка питания, гамма-коррекции и т. д — часто init sequence вставляет сам производитель в даташит.

image

После инициализации дисплея пробуем что-нибудь вывести. Да, все работает без проблем. Пару важных нюансов: ST7735 требует посаженный на землю CS, в воздухе его оставлять нельзя, как некоторые ILI (вы ведь навряд ли будете вешать несколько устройств на одну шину с дисплеем, когда есть вторая?) и логическое состояние 1 на пине RESET (в воздухе и «на земле» он будет висеть в постоянном ресете).

image

Для полустатичной графики, можно обойтись лишь командами дисплея — например, тут есть удобные функции для заливки прямоугольников (setArea и пишем цвет без остановки) или скроллинга. Сделано это для более слабых микроконтроллеров. Нам они не подойдут — выделяем память под фреймбуфер/бэкбуфер и настраиваем канал DMA для разгрузки процессора в процессе передачи данных:

image

Саму картинку подготавливает процессор: именно он рисует картинки и он же делает их прозрачными. На него ложится основная работа, однако мы можем ему помочь разгрузиться, если отдадим передачу уже подготовленного кадра на дисплей на DMA (Direct Memory Access) — устройство в микроконтроллере, которое позволяет процессору настроить параметры передачи данных, а DMA их будет сам копировать из памяти или в память. Таким образом, можно реализовать асинхронное копирование нескольких блоков ОЗУ, или, как в моем случае — передачу буфера кадра на дисплей, пока процессор готовит следующий. Чем больше разрешение — тем больше эффекта от DMA!

image

Кроме того, важно выбрать формат цвета для нашего дисплея: я выбрал 2-х байтный RGB565 (5 бит красный, 6 бит зеленый, 5 бит синий). Это экономичный формат который выглядит красивее палитровой графики и кушает не так уж и много драгоценной памяти. Кроме того, на данный момент мы умеем отрисовывать изображения произвольных размеров с прозрачностью — вместо альфа-канала здесь используется так называемый colorkey — концепция, очень близкая к хромакею, только она берет в качестве трафарета конкретный цвет. В нашем случае это «255 0 255» (ярко розовый).

image

Общая производительность рендерера порадовала: он легко осилит около сотни-двух различных спрайтов с адекватной производительностью, в зависимости от их размера. Но для такого разрешения экрана и будущих игр — это неплохой результат!

Ввод

Теперь нам нужно как-то управлять нашим девайсом. Для этого пора сделать реализовать геймпад: в рамках этой статьи, я собрал его на макетке.

image

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

image

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

image

Есть ещё способ реализации больших клавиатур и геймпадов: когда все кнопки вешаются на пару линий, где на выходе каждой кнопки есть резистор определенного номинала. ЦАП микроконтроллера считывает это значение (допустим — 1024 это вверх, а 2048 — вниз) и таким образом определяет текущую нажатую кнопку. Таким раньше любили промышлять китайцы, из-за чего нельзя было нажать одновременно вверх и вправо, или вниз и влево и т. п.

image

Пишем игру

Теперь у нас есть минимально-необходимая основа для написания игры. Первой игрой для своей консоли я решил написать классический шутер в космосе — летаем на кораблике и сбиваем врагов, попутно уворачиваясь от их пулек. Заодно проверим консоль на стабильность.
Писать я её решил в классическом C-стиле, как и принято в embedded-мире: без std и тем более stl, без ООП и виртуальных методов, аллокаций по минимуму. В общем, примерно как писали игры под GBA! В первую очередь, подготавливаем спрайты нашей игры, прямо в пейнте, а затем конвертируем их в представление обычного массива байтов в виде header-файла. На первых порах это удобнее, чем делать свой ассет-пул:

image

Архитектуру я организовал в виде нескольких подфункций, каждая из которых занимается своим стейтом (world/menu) и своими объектами (playerUpdate) и их отдельные версии для отрисовки. Сами игровые объекты я описал в виде структур, а центральным объектом сделал CWorld.

image

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

image

Единственные аллокации, что я использовал — это для пулов с пулями, и с врагами. Оба пула четко ограничены — до 8 врагов на экране, и до 16 пулек — вполне хватает. Динамические аллокации помогли мне найти серьезную ошибку в коде — в один из моментов игра просто валилась с Out Of Memory. После того, как я немного поменял условия и делал аллокейты тех же самых объектов каждый кадр — игра переставала крашится. Причина оказалась простая — невнимательность (вместо >= было >), по итогу при отрисовке спрайтов за пределами экрана, программа сама начинала портить вунтренние структуры аллокатара и самой игры (проявлялось в глюках и телепортациях). После фикса, все заработало как нужно. 🙂

image

Ну и для основной части геймплея с выстрелами и столкновениями, я предусмотрел несколько функций, которые спавнят игровые объекты и сами управляют пулом. Противники обновляются как обычно, для коллизий используется AABB (axis aligned bounding box, ну или его 2D-подмножество в виде rect vs rect).

image

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

image

Ниже выкладываю принципиальную схему устройства, она очень простая, поэтому смысла делить ее на несколько листов нет. Разводить учился, читая сервис-мануалы и схемы 🙂

image

Заключение

Полная цена сборки прототипа составила:

  • Raspberry Pi Pico — 557 рублей (но я брал на Яндекс Маркете, на «алике» дешевле — около 300 рублей).
  • Дисплей — 380 рублей, заказывал на «алике».
  • Макетка — 80 рублей, в местном радиомагазине.
  • Кнопки. По 5 или 10 рублей штучка, пусть будет 60 рублей.

По итогу, прототип мне обошелся в 1077 рублей. Бюджетненько, да, с учетом того, что можно сделать еще дешевле? Я тут так подумал, у меня есть желание развивать и поддерживать консоль в будущем и под консоль уже можно делать что-то своё… может, если вам будет интересно, делать их на заказ? Соберу вам по себестоимости (до 1.000 рублей) + доставка, если хочется попрограммировать под что-то маленькое, но самому паять не хочется. Мне было бы очень приятно. Пишите в личку или комменты, если вас заинтересовало бы такое! 🙂

image

Весь процесс разработки этого девайса занял у меня всего несколько дней. Я и до этого понимал концепцию работы 2D-графики на видеокартах прошлого века, поэтому ничего особо нового я для себя не открыл. Однако, я попробовал свои силы в разработке игровых девайсов, которые могут приносить удовольствие — как ментальное от самого процесса сборки и программирования, так и физическое от осознания того, что игра на нем работает. 🙂

Однако, это далеко не конец проекта! У нас ещё много работы: нужно развести и протравить полноценную плату, реализовать звук и API для сторонних игр, придумать корпус и распечатать его 3D-принтере. Кстати, я ведь обещал что скоро будут и другие интересные проекты с 3D-принтером: как минимум, мы доделаем предыдущий проект игровой консоли из планшета с нерабочим тачскрином и RPi Pico.


Возможно, захочется почитать и это:

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Есть смысл в этом проекте?
100% Да, конечно есть! Прямо таки Pico-8, но нативная и в железе 🙂 7
0% Нет. Их тысячи уже, и пофиг что базируются друг на друге — результат один! 0
0% Тебе нужна изюминка, как у Playdate. Например, выведи гребенку под расширения и присоедини к шине SPI! 0
Проголосовали 7 пользователей. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Если бы я представил частично готовое устройство (с корпусом, АКБ и полноценной платой) — заказали бы его по себестоимости?
83.33% Ну да. Почему-б нет, возможно и попробовал бы под неё что-то написать. 5
16.67% Неа. Не вижу смысла. 1
Проголосовали 6 пользователей. Воздержался 1 пользователь.

ссылка на оригинал статьи https://habr.com/ru/companies/timeweb/articles/750108/

Обработка растровых данных для ML-индикации оруденения. Или как можно сэкономить миллиарды на геологоразведке

Всем добрый день. Представляю вашему вниманию метод обработки первичных данных для последующего практического применения методов машинного обучения (ML) с целью индикации вероятной локализации оруденения.

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

Сначала о самом методе. Метод довольно прост в исполнении. В геологии в открытом доступе имеется множество карт разной информативной наполненности (см. источники).

В основу получения данных легли растровые изображения геологических карт. Это может быть склейка из нескольких карт, фотография, скан. Главное — парсинг итогового изображения в слои данных «маскировки». Маскировка является закрашенным контуром какого-либо признака. Это могут быть магнитные аномалии, линии разломов, массивы кислых или основных горных пород, дифференциация пород по временным характеристикам и так далее, которые именуем признаками.

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

Принцип алгоритмической обработки растровых изображений в данном методе

Принцип алгоритмической обработки растровых изображений в данном методе

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

Как обработка данных выглядит на примере одного обработанного слоя признака:

Исходный растр данных (геологическая карта)

Исходный растр данных (геологическая карта)
Маскировка (слева) и результат преобразования маски признака в карту расстояний (справа)

Маскировка (слева) и результат преобразования маски признака в карту расстояний (справа)

Карты расстояний в совокупности являются в геологии аналогом сильно усредненных ореолов распространения энергии и вещества.

Простая реализация алгоритма подготовки данных
import warnings warnings.filterwarnings('ignore')  import numpy as np import matplotlib.pyplot as plt import pandas as pd  from PIL import Image
# загрузка изображения геологической карты с маской одного признака img = np.asarray(Image.open('Figure.png').convert('RGB'))
# приведение всего растра слоя к таблице датафрейма result = pd.DataFrame({'x': [], 'y': [], 'l': []})  for y in np.arange(len(img)):          for x in np.arange(len(img[y])):                  if sum(img[y][x]) == 0:             result.loc[result.shape[0]] = [x, y, 1]         else:             result.loc[result.shape[0]] = [x, y, 0]
# разделяем маску от поля точек zeros_data = result[result['l'] == 0] ones_data = result[result['l'] == 1]
# разные варианты представлены для демонстрации модификаций использования расстояний variant = 1  # главный алгоритм for i1, (x1, y1, l1) in zip(zeros_data.index, zeros_data.values):          if zeros_data.loc[i1, 'l'] != 0: continue          # пул всех расстояний до точки     pool_distance = []          for i2, (x2, y2, l2) in zip(ones_data.index, ones_data.values):                  pool_distance.append(         ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))**.5         )          # простое правило - ближайшее расстояние до точки     if variant == 1:         zeros_data.loc[i1, 'l'] = np.min(pool_distance)      # предпоследнее или другое значение -      # как способ защиты от одиночных случайных точек     elif variant == 2:         zeros_data.loc[i1, 'l'] = np.sort(pool_distance)[1]      # среднее по нескольким близким расстояниям - возможность сглаживания     elif variant == 3:         zeros_data.loc[i1, 'l'] = np.mean(np.sort(pool_distance)[1:101])
# сводим в готовый слой данных для ML result_data = pd.concat([zeros_data, ones_data]).sort_index()
# сохраняем новый слой данных result_data.to_csv('new_fuature', index=False)

Ниже представлены практические результаты применения данного метода в формате презентаций

Кейс № 1. Региональные поиски золотого оруденения в Свердловской области.
Слайд 1. Обзорная карта

Слайд 1. Обзорная карта

В качестве исходных данных были использованы две склеенные геологические карты, схема расположения которых по номенклатуре листов — P-40-XXX и P-40-XXXVI, также прилагаемые карты магнитных аномалий, тектонические карты и карта локализации известных рудопроявлений золота. Данные были преобразованы в карты расстояний описанным выше способом с использованием дополнительных фильтров (к примеру, логарифмирование). Разметка данных обучения содержала четыре класса данных: промышленные месторождения, рудопроявления, пункты минерализации и «пустота». Данные были разделены на тренировочные и тестовые выборки.

Слайд 2. Преобразование данных

Слайд 2. Преобразование данных

В результате была получена «золото‑вероятностная» тепловая карта, позволяющая обратить внимание на отдельные ее области.

Слайд 3. Результаты прогнозирования натренированной модели.

Слайд 3. Результаты прогнозирования натренированной модели.

Здесь на слайде 3, выделен один из перспективных объектов — потенциальное оруденение в районе р. Вижай. Также здесь приводятся данные, поднятые с отчетов предшественников в сравнении с прогнозом обученной модели по картам расстояний.

кстати…

Название реки «Вижай» с санскрита переводится как Победа — Виджай

Слайд 4. Таблица данных предшественников о районе.

Слайд 4. Таблица данных предшественников о районе.

Давайте рассчитаем ресурсный потенциал по золоту Вижайского рудного поля. По данным предшественников, в районе выделено несколько потенциальных рудных зон, перспективных на золотометальное оруденение. Рудоносная формация — терригенная черносланцевая, рудная формация черносланцевая золоторудная, геолого‑промышленный тип руд — золото‑сульфидно-кварцевый.

Слайд 5. Наглядное представление о геологической изученности района.

Слайд 5. Наглядное представление о геологической изученности района.
Слайд 6. Сравнение объектов-эталонов

Слайд 6. Сравнение объектов-эталонов

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

Сурьинский рудный узел расположен в пределах Ишемирской минерагенической зоны, в то время как Вижайское и Оленевское рудные поля расположены в пределах Западно-Тагильской минерагенической зоны.

В геологическом строении толщ Оленевского и Вижайского рудных полей принимает участие Войкаро-Кемпирсайский тектоногенный комплекс. По этим причинам и другим причинам Автором настоящего проекта был пересчитан металлоносный потенциал рудных зон Вижайского рудного поля, который составил порядка 130 т. Глубина оценки: 150 метров.

Слайд 7. Сходство обоих рудных объектов

Слайд 7. Сходство обоих рудных объектов

Выдвинем аналогичное предположение:
площадь Оленевского рудного поля: 6,5 км. * 0,4 км. = 2,6 кв.км.
удельная продуктивность Оленевского рудного поля: 51 т. /2,6 кв.км. = 16,7 т/кв.км., где
ресурсы категории P3 — 51 т.;
16,7 — Удель ная продуктивность рудной зоны эталонного объекта;

Итого предполагаемые ресурсы с учетом продуктивности эталонного объекта составили: 26 кв.км. * 16,7 т/кв.км. * 0,3 = 130 т.,
где
26 — суммарная площадь двух потенциальных золоторудных зон Вижайского рудного поля, выделенных автором (см. приложение 6);
0,3 — коэффициент достоверности прогноза;

В результате предполагаемый ресурсный потенциал Вижайского рудного поля— 130 тонн Au металла, подсчитанный методом аналогии.

Кейс № 2. Региональные поиски медного оруденения в Красноярском крае.

Территориально объект расположен на юге Красноярского края, в 100 км от областного центра

Слайд 1. Обзорная карта расположения объекта

Слайд 1. Обзорная карта расположения объекта

В данном кейсе рассмотрим Тасеевский потенциальный медно-рудный узел. С помощью машинного обучения исследуем весь медно-рудный узел, который является на сегодняшний день слабоизученным.

В строении Таеевского узла принимают участие осадочные породы так называемой Тасеевской серии. Синклинорий сложен измененными отложениями верхнего протерозоя и прорванными интрузивными образованиями Синийского и Токминского комплексов. Основные усилия предшественников были сосредоточены на южную часть узла. Так по результатам поисков была выделена Ологашская площадь. В пределах площади было выявлено множество геохимических аномалий по халькофильным элементам и ряд пунктов медной минерализации и рудопроявлений. Предшественники отметили более 300 геохимических ореолов меди размерами до полукилометра с максимальными содержаниями 0,1%. Присутствие урана в породах тасеевской серии добавило гриф секретности проводимым поисковым работам, который позже был снят.

Слайд 2. Геологическая обстановка Антоновская и Ологашская площади

Слайд 2. Геологическая обстановка Антоновская и Ологашская площади

Ресурсный потенциал Ологашской площади по разным оценкам составляет порядка от 8 — 20 млн. тонн Cu металла.

Слайд 3. Меденосный потенциал Ологашской площади

Слайд 3. Меденосный потенциал Ологашской площади

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

В качестве признаков обучения использовались карты расстояний до разломов, расстояния до зон халькофильных аномалий и расстояния до ближайших выходов магматических пород. В качестве целевой переменной использовались данные о геохимических аномалиях, пунктах минерализации и рудопроявлениях меди. Здесь я привожу результаты анализа всему Тасеевскому узлу.

Слайд 4. Маскировка признаков и конвертация слоев признаков в карты расстояний

Слайд 4. Маскировка признаков и конвертация слоев признаков в карты расстояний
Слайд 5. Результаты прогнозирования обученной модели. Желтый контур - границы Центральной площади

Слайд 5. Результаты прогнозирования обученной модели. Желтый контур — границы Центральной площади

Таким образом была выявлена перспективная Центральная площадь, которая находится в контуре антоновской площади. К счастью, в границах антоновской площади предшественники-уранщики пробурили скважины прямо по западному флангу Тасеевской серии, таким образом вскрыв полный разрез Тасеевских отложений. Так удалось оценить меденосный потенциал на данной площади, взяв бортовое содержание 0,35%. Таким образом, предполагается ресурсный потенциал на Антоновской площади в 1,6 миллиона тонн меди.

Слайд 6. Центральная площадь. Результаты подсчетов ресурсного потенциала по Антоновской площади

Слайд 6. Центральная площадь. Результаты подсчетов ресурсного потенциала по Антоновской площади

Хотелось бы отметить, что по результатам работ на Антоновской площади электроразведка методом переходных процессов здесь хорошо сработала. В аномалиях выявляется медное оруденение, и этому есть подтверждения. Так, при заверке выявленных геофизических аномалий скважинами 2112, 2120 и 2140 были вскрыты горизонты медного оруденения. Согласно рекомендациям машинного обучения, я выделил Центральную площадь размером в 120 квадратных километров, в пределах которой также выделяются рудопроявления меди и возможны геофизические аномалии.

Слайд 7. Разрезы по скважинам с содержанием халькофильных элементов. Зеленый - медь.

Слайд 7. Разрезы по скважинам с содержанием халькофильных элементов. Зеленый — медь.

По итогу прогнозируется в пределах Центральной площади 6,9 миллионов тонн меди со средним содержанием в 1,69%

Слайд 8. Подсчет прогноза ресурсного потенциала Центральной площади

Слайд 8. Подсчет прогноза ресурсного потенциала Центральной площади

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

Результатом практического применения данного индикатора прирост ресурсного потенциала минерально-сырьевой базы сообщества Хабр составил:
+ 130 тонн золота
+ 6,9 млн. тонн меди

Источники

интернет ресурсы растровых геоданных
Государственные геологические карты России
Карта прогнозных ресурсов категории Р3

кейс № 1
1.Петров Г.А, Ильясова Г.А., Останин С.Ю., Тристан Н.И., Е.Н.Михалёва и др. Геологический отчет по объекту “ГДП-200 в пределах Северо-Уральской площади (лист Р-40-XXXVI)” за 2003-2006 гг.

2.Зубков А.И, Прогнозно-поисковые работы на благородные металлы (золото, платина) на площади листов P-40-XXX, P-40-XXXVI (Средний и Северный Урал)» за 2001-2004гг. Екатеринбург, 2004 г.

3.Петров Г.А, Ильясова Г.А., Останин С.Ю., Е.Н.Михалёва и др. Отчет о результатах работ по объекту “ГДП-200 листа Р-40-XXX (Тохтинская площадь)”, 2014г.

кейс № 2
1.Моргун В.Н, Отчет Букарской партии Ивановской экспедиции о поисках меди в междуречье рек Верхний и Малый Курыш в 1960-61 гг. (м-б 1:50000, листы 0-46-XXXV, XXXVI)

2.Домаренко В.А, Оценка перспектив ураноносности зоны структурно-стратиграфического несогласия Восточной части Ангаро-Канского выступа Енисейского кряжа. Отчет Саянской экспедиции № 53 о результатах прогнозно-поисковых работ м-ба 1:200000-1:50000 и крупнее по геолог.заданию 53-15 за 1986-1990 гг.

3.Семеняко В.В, Результаты геохимических поисков масштаба 1:50000 в Ангаро-Канской части Енисейского кряжа на Немкинской площади. Отчет Богунайской партии о результатах геохимических поисков масштаба 1:50000, проведенных в 1989 — 1991годах на Немкинской площади.

4.Заблоцкий К.А, Информационный отчет по производству ГГС масштаба 1:50 000 с общими поисками на площади листов О-46-129-Б; 130-А,Б,В,Г (Немкинская площадь) за 1991-1994 гг.

5.Зуев В.К, Создание комплектов Государственной геологической карты масштаба 1:1000000. ФГУГП «Красноярскгеолсъёмка» , ФГУП «ВСЕГЕИ». 2005 г.

6.Зуев В.К, Государственная геологическая карта Российской Федерации. Масштаб 1 : 1 000 000 (третье поколение). Серия Ангаро-Енисейская. Лист O-46 – Красноярск. Объяснительная записка


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

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

Первый трансатлантический телеграфный кабель был проложен в 1858 году после нескольких неудачных попыток. Хотя кабель впервые позволил передавать сообщения через Атлантику, передача была медленной и ненадежной. Кабель вышел из строя всего через несколько недель работы из-за плохого проектирования и изготовления. Несмотря на неудачу, в то время этот проект считался технологическим триумфом и вдохновил на создание множества сувениров, сделанных из кусков оставшегося кабеля. Хотя сувениры прослужили дольше, чем сам кабель, проект продемонстрировал трудности дальней связи и помог проложить путь для будущих успешных трансатлантических кабелей.


Начало: Фредерик Гисборн встречает Сайруса Филда 

Идея прокладки трансатлантического кабеля связи была впервые выдвинута в 1839 г., когда Уильям Кук и Чарльз Уитстон создали действующий телеграф. В 1840 г. идею поддержал Сэмюэл Морзе, а к 1850 г. была проложена линия связи между Великобританией и Францией. В том же году началось строительство телеграфной линии вдоль северо-восточного побережья Северной Америки — от Новой Шотландии до острова Ньюфаундленд.

Команду, создавшую кабель для восточного побережья, возглавлял Фредерик Ньютон Гисборн, телеграфный инженер из Ланкашира. Но линия оказалась не слишком прибыльной, и в 1853 г. компания распалась. Судьба Гисборна изменилась, когда он познакомился с Сайрусом Вест Филдом. Именно Филд станет тем человеком, с которым навсегда будет ассоциироваться успех этого мероприятия — соединения США и Европы телеграфным кабелем.

Американский бизнесмен и предприниматель Сайрус Филд родился 30 ноября 1819 г. в Стокбридже, штат Массачусетс. Филд начал карьеру в галантерейном магазине, куда его устроил брат. В 1840 г. он основал свое первое предприятие по производству бумаги, на чем и сделал состояние.

В 1853 г. Филд отошел от дел и вместе с женой отправился в 6-месячное путешествие по Южной Америке, после которого и началась «кабельная» эпопея. 

После поездки Филд познакомился с Фредериком Гисборном. Филд на тот момент специализировался на бумаге и галантерее и не имел никакого отношения к телеграфии. Однако, ознакомившись с планами Гисборна по прокладке кабеля, он пришел в восторг от огромной важности этого предприятия. 

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

План Гисборна позволил бы сократить время прохождения новостей между Лондоном и Нью-Йорком до шести дней, что в начале 1850-х годов считалось очень быстрым. 

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

Филд обратился за советом к Сэмюэлю Морзе, изобретателю телеграфа, и лейтенанту ВМС США Мэтью Мори, который недавно проводил исследования глубин Атлантического океана. Оба ответили утвердительно: возможность протянуть подводный телеграфный кабель через Атлантический океан есть.

В феврале 1854 г. Мори готовил письмо министру военно-морского флота с результатами зондирования, когда получил сообщение от Филда с вопросом о целесообразности прокладки трансатлантического кабеля. Мори отправил Филду копию своего письма, в котором, в частности, говорилось, что в результате зондирования было обнаружено существование плато, «которое, по-видимому, было создано специально для того, чтобы удерживать провода подводного телеграфа и держать их вдали от опасности». Это геологическое образование, которое Мори назвал «телеграфным плато», должно стать идеальным маршрутом для кабеля Филда.

В 1854 г. Филд стал основателем телеграфной компании Нью-Йорка, Ньюфаундленда и Лондона. Соучредителями стали Питер Купер, Мозес Тейлор и Маршалл Робертс. Гисборн стал главным инженером компании. Новая компания намеревалась проложить первый глубоководный телеграфный кабель между Европой и Северной Америкой.

1.Питер Купер (президент), 2. Дэвид Филд, 3. Чандлер Уайт (секретарь), 4. Маршалл Робертс, 5. Сэмюэл Морзе (вице-президент), 6. Дэниел Хантингтон, 7. Мозес Тейлор (казначей), 8. Сайрус Филд, 9. Уилсон Хант

1.Питер Купер (президент), 2. Дэвид Филд, 3. Чандлер Уайт (секретарь), 4. Маршалл Робертс, 5. Сэмюэл Морзе (вице-президент), 6. Дэниел Хантингтон, 7. Мозес Тейлор (казначей), 8. Сайрус Филд, 9. Уилсон Хант

Ключевым положением Устава 1854 г. было предоставление компании и ее правопреемникам пятидесятилетней монополии на все виды связи с Ньюфаундлендом, срок действия которой истекал в 1904 г:

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

Основание Atlantic Telegraph Company

Нью-йоркский промышленник, финансист и филантроп Питер Купер стал первым инвестором, присоединившимся к Сайрусу Филду в новой компании. 

Купер сделал свое состояние, купив клеевую фабрику и подняв ее прибыль в 5 раз благодаря усовершенствованию продукции. Затем он занялся металлургической промышленностью и создал первый в США локомотив Tom Thumb

Куперу было уже за 60, когда он познакомился с Фидом, и его главной заботой на тот момент было создание технического колледжа Cooper Union. Но что-то в проекте строительства кабеля заинтриговало его. Как он позже напишет, в кабеле он видел «средство, с помощью которого мы могли бы поддерживать связь между двумя континентами и передавать знания во все части света». Полагая, что кабель «открывает возможности для создания могущественной силы на благо всего мира», Купер согласился инвестировать в этот проект.

Разработчик телеграфа в США и один из первых сторонников строительства Атлантического кабеля Сэмюэль Морзе был привлечен к проекту строительства кабеля Филдом, чтобы придать предприятию узнаваемость и авторитет.

Затем Филд привлек и других участников: Мозеса Тейлора, директора компании, ставшей впоследствии Citibank, Маршалла Робертса, успешного судовладельца, и Чандлера Уайта, давнего соратника по бумажному бизнесу. 

С одобрения Купера были привлечены другие акционеры и собрано более 1 млн долл. Вновь созданная компания, получившая название New York, Newfoundland and London Telegraph Company, выкупила канадский патент Гисборна и начала работы по прокладке подводного кабеля от канадского материка до Сент-Джонса.

В итоге удалось привлечь 1,5 млн долл. частных средств на реализацию проекта, что равнялось примерно 2,5% от общих расходов правительства США на тот момент. 

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

Филд начал собирать средства на трансатлантическую экспедицию, продавая в Лондоне и Нью-Йорке акции материнской компании Atlantic Telegraph. Британское правительство помогло Филду субсидией. Ему удалось добиться помощи и от конгресса США, несмотря на яростное сопротивление сенаторов-англофобов. Кроме того, Филд выделил четверть личных средств на строительство кабеля.

В конце концов ему удалось добиться от правительств США и Великобритании выделения судов для прокладки предполагаемого трансатлантического кабеля.

В июне 1857 г., вернувшись в Нью-Йорк из Англии с планами первой попытки прокладки атлантического кабеля летом того же года, Сайрус Филд опубликовал статью, где ответил на вопросы жителей США, которых интересовала деятельности Atlantic Telegraph. Текст был также напечатан в журналах Canadian Merchants’ Magazine и Commercial Review. В статье Филд вдохновенно писал:

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

Изготовление кабеля

Трансатлантический кабель был изготовлен английскими компаниями Glass Elliot & Co из Гринвича и R.S.Newall & Co из Ливерпуля. Важное значение имели защитные свойства кабеля: он был покрыт латексом из гуттаперчи, который считался устойчивым к воздействию морских растений и животных, обмотан просмоленной пенькой и окружен спиралевидной оболочкой из железной проволоки. Идея заключалась в том, чтобы обеспечить тягу в несколько тонн, но при этом сохранить относительную гибкость.

Контракт на изготовление изолированной жилы получила компания Gutta Percha Company, а контракт на изготовление брони был разделен поровну между Glass, Elliot & Co и R. S. Newall & Co. Кабель состоял из 7 жил медного провода № 22 BWG, шесть из которых были обернуты вокруг седьмой, а затем были покрыты тремя слоями гуттаперчи. Затем жила обматывалась джутовой пряжей, пропитанной составом, состоящим из 5/12 стокгольмского дегтя, 5/12 смолы, 1/12 вареного льняного масла и 1/12 обычного пчелиного воска. 

Броня состояла из 18 прядей, каждая из которых состояла из 7 проволок из лучшего угольного железа № 22 BWG, шесть из которых были обернуты вокруг седьмой. Готовый кабель после выхода из машины покрывался составом, состоящим из 3 бочек дегтя, половины бочки смолы, 12 фунтов пчелиного воска и 6 галлонов льняного масла для смешивания. На один нанометр уходило от 12 до 13 галлонов этой смеси. 

В своей статье Филд писал:

«За строительством кабеля внимательно следят доктор Уайтхаус, занимающий первое место в ряду выдающихся специалистов в области электричества, и главный инженер компании мистер Брайт, имеющий большой практический опыт работы с электрическими телеграфами. С этими господами, под началом которых днем и ночью работают несколько надежных суперинтендантов, уже некоторое время поддерживают постоянную связь профессор Морзе и профессор Томсон из Глазго, что является дополнительной гарантией добросовестного выполнения работ».

Чарльз Тилстон Брайт — плодовитый изобретатель, в возрасте 24 лет был назначен главным инженером Atlantic Telegraph Company в 1856 году. В 1858 году его назначали ответственным за прокладку атлантического кабеля, позже за это он был посвящен в рыцари. 

Уайлдман Уайтхаус был нанят Филдом в качестве главного электрика Atlantic Telegraph Company. Хотя 16 августа 1858 г. Уайтхаусу и удалось отправить первое телеграфное сообщение в США, именно на него возложили ответственность за выход из строя подводного кабеля после того, как тот использовал повышенное напряжение, пытаясь усилить затухающие сигналы.

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

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

Физик Уильям Томсон пытался решить эту проблему: в 1854 году он вывел закон квадратов, согласно которому снижение качества сигнала «увеличивается с квадратом пройденного расстояния». Томсон пришел к выводу, что если «диаметр проводника и изоляции кабеля увеличить пропорционально его общей длине, то задержка (качество) сигнала и то, что Томсон, подыскивая технический язык для описания четкости сигнала, причудливо назвал “отчетливостью произношения”, останутся неизменными». 

Кабель на палубе «Ниагары».

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

Наконец, в июле 1857 года все 2500 морских миль первого трансатлантического кабеля были готовы, и настало время погрузить его на корабли.

Первая попытка

В качестве кабелеукладочных судов использовались переоборудованные военные корабли HMS Agamemnon и USS Niagara. Процесс загрузки кабеля на «Ниагару» занял около 3 недель. На «Агамемноне» было установлено 10 якорей, которые должны были «остановить любое движение, пока громоздкие катушки переносились в трюм». Чтобы кабель достиг корабля, он был переброшен через опоры, закрепленные на 10 баржах между заводом и кораблем, и смотан в одну катушку высотой 12 футов и диаметром 45 футов.

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

«Ниагара» подает береговой кабель на катера и паровой буксир «Willing Mind»

«Ниагара» подает береговой кабель на катера и паровой буксир «Willing Mind»

Наконец, 6 августа 1857 г. корабли с большой помпой отчалили от острова Валентия, расположенного у западного побережья Ирландии. Инженеры решили, что лучше всего прокладывать кабель в одном направлении — с востока на запад, чтобы всегда поддерживать связь с сушей. Первой шла «Ниагара», а за ней «Агамемнон».

Кабель должен был прокладываться со скоростью, соответствующей скорости корабля. Но в какой-то момент кабель стал двигаться примерно на 50% быстрее корабля. Чтобы остановить разгон кабеля, дежурный инженер включил тормоза на машине для укладки кабеля. В этот момент «Ниагара» оказалась во впадине волны, и когда поднялась на следующий гребень, трос оборвался из-за резкого увеличения веса, вызванного его отрывом от судна.

В итоге «Ниагара» проложила 335 миль кабеля, когда 11 августа в 3:45 утра кабель оборвался, опустившись на дно океана. У кораблей не осталось достаточного количества кабеля для продолжения работ, и экспедиция была отложена на год.

Вторая и третья попытки

К концу весны 1858 г. Филд был готов к новым попыткам. 

Был изготовлен новый кабель для замены потерянной в море части. Уильям Эверетт, бывший главный инженер «Ниагары», перепроектировал тормоз кабелеразмоточной машины, сделав его меньше и легче и снабдив саморегулирующейся функцией, которая могла быстро разжиматься, чтобы предотвратить обрыв троса.

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

Они должны были стартовать в середине океана на 52°2′ северной широты, 33°18′ западной долготы, соединить две половины кабеля вместе, а затем каждый корабль одновременно проложить кабель в противоположных направлениях. В этот раз корабли были лучше подготовлены, предварительно отработав различные маневры. 

Но вмешалась погода: на флот обрушился жестокий шторм, продолжавшийся больше недели и едва не ставший причиной гибели «Агамемнона». Наконец, 25 июня 1858 года судно достигло места встречи и на следующий день приступило к прокладке кабеля. После того, как «Агамемнон» проложил более 140 миль, кабель оборвался, и снова от попытки пришлось отказаться. 

Филд был на грани. Брат Филда Генри писал: «Нагрузка на него была больше, чем на кабель, и мы боялись, что оба они порвутся вместе».

Маршрут 1858 года

Маршрут 1858 года

17 июля 1858 г. была предпринята третья попытка. В очередной раз кабель внезапно перестал работать, а затем так же внезапно ожил. Из-за огромного количества железа в кабеле исказились показания компаса на «Ниагаре», и она сбилась с курса. Но, к счастью, проблема обнаружилась сразу, и другой корабль флотилии указал «Ниагаре» путь. На «Агамемноне» же было на исходе топливо, однако выручило разумное использование мощности парусов. 

«Ниагара» достигла Ньюфаундленда 5 августа, «Агамемнон» в тот же день достиг Валентии. Окончательно соединение было установлено 16 августа. В итоге «Ниагара» проложила 1030 морских миль кабеля, а «Агамемнон» — 1020.

Филд телеграфировал жене с Ньюфаундленда: «Все хорошо. Атлантический телеграфный кабель успешно проложен». 

Высочайшие телеграммы и неудача

«Европа и Америка объединены телеграфной связью. Слава в вышних Богу, на земле мир, в людях благоволение!», — это было первое сообщение, отправленное советом директоров в Лондоне своим партнерам в США. 

Затем королева Виктория отправила президенту Джеймсу Бьюкенену поздравление, и передача этого сообщения заняла 16 часов. Послание королевы Бьюкенену гласило: «Ее Величество желает поздравить Президента с успешным завершением великой международной работы, к которой Королева проявляет глубочайший интерес».

Как только Белый дом убедился, что это не мистификация, президент написал ответное сообщение из 149 слов, которое было отправлено через 10 часов.

Скорость передачи составляла всего около 0,1 слова в минуту. Но без кабеля отправка депеши только в одном направлении заняла бы около двенадцати дней при самом быстром сочетании внутреннего телеграфа и быстроходного парохода.

Лондонская газета «Таймс» писала об этом событии: «Со времен открытия Колумба ничто не могло сравниться с тем огромным расширением сферы человеческой деятельности, которое произошло благодаря этому».

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

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

Уже 20 августа в газете New-York Daily Tribune было опубликовано объявление об официальной дате празднества. Аналогичные сообщения появились и в других газетах по всей стране:

ЮБИЛЕЙ МЕЖДУНАРОДНОЙ КАБЕЛЬНОЙ СВЯЗИ

По общему согласию, 1 и 2 сентября в Соединенных Штатах, Канаде и Великобритании будет проведен большой международный юбилей кабельной связи. Бостон, Монреаль, Буффало, Чикаго, Сент-Луис, Сент-Джонс, Новый Орлеан, Лондон и, собственно, большинство ведущих городов этой страны и Англии выразили свое желание провести такую демонстрацию, и в настоящее время идет подготовка к ее осуществлению.

Вид на шествие по Бродвею. Фотография Уильяма Ингленда

Вид на шествие по Бродвею. Фотография Уильяма Ингленда

Кабель, конечно, поспешили разобрать на сувениры. Когда 18 августа 1858 г. в Нью-Йорк прибыла «Ниагара», это стало событием века. «Ниагара» привезла с собой множество остатков кабеля, часть которого была затоплена и поднята во время экспедиции. Главным покупателем среди нью-йоркских торговцев была компания Tiffany & Company, которая утверждала, что купила весь запас. Tiffany продала тысячи образцов кабеля по 50 центов за штуку, а также другие сувениры: брелоки, амулеты и даже трости из серебра. 

Трость из атлантического кабеля

Трость из атлантического кабеля

Однако всеобщая радость оказалась недолгой. С самого начала сообщения по кабелю принимались мучительно долго. 1 сентября 1858 года в 270 милях от Валентии кабель оборвался. Выяснять причины неисправности пришлось комиссии по расследованию.

К 20 октября линия окончательно перестала работать. При изучении причин было обнаружено множество проблем. 

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

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

Именно настойчивость Уайтхауса в использовании высоковольтных индукционных катушек в итоге и стала причиной отказа кабеля. Совет директоров Atlantic Telegraph уволил Уайтхауса и опубликовал заявление с осуждением его деятельности, на которое тот публично ответил заявлением, опубликованным в британских газетах Daily News и The Times.

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

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

Более долговечный трансатлантический кабель будет проложен только через 8 лет,  в 1866 году.

Влияние

Успешная прокладка трансатлантического телеграфного кабеля имела далеко идущие последствия и во многих отношениях изменила мир.

Кабель произвел революцию в сфере дальней связи, сократив время, необходимое для отправки сообщений через Атлантику, с недель до минут. Он способствовал быстрому обмену информацией, существенно повлияв на торговлю, финансы, дипломатию и личную переписку.

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

Кабель Marea — высокопроизводительный подводный кабель, проложенный через Атлантику и обеспечивающий передачу до 160 терабит данных в секунду

Кабель Marea — высокопроизводительный подводный кабель, проложенный через Атлантику и обеспечивающий передачу до 160 терабит данных в секунду

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

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


НЛО прилетело и оставило здесь промокод для читателей нашего блога:

-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.


ссылка на оригинал статьи https://habr.com/ru/companies/first/articles/750758/

Под капотом анимаций в React Native. Часть 1/2: Animated and Bridge

Привет! Меня зовут Евгений Прокопьев, я старший инженер-разработчик в СберМаркете. На React Native (далее просто RN) я пишу уже больше 6 лет и очень люблю создавать красивые анимации.

В интернете часто сталкиваюсь с мнением, что на RN невозможно сделать красивые и стабильные анимации: их трудно делать и они ужасно лагают. По моему опыту всё обстоит совсем не так — вопрос именно в том, как их готовить. Хочу поделиться опытом, что сделать, чтобы у вас анимации не лагали, и по возможности восстановить репутацию React Native в глазах комьюнити.

Я уже делал обзор на 6 инструментов для анимаций в RN, а в этот раз хочу углубиться в работу Animated и Reanimated под капотом. Этот материал выйдет в двух частях и я преследую цель объяснить, почему анимации в React Native работают именно так, как работают. Это поможет вам более осознанно выбирать инструмент под ваши задачи и умело лавировать грабли, на которые можно наступить, работая с RN.

Если вы плохо знакомы с историей RN или специализируетесь на Web, а сюда заглянули из любопытства, предлагаю начать с небольшой исторической вводной. Если нет, просто не раскрывайте спойлер.

Краткая история сотворения React Native

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

Вскоре приложение полностью переписали на нативку, собрали восторженные отзывы, и где-то в недрах компании человек по имени Jordan Walke (да, это тот самый Jordan Walke, который написал React) думал, как выполнить обещание о новом опыт мобильной разработки, и в 2013 году придумал способ рендерить нативные iOS-элементы из JavaScript-потока. Затем компания организовала хакатон, чтобы улучшить эту технологию, и в 2015 году впервые вышел React Native. К этому времени Facebook частично использовал RN под капотом, а Ads Manager был полностью написан на нем.

Предложенная идея была простой — берём React и просто прикручиваем туда свой механизм рендера. Выглядит просто, что может пойти не так?

Я разрабатываю на RN 6 лет и постоянно вижу следующую картину. Есть продуктовая компания или студия, у которой веб-разработчики прекрасно знают React. Компания решает сделать мобильное приложение и выбирает React Native, потому что это кажется простым решением, сделаем все сами, тут же React ну и вот это вот все. В начале всё идет прекрасно, получается быстро добавлять функционал и даже где-то переиспользовать существующую на вебе логику. Но потом задачи усложняются, приложение разрастается, в нём появляются анимации, и процесс становится уже не таким безобидным, подкидывая всё новые сложности. И тут разработчики идут писать, что RN не оптимизирован, работает плохо, анимации нормально не сделаешь и вообще он до сих пор в бетте.

Я думаю, что в основном это происходит из-за того, что Facebook позиционирует его так, чтобы показать всем, что RN очень похож на React и писать на нём можно как под веб, не углубляясь что происходит внутри. Но на деле реальность заставляет писать код всё-таки учитывая что это больше Native, чем React.

Мифы об анимациях в React Native

Итак, неужели на RN реально незозможно сделать анимации, которые не лагают? Ловите контраргумент. Всё сделано на React Native и работает на частоте 60 FPS.

Пойдем разбираться как же создавать такие анимации и начнем с постановки задачи. Реализуем экран с двумя типами анимаций:

  1. Параллакс картинки (картинка немного смещается и масштабируется) — здесь мы посмотрим, как React Native работает с трансформ-свойствами стилей.

  2. Эффект аккордеона (раскрытие карточки в интерфейсе) — тут мы проверим, как React Native работает со свойствами, которые заставляют перерисовываться макет.

Чтобы было интереснее, давайте представим, что мы совсем не имеем опыта в создании анимаций на RN, и таким образом пощупаем различные варианты решения задачи.

Итак, погуглив, как вообще можно реализовать анимации на RN, первым делом мы натыкаемся на…

Animated API

Это библиотека, которая идет с React Native из коробки, и, позволяет строить «мощные плавные анимации» (это не мои слова, они сами написали так в readme). Звучит как то, что нам нужно!

Начнём с параллакса картинки. Сам эффект полностью завязан на позицию скролла. Поэтому нам надо определить переменную, где мы будем хранить эту позицию и с помощью Animated.event связать ее с самим положением реального скролла. Дальше с помощью математических функций (все из той же библиотеки Animated) нужно рассчитать масштаб картинки и её смещение. Ну и все стили, которые у нас получились передадим в компонент, импортируемый из Animated. Смотрим.

Работает как надо… Тогда переходим к эффекту аккордеона. Здесь мы также используем переменную, в которой храним анимированное значение для плавного изменения высоты самого компонента, а анимация запускается на действие юзера. Значит мы просто можем запустить изменение высоты, вызвав Animated.timing куда передадим новое значние высоты и продолжительность анимации.

Запускаем. Тут тоже все работает хорошо. Но не просто так же все хейтят работу с анимациями. Давайте попробуем посмотреть загруженность потока в Perfomance monitor’е. На этом графике JS FPS показывает, сколько раз в секунду у нас вызывается requestAnimationFrame, ну и соответственно есть ли пропуск кадров. И у нас их нет!

Но! Приложение находится в несколько лабораторных условиях — у нас один экран и нет никакой логики, а весь поток занимается обработкой только нашей анимации. В реальных приложениях так не бывает: в них есть запросы к серверу, к базе данных, обработка различных нажатий, клиентской логики, вообще все что нам в голову взбредет… И это я еще не говорил про GC и сами расходы на работу React. В общем я хочу сказать, что в обычном RN-приложении невозможно добиться стабильных 60 JS FPS. Поэтому давайте нагрузим наш JS-поток и посмотрим, как справится наша анимация.

И тут появляются они…

Лаги

Интуитивно, конечно, понятно почему так происходит. В браузере мы точно также завязаны на requestAnimationFrame, и если не будем успевать в каждом кадре обновлять значения, то будут лаги. Но лаги такого типа в браузере встречаются сильно реже. Давайте разберемся в чем же тут разница.

Посмотрим как в RN отрисовываются элементы. Есть несколько блоков, которые связаны между собой через Bridge, и все они передают сообщения в виде сериализованных строк. Всего есть 3 потока:

  1. JavaScript-поток, в котором реализована вся бизнес логика, React со своей магией и вообще всё, что мы пишем как JS-разработчики, включая анимацию. 

  2. Shadow thread (также известен как Background thread) — там работает Yoga. Это фреймворк, написанный все тем же Facebook на С++. Он берет наши flexbox-стили, которые ему отдает React, и преобразует их в Bounding Box, который дальше отдаёт в UI-поток для отрисовки элементов.

  3. UI-поток (или Main thread) написан уже на нативных языках. Для Android это будет Kotlin или Java, для iOS — Swift либо Obj C. И он реализует 2 интерфейса:

    • Нативные модули предоставляют доступ к нативному API платформы: доступ к геопозиции, фотографиям из галереи и др.

    • UI-модули отвечают за отрисовку всех компонентов.

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

  1. У нас возникает событие от пользователя в UI-thread, отправляется через bridge в JS.

  2. Ловим и обрабатываем событие от пользователя (event) в JS-thread.

  3. Начинается стадия render, где происходит обработка всего нашего кода.

  4. Затем разница между старым и новым состоянием передается в Shadow thread и отправляется оно конечно через bridge.

  5. В нём Yoga выполняет стадию layout, где вычисляется новый макет.

  6. На стадии commit в Shadow thread старый макет заменяется новым.

  7. Этот новый макет отправляется уже в UI-thread опять же через bridge

  8. Наконец стадия mount, тут UI-thread отрисовывает наши изменения.

И все это должно происходить за 1/60 секунды (если девайс работает с 60 fps). На первый взгляд все это выглядит не очень производительно, но на самом деле, пока мы не доходим до анимаций, всё работает достаточно хорошо.

Теперь давайте посмотрим, что пошло не так с анимацией в нашем примере.

Из-за тяжелых операций стадия рендера затягивается. В следствии отправка из JS thread нового состояния в Shadow thread откладывается, и на UI, естественно, тоже. Как итог мы видим пропуск в отрисовках нового состояния в UI-треде поэтому и появляются лаги.

Давайте немного подытожим, что мы узнали:

Animated использует под капотом requestAnimationFrame. Все взаимодействие между потоками работает через Bridge. Как итог: анимации полностью завязаны на JS-поток. Отсюда и возникает боль.

Кажется, что вся проблема в сильной зависимости от работы JS-потока. Вот было бы здорово найти способ, который позволит отвязаться от него и перенести всю логику обработки анимации в UI thread…

Animated с useNativeDriver: true

Снова немного погуглив, можно найти пост в блоге RN, о том, как они решили эту самую проблему. Они предлагают использовать для анимации так называемый нативный драйвер. Говорят, можно просто добавить useNativeDriver: true в конфиг анимаций и всё заработает.

Звучит легко, надо пробовать. Вносим пару изменений. Первое — в том месте, где мы завязываемся на события скролла. 

Мы видим, что JS-поток немного загружен, показывает 30 fps, но анимация работает не замечая этого.

Второе — где мы вызываем изменение высоты наших карточек. Тоже добавим useNativeDriver: true. Запускаем пример и получаем ошибку.

В ней написано, что свойство maxHeight недоступно для анимации с помощью нативного драйвера. Чтобы разобраться, почему так, посмотрим на сообщения, которые ходят между UI и JS потоками.

Ниже подписываю, что происходит под каждым номером, указанным на картинке

Ниже подписываю, что происходит под каждым номером, указанным на картинке

Здесь мы видим, что на маунт компонента из JS в UI отправляется много сообщений. Вызывается NativeAnimatedModule, который, судя по названиям, создаёт и привязывает анимированные ноды друг к другу. Эти ноды создаются буквально на всё:

(1) Создаются переменные стиля для изменения масштаба, положения по оси Y и задаются для них сразу параметры интерполяции.

(2) Создаётся переменная, где хранится значение скрола с начальным значением 0.

(3) Здесь цифра 2 используется в вычислениях и анимированная нода для неё не создаётся нами напрямую, но под капотом NativeAnimatedModule всё-таки её генерирует. Это необходимо, потому что в графе зависимостей у анимированных нод могут находиться только анимированные ноды.

(4) Значение положения по оси Y, которое является результатом деления положения скрола на 2.

(…) Дальше все реализуется похожим образом, все достаточно комфортно читается и расшифровывается.

(n-1) Здесь создаётся обработчик анимированного события, который будет привязан к скроллу.

(n) Создаётся сам анимированный компонент.

Говоря простыми словами, React Native создает граф зависимостей на стороне JavaScript, в котором связана вся анимация, которая должна происходить с помощью useNativeDriver: true и за один раз весь граф отправляется в UI поток (после этого JS о состоянии анимации ничего не знает). 

Итак, мы хотели избавиться от расчетов в JS и у нас это получилось. Давайте посмотрим, как теперь выглядят взаимодействия между потоками.

JS-поток создает все значения и отправляет их в UI thread. Потом у нас возникает очень долгая стадия рендера, но при этом анимация отрабатывает хорошо, интерфейс плавно реагирует. Это происходит потому, что мы при монтировании один раз рассчитали весь граф и дальше передали управление в UI thread. В итоге стадия mount каждый раз вызывается и в ней есть все актуальные значения стилей, которые не зависят от расчетов в JS.

Остаётся вопрос: почему нативный реднер не работает со свойством maxHeight? Разработчики из команды RN в конце поста с анонсом новой фичи, написали и про ограничения. А конкретно, что она не работает со свойствами, которые перерисовывают макет. Давайте еще раз посмотрим на диаграмму, которая была выше.

Из-за долгой стадии рендера, у нас ничего не передается из JS thread в Shadow thread, а именно Yoga занимается тем, что рассчитывает новые положения и размеры элементов (по сути перерасчет нашего макета). Получается что мы до сих заблокированы JS-потоком для реализации анимаций, завязанных на свойства макета. Именно поэтому мы не можем анимировать высоту, ширину и подобные значения.

Хороша новость: мы научились анимировать наш UI и это работает, даже когда JS лагает.

Плохая новость: к сожалению, не все свойства.

Кажется, теперь можно не так сильно хейтить RN. Но анимировать высоту нам всё ещё нужно научиться. Гугл подсказывает, что есть библиотека Reanimated. Но об этом — во второй части статьи, которая выйдет примерно через неделю-две.

Tech-команда СберМаркета ведет соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и на  YouTube. А также слушай подкаст «Для tech и этих» от наших it-менеджеров. 


ссылка на оригинал статьи https://habr.com/ru/companies/sbermarket/articles/748724/

Профилирование производительности R-скриптов

Сегодня мы поговорим о профилировании производительности R-скриптов и методах оптимизации, которые помогут нам создавать более эффективные программы.

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

Цель этой статьи — рассмотреть методы профилирования производительности и оптимизации для повышения эффективности R-скриптов. Мы поговорим о встроенных инструментах R, которые помогают нам профилировать код, а также о том, как правильно анализировать результаты профилирования.

Профилирование производительности в R

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

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

Встроенные инструменты R для профилирования (profvis, Rprof, system.time)

R предлагает нам несколько удобных инструментов для профилирования производительности нашего кода.

Рассмотрим каждый из встроенных инструментов R:

profvis

profvis — это мощный инструмент для визуализации профилирования, который поможет нам наглядно представить, как работает наш код. Посмотрим на пример, где у нас есть функция для нахождения суммы чисел от 1 до n:

# Пример использования profvis install.packages("profvis") library(profvis)  sum_numbers <- function(n) {   result <- 0   for (i in 1:n) {     result <- result + i   }   return(result) }  # Запускаем profvis для функции sum_numbers profvis({   sum_numbers(100000) }) 

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

Rprof

Rprof — это встроенный в R профилировщик, который позволяет собирать информацию о времени выполнения функций и их вызовов:

# Пример использования Rprof # Запускаем профилировщик Rprof(filename = "output.txt")  # Наша функция, которую будем профилировать fibonacci <- function(n) {   if (n <= 1) {     return(n)   } else {     return(fibonacci(n - 1) + fibonacci(n - 2))   } }  # Вызываем функцию и профилируем её fibonacci(20)  # Останавливаем профилировщик Rprof(NULL)  # Анализ результатов профилирования summaryRprof("output.txt") 

После выполнения кода в файле «output.txt» будут содержаться результаты профилирования. Мы можем вызвать функцию summaryRprof("output.txt"), чтобы получить сводку, в которой будет указано, сколько времени занимают функции и их вызовы. Это поможет нам выявить функции, которые занимают наибольшее количество времени, и рассмотреть возможности их оптимизации.

system.time

system.time — это простой встроенный инструмент, который позволяет нам измерить время выполнения определенного кода. Посмотрим на пример с функцией для вычисления факториала числа:

# Пример использования system.time factorial <- function(n) {   if (n == 0 || n == 1) {     return(1)   } else {     return(n * factorial(n - 1))   } }  # Измеряем время выполнения функции для n = 10 timing <- system.time({   factorial(10) })  print(timing) 

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

Анализ результатов профилирования: идентификация узких мест и затратных операций

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

Пример 1: Идентификация узких мест в функции

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

find_max <- function(numbers) {   max_value <- numbers[1]   for (i in 2:length(numbers)) {     if (numbers[i] > max_value) {       max_value <- numbers[i]     }   }   return(max_value) }  # Вектор чисел my_numbers <- c(3, 8, 2, 5, 10, 4, 7, 6)  # Запускаем профилирование Rprof(filename = "output.txt") find_max(my_numbers) Rprof(NULL)  # Анализ результатов профилирования summaryRprof("output.txt") 

В результате анализа профилирования мы можем обнаружить, что операция обращения к элементам вектора (numbers[i]) занимает значительную часть времени выполнения функции. В данном случае, мы видим, что в цикле for происходит много обращений к элементам, что может быть затратным для больших векторов. Для оптимизации можно воспользоваться функцией max() для нахождения максимального значения, что позволит уменьшить количество обращений к элементам.

Пример 2: Идентификация затратных операций

Допустим, у нас есть функция для сортировки вектора, и мы хотим оптимизировать её производительность. Воспользуемся профилированием для анализа затратных операций:

# Функция сортировки вставками insertion_sort <- function(numbers) {   n <- length(numbers)   for (i in 2:n) {     key <- numbers[i]     j <- i - 1     while (j >= 1 && numbers[j] > key) {       numbers[j + 1] <- numbers[j]       j <- j - 1     }     numbers[j + 1] <- key   }   return(numbers) }  # Вектор чисел my_numbers <- c(3, 8, 2, 5, 10, 4, 7, 6)  # Запускаем профилирование Rprof(filename = "output.txt") insertion_sort(my_numbers) Rprof(NULL)  # Анализ результатов профилирования summaryRprof("output.txt") 

В результате анализа профилирования мы можем увидеть, что операции обмена элементов (numbers[j + 1] <- numbers[j]) во время сортировки занимают значительную часть времени. Здесь можно попробовать использовать более оптимизированные алгоритмы сортировки, такие как быстрая сортировка (quick sort) или сортировка слиянием (merge sort).

Пример 3: Повторяющиеся вызовы функций

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

# Функция для вычисления чисел Фибоначчи с использованием рекурсии fibonacci <- function(n) {   if (n <= 1) {     return(n)   } else {     return(fibonacci(n - 1) + fibonacci(n - 2))   } }  # Запускаем профилирование Rprof(filename = "output.txt") fibonacci(10) Rprof(NULL)  # Анализ результатов профилирования summaryRprof("output.txt") 

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

Помните, что профилирование и оптимизация — это непрерывный процесс, и мы можем постоянно улучшать наш код для достижения наилучших результатов.

Методы оптимизации R-скриптов

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

Использование векторизации и функций из базового пакета R

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

Давайте рассмотрим пример вычисления суммы элементов вектора с использованием векторизации:

# Пример использования векторизации my_vector <- c(1, 2, 3, 4, 5)  # Обычный способ с использованием цикла sum_result <- 0 for (i in 1:length(my_vector)) {   sum_result <- sum_result + my_vector[i] }  # Способ с использованием векторизации sum_result_vectorized <- sum(my_vector)  print(sum_result) print(sum_result_vectorized) 

Обратите внимание, что векторизованный способ с помощью функции sum() является более простым и эффективным.

Оптимизация циклов: сравнение различных методов и подходов

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

Давайте рассмотрим пример с вычислением суммы квадратов чисел с использованием различных подходов:

# Пример оптимизации циклов n <- 1000 numbers <- 1:n  # Обычный цикл sum_squared <- 0 for (i in numbers) {   sum_squared <- sum_squared + i^2 }  # Использование функции sum() и векторизации sum_squared_vectorized <- sum(numbers^2)  print(sum_squared) print(sum_squared_vectorized) 

Заметим, что векторизованный подход с использованием sum(numbers^2) работает намного быстрее, чем обычный цикл.

Пакеты для оптимизации: compiler, Rcpp, data.table и другие

R предлагает разнообразие пакетов, которые могут существенно улучшить производительность кода и обработку данных.

  • compiler — пакет, который позволяет нам компилировать функции в машинный код, что значительно ускоряет их выполнение.

  • Rcpp — мощный инструмент для интеграции кода на C++ в R. Этот пакет позволяет нам создавать высокоэффективные функции, которые будут работать быстрее, чем их аналоги на R.

  • data.table — оптимизированный пакет для работы с таблицами данных. Он предоставляет быструю обработку и суммирование данных, что делает его предпочтительным выбором для работы с большими объемами данных.

Конечно! Давайте подробнее рассмотрим некоторые из популярных пакетов для оптимизации R-скриптов: compiler, Rcpp и data.table.

compiler

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

Давайте рассмотрим простой пример с использованием функции compiler::cmpfun(), которая компилирует функцию в машинный код:

# Пример использования пакета compiler install.packages("compiler") library(compiler)  # Некомпилированная функция my_function <- function(x) {   result <- 0   for (i in 1:x) {     result <- result + i   }   return(result) }  # Компилируем функцию compiled_function <- cmpfun(my_function)  # Проверяем производительность обеих функций x <- 10000 print(system.time(my_function(x))) print(system.time(compiled_function(x))) 

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

Rcpp

Rcpp — это ещё один мощный инструмент для оптимизации кода на R. Он позволяет нам вставлять код на C++ непосредственно в наши R-скрипты, что может существенно ускорить выполнение сложных операций.

Давайте рассмотрим пример вычисления факториала числа с использованием Rcpp:

# Пример использования пакета Rcpp install.packages("Rcpp") library(Rcpp)  # Код на C++ для вычисления факториала cppFunction(' int factorial_cpp(int n) {   if (n == 0 || n == 1) {     return 1;   } else {     return n * factorial_cpp(n - 1);   } }')  # Вызываем функцию на R с использованием C++ кода x <- 10 print(factorial_cpp(x)) 

Заметим, что функция cppFunction() позволяет нам создавать функции на C++, которые затем могут быть вызваны прямо из R. Это может быть особенно полезно для вычислительно интенсивных задач, таких как факториал, где мы можем достичь существенного ускорения по сравнению с чисто R-реализацией.

data.table

data.table — это пакет, предоставляющий оптимизированные структуры данных и функции для работы с таблицами данных. Он предоставляет более эффективные операции по сравнению с базовыми структурами данных R, такими как data.frame, особенно при работе с большими объемами данных.

Давайте рассмотрим пример с использованием data.table для выполнения операции слияния таблиц:

# Пример использования пакета data.table install.packages("data.table") library(data.table)  # Создаем две таблицы table1 <- data.table(id = 1:5, value = c(10, 20, 30, 40, 50)) table2 <- data.table(id = c(2, 4, 6), additional_value = c(100, 200, 300))  # Выполняем операцию слияния таблиц merged_table <- merge(table1, table2, by = "id", all.x = TRUE)  print(merged_table)

Здесь мы используем функцию merge() из data.table для слияния таблиц по общему ключу «id». Этот подход работает быстрее и использует меньше памяти по сравнению с базовым методом слияния merge() для data.frame.

Использование пакетов compiler, Rcpp и data.table может существенно улучшить производительность вашего кода на R, особенно при работе с большими объемами данных и сложными операциями. Они предоставляют нам много возможностей для оптимизации и создания быстрых и эффективных R-скриптов.

Оптимизация работы с памятью и управление переменными

Управление памятью и переменными играет важную роль в оптимизации кода. Мы можем освобождать память после использования объектов, а также оптимизировать использование переменных.

# Пример оптимизации работы с памятью и переменными # Удаляем ненужные объекты после использования x <- 1:10000 y <- x^2 z <- x + y rm(y)  # Используем меньше переменных, чтобы избежать дополнительного копирования x_squared <- x^2 result <- x + x_squared  print(result)

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

Работа с большими данными в R

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

Оценка объема данных: когда и как данные становятся «большими»

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

Давайте рассмотрим пример с созданием большого вектора данных и оценкой его размера:

# Пример оценки объема данных n <- 10^7 big_vector <- 1:n  # Оцениваем размер в байтах object_size <- object.size(big_vector) print(paste("Размер вектора данных:", object_size, "байт"))

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

Стратегии для работы с большими объемами данных на R

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

  • Данные разделения (chunking): Это подход, при котором мы разделяем данные на небольшие части (чанки) и обрабатываем их по очереди. Например, мы можем обрабатывать блоки строк в таблице, а не всю таблицу целиком.

  • Использование баз данных: Перемещение данных из памяти на диск может быть полезным для обработки больших объемов данных. R поддерживает различные пакеты для работы с базами данных, такие как RSQLite, RMySQL, MonetDB, которые позволяют нам выполнять запросы и агрегации непосредственно на данных в базах данных, минимизируя использование оперативной памяти.

  • Пакеты для обработки больших данных: R имеет несколько пакетов, специально разработанных для работы с большими объемами данных, такие как bigmemory, ff, data.table, dplyr backend. Эти пакеты предоставляют оптимизированные структуры данных и алгоритмы для эффективной обработки больших данных в памяти или на диске.

Пакеты для эффективной обработки больших наборов данных (bigmemory, ff, dplyr backend)

Примеры с использованием некоторых пакетов:

Пакет bigmemory:

# Пример использования пакета bigmemory install.packages("bigmemory") library(bigmemory)  # Создаем большую матрицу n <- 10000 big_matrix <- matrix(1:n, ncol = 1000)  # Конвертируем в big.matrix big_matrix <- as.big.matrix(big_matrix)  # Выполняем агрегацию данных aggregated_data <- colsum(big_matrix)  print(aggregated_data) 

Пакет ff:

# Пример использования пакета ff install.packages("ff") library(ff)  # Создаем большой вектор n <- 10^7 big_vector <- ff(1:n)  # Выполняем агрегацию данных sum_result <- sum(big_vector)  print(sum_result) 

Пакет data.table для dplyr:

# Пример использования пакета data.table с dplyr backend install.packages("data.table") install.packages("dtplyr") library(data.table) library(dtplyr)  # Создаем большую таблицу n <- 10^6 big_data <- data.table(id = 1:n, value = rnorm(n))  # Применяем фильтры и агрегации с помощью dplyr-подобного синтаксиса filtered_data <- big_data %>%   filter(value > 0) %>%   group_by(id) %>%   summarize(mean_value = mean(value))  print(filtered_data) 

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

Кейсы из реальной практики

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

Исследование и сравнение производительности алгоритмов на примере реальных данных

Пример 1: Использование метода опорных векторов (SVM) для классификации ирисов

# Пример использования метода опорных векторов (SVM) для классификации ирисов install.packages("e1071") library(e1071)  # Загружаем данные data(iris)  # Разбиваем данные на обучающую и тестовую выборки set.seed(123) train_index <- createDataPartition(iris$Species, p = 0.7, list = FALSE) train_data <- iris[train_index, ] test_data <- iris[-train_index, ]  # Обучаем модель SVM model_svm <- svm(Species ~ ., data = train_data, kernel = "linear")  # Выполняем предсказания pred_svm <- predict(model_svm, newdata = test_data)  # Оцениваем точность accuracy_svm <- sum(pred_svm == test_data$Species) / nrow(test_data)  print(accuracy_svm) 

Пример 2: Использование случайного леса (Random Forest) для классификации ирисов

# Пример использования случайного леса (Random Forest) для классификации ирисов install.packages("randomForest") library(randomForest)  # Загружаем данные data(iris)  # Разбиваем данные на обучающую и тестовую выборки set.seed(123) train_index <- createDataPartition(iris$Species, p = 0.7, list = FALSE) train_data <- iris[train_index, ] test_data <- iris[-train_index, ]  # Обучаем модель случайного леса model_rf <- randomForest(Species ~ ., data = train_data, ntree = 100)  # Выполняем предсказания pred_rf <- predict(model_rf, newdata = test_data)  # Оцениваем точность accuracy_rf <- sum(pred_rf == test_data$Species) / nrow(test_data)  print(accuracy_rf) 

Пример 3: Использование градиентного бустинга (Gradient Boosting) для классификации ирисов

# Пример использования градиентного бустинга (Gradient Boosting) для классификации ирисов install.packages("gbm") library(gbm)  # Загружаем данные data(iris)  # Разбиваем данные на обучающую и тестовую выборки set.seed(123) train_index <- createDataPartition(iris$Species, p = 0.7, list = FALSE) train_data <- iris[train_index, ] test_data <- iris[-train_index, ]  # Обучаем модель градиентного бустинга model_gbm <- gbm(Species ~ ., data = train_data, n.trees = 100, interaction.depth = 3)  # Выполняем предсказания pred_gbm <- predict(model_gbm, newdata = test_data, n.trees = 100)  # Преобразуем предсказания в классы pred_gbm_class <- ifelse(pred_gbm <= 0.5, "setosa", ifelse(pred_gbm <= 1.5, "versicolor", "virginica"))  # Оцениваем точность accuracy_gbm <- sum(pred_gbm_class == test_data$Species) / nrow(test_data)  print(accuracy_gbm) 

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

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

Лучшие практики профилирования и оптимизации R-скриптов

На пути оптимизации R-скриптов важно использовать профилирование для выявления узких мест и затратных операций. Кроме того, следует придерживаться нескольких лучших практик:

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

  • Оптимизировать работу с памятью, освобождая ненужные объекты и минимизируя количество переменных.

  • При необходимости использовать специальные пакеты для работы с большими объемами данных, такие как bigmemory, ff, data.table, которые предоставляют оптимизированные структуры данных и алгоритмы.

  • Тестировать и сравнивать производительность различных решений для выбора наиболее эффективного.

Заключение

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

Всем, кто интересуется использованием R в своей работе с данными хочу порекомендовать бесплатный вебинар, в ходе которого вы познакомитесь с тремя популярными средствами разработки и анализа данных, которые помогут стать более продуктивным и эффективным при работе с R: Rstudio, Jupyter, VSCode. Узнать о вебинаре подробнее можно по этой ссылке.


ссылка на оригинал статьи https://habr.com/ru/companies/otus/articles/750660/