게임 개발용 CPUSetsCPUSets for game development

소개Introduction

UWP (유니버설 Windows 플랫폼)는 광범위 한 소비자 전자 장치에서 핵심입니다.The Universal Windows Platform (UWP) is at the core of a wide range of consumer electronic devices. 따라서 게임에서 포함 된 앱으로 서버에서 실행 되는 모든 유형의 응용 프로그램에 대 한 요구 사항을 해결 하기 위해 범용 API가 필요 합니다.As such, it requires a general purpose API to address the needs of all types of applications from games to embedded apps to enterprise software running on servers. API에서 제공 하는 올바른 정보를 활용 하 여 게임이 모든 하드웨어에서 가장 잘 실행 되도록 할 수 있습니다.By leveraging the right information provided by the API, you can ensure your game runs at its best on any hardware.

CPUSets APICPUSets API

CPUSets API는 스레드를 예약 하는 데 사용할 수 있는 CPU 집합을 제어 합니다.The CPUSets API provides control over which CPU sets are available for threads to be scheduled on. 스레드가 예약 되는 위치를 제어 하는 데 사용할 수 있는 두 가지 함수는 다음과 같습니다.Two functions are available to control where threads are scheduled:

  • SetProcessDefaultCpuSets –이 함수는 특정 cpu 집합에 할당 되지 않은 경우 새 스레드가 실행 될 수 있는 cpu 집합을 지정 하는 데 사용할 수 있습니다.SetProcessDefaultCpuSets – This function can be used to specify which CPU sets new threads may run on if they are not assigned to specific CPU sets.
  • Setthreadselectedcpusets –이 함수를 사용 하면 특정 스레드가 실행 될 수 있는 CPU 집합을 제한할 수 있습니다.SetThreadSelectedCpuSets – This function allows you to limit the CPU sets a specific thread may run on.

SetProcessDefaultCpuSets 함수를 사용 하지 않으면 프로세스에 사용할 수 있는 모든 CPU 집합에서 새로 만든 스레드가 예약 될 수 있습니다.If the SetProcessDefaultCpuSets function is never used, newly created threads may be scheduled on any CPU set available to your process. 이 섹션에서는 CPUSets API의 기본 사항에 대해 설명 합니다.This section goes over the basics of the CPUSets API.

GetSystemCpuSetInformationGetSystemCpuSetInformation

정보를 수집 하는 데 사용 되는 첫 번째 API는 GetSystemCpuSetInformation 함수입니다.The first API used for gathering information is the GetSystemCpuSetInformation function. 이 함수는 제목 코드에서 제공 하는 SYSTEM_CPU_SET_INFORMATION 개체의 배열로 정보를 채웁니다.This function populates information in an array of SYSTEM_CPU_SET_INFORMATION objects provided by title code. 대상에 대 한 메모리는 게임 코드에 의해 할당 되어야 하며, 크기는 GetSystemCpuSetInformation 를 호출 하 여 결정 됩니다.The memory for the destination must be allocated by game code, the size of which is determined by calling GetSystemCpuSetInformation itself. 이렇게 하려면 다음 예제에서 보여 주는 것 처럼 GetSystemCpuSetInformation 에 대 한 두 호출이 필요 합니다.This requires two calls to GetSystemCpuSetInformation as demonstrated in the following example.

unsigned long size;
HANDLE curProc = GetCurrentProcess();
GetSystemCpuSetInformation(nullptr, 0, &size, curProc, 0);

std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);

PSYSTEM_CPU_SET_INFORMATION cpuSets = reinterpret_cast<PSYSTEM_CPU_SET_INFORMATION>(buffer.get());
  
GetSystemCpuSetInformation(cpuSets, size, &size, curProc, 0);

반환 된 SYSTEM_CPU_SET_INFORMATION 의 각 인스턴스에는 하나의 고유한 처리 단위에 대 한 정보 (예를 들어, CPU 집합이 라고도 함)가 포함 됩니다.Each instance of SYSTEM_CPU_SET_INFORMATION returned contains information about one unique processing unit, also known as a CPU set. 이는 반드시 고유한 물리적 하드웨어 부분을 나타내는 것은 아닙니다.This does not necessarily mean that it represents a unique physical piece of hardware. 하이퍼스레딩을 활용 하는 Cpu는 단일 물리적 처리 코어에서 실행 되는 여러 논리 코어를 가집니다.CPUs that utilize hyperthreading will have multiple logical cores running on a single physical processing core. 동일한 실제 코어에 있는 서로 다른 논리 코어에서 여러 스레드를 예약 하면 하드웨어 수준 리소스를 최적화 하 여 커널 수준에서 추가 작업을 수행 해야 하는 경우를 들 수 있습니다.Scheduling multiple threads on different logical cores that reside on the same physical core allows hardware-level resource optimization that would otherwise require extra work to be done at the kernel level. 동일한 실제 코어에서 별도의 논리 코어에 예약 된 두 스레드는 CPU 시간을 공유 해야 하지만 동일한 논리 코어로 예약 된 경우 보다 더 효율적으로 실행 됩니다.Two threads scheduled on separate logical cores on the same physical core must share CPU time, but would run more efficiently than if they were scheduled to the same logical core.

SYSTEM_CPU_SET_INFORMATIONSYSTEM_CPU_SET_INFORMATION

GetSystemCpuSetInformation 에서 반환 된이 데이터 구조의 각 인스턴스에 있는 정보에는 스레드가 예약 될 수 있는 고유한 처리 단위에 대 한 정보가 포함 되어 있습니다.The information in each instance of this data structure returned from GetSystemCpuSetInformation contains information about a unique processing unit that threads may be scheduled on. 가능한 대상 장치 범위가 지정 된 경우 SYSTEM_CPU_SET_INFORMATION 데이터 구조의 많은 정보를 게임 개발에 적용할 수 없습니다.Given the possible range of target devices, a lot of the information in the SYSTEM_CPU_SET_INFORMATION data structure may not applicable for game development. 표 1에는 게임 개발에 유용한 데이터 멤버에 대 한 설명이 나와 있습니다.Table 1 provides an explanation of data members that are useful for game development.

표 1. 데이터 멤버는 게임 개발에 유용 합니다.Table 1. Data members useful for game development.

멤버 이름Member name 데이터 형식Data type DescriptionDescription
형식Type CPU_SET_INFORMATION_TYPECPU_SET_INFORMATION_TYPE 구조체의 정보 형식입니다.The type of information in the structure. 이 값이 CpuSetInformation않는 경우 무시 해야 합니다.If the value of this is not CpuSetInformation, it should be ignored.
IdId unsigned longunsigned long 지정 된 CPU 집합의 ID입니다.The ID of the specified CPU set. 이 ID는 Setthreadselectedcpusets와 같은 CPU 집합 함수에 사용 해야 합니다.This is the ID that should be used with CPU set functions such as SetThreadSelectedCpuSets.
그룹Group unsigned shortunsigned short CPU 집합의 "프로세서 그룹"을 지정 합니다.Specifies the “processor group” of the CPU set. 프로세서 그룹을 사용 하면 PC에 논리 코어를 64 개 이상 포함할 수 있으며 시스템이 실행 되는 동안 Cpu의 핫 교체가 허용 됩니다.Processor groups allow a PC to have more than 64 logical cores, and allow for hot swapping of CPUs while the system is running. 둘 이상의 그룹이 있는 서버가 아닌 PC가 표시 되는 경우가 종종 있습니다.It is uncommon to see a PC that is not a server with more than one group. 큰 서버나 서버 팜에서 실행 되는 응용 프로그램을 작성 하지 않는 한 대부분의 소비자 Pc에는 하나의 프로세서 그룹만 있으므로 단일 그룹에서 CPU 집합을 사용 하는 것이 가장 좋습니다.Unless you are writing applications meant to run on large servers or server farms, it is best to use CPU sets in a single group because most consumer PCs will only have one processor group. 이 구조체의 다른 모든 값은 그룹을 기준으로 합니다.All other values in this structure are relative to the Group.
LogicalProcessorIndexLogicalProcessorIndex unsigned charunsigned char CPU 집합의 그룹 상대 인덱스Group relative index of the CPU set
CoreIndexCoreIndex unsigned charunsigned char CPU 집합이 있는 실제 CPU 코어의 그룹 상대 인덱스Group relative index of the physical CPU core where the CPU set is located
LastLevelCacheIndexLastLevelCacheIndex unsigned charunsigned char 이 CPU 집합에 연결 된 마지막 캐시의 그룹 상대 인덱스입니다.Group relative index of the last cache associated with this CPU set. 시스템이 NUMA 노드 (일반적으로 L2 또는 L3 캐시)를 활용 하지 않는 한 가장 느린 캐시입니다.This is the slowest cache unless the system utilizes NUMA nodes, usually the L2 or L3 cache.

다른 데이터 구성원은 소비자 Pc 또는 기타 소비자 장치에서 Cpu를 설명할 가능성이 낮은 정보를 제공 하므로 유용 하지 않을 수 있습니다.The other data members provide information that is unlikely to describe CPUs in consumer PCs or other consumer devices and is unlikely to be useful. 반환 된 데이터에서 제공 하는 정보를 사용 하 여 다양 한 방식으로 스레드를 구성할 수 있습니다.The information provided by the data returned can then be used to organize threads in various ways. 이 백서의 게임 개발에 대 한 고려 사항 섹션에서는이 데이터를 활용 하 여 스레드 할당을 최적화 하는 몇 가지 방법을 설명 합니다.The Considerations for game development section of this white paper details a few ways to leverage this data to optimize thread allocation.

다음은 다양 한 유형의 하드웨어에서 실행 되는 UWP 응용 프로그램에서 수집 되는 정보 유형의 몇 가지 예입니다.The following are some examples of the type of information gathered from UWP applications running on various types of hardware.

표 2. Microsoft Lumia 950에서 실행 되는 UWP 앱에서 반환 되는 정보입니다. 이는 마지막 수준 캐시가 여러 개인 시스템의 예입니다. Lumia 950는 듀얼 코어 ARM Cortex A57 및 쿼드 코어 ARM Cortex A53 Cpu를 포함 하는 Qualcomm 808 Snapdragon 프로세스를 제공 합니다.Table 2. Information returned from a UWP app running on a Microsoft Lumia 950. This is an example of a system that has multiple last level caches. The Lumia 950 features a Qualcomm 808 Snapdragon process that contains a dual core ARM Cortex A57 and quad core ARM Cortex A53 CPUs.

표 2

표 3. 일반 PC에서 실행 되는 UWP 앱에서 반환 되는 정보입니다. 이는 하이퍼 스레딩을 사용 하는 시스템의 예입니다. 각 실제 코어에는 스레드를 예약할 수 있는 두 개의 논리 코어가 있습니다. 이 경우 시스템은 Intel 크 크 CPU E5-2620을 포함 합니다.Table 3. Information returned from a UWP app running on a typical PC. This is an example of a system that uses hyperthreading; each physical core has two logical cores onto which threads can be scheduled. In this case, the system contained an Intel Xenon CPU E5-2620.

표 3

표 4. 쿼드 코어 Microsoft Surface Pro 4에서 실행 되는 UWP 앱에서 반환 된 정보입니다. 이 시스템에는 Intel Core i5-6300 CPU가 있습니다.Table 4. Information returned from a UWP app running on a quad core Microsoft Surface Pro 4. This system had an Intel Core i5-6300 CPU.

표 4

SetThreadSelectedCpuSetsSetThreadSelectedCpuSets

이제 CPU 집합에 대 한 정보를 사용할 수 있으므로 스레드를 구성 하는 데 사용할 수 있습니다.Now that information about the CPU sets is available, it can be used to organize threads. CreateThread 를 사용 하 여 만든 스레드의 핸들은 스레드를 예약할 수 있는 CPU 집합의 id 배열과 함께이 함수에 전달 됩니다.The handle of a thread created with CreateThread is passed to this function along with an array of IDs of the CPU sets that the thread can be scheduled on. 다음 코드에서는 사용법의 한 가지 예를 보여 줍니다.One example of its usage is demonstrated in the following code.

HANDLE audioHandle = CreateThread(nullptr, 0, AudioThread, nullptr, 0, nullptr);
unsigned long cores [] = { cpuSets[0].CpuSet.Id, cpuSets[1].CpuSet.Id };
SetThreadSelectedCpuSets(audioHandle, cores, 2);

이 예제에서 스레드는 오디오 스레드로선언 된 함수를 기반으로 생성 됩니다.In this example, a thread is created based on a function declared as AudioThread. 그러면이 스레드를 두 CPU 집합 중 하나에서 예약할 수 있습니다.This thread is then allowed to be scheduled on one of two CPU sets. CPU 집합의 스레드 소유권이 단독으로 적용 되지 않습니다.Thread ownership of the CPU set is not exclusive. 특정 CPU 집합에 잠기지 않고 생성 된 스레드는 오디오 스레드에서시간을 걸릴 수 있습니다.Threads that are created without being locked to a specific CPU set may take time from the AudioThread. 마찬가지로, 만든 다른 스레드도 나중에 이러한 CPU 집합 중 하나 또는 둘 다로 잠길 수도 있습니다.Likewise, other threads created may also be locked to one or both of these CPU sets at a later time.

SetProcessDefaultCpuSetsSetProcessDefaultCpuSets

Setthreadselectedcpusets 는 반대입니다. SetProcessDefaultCpuSetsThe converse to SetThreadSelectedCpuSets is SetProcessDefaultCpuSets. 스레드를 만들 때 스레드를 특정 CPU 집합으로 잠글 필요가 없습니다.When threads are created, they do not need to be locked into certain CPU sets. 이러한 스레드를 특정 CPU 집합에서 실행 하지 않으려면 (예를 들어 렌더링 스레드나 오디오 스레드에서 사용 하는 경우),이 함수를 사용 하 여 이러한 스레드가 예약 되도록 허용할 코어를 지정할 수 있습니다.If you do not want these threads to run on specific CPU sets (those used by your render thread or audio thread for example), you can use this function to specify which cores these threads are allowed to be scheduled on.

게임 개발에 대 한 고려 사항Considerations for game development

앞서 살펴본 것 처럼 CPUSets API는 스레드를 예약 하는 데 많은 정보와 유연성을 제공 합니다.As we've seen, the CPUSets API provides a lot of information and flexibility when it comes to scheduling threads. 이 데이터에 대 한 사용을 찾으려고 하는 하향식 접근 방법을 사용 하는 대신 데이터를 사용 하 여 일반적인 시나리오를 수용 하는 방법을 찾는 하향식 방법을 사용 하는 것이 더 효과적입니다.Instead of taking the bottom-up approach of trying to find uses for this data, it is more effective to take the top-down approach of finding how the data can be used to accommodate common scenarios.

시간이 중요 한 스레드 및 하이퍼스레딩 작업Working with time critical threads and hyperthreading

이 메서드는 CPU 시간이 비교적 적은 다른 작업자 스레드와 함께 실시간으로 실행 해야 하는 몇 개의 스레드가 게임에 있는 경우에 효과적입니다.This method is effective if your game has a few threads that must run in real time along with other worker threads that require relatively little CPU time. 지속적인 백그라운드 음악과 같은 일부 작업은 최적의 게임 환경을 위해 중단 없이 실행 해야 합니다.Some tasks, like continuous background music, must run without interruption for an optimal gaming experience. 오디오 스레드에 대해 고갈 된 단일 프레임의 경우에도 팝 또는 glitching 발생할 수 있으므로 모든 프레임에 필요한 CPU 시간을 수신 하는 것이 중요 합니다.Even a single frame of starvation for an audio thread may cause popping or glitching, so it is critical that it receives the necessary amount of CPU time every frame.

SetProcessDefaultCpuSets 와 함께 Setthreadselectedcpusets 를 사용 하면 작업자 스레드가 많은 스레드를 중단 없이 유지 하도록 할 수 있습니다.Using SetThreadSelectedCpuSets in conjunction with SetProcessDefaultCpuSets can ensure your heavy threads remain uninterrupted by any worker threads. Setthreadselectedcpusets 를 사용 하 여 많은 스레드를 특정 CPU 집합에 할당할 수 있습니다.SetThreadSelectedCpuSets can be used to assign your heavy threads to specific CPU sets. 그런 다음 SetProcessDefaultCpuSets 를 사용 하 여 생성 된 할당 되지 않은 스레드가 다른 CPU 집합에 배치 되도록 할 수 있습니다.SetProcessDefaultCpuSets can then be used to make sure any unassigned threads created are put on other CPU sets. 하이퍼스레딩을 활용 하는 Cpu의 경우 동일한 물리적 코어에서 논리 코어를 고려 하는 것도 중요 합니다.In the case of CPUs that utilize hyperthreading, it's also important to account for logical cores on the same physical core. 작업자 스레드는 실시간 응답성을 사용 하 여 실행 하려는 스레드와 동일한 실제 코어를 공유 하는 논리 코어에서 실행 되도록 허용 하면 안 됩니다.Worker threads should not be allowed to run on logical cores that share the same physical core as a thread that you want to run with real time responsiveness. 다음 코드에서는 PC가 하이퍼 스레딩을 사용 하는지 여부를 확인 하는 방법을 보여 줍니다.The following code demonstrates how to determine whether a PC uses hyperthreading.

unsigned long retsize = 0;
(void)GetSystemCpuSetInformation( nullptr, 0, &retsize,
    GetCurrentProcess(), 0);
 
std::unique_ptr<uint8_t[]> data( new uint8_t[retsize] );
if ( !GetSystemCpuSetInformation(
    reinterpret_cast<PSYSTEM_CPU_SET_INFORMATION>( data.get() ),
    retsize, &retsize, GetCurrentProcess(), 0) )
{
    // Error!
}
 
std::set<DWORD> cores;
std::vector<DWORD> processors;
uint8_t const * ptr = data.get();
for( DWORD size = 0; size < retsize; ) {
    auto info = reinterpret_cast<const SYSTEM_CPU_SET_INFORMATION*>( ptr );
    if ( info->Type == CpuSetInformation ) {
         processors.push_back( info->CpuSet.Id );
         cores.insert( info->CpuSet.CoreIndex );
    }
    ptr += info->Size;
    size += info->Size;
}
 
bool hyperthreaded = processors.size() != cores.size();

시스템에서 하이퍼스레딩을 활용 하는 경우에는 기본 CPU 집합 집합에 실제 시간 스레드와 동일한 실제 코어의 논리적 코어가 포함 되지 않아야 합니다.If the system utilizes hyperthreading, it is important that the set of default CPU sets does not include any logical cores on the same physical core as any real time threads. 시스템이 하이퍼스레딩을 사용 하지 않는 경우에는 기본 CPU 집합이 오디오 스레드를 실행 하는 CPU와 동일한 코어를 포함 하지 않는지 확인 해야 합니다.If the system is not hyperthreading, it is only necessary to make sure that the default CPU sets do not include the same core as the CPU set running your audio thread.

실제 코어를 기반으로 스레드를 구성 하는 예제는 추가 리소스 섹션에 연결 된 GitHub 리포지토리에서 사용할 수 있는 cpusets 샘플에서 찾을 수 있습니다.An example of organizing threads based on physical cores can be found in the CPUSets sample available on the GitHub repository linked in the Additional resources section.

마지막 수준 캐시를 사용 하 여 캐시 일관성 비용 절감Reducing the cost of cache coherence with last level cache

캐시 일관성은 캐시 된 메모리가 동일한 데이터에 대해 작동 하는 여러 하드웨어 리소스에서 동일 하다는 개념입니다.Cache coherency is the concept that cached memory is the same across multiple hardware resources that act on the same data. 스레드가 서로 다른 코어에서 예약 되었지만 동일한 데이터에 대해 작업을 수행 하는 경우 서로 다른 캐시에서 해당 데이터의 개별 복사본에 대해 작업을 수행할 수 있습니다.If threads are scheduled on different cores, but work on the same data, they may be working on separate copies of that data in different caches. 올바른 결과를 얻기 위해서는 이러한 캐시를 서로 일관 되 게 유지 해야 합니다.In order to get correct results, these caches must be kept coherent with each other. 여러 캐시 간에 일관성을 유지하는 것은 상대적으로 비용이 많이 들지만 모든 다중 코어 시스템이 작동하는 데 필수적입니다.Maintaining coherency between multiple caches is relatively expensive, but necessary for any multi-core system to operate. 또한 클라이언트 코드를 완전히 제어할 수 있습니다. 기본 시스템은 독립적으로 작동 하 여 코어 간의 공유 메모리 리소스에 액세스 하 여 캐시를 최신 상태로 유지 합니다.Additionally, it is completely out of the control of client code; the underlying system works independently to keep caches up to date by accessing shared memory resources between cores.

게임에 특히 많은 양의 데이터를 공유 하는 스레드가 여러 개 있는 경우 마지막 수준 캐시를 공유 하는 CPU 집합에 대해 예약 된 공간을 확보 하 여 캐시 일관성을 최소화할 수 있습니다.If your game has multiple threads that share an especially large amount of data, you can minimize the cost of cache coherency by ensuring that they are scheduled on CPU sets that share a last level cache. 마지막 수준 캐시는 NUMA 노드를 활용 하지 않는 시스템에서 코어에 사용할 수 있는 가장 느린 캐시입니다.The last level cache is the slowest cache available to a core on systems that do not utilize NUMA nodes. 게임 PC에서 NUMA 노드를 활용 하는 것은 매우 드뭅니다.It is extremely rare for a gaming PC to utilize NUMA nodes. 코어가 마지막 수준 캐시를 공유 하지 않는 경우 일관성을 유지 하려면 더 높은 수준에 액세스 하 여 메모리 리소스를 더 느리게 액세스 해야 합니다.If cores do not share a last level cache, maintaining coherency would require accessing higher level, and therefore slower, memory resources. 캐시를 공유 하는 개별 CPU 집합에 대해 두 스레드를 잠그면 지정 된 프레임에서 50% 이상이 필요 하지 않은 경우 별도의 물리적 코어에서 일정을 지정 하는 것 보다 성능이 향상 될 수 있습니다.Locking two threads to separate CPU sets that share a cache and a physical core may provide even better performance than scheduling them on separate physical cores if they do not require more than 50% of the time in any given frame.

이 코드 예제에서는 자주 통신 하는 스레드가 마지막 수준 캐시를 공유할 수 있는지 여부를 확인 하는 방법을 보여 줍니다.This code example shows how to determine whether threads that communicate frequently can share a last level cache.

unsigned long retsize = 0;
(void)GetSystemCpuSetInformation(nullptr, 0, &retsize,
    GetCurrentProcess(), 0);
 
std::unique_ptr<uint8_t[]> data(new uint8_t[retsize]);
if (!GetSystemCpuSetInformation(
    reinterpret_cast<PSYSTEM_CPU_SET_INFORMATION>(data.get()),
    retsize, &retsize, GetCurrentProcess(), 0))
{
    // Error!
}
 
unsigned long count = retsize / sizeof(SYSTEM_CPU_SET_INFORMATION);
bool sharedcache = false;
 
std::map<unsigned char, std::vector<SYSTEM_CPU_SET_INFORMATION>> cachemap;
for (size_t i = 0; i < count; ++i)
{
    auto cpuset = reinterpret_cast<PSYSTEM_CPU_SET_INFORMATION>(data.get())[i];
    if (cpuset.Type == CPU_SET_INFORMATION_TYPE::CpuSetInformation)
    {
        if (cachemap.find(cpuset.CpuSet.LastLevelCacheIndex) == cachemap.end())
        {
            std::pair<unsigned char, std::vector<SYSTEM_CPU_SET_INFORMATION>> newvalue;
            newvalue.first = cpuset.CpuSet.LastLevelCacheIndex;
            newvalue.second.push_back(cpuset);
            cachemap.insert(newvalue);
        }
        else
        {
            sharedcache = true;
            cachemap[cpuset.CpuSet.LastLevelCacheIndex].push_back(cpuset);
        }
    }
}

그림 1에 설명 된 캐시 레이아웃은 시스템에서 볼 수 있는 레이아웃 형식의 예입니다.The cache layout illustrated in Figure 1 is an example of the type of layout you might see from a system. 이 그림은 Microsoft Lumia 950에 있는 캐시의 그림입니다.This figure is an illustration of the caches found in a Microsoft Lumia 950. CPU 256와 CPU 260 간에 발생 하는 스레드 간 통신은 시스템이 L2 캐시를 일관 되 게 유지 해야 하기 때문에 상당한 오버 헤드가 발생 합니다.Inter-thread communication occurring between CPU 256 and CPU 260 would incur significant overhead because it would require the system to keep their L2 caches coherent.

그림 1. Microsoft Lumia 950 장치에 캐시 아키텍처가 있습니다.Figure 1. Cache architecture found on a Microsoft Lumia 950 device.

Lumia 950 캐시

요약Summary

UWP 개발에 사용할 수 있는 CPUSets API는 상당한 양의 정보를 제공 하 고 다중 스레딩 옵션에 대 한 제어를 제공 합니다.The CPUSets API available for UWP development provides a considerable amount of information and control over your multithreading options. Windows 개발을 위한 이전 다중 스레드 Api와 비교할 때 추가 된 복잡성은 몇 가지 학습 곡선이 있지만 궁극적으로 유연 하 게 다양 한 소비자 Pc 및 기타 하드웨어 대상에서 더 나은 성능을 얻을 수 있습니다.The added complexities compared to previous multithreaded APIs for Windows development has some learning curve, but the increased flexibility ultimately allows for better performance across a range of consumer PCs and other hardware targets.

추가 리소스Additional resources