Найти тему
Я, Golang-инженер

#25. Запись строк в ОЗУ и ПЗУ через буфер обмена на Golang. Сравнение скорости записи в оперативную и постоянную память. Объём информации

Оглавление

Это статья об основах программирования на Go. На канале я рассказываю об опыте перехода в IT с нуля, структурирую информацию и делюсь мнением.

Хой! Джедаи и Амазонки!

В первой части статьи - разбор кода записи строки через буфер обмена в файл .txt. Во второй части расскажу о хранении информации в компьютере и объясню, почему согласно СИ, 1 КБ не равен 1024 байт.

Эта информация будет полезна для понимания разницы записи строк напрямую в файл и записи в файл через буфер обмена.

Запись в .txt через буфер

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

package main

import (
"bufio"
"fmt"
"os"
)

func main() {
file, err := os.Create("SomeProject.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()

writer := bufio.NewWriter(file)

writer.WriteString("May the Force be with us")
writer.WriteString("\n")//перенос каретки

writer.WriteRune('S')//запись руны
writer.WriteString("\n")

writer.WriteByte(65)//запись байт - см. таблицу кодировки ASCII
writer.WriteString("\n")

writer.Write([]byte{80, 81, 92})
writer.WriteString("\n")

writer.Flush()
}

В коде мы использовали три пакета: fmt, os, bufio. Что здесь интересного. Есть четыре метода записи в буфер, в коде выше использованы все четыре:

  • WriteString(): записывает строку
  • WriteRune(): записывает один объект типа rune
  • WriteByte(): записывает один байт
  • Write(): записывает срез байтов

bufio.NewWriter() - функция создания потока вывода через буфер обмена.
writer.Flush() - метод сбрасывает информацию в файл и очищает буфер обмена.


Код выше ничего не выведет в терминал, только запишет информацию в лог-файл:

Результат выполнения программы
Результат выполнения программы

Если хотим увидеть результат работы программы в терминале, добавим фрагмент кода после метода writer.Flush():

file, err = os.Open("SomeProject.txt")
if err != nil{
fmt.Println("Не смогли открыть файл,", err)
return
}
defer file.Close()

buffer := make([]byte, 155)
_, err = file.Read(buffer)
if err != nil {
fmt.Println("Не смогли прочитать последовательность байт из файла,", err)
return
}
fmt.Println(string(buffer))

Результат выполнения программы будет следующий:

Вывод содержимого лог-файла в терминал
Вывод содержимого лог-файла в терминал

Что во второй части кода интересного: мы создали переменную, которая задаёт, какую часть информации выводить в терминал: buffer := make([]byte, 155). 155 в данном случае - размер выводимой строки, можно его уменьшить или увеличить. Давайте уменьшим до 28 и посмотрим, что выйдет:

Вывод программы в терминал при 28 байтах
Вывод программы в терминал при 28 байтах

При этом в самом файле информация сохранилась в полном объёме

Результат работы программы при 28 байт: файл и терминал
Результат работы программы при 28 байт: файл и терминал

Ну и к слову, переменные file (в коде фрагмент file, err...) связана со строками кода:

  • defer file.Close()
  • writer := bufio.NewWriter(file)
  • _, err = file.Read(buffer)

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

Такая же ситуация с переменной writer (в коде writer := bufio.NewWriter(file)), она связана со следующими строками кода:

  • writer.WriteString("May the Force be with us")
  • writer.WriteString("\n")
  • writer.WriteRune('S')
  • и т.д.

Переменные file или writer можно назвать как угодно, но логичнее именно так, см. статью "Читаем чистый код".

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

у нас 100 000 строк. Каждую нужно записать в файл.

Какой способ использовать - напрямую в файл, или через буфер? Если в файл напрямую, это 100 000 обращений к файлу. Ну и ладно, сто так сто - можно сказать. В общем, сходу непонятно, что лучше. Разберём теорию.

Хранение данных

В компьютере есть постоянная память (ПЗУ) и переменная/оперативная (ОЗУ, она же RAM). При работе с оперативной памятью, процессор получает информацию из неё намного быстрее, чем из ПЗУ.

ПЗУ

Два основных вида:

  • SSD - "твердотельные диски";
  • HDD - "жёсткие диски".

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

Помимо SSD и HDD, есть SAN - сетевые хранилища данных, комбинация аппаратного и программного обеспечения. В статье я не рассказываю о SAN, просто полезно знать, что есть и такая система хранения информации.

Скорость ПЗУ

Существует линейное чтение/запись данных и запись/чтение блоками. Я с этими терминами пока не разбирался детально, важно знать - что скорость операций с "блочными" данными меньше линейной скорости - в которой чтение или запись происходит с данными больших размеров, расположенных последовательно.

Ниже укрупнённая информация по различным моделям HDD и SDD для линейного чтения/записи, т.е. условий, близких к идеальным.

Скорость SSD примерно в пять раз быстрее HDD:

  • Запись SSD - 460 Мбайт/с, HDD - 96 Мбайт/с;
  • Чтение SSD - 550 Мбайт/с, HDD - 120 Мбайт/с.

Современные модели SSD могут иметь скорость чтения намного большую - до 7 000 Мбайт/с.

Что такое эти 96 или 550 Мбайт/с? Это пропускная способность.

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

SSD можно сравнить с флешкой. По-сути, это флешка и есть, только понавороченнее и пошустрее. HHD по устройству работы ближе всего к граммофону и виниловой пластинке.

От скорости вращения диска в HDD зависит скорость его работы. При вращении 5400 об./мин., HDD скорость считывания данных как правило будет около 100 Мбайт/с, а при 7200 около 150 Мбайт/с. Есть и более шустрые HDD.

Чтобы считать/записать данные в SSD, не нужно физически ничего вращать и перемещать.

ОЗУ

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

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

Тактовая частота важна не только для совместимости с процессором или с материнской платой. Тактовая частота ОЗУ - её ключевая характеристика. Многие приложения получают существенный прирост в производительности с ростом тактовой частоты. Типичный пример - архивация: чем выше частота модуля ОЗУ, тем шустрее процесс архивации/распаковки.

Сейчас поподробнее познакомимся с буфером обмена (далее - БО).

Бу́фер обме́на (англ. clipboard) — промежуточное хранилище данных, предоставляемое ПО и предназначенное для переноса или копирования информации между приложениями или частями одного приложения через операции вырезать, копировать или вставить.

Работая за компьютером, мы можем десятки раз за день использовать БФ и не задумываться о нём.

Суть в чём - когда мы сохраняем информацию в БО, информация попадает в ОЗУ - т.е. скорость записи и чтения из неё огромна. Логичный вопрос - огромная - это сколько?

Ниже информация для моего ноутбука 2012 года выпуска.

Скорость ОЗУ

  • Чтение из памяти - 13401 Мбайт/с;
  • Запись в память - 23876 Мбайт/с
  • Копирование в память - 13261 Мбайт/с
  • Задержка памяти - 44,7 наносекунды

Непонятно почему запись в память почти вдвое выше чтения, обычно происходит наоборот.

ОЗУ 2012 г. вдвое быстрее современных SSD дисков со скоростью чтения до 7000 Мбайт/с.

По частоте ОЗУ можно укрупнённо найти пропускную способность модуля памяти. Для этого нужно частоту модуля ОЗУ в мегагерцах умножить на восемь (бит), и получим ориентировочную скорость в Мбайт/с.

Так, для планки с частотой 1600 МГц, пропускная способность будет равна:

~ 1600 х 8 = 12 800 Мбайт/с

Чтобы узнать тактовую частоту ОЗУ, достаточно в командную строку (для Windows) ввести wmic memorychip get speed

Определение тактовой частоты ОЗУ через CMD
Определение тактовой частоты ОЗУ через CMD

Всё верно, два модуля по 1600 МГц. И по расчёту 12800 очень похоже на данные из ПО Everest - 13401.

Также можно посмотреть, в каком слоте находится модуль ОЗУ: в командную строку написать wmic memorychip get speed,devicelocator

Тактовая частота ОЗУ с информацией о расположении модуля
Тактовая частота ОЗУ с информацией о расположении модуля

Либо для определения частоты ОЗУ можно воспользоваться ПО, например, бесплатной утилитой CPU-Z.

Скриншот ПО для анализа "железа"
Скриншот ПО для анализа "железа"

Частота указана какая-то странная - 802,1 МГц. Не в том плане, что она дробная - она скачет, как нагрузка на процессор в диспетчере задач, например. И через секунду будет 798.5, например.

Почему в командной строке показаны два значения по 1600, а тут ~800 МГц?

Дело в том, что DDR (а у меня ОЗУ DDR) - вид ОЗУ, который относится к виду динамической синхронной памяти с удвоенной скоростью передачи данных. Удвоение скорости выполнено за счёт нового способа считывания команд: распознаёт команды по фронту и спаду тактового сигнала. И хотя работает при 800 МГц, выдаёт эффективность, сопоставимую с 1600 МГц для SDR. SDR был стандартом ОЗУ в прошлом.

CPU-Z выдаёт истинную частоту ОЗУ, а CMD (командная строка) - эффективную частоту ОЗУ.

Что если взять современные модули ОЗУ с тактовой частотой 3200 МГц. В ней по формуле, пропускная способность будет равна 3200 х 8 = 25 600 Мбайт/с. Нужно будет поподробнее протестить это уравнение, думаю здесь есть подводные камни. Т.к. видел тесты, которые показывали скорость чтения под 50 000 Мбайт/с - там должны быть модули ОЗУ с тактовой частотой 6250 МГц.

Ладно, осталось выяснить, 12800 Мбайт/с или 25600 Мбайт/с - это сколько? С секундами всё понятно, а с единицей измерения Мбайт- что это. К тому же ещё встречаются обозначения Мб, МБ, Мбит - разбираемся.

Единицы количества информации

Согласно ГОСТ 8.417-2002 "Единицы величин", где рассказывается об СИ и других единицах измерения, сокращением МБ обозначают 10^6, а не 2^20.

Фрагмент ГОСТ 8.417-2002
Фрагмент ГОСТ 8.417-2002

Краткий вывод: верно обозначать МБ или Мбайт. Мбит - тоже верно, но мы помним: 8 Мбит = 1 МБ = 10^6 байт.

В 2015 году вышел ещё один стандарт - ГОСТ IEC 60027-2-2015 «Обозначения буквенные, применяемые в электротехнике. Часть 2. Электросвязь и электроника». Стандарт закрепляет термин мебибит (мегабинарный):

1 МиБ = 2^20 бит
Выдержка из ГОСТ IEC 60027-2-2015
Выдержка из ГОСТ IEC 60027-2-2015

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

Заявка на подключение интернета 100 Мбит/с
Заявка на подключение интернета 100 Мбит/с

100 Мбит/с - это не 100 мегабайт в секунду, а 12,5 мегабайт в секунду.

Единицы количества информации по ГОСТ представлены в таблице ниже:

Фрагмент из Википедии
Фрагмент из Википедии

Выводы

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

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

  • Пишем сразу в файл;
  • Пишем сначала в буфер (в ОЗУ), потом записываем в содержимое буфера в файл.

Остальное - вариации на тему работы с буфером.

Применение

Так как обращение к файловой системе - операция медленная, выбираем вариант записи так:

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

Скорость записи данных

Вспомним усреднённые значения по скорости записи:

  • В HDD - 96 Мбайт/с;
  • В SSD - 490 Мбайт/с (современные - до 7000 Мбайт/с);
  • В ОЗУ - от 10 000 Мбайт/с.

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

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

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

Объём информации

  • 1 КБ = 1 000 байт; другое написание 1 Кбайт;
  • 1 МБ = 1 000 КБ = 1 000 000 байт; другое написание 1 Мбайт;
  • 1 КиБ = 1 024 байт = 2^10 байт; другое название - килобинарный байт или кибибайт;
  • 1 МиБ = 1 024 х 1024 байт = 2^20 байт; другое название - мегабинарный байт или мебибайт;
  • 1 Мбит = 1 000 000 бит;
  • Мб - некорректный синоним Мбайта или Мбита согласно стандартра ГОСТ 8.417-2002. Однако в стандартах ГОСТ Р МЭК 80000-13—2016, ГОСТ IEC 60027-2-2015, IEEE 1541-2002, IEEE Std 260.1-2004 используется сокращение b для bit. Для однозначности трактовки, следует применять единицы измерения по СИ, т.е. исходя из примеров выше.

-//-//-

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

PS Сообщите, если купили курс по моей ссылке. Этим ребятам иногда нужно напоминать, что курс куплен по рефу, иначе что-то может пойти не так и кэшбэк не начисляют.

Patrick Robert Doyle https://unsplash.com/photos/D3Lum2QFvcI
Patrick Robert Doyle https://unsplash.com/photos/D3Lum2QFvcI

Бро, ты уже здесь? 👉 Подпишись на канал в «Я, Golang-инженер» в Telegram, будем изучать IT вместе 👨‍💻👩‍💻👨‍💻