Введение

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

Потоковые данные

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

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

  • A выделяет буфер размера M
  • A считывает M байтов данных из своего источника и загружает их в буфер . Затем A обновляет указатель файла
  • A отправляет данные через TCP-канал в B
  • B считывает данные в буфер размера N
  • Как только буфер B заполнится, данные будут записаны в файл в файловой системе, и B обновит свой файловый указатель.
  • Этот процесс повторяется до тех пор, пока не будет передан полный файл.

Факторы, влияющие на скорость передачи файлов при потоковой передаче данных

  • Скорость передачи данных в буфер А — скорость, с которой данные считываются с носителя данных А в его буфер.
  • Размер буфера M из A — чем больше данных загружается в M, тем быстрее будет передача данных. Однако это также зависит от скорости передачи TCP и размера окна (обсуждается ниже). Нет причин загружать в M больше данных, чем TCP может принять за один кадр.
  • Скорость передачи TCP — скорость, с которой данные передаются по каналу TCP. На эту скорость влияют два фактора: a.) Скорость передачи данных на физическом сетевом уровне (радиоволны, оптоволоконный кабель и т. д.) b.) Размер окна TCP, установленный между A и B. Этот размер окна устанавливается во время TCP Рукопожатие. Чем больше размер окна, тем больше данных передается в каждом кадре TCP.
  • Размер буфера N из B. Чем больше размер N, тем больше данных может быть буферизовано с сетевого уровня в память, прежде чем они будут сохранены на носителе данных B. Это означает, что нет. операций записи, происходящих на носителе данных, обратно пропорциональна размеру буфера N. Размер буфера N для B не обязательно должен быть намного больше, чем объем данных, передаваемых по каналу TCP в любой момент времени, если запись на носитель B уровень хранения является асинхронным или если он быстрее, чем скорость передачи канала TCP.

Оптимизация приемного буфера файлового хранилища

В этом документе основное внимание уделяется оптимизации размера буфера приема сервера хранения файлов, который аналогичен буферу B в приведенном выше примере. Так как в нашем случае B будет сервером Хранилища Файлов, который одновременно обслуживает несколько клиентов, важно оптимальное использование буферной памяти на сервере Хранилища Файлов. Обычно на потоковом сервере B размер буфера N является константой, которая определяется при установлении потокового соединения. Направление, которое мы собираемся выбрать, заключается в том, чтобы выделить размер буфера, эквивалентный тому, что требуется для соответствующего соединения, которое он обслуживает. Для соединений с высокой скоростью передачи данных требуется больше буферной памяти, а для соединений с низкой скоростью передачи данных требуется меньше буферной памяти. Рациональность этой идеи заключается в том, что для заполнения буферной памяти более медленным соединениям требуется больше времени, чем для более быстрого соединения. Это означает, что если мы выделяем одну и ту же постоянную буферную память как для медленных, так и для быстрых соединений, более медленные передачи данных будут занимать больше времени для заполнения буфера, а более быстрые соединения будут часто заполнять буферы. Для сервера хранения файлов, поскольку он одновременно обрабатывает несколько клиентов, выделение большего объема буферной памяти для более медленного соединения может привести к тому, что для нового соединения не потребуется доступной памяти.

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

Шаги, связанные с оптимизацией приемного буфера

  1. Сбор данных — с сервера хранилища файлов для разных клиентов, которые он обслуживает, собирайте данные, относящиеся к их скорости передачи, в разное время в ходе соединения. Чем больше частота дискретизации данных, тем лучше. Этот процесс включает в себя сбор данных характеристик передачи от разных типов клиентов, которые подключаются к серверу из разных типов сетей, таких как WIFI, LTE и т. д.…
  2. Обучение модели машинного обучения. Этот шаг включает в себя обучение модели машинного обучения, которая при предоставлении скорости передачи данных предсказывает размер окна буфера, необходимый для обработки соединения. Для нашей модели будет использоваться нейронная сеть, которая при предоставлении последних 4 скоростей передачи данных предсказывает необходимый размер окна.
  3. Система управления ресурсами на сервере хранения файлов — этот шаг включает в себя настройку системы управления ресурсами на сервере хранения файлов, целью которой является периодическая настройка окна буфера сервера в зависимости от скорости передачи данных. Для этого примера, приведенного в документе здесь, этот модуль оценивает скорость передачи данных каждые 2 секунды и, когда доступны 4 таких выборки (т. е. каждые 8 ​​секунд), проверяет нашу модель машинного обучения, чтобы получить соответствующий размер окна буфера. Затем окно буфера корректируется соответствующим образом.

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

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

Как видно из таблицы выше, каждая обучающая запись состоит из 4 выборок скорости передачи данных — T1, T2, T3 и T4, каждая из которых собирается через фиксированные интервалы времени. Результатом является Размер окна, который показывает, сколько буферной памяти будет выделено для скоростей передачи, указанных от T1 до T4.

Как видно выше, в приведенной выше выборке данных отражены различные характеристики скорости передачи.

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

Нейронная сеть, используемая для обучения модели

Используемая здесь нейронная сеть примет 4 входа, соответствующих 4 скоростям передачи данных от T1 до T4, и предскажет 1 значение, соответствующее размеру окна. Поскольку из входных данных не нужно выделять много сложных характеристик, нейронная сеть использует только 1 скрытый слой с 3 узлами. Полная архитектура используемой нейронной сети показана ниже:

Обучение нейронной сети

В этом разделе подробно описывается, как нейронная сеть обучается на доступных демонстрационных данных. Модель обучается с использованием библиотеки Pytorch на Python.

Импорт необходимых библиотек

import torch
from torch import nn
from torch import optim
from torch.functional import F
import numpy as np
import pandas as pd
from torch.utils.data import Dataset
from matplotlib import pyplot as plt

Загрузка данных обучения. Показанные выше данные обучения сохраняются в файле CSV с именем TrainingData.csv. В разделе ниже используется загрузчик данных Pytorch и загрузка данных в пакеты по 5. Следовательно, входными данными для обучения является тензор 5x4, представляющий 5 выборок, каждая из которых представляет скорости передачи данных от T1 до T4. Результатом обучения является тензор 5x1, каждый из которых представляет размер окна для соответствующих входных скоростей данных. Обратите внимание, что загрузчик данных каждый раз перемешивает данные, чтобы избежать переобучения во время обучения сети.

Тестовые данные, используемые для оценки сети, сохраняются в файле TestData.csv. Он также имеет структуру, аналогичную TrainingData.csv. Он также загружается партиями по 5 штук.

class WindowSizingDataset(Dataset):
    def __init__(self, dataset:pd.DataFrame):
        self.dataset = dataset
    
    def __len__(self):
        return self.dataset.shape[0]
    
    def __getitem__(self, index):
        input_data = torch.Tensor(self.dataset.iloc[index]['T1':'T4'].values.astype('float32'))
        output_data = self.dataset.iloc[index]['Window Size'].astype('float32')
        return input_data, output_data
    
train_dataset = WindowSizingDataset(pd.read_csv("./TrainingData.csv"))
test_dataset = WindowSizingDataset(pd.read_csv("./TestData.csv"))

train_data_loader = torch.utils.data.DataLoader(train_dataset, batch_size=5, shuffle=True)
test_data_loader = torch.utils.data.DataLoader(test_dataset, batch_size=5, shuffle=True)

Определение сетиBufferPredictionNetwork, как показано на диаграмме выше, имеет следующие характеристики:

  • Принимает 4 входа, соответствующих 4 выборкам скорости передачи данных от T1 до T4.
  • Использует один скрытый слой с 3 узлами с функцией активации ReLU.
  • Выходной слой с 1 значением, соответствующим результирующему размеру окна.
device = torch.device("cpu")    
class BufferPredictionNetwork(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.fc1 = nn.Linear(in_features=4, out_features=3, device=device)
        self.fc2 = nn.Linear(in_features=3, out_features=1, device=device)
    
    def forward(self, input_wt):
        x = F.relu(self.fc1(input_wt))
        x = self.fc2(x)
        return x.cpu()

Параметры обучения

  • Функция потерь — Функция потерь среднего квадрата ошибки (MSE), поскольку это проблема регрессии
  • Optimized — оптимизатор Адама для Gradient Descent
  • Скорость обучения — 0,0003
net = BufferPredictionNetwork()
optimizer = optim.Adam(params=net.parameters(), lr=0.0003)
criterion = nn.MSELoss()

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

LOG_INTERVAL = 1000
net.train()
for epoch in range(0,7001):
    for training_input, target_output in train_data_loader:
        optimizer.zero_grad()
        prediction = net(training_input.to(device=device))
        loss = criterion(prediction.squeeze().to(device), target_output.to(device=device))
        loss.backward()
        optimizer.step()
    if(epoch%LOG_INTERVAL == 0):
        print("Epoch {}; Loss {}".format(epoch,loss.item()))

Для каждого тренировочного набора —

  • optimizer сбрасывает вычисленные градиенты
  • training_input затем подается через сеть
  • prediction, который является выходом сети, и ожидаемые выходные значения target_output затем передаются через функцию потерь criterion для определения loss
  • Затем loss используется для определения скорости коррекции, необходимой для разных весов в сети.
  • Затем оптимизатор Адама optimizer используется для коррекции весов с использованием метода градиентного спуска.

В каждую эпоху можно видеть, что потери уменьшаются. loss печатается каждые 1000 эпох.

Сохранение сети.После значительного снижения потерь обученная сеть сохраняется в файл window_size_model.pt.

torch.save(net.state_dict(), './window_size_model.pt')

Оценка модели

Сначала будет загружена обученная модель, сохраненная в window_size_model.pt. Затем с использованием тестовых данных, загруженных в test_data_loader, модель оценивается.

net.load_state_dict(torch.load("./window_size_model.pt"))
in_data, out_data = iter(test_data_loader).next()

out = net(in_data.to(device))

plt.plot(range(0,len(out_data)), out.detach().numpy().squeeze(), label="Predicted Data")
plt.plot(range(0,len(out_data)), out_data.numpy(),  label='Actual Data')
plt.legend()

На графике показано для одной партии тестовых данных, содержащих 5 входных выборок, как обученная сеть предсказывает значения размера буфера. Фактические значения окна буфера в сравнении с предсказанными значениями окна буфера указаны на графике. Как видно из графика ниже, Predicated Data следует шаблону Actual Data.

Микросервис прогнозирования размера окна

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

Модель, обученная выше, заключена в класс — AdaptiveWindowSizing, и микросервис на основе flask использует его для прогнозирования следующего размера окна, когда в качестве входных данных предоставляются скорости передачи данных T1 — T4. Код для этого можно найти здесь — https://github.com/aronsajan/AdaptiveWindowSizing/tree/main/WindowPredictionService

Система управления ресурсами на файловом сервере хранения

Система управления ресурсами на сервере хранения файлов периодически оценивает скорость передачи данных. После сбора 4 таких точек данных (T1, T2, T3 и T4) он проверяет размер окна буфера, который будет выделен для текущей потоковой передачи файлов, с помощью службы прогнозирования размера окна. Затем будет установлен новый размер окна для оставшейся части потоковых данных. Этот процесс будет повторяться, чтобы адаптироваться к изменяющемуся характеру скорости передачи данных.

Базу кода для службы хранения файлов с системой управления ресурсами можно найти здесь — https://github.com/aronsajan/AdaptiveWindowSizing/tree/main/File%20Storage%20Server/filestorageservice

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

Оценка

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

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

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

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

При отключенной адаптивной буферизации, когда начинается потоковая передача данных, для буферизации данных используется постоянный 1 МБ. Сборка мусора выполняется, когда заполнен 1 МБ. Как вы можете видеть выше, частота GC ниже, чем при включенной адаптивной буферизации. Зигзаги здесь показывают, когда буфер заполняется и опорожняется. Как мы можем сделать вывод выше, зигзаги остаются почти одинаковыми, что указывает на то, что распределение памяти остается почти постоянным, в отличие от расширения и сужения в случае адаптивной буферизации.

Заключение

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

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