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

Эта история была первоначально опубликована в HackerNoon.

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

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

Кстати, я куратор CodesMyth, онлайн-платформы для упрощения кода и разрушения мифов.

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

Наша ситуация

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

Когда абонент одного поставщика услуг, скажем, A звонит другому абоненту какого-либо другого поставщика услуг, скажем, B, значение LRN сбрасывается в CDR.

Согласно Google, номер маршрутизации местоположения (LRN) представляет собой 10-значный номер в базе данных, называемой точкой управления услугами (SCP), который идентифицирует порт переключения для местной телефонной станции.

По сути, LRN — это уникальное значение, которое сопоставляется с кодом города и поставщиком услуг вызывающего абонента.

ВХОДЫ —

а) корпус, скажем, C1 [формат XML], состоящий из почти 8 миллионов записей, распределенных по нескольким файлам, находящимся в нескольких каталогах. Каждая запись состоит примерно из 30 записей, некоторые из которых включают Номер вызывающего абонента, Номер вызываемого абонента, Код исходящей зоны, Код конечной зоны, LRN, объект и т. д.

b) Другой корпус, скажем, C2, состоящий из 85 миллионов записей CDR, распределенных по нескольким файлам в нескольких папках. Это был Корпус, который нужно было исправить.

НАША ЗАДАЧА —

Нам нужно было подставить значение поля [LRN] из корпуса C1 в корпус C2 на основании вызываемого номера корпуса C2.

Короче говоря, все, что мы хотели сделать, это извлечь параметр «Вызываемый номер» из каждой отдельной записи CDR корпуса C2, а затем найти параметр «LRN» внутри всего корпуса C1 на основе параметра «Вызываемый номер» корпуса C2 и, наконец, замените параметр LRN внутри C2 для этой конкретной CDR. Этот процесс должен был быть выполнен почти для 85 миллионов записей.

ПРЕДЫДУЩИЙ СЦЕНАРИЙ —

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

1 — Извлечь вызываемый номер из C2.

2 — Один за другим открывайте файлы из подкаталогов C1 и затем ищите нужный LRN для соответствующего вызываемого номера. [8 миллионов записей]

3 — Получив его, замените LRN в CDR C1 на соответствующий вызываемый номер.

4 — Повторите это для 85 миллионов записей.

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

КАК МЫ ЭТО ОПТИМИЗИРОВАЛИ?

После запуска исходного сценария в течение одного дня он обработал почти 0,5 ГБ CDR, а общий объем CDR составил 9 ГБ.
Один простой математический расчет говорит нам, что это займет около 18–20 дней при условии бесперебойной работы, что невозможно при нехватке ресурсов в большинстве организаций.

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

Шаг 1:

Иногда то, что вы ищете, уже есть.
– Арета Франклин

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

Вот что мы сделали —

  • Каждый раз, когда мы читали запись в файле в Корпусе C1, мы искали теги XML «Called_Number» и «LRN», затем извлекали их и выводили вывод в виде файл в следующем формате, разделенном точкой с запятой («;»). Мы можем рассматривать («;») как разделитель между полями.

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

Вызываемый_номер; LRN

  • Мы сделали то же самое со всеми другими записями и в итоге создали выходной файл почти с 8 миллионами строк, так как было 8 миллионов записей. Допустим, этот файл (A).
  • "A" содержит много повторяющихся символов, что и ожидалось, например —
    1– между цифрами и в конце цифр были лишние символы.
    2- Для некоторых вызываемых номеров не было значений LRN.
    3- Размер номера вызывающего абонента не был одинаковым, который должен был быть 10. Они были как мусорные значения, напечатанные в поле Called Number.
    4- В файлах были повторяющиеся значения.
  • Чтобы удалить их, мы сделали следующие проверки при анализе корпуса C1 и создании выходного файла («A»).
    *Строка «Called_number» должна быть преобразовано в целое число с помощью функции «atoi()» в C. В случае неудачи мы не будем записывать вывод в выходной файл («A»). (Обработка случая 1)
    *Длина строки «Called_number» должна быть 10. (Обработка случая 3)
  • Создав выходной файл («A»), мы отредактировали его в vim и обработали (Случай 2 и Случай 4) с помощью двух внутренних команд vim:

:g/;$/d [Удаление всех строк, заканчивающихся на «;»]
:sort u [Для сортировки по возрастанию и удаления повторяющиеся строки]

Удаление повторяющихся строк из файла часто легко выполняется в текстовом редакторе. Например, атом.

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

Этот выходной файл («A») был нашим последним файлом, который будет использоваться из корпуса C1 из 8 миллионов записей. Общее количество записей, которые остались в нем после должной обработки, составило всего 2,2 миллиона записей.

Из колоссальных 8 миллионов записей (каждая запись состоит из 30 записей) мы сузили до 2,2 миллиона записей.

Размер корпуса = (8000000*30)/2200000 = 109 раз.

что важно, если мы подумаем об этом после анализа доступных нам ресурсов.

Следующим шагом была оптимизация обработки CDR Corpus C2 —

Как и в случае с CDR, мы искали определенный ключ во всем файле («A») линейно [используя линейный поиск]. Поскольку наш вновь сгенерированный файл из корпуса C1 («A») уже был отсортирован, мы решили реализовать бинарный поиск.

Следовательно, преобразование сложности из O(N) в O(logN).

CDR из Корпуса C2 были распределены по нескольким каталогам, каждый для разных кодов города (около 252 каталогов). Раньше, когда мы обрабатывали CDR, мы переходили к следующему каталогу, только если был обработан предыдущий каталог. [Обработка одного каталога за раз].

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

Поэтому мы реализовали многопоточность на многоядерном процессоре. Блейд-серверы, на которых исполнялись скрипты, состояли из 80 ядер.Следовательно, при параллельной реализации, после реализации многопоточности через оболочку, мы смогли выполнять CDR из 80 каталогов одновременно. Таким образом, наше общее время сократилось в 80 раз.

Мы только что создали простой сценарий оболочки, который делал всего лишь:

./script_what_process_cdr [Имя каталога] [Имя выходного каталога] &

Следующая проблема заключалась в том, что хотя время обработки катастрофически сократилось, каждый сценарий для каждого каталога создавался с новым идентификатором процесса и не имел никакой корреляции с идентификатором процесса, работающего одновременно с другим потоком. Следовательно, файл ("A") из Корпуса C1 загружался в память почти 242 раза, что было пустой тратой ресурсов.

Итак, вместо того, чтобы применять многопоточность через оболочку, мы решили реализовать многопоточность на языке C, на котором изначально был написан наш скрипт.

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

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

Кричащее время выполнения, рассчитанное из времени, когда Функция была

$ time process_cdr.sh ‹Каталог пути, в котором присутствовали CDR’ы›
$ Выполнение завершено за 4 минуты 27 секунд.

Из колоссальных 18 дней обработки наша обработка данных была завершена всего за 4 минуты 27 секунд.

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

Удачного кодирования и больше мощности вам.