Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
При выполнении нескольких задач в кластере HPC часто многие задачи работают с общим набором данных. Например, программа анализа рисков акций может выполнять большое количество имитаций по набору исторических данных фондового рынка. В этом примере каждый запрос SOA может иметь разные параметры, но все анализы используют одни и те же исторические данные. В HPC общий набор данных называется общими данными.
Общие данные должны передаваться службам, работающим на вычислительных узлах. Так как данные являются статическими, они будут неэффективны для передачи данных в каждом запросе SOA. Хорошее решение — отправить набор данных в кластер и сохранить его в централизованном месте, где у всех служб есть доступ. В этой записи блога мы посмотрим, как это сделать. (Примечание. Вы также можете хранить эти данные в базе данных, к которым имеется доступ к кластеру HPC.)
Мы по-прежнему будем использовать наш пример прайм-факторизации (см. этом руководстве подробные сведения). Чтобы ускорить алгоритм, мы хотим использовать таблицу простых чисел, чтобы мы могли искать таблицу для основных факторов вместо обхода всех чисел. Очевидно, что основная таблица чисел должна быть общими данными среди всех запросов факторизации. Мы выполните следующие действия, чтобы обработать его:
Реализация диспетчера данных
- Отправка общих данных
- Выпуск общих данных
Реализация службы и получение общих данных в службу
Реализация клиента
Тестирование общей службы данных
Мы также обсудим настройку общего хранилища данных.
Ознакомьтесь с примером кода , чтобы выполнить действия, описанные в этой статье.
1. Реализация диспетчера данных
Сначала мы создадим диспетчер данных для обработки жизненного цикла общих данных.
Мы создаем таблицу праймерных чисел в качестве общих данных. Реализация CreatePrimeNumberTable не относится к нашей теме, поэтому мы не будем подробно идти здесь.
//create prime number table of 200000 prime numbers
List<int> PrimeNumberTable = CreatePrimeNumberTable(200000);
Общие данные управляются типом DataClient в HPC. Чтобы создать клиент данных, необходимо подготовить следующие сведения.
const string headnode = "head.contoso.com";
string dataId = "PRIME_NUMBER_TABLE";
Идентификатор данных используется для идентификации каждого клиента данных, поэтому он должен быть уникальным значением.
Создайте клиент данных:
//create DataClient to send data
DataClient dataClient = DataClient.Create(headnode, dataId);
Если клиент данных с тем же идентификатором уже существует, создается исключение. В этом случае, если существующие общие данные по-прежнему необходимы, нам придется изменить идентификатор данных; в противном случае просто удалите его следующим образом:
//delete the data client
DataClient.Delete(headnode, dataId);
Отправьте общие данные в службу, просто вызвав метод WriteAll.
//Send data to service.
//WriteAll() can only be called once on a DataClient object
dataClient.WriteAll<List<int>>(PrimeNumberTable);
Мы используем WriteAll для отправки структурированных данных в кластер. Все сериализуемые данные можно отправлять таким образом.
Кроме того, необработанные данные можно отправлять путем вызова метода WriteRawBytesAll. Например, если мы получаем простые числа непосредственно из файла PrimeNumbers, мы можем отправлять данные следующим образом:
//WriteAllRawBytesAll can be called only once per data client.
dataClient.WriteRawBytesAll(File.ReadAllBytes("PrimeNumbers"));
Однако writeAll или WriteRawBytesAll можно вызывать только один раз для каждого клиента данных. На каждом клиенте данных разрешена только одна операция записи, но можно выполнить несколько операций чтения.
Как правило, мы отправляем общие данные в клиенте и считываем их в службах. В этом сценарии общие данные доступны только для чтения в службы.
К настоящему времени основная таблица номеров была отправлена в кластер HPC, и все запросы на обслуживание могут получить доступ к нему, предоставив правильный идентификатор данных. Данные будут оставаться в кластере до тех пор, пока данные не будут удалены явным образом.
2. Реализация службы и получение общих данных в службу
При реализации нашей службы контракт службы остается неизменным.
Как упоминалось ранее, мы можем получить клиент данных с помощью идентификатора данных и получить общие данные, вызвав метод ReadAll.
List<int> PrimeNumberTable;
using (DataClient dataClient = ServiceContext.GetDataClient("PRIME_NUMBER_TABLE"))
{
PrimeNumberTable = dataClient.ReadAll<List<int>>();
}
Или используйте метод ReadRawBytesAll для отправки необработанных данных.
byte[] PrimeNumberTableRaw;
using(DataClient dataClient = ServiceContext.GetDataClient("PRIME_NUMBER_TABLE"))
{
PrimeNumberTableRaw = dataClient.ReadRawBytesAll();
}
Чтобы избежать чтения общих данных в память каждый раз при создании нового объекта службы, рекомендуется считывать их в статическом конструкторе.
С помощью простой таблицы чисел факторизация может быть реализована быстрее, как это.
public List<int> Factorize(int n)
{
List<int> factors = new List<int>();
//When factors are in PrimeNumberTable
for (int i = 0; i < PrimeNumberTable.Count; )
{
if (n % i == 0)
{
factors.Add(i);
n /= i;
}
else
{
i++;
}
}
//When factors are not in PrimeNumberTable
for (int i = PrimeNumberTable.Max() + 1; i <= n; )
{
if (n % i == 0)
{
factors.Add(i);
n /= i;
}
else
{
i++;
}
}
return factors;
}
3. Реализация клиента
Мы создадим простой клиент для тестирования службы.
//Change headnode here
const string headnode = "head.contoso.com";
const string serviceName = "PrimeFactorizationWithCommonData";
SessionStartInfo info = new SessionStartInfo(headnode, serviceName);
Random random = new Random();
try
{
//create an interactive session
using (Session session = Session.CreateSession(info))
{
Console.WriteLine("Session {0} has been created", session.Id);
using (BrokerClient<IPrimeFactorization> client = new BrokerClient<IPrimeFactorization>(session))
{
//send request
int num = random.Next(1, Int32.MaxValue);
FactorizeRequest request = new FactorizeRequest(num);
client.SendRequest<FactorizeRequest>(request, num);
client.EndRequests();
//get response
foreach (BrokerResponse<FactorizeResponse> response in client.GetResponses<FactorizeResponse>())
{
int number = response.GetUserData<int>();
int[] factors = response.Result.FactorizeResult;
Console.WriteLine("{0} = {1}", number,
string.Join<int>(" * ", factors));
}
}
session.Close();
Console.WriteLine("done");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
4. Тестирование общей службы данных
Запустите DataManager.exe для отправки общих данных в кластер.
Теперь запустите Client.exe. Мы видим, что служба работает правильно.
5. Настройка общего хранилища данных
Общие данные хранятся в общей папке, к которой можно получить доступ ко всем вычислительным узлам. Этот путь определяется переменной среды HPC_RUNTIMESHARE. Его можно проверить с помощью средства командной строки пакета HPC cluscfg.
Введите прослушиватель cluscfg в командном окне, и вы увидите список переменных среды. По умолчанию значение HPC_RUNTIMESHARE равно \\COMPUTE_NAME\Runtime$. Это общий путь, сопоставленный с C:\HPCRuntimeDirectory на головном узле.
Если вам нужно, можно изменить значение HPC_RUNTIMESHARE с помощью cluscfg setenvs. Убедитесь, что все вычислительные узлы имеют доступ на чтение к этому пути, и клиенты должны иметь доступ на запись, если они должны отправлять общие данные.
Дополнительные сведения см. в разделе Настройка общего ресурса данных среды выполнения.