Each Visual C++ version comes with interesting new features and enhancements to simplify more native programming. The Concurrency Runtime is an added framework to simplify parallel programming and helps you write robust, scalable, and responsive parallel applications.The Concurrency Runtime raises the level of abstraction so that you do not have to manage the infrastructure details that are related to concurrency. The Concurrency Runtime also enables you to specify scheduling policies that meet the quality of service demands of your applications.Here’s the architecture of Concurrent Runtime Framework:
And to have more information’s about this framework, here’s a good introduction about it.
In this article we will discuss the Resource Manager layer, and try to discover how it works internally.For that CppDepend is used to analyse CRT Concurrency Runtime code source.
Before reading this article it’s better to take a look at these interesting articles about ResourceManager:
Which classes are used by the ResourceManager to achieve its responsibility?
SELECT TYPES WHERE IsDirectlyUsedBy “Concurrency.details.ResourceManager”
The first remark about this design is that it enforces the:
- High Cohesion: Many classes and structs are used by resource manager, each one has a specific responsability, it makes the design very clear.The single responsibility principle states that a class should have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0-1]. The LCOMHS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. Note that the LCOMHS metric is often considered as more efficient to detect non-cohesive types.
LCOMHS value higher than 1 should be considered alarming.
WARN IF Count > 0 IN SELECT TYPES WHERE LCOMHS > 1 AND NbFields > 10 AND NbMethods >10 AND!IsGlobal ORDER BY LCOMHS DESC
So among classes used by ResourceManager, only UMS is considered as class with a poor cohesion.
- Low Coupling:Many interfaces and proxies are used to isolate ResourceManager from other components. The Scheduler communicate with ResourceManager but there’s no direct link between Two components, so how the scheduler communicate with ResourceManager?
To answer to this question let’s discover classes using ResourceManager:
SELECT TYPES WHERE IsDirectlyUsing “Concurrency.details.ResourceManager”
Only these classes use the ResourceManager, and the Scheduler doesnt know the ResourceManager directly , so how it interact with it? let’s add to dependency graph the scheduler and interfaces used by it.
As shown in the dependency graph the Scheduler communicate with the ResourceManager using the interfaces IResourceManager,ISchedulere and ISchedulerProxy.
Where the ResourceManager is created?
SELECT METHODS WHERE DepthOfCreateA “Concurrency.details.ResourceManager” == 1
Only ResourceManager create an instance, and it’s a singleton so only one ResourceManager exist for the whole process.
Workflow of resource allocation:
Step 1: Get resources data from system
The Resource manager need all physical information’s concerning processors and cores to use them when allocating resources for schedulers. Let’s discover all data manipulated by this component.
SELECT FIELDS WHERE IsDirectlyUsedBy “Concurrency.details.ResourceManager”
These structs represent all data needed by ResourceManager, the basic ones are ProcessorCore and ProcessorNode representing information’s about physical resources.
Other structs contains some scheduler data needed for the allocation algorithm.
The good news is that ResourceManager didnt directly access to fields of other logical classes used by it.
Let’s search where Resourcemanager get basic information’s about physical resources, for that we can search for methods using GetLogicalProcessorInformation.
SELECT METHODS WHERE IsDirectlyUsing “WindowsAPIGlobalMembers.GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,PDWORD)”
Only ResourceManager::InitializeSystemInformation use this method, to get information’s from system, but it’s this method responsible of populating ProcessorCore and ProcessorNode?
For that we can search for methods assigning a fields of ProcessorCore: SELECT METHODS WHEREIsDirectlyWritingField “Concurrency.details.ProcessorCore.m_processorNumber”
So the ResourceManager invokes the DetermineTopology method to populate data into structs.
And here’s a dependency graph showing the methods concerned by getting nodes and cores information’s.
Step2: Scheduler ask for resource allocation
After the ResourceManager get all data needed for allocation algorithm, a scheduler can ask it to allocate ressources.
When the Scheduler is created it ask for a ResourceManager and invokes CreateResourceManager to get the singleton, after that it invokes IResourceManager::RegisterScheduler to register it self with the resource manager, and get a pointer to ISchedulerProxy, and with this interface the Scheduler will interact with the resource manager.
After that the Scheduler is ready to ask for resources allocation by invocking RequestInitialVirtualProcessors.
And as explained before the Scheduler communicate with resource manager using IScheduleProxy interface to enforces low coupling, and the concrete implemenatation of this interface invoke ResourceManager::RequestInitialVirtualprocessors.
Step3: Resources allocation
The resource Manager have to give resources to schedulers when schedulers are created.It will do the allocation depending on the policies and taking into account the other schedulers in the process. Eventually the resources will be provided to the scheduler in need.
The entry point of allocating request is RequestInitialVirtualProcessors, and here’s a dependency graph showing methods used to acheive allocation.
PerformAllocation method implements the allocation logic, it ask for Scheduler policies from SchedulerProxy, and invoke AllocateCores method to perform allocation.
Here’s some characteristics of PerformAllocation and AllocateCores:
Thoses methods are very well documented and the cyclomatic complexity is not high, and it’s a good indicator that the algorithm of initial allocation is not very complicated.
Step4: giving resources to Scheduler
After ResourceManager allocate Resources , the scheduler is notified with resources allocated, for that the resource manager invoke GrantAllocation method.
What resources the ResourceManager have to give to Scheduler?
As described in msdn articles mentioned below related to resource manager, the resource is a virtual processor associated to a specific thread and runing in a specific core.
But as shown in the dependency graph the resource manager dont give VirtualProcessors to SchedulerProxy but an array of SchedulerNodes, the SchedulerProxy create VirtualProcessorRoot classes, VirtualProcessorRoot represent an abstraction for a hardware thread on which a thread proxy can execute.
Who is creating VirtualProcessor?
Before answering to this question let’s search for classes that represent a Virtual Processor, so let’s search for classes inheriting from this one.
So VirtualProcessor could concern a simple thread or an UMS Thread.
User-mode scheduling (UMS) is a light-weight mechanism introduced in Windows7 and Windows server 2008 R2, that applications can use to schedule their own threads. For more information, see User-Mode Scheduling.
Let’s search for methods creating ThreadVirtualProcessor.
So the Scheduler is the responsible of creating VirtualProcessors, and all information’s needed to create it are given by manager.
Step5:Dynamic managing resources
After the initial allocation of resources, the second main responsability of resource manager is to Constantly monitoring utilization of resources by schedulers, and dynamically migrating resources between schedulers.
And as shown in the following graph the resource manager create DynamicRM Thread,This worker thread wakes up at fixed intervals and load balances resources among schedulers.
And here’s the dependency graph of DynamicResourceManager:
Is ResourceManager customizable?
ResourceManager is well designed and is decoupled from other Concurrency Runtime framework by using interfaces, but it’s possible to customize the allocation behavior of resource manager.
For that let’s search for ResourceManager virtual methods:
SELECT METHODS FROM TYPES “Concurrency.details.ResourceManager“ WHERE IsVirtual
Only these methods are virtual and they implements the IResourceManager methods,so it’s not possible to overload the allocation resource manager logic and customize it.