Практическое руководство. Создание агентов, использующих определенные политики планировщика
Мақала
Агент — это компонент приложения, который работает асинхронно с другими компонентами для решения более крупных вычислительных задач. Агент обычно имеет заданный жизненный цикл и сохраняет состояние.
Каждый агент может иметь уникальные требования к приложению. Например, агент, который обеспечивает взаимодействие с пользователем (получение входных данных или отображение выходных данных), может потребовать более высокого приоритета доступа к вычислительным ресурсам. Политики планировщика позволяют управлять стратегией, которую планировщик использует при управлении задачами. В этом разделе показано, как создавать агенты, использующие определенные политики планировщика.
В этом разделе используются функции из библиотеки асинхронных агентов, таких как агенты, блоки сообщений и функции передачи сообщений для выполнения работы. Дополнительные сведения о библиотеке асинхронных агентов см. в разделе "Библиотека асинхронных агентов".
Пример
В следующем примере определяются два класса, производных от параллелизма::agent: permutor и printer. Класс permutor вычисляет все перемутации заданной входной строки. Класс printer выводит сообщения о ходе выполнения в консоль. Класс permutor выполняет операцию с интенсивным вычислением, которая может использовать все доступные вычислительные ресурсы. Чтобы быть полезным, printer класс должен своевременно распечатать каждое сообщение о ходе выполнения.
Чтобы обеспечить printer справедливый доступ к вычислительным ресурсам класса, в этом примере используются шаги, описанные в разделе "Практическое руководство. Управление экземпляром планировщика для создания экземпляра планировщика, имеющего настраиваемую политику". Пользовательская политика указывает приоритет потока, который должен быть самым высоким классом приоритета.
Чтобы проиллюстрировать преимущества использования планировщика с пользовательской политикой, в этом примере выполняется общая задача в два раза. В примере сначала используется планировщик по умолчанию для планирования обоих задач. Затем в примере используется планировщик по умолчанию для планирования permutor объекта и планировщика, имеющего настраиваемую политику для планирования printer объекта.
C++
// permute-strings.cpp// compile with: /EHsc#include<windows.h>#include<ppl.h>#include<agents.h>#include<iostream>#include<sstream>usingnamespace concurrency;
usingnamespacestd;
// Computes all permutations of a given input string.classpermutor :public agent
{
public:
explicitpermutor(ISource<wstring>& source,
ITarget<unsignedint>& progress)
: _source(source)
, _progress(progress){
}
explicitpermutor(ISource<wstring>& source,
ITarget<unsignedint>& progress,
Scheduler& scheduler)
: agent(scheduler)
, _source(source)
, _progress(progress){
}
explicitpermutor(ISource<wstring>& source,
ITarget<unsignedint>& progress,
ScheduleGroup& group)
: agent(group)
, _source(source)
, _progress(progress){
}
protected:
// Performs the work of the agent.voidrun(){
// Read the source string from the buffer.
wstring s = receive(_source);
// Compute all permutations.
permute(s);
// Set the status of the agent to agent_done.
done();
}
// Computes the factorial of the given value.unsignedintfactorial(unsignedint n){
if (n == 0)
return0;
if (n == 1)
return1;
return n * factorial(n - 1);
}
// Computes the nth permutation of the given wstring.wstring permutation(int n, const wstring& s){
wstring t(s);
size_t len = t.length();
for (unsignedint i = 2; i < len; ++i)
{
swap(t[n % i], t[i]);
n = n / i;
}
return t;
}
// Computes all permutations of the given string.voidpermute(const wstring& s){
// The factorial gives us the number of permutations.unsignedint permutation_count = factorial(s.length());
// The number of computed permutations.
LONG count = 0L;
// Tracks the previous percentage so that we only send the percentage// when it changes.unsignedint previous_percent = 0u;
// Send initial progress message.
send(_progress, previous_percent);
// Compute all permutations in parallel.
parallel_for (0u, permutation_count, [&](unsignedint i) {
// Compute the permutation.
permutation(i, s);
// Send the updated status to the progress reader.unsignedint percent = 100 * InterlockedIncrement(&count) / permutation_count;
if (percent > previous_percent)
{
send(_progress, percent);
previous_percent = percent;
}
});
// Send final progress message.
send(_progress, 100u);
}
private:
// The buffer that contains the source string to permute.
ISource<wstring>& _source;
// The buffer to write progress status to.
ITarget<unsignedint>& _progress;
};
// Prints progress messages to the console.classprinter :public agent
{
public:
explicitprinter(ISource<wstring>& source,
ISource<unsignedint>& progress)
: _source(source)
, _progress(progress){
}
explicitprinter(ISource<wstring>& source,
ISource<unsignedint>& progress, Scheduler& scheduler)
: agent(scheduler)
, _source(source)
, _progress(progress){
}
explicitprinter(ISource<wstring>& source,
ISource<unsignedint>& progress, ScheduleGroup& group)
: agent(group)
, _source(source)
, _progress(progress){
}
protected:
// Performs the work of the agent.voidrun(){
// Read the source string from the buffer and print a message.
wstringstream ss;
ss << L"Computing all permutations of '" << receive(_source) << L"'..." << endl;
wcout << ss.str();
// Print each progress message.unsignedint previous_progress = 0u;
while (true)
{
unsignedint progress = receive(_progress);
if (progress > previous_progress || progress == 0u)
{
wstringstream ss;
ss << L'\r' << progress << L"% complete...";
wcout << ss.str();
previous_progress = progress;
}
if (progress == 100)
break;
}
wcout << endl;
// Set the status of the agent to agent_done.
done();
}
private:
// The buffer that contains the source string to permute.
ISource<wstring>& _source;
// The buffer that contains progress status.
ISource<unsignedint>& _progress;
};
// Computes all permutations of the given string. voidpermute_string(const wstring& source,
Scheduler& permutor_scheduler, Scheduler& printer_scheduler){
// Message buffer that contains the source string.// The permutor and printer agents both read from this buffer.
single_assignment<wstring> source_string;
// Message buffer that contains the progress status.// The permutor agent writes to this buffer and the printer agent reads// from this buffer.
unbounded_buffer<unsignedint> progress;
// Create the agents with the appropriate schedulers.permutor agent1(source_string, progress, permutor_scheduler);
printer agent2(source_string, progress, printer_scheduler);
// Start the agents.
agent1.start();
agent2.start();
// Write the source string to the message buffer. This will unblock the agents.
send(source_string, source);
// Wait for both agents to finish.
agent::wait(&agent1);
agent::wait(&agent2);
}
intwmain(){
const wstring source(L"Grapefruit");
// Compute all permutations on the default scheduler.
Scheduler* default_scheduler = CurrentScheduler::Get();
wcout << L"With default scheduler: " << endl;
permute_string(source, *default_scheduler, *default_scheduler);
wcout << endl;
// Compute all permutations again. This time, provide a scheduler that// has higher context priority to the printer agent.SchedulerPolicy printer_policy(1, ContextPriority, THREAD_PRIORITY_HIGHEST);
Scheduler* printer_scheduler = Scheduler::Create(printer_policy);
// Register to be notified when the scheduler shuts down.
HANDLE hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
printer_scheduler->RegisterShutdownEvent(hShutdownEvent);
wcout << L"With higher context priority: " << endl;
permute_string(source, *default_scheduler, *printer_scheduler);
wcout << endl;
// Release the printer scheduler.
printer_scheduler->Release();
// Wait for the scheduler to shut down and destroy itself.
WaitForSingleObject(hShutdownEvent, INFINITE);
// Close the event handle.
CloseHandle(hShutdownEvent);
}
В этом примере формируются следующие данные:
Output
With default scheduler:
Computing all permutations of 'Grapefruit'...
100% complete...
With higher context priority:
Computing all permutations of 'Grapefruit'...
100% complete...
Хотя оба набора задач создают один и тот же результат, версия, которая использует пользовательскую политику, позволяет printer объекту выполняться с повышенным приоритетом, чтобы он выполнялся более быстро.
Компиляция кода
Скопируйте пример кода и вставьте его в проект Visual Studio или вставьте его в файл с именем permute-strings.cpp , а затем выполните следующую команду в окне командной строки Visual Studio.
Azure HPC — это специально разработанная облачная возможность для рабочей нагрузки HPC и ИИ, использующая современные отраслевые процессоры и обмен данными по сети InfiniBand для обеспечения максимальной производительности, масштабируемости и ценности приложений. Azure HPC позволяет реализовывать инновации, повышать продуктивность и развивать гибкость бизнеса за счет высокодоступного набора технологий HPC и ИИ с возможностью их динамического распределения в соответствии с изменением коммерческих и техническ