Skip to content

Commit

Permalink
If the user has < 65 cores and passes in an affinitized range for the…
Browse files Browse the repository at this point in the history
… 0th CPU group, if valid, honor it. (dotnet#68283)

* If the user has < 65 cores and passes in a affinitized range for the 0th CPU group, honor that passed in affinitized range if valid.

* Added the logic to apply the process afinity set for Windows if No CPU Groups are available

* Updated logic to make use of the config affinity mask for cases where there are < 65 cores and the user passed in a config related to the 0th CPU Group

* Include an additional case where if the user passes in the affinity mask with CPU groups enabled, we error out since this case isn't valid

* Incorporated further feedback
  • Loading branch information
mrsharm authored Apr 22, 2022
1 parent 0d889f5 commit 0ae6b95
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 35 deletions.
4 changes: 2 additions & 2 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43876,12 +43876,12 @@ HRESULT GCHeap::Initialize()
AffinitySet config_affinity_set;
GCConfigStringHolder cpu_index_ranges_holder(GCConfig::GetGCHeapAffinitizeRanges());

if (!ParseGCHeapAffinitizeRanges(cpu_index_ranges_holder.Get(), &config_affinity_set))
uintptr_t config_affinity_mask = static_cast<uintptr_t>(GCConfig::GetGCHeapAffinitizeMask());
if (!ParseGCHeapAffinitizeRanges(cpu_index_ranges_holder.Get(), &config_affinity_set, config_affinity_mask))
{
return CLR_E_GC_BAD_AFFINITY_CONFIG_FORMAT;
}

uintptr_t config_affinity_mask = static_cast<uintptr_t>(GCConfig::GetGCHeapAffinitizeMask());
const AffinitySet* process_affinity_set = GCToOSInterface::SetGCThreadsAffinitySet(config_affinity_mask, &config_affinity_set);

if (process_affinity_set->IsEmpty())
Expand Down
83 changes: 53 additions & 30 deletions src/coreclr/gc/gcconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,47 +81,70 @@ bool ParseIndexOrRange(const char** config_string, size_t* start_index, size_t*
return true;
}

bool ParseGCHeapAffinitizeRanges(const char* cpu_index_ranges, AffinitySet* config_affinity_set)
bool ParseGCHeapAffinitizeRanges(const char* cpu_index_ranges, AffinitySet* config_affinity_set, uintptr_t& config_affinity_mask)
{
bool success = true;

// Unix:
// The cpu index ranges is a comma separated list of indices or ranges of indices (e.g. 1-5).
// Example 1,3,5,7-9,12
// Windows:
// The cpu index ranges is a comma separated list of group-annotated indices or ranges of indices.
// The group number always prefixes index or range and is followed by colon.
// Example 0:1,0:3,0:5,1:7-9,1:12

if (cpu_index_ranges != NULL)
// Case 1: config_affinity_mask and config_affinity_set are both null. No affinitization.
// Case 2: config_affinity_mask is not null but config_affinity_set is null. Affinitization is based on config_affinity_mask.
if (cpu_index_ranges == nullptr)
{
const char* number_end = cpu_index_ranges;

do
// Case 2.5: If CPU Groups are enabled, however, if the user passes in the config_affinity_mask, it can't apply.
// Therefore, we return a CLR_E_GC_BAD_AFFINITY_CONFIG_FORMAT error.
if (config_affinity_mask != 0 && GCToOSInterface::CanEnableGCCPUGroups())
{
size_t start_index, end_index;
if (!GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(&cpu_index_ranges, &start_index, &end_index))
{
break;
}
success = false;
}

if ((start_index >= MAX_SUPPORTED_CPUS) || (end_index >= MAX_SUPPORTED_CPUS) || (end_index < start_index))
{
// Invalid CPU index values or range
break;
}
return success;
}

// Case 3: config_affinity_mask is null but cpu_index_ranges isn't.
// To facilitate the case where there are less than 65 cores but the user passes in an affinitized range associated
// with the 0th CPU Group, we override the config_affinity_mask with the same contents as the cpu_index_ranges.
else if (config_affinity_mask == 0 && cpu_index_ranges != nullptr)
{
// Unix:
// The cpu index ranges is a comma separated list of indices or ranges of indices (e.g. 1-5).
// Example 1,3,5,7-9,12
// Windows:
// The cpu index ranges is a comma separated list of group-annotated indices or ranges of indices.
// The group number always prefixes index or range and is followed by colon.
// Example 0:1,0:3,0:5,1:7-9,1:12

if (cpu_index_ranges != nullptr)
{
const char* number_end = cpu_index_ranges;

for (size_t i = start_index; i <= end_index; i++)
do
{
config_affinity_set->Add(i);
size_t start_index, end_index;
if (!GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(&cpu_index_ranges, &start_index, &end_index))
{
break;
}

if ((start_index >= MAX_SUPPORTED_CPUS) || (end_index >= MAX_SUPPORTED_CPUS) || (end_index < start_index))
{
// Invalid CPU index values or range
break;
}

static const size_t BitsPerBitsetEntry = 8 * sizeof(uintptr_t);

for (size_t i = start_index; i <= end_index; i++)
{
config_affinity_set->Add(i);
config_affinity_mask |= (uintptr_t)1 << (i & (BitsPerBitsetEntry - 1));
}

number_end = cpu_index_ranges;
cpu_index_ranges++;
}
while (*number_end == ',');

number_end = cpu_index_ranges;
cpu_index_ranges++;
success = (*number_end == '\0');
}
while (*number_end == ',');

success = (*number_end == '\0');
}

return success;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/gc/gcconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,6 @@ static void Initialize();

};

bool ParseGCHeapAffinitizeRanges(const char* cpu_index_ranges, AffinitySet* config_affinity_set);
bool ParseGCHeapAffinitizeRanges(const char* cpu_index_ranges, AffinitySet* config_affinity_set, uintptr_t& config_affinity_mask);

#endif // __GCCONFIG_H__
17 changes: 15 additions & 2 deletions src/coreclr/vm/gcenv.os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1171,12 +1171,25 @@ bool GCToOSInterface::ParseGCHeapAffinitizeRangesEntry(const char** config_strin
return false;
}

// If the user passes in 0 as the CPU group and they don't have > 64 cores,
// honor the affinitized range passed in by bypassing the check.
bool bypass_cpu_range_check = !CanEnableGCCPUGroups() && group_number == 0;

WORD group_begin;
WORD group_size;
if (!CPUGroupInfo::GetCPUGroupRange((WORD)group_number, &group_begin, &group_size))
{
// group number out of range
return false;
if (!bypass_cpu_range_check)
{
// group number out of range
return false;
}
else
{
// the offset in this case where we bypass this check should be from 0 till the # of Processors.
group_begin = 0;
group_size = GetTotalProcessorCount();
}
}

index_offset = group_begin;
Expand Down

0 comments on commit 0ae6b95

Please sign in to comment.