-
Notifications
You must be signed in to change notification settings - Fork 787
[SYCL] Improve range reduction performance on CPU #6164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5ad06b7
b345236
782b29c
8d23d5c
4a9ef8a
c8ec67f
469c122
74e48b6
0643208
b5cd65d
245ef3c
1726491
72ce50b
2e4a326
29a789b
3d0118f
648baea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,11 @@ template <ConfigID Config> class SYCLConfigBase; | |
#include "config.def" | ||
#undef CONFIG | ||
|
||
#define INVALID_CONFIG_EXCEPTION(BASE, MSG) \ | ||
sycl::exception(sycl::make_error_code(sycl::errc::invalid), \ | ||
"Invalid value for " + std::string{BASE::MConfigName} + \ | ||
" environment variable: " + MSG) | ||
|
||
template <ConfigID Config> class SYCLConfig { | ||
using BaseT = SYCLConfigBase<Config>; | ||
|
||
|
@@ -467,6 +472,127 @@ template <> class SYCLConfig<SYCL_CACHE_DIR> { | |
} | ||
}; | ||
|
||
template <> class SYCLConfig<SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE> { | ||
using BaseT = SYCLConfigBase<SYCL_REDUCTION_PREFERRED_WORKGROUP_SIZE>; | ||
|
||
struct ParsedValue { | ||
size_t CPU = 0; | ||
size_t GPU = 0; | ||
size_t Accelerator = 0; | ||
}; | ||
|
||
public: | ||
static size_t get(info::device_type DeviceType) { | ||
ParsedValue Value = getCachedValue(); | ||
return getRefByDeviceType(Value, DeviceType); | ||
} | ||
|
||
static void reset() { (void)getCachedValue(/*ResetCache=*/true); } | ||
|
||
static const char *getName() { return BaseT::MConfigName; } | ||
|
||
private: | ||
static size_t &getRefByDeviceType(ParsedValue &Value, | ||
info::device_type DeviceType) { | ||
switch (DeviceType) { | ||
case info::device_type::cpu: | ||
return Value.CPU; | ||
case info::device_type::gpu: | ||
return Value.GPU; | ||
case info::device_type::accelerator: | ||
return Value.Accelerator; | ||
default: | ||
// Expect to get here if user used wrong device type. Include wildcard | ||
// in the message even though it's handled in the caller. | ||
throw INVALID_CONFIG_EXCEPTION( | ||
steffenlarsen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
BaseT, "Device types must be \"cpu\", \"gpu\", \"acc\", or \"*\"."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We aren't handling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are not, but the message is most relevant for users rather than internal. I.e. if a user gives a wrong device type we want to also include * in the message to make sure they know it's an option. If get uses an invalid value for this then this doesn't help the user much anyway. I could try and move it out of this function, but it will need a bit of refactoring. |
||
} | ||
} | ||
|
||
static ParsedValue parseValue() { | ||
const char *ValueRaw = BaseT::getRawValue(); | ||
ParsedValue Result{}; | ||
|
||
// Default to 0 to signify an unset value. | ||
if (!ValueRaw) | ||
return Result; | ||
|
||
std::string ValueStr{ValueRaw}; | ||
auto DeviceTypeMap = getSyclDeviceTypeMap(); | ||
|
||
// Iterate over all configurations. | ||
size_t Start = 0, End = 0; | ||
do { | ||
End = ValueStr.find(',', Start); | ||
if (End == std::string::npos) | ||
End = ValueStr.size(); | ||
|
||
// Get a substring of the current configuration pair. | ||
std::string DeviceConfigStr = ValueStr.substr(Start, End - Start); | ||
|
||
// Find the delimiter in the configuration pair. | ||
size_t ConfigDelimLoc = DeviceConfigStr.find(':'); | ||
if (ConfigDelimLoc == std::string::npos) | ||
throw INVALID_CONFIG_EXCEPTION( | ||
BaseT, "Device-value pair \"" + DeviceConfigStr + | ||
"\" does not contain the ':' delimiter."); | ||
|
||
// Split configuration pair into its constituents. | ||
std::string DeviceConfigTypeStr = | ||
DeviceConfigStr.substr(0, ConfigDelimLoc); | ||
std::string DeviceConfigValueStr = DeviceConfigStr.substr( | ||
ConfigDelimLoc + 1, DeviceConfigStr.size() - ConfigDelimLoc - 1); | ||
|
||
// Find the device type in the "device type map". | ||
auto DeviceTypeIter = std::find_if( | ||
std::begin(DeviceTypeMap), std::end(DeviceTypeMap), | ||
[&](auto Element) { return DeviceConfigTypeStr == Element.first; }); | ||
if (DeviceTypeIter == DeviceTypeMap.end()) | ||
throw INVALID_CONFIG_EXCEPTION( | ||
BaseT, | ||
"\"" + DeviceConfigTypeStr + "\" is not a recognized device type."); | ||
|
||
// Parse the configuration value. | ||
int DeviceConfigValue = 1; | ||
try { | ||
DeviceConfigValue = std::stoi(DeviceConfigValueStr); | ||
} catch (...) { | ||
throw INVALID_CONFIG_EXCEPTION( | ||
BaseT, "Value \"" + DeviceConfigValueStr + "\" must be a number"); | ||
} | ||
|
||
if (DeviceConfigValue < 1) | ||
throw INVALID_CONFIG_EXCEPTION(BaseT, | ||
"Value \"" + DeviceConfigValueStr + | ||
"\" must be larger than zero"); | ||
|
||
if (DeviceTypeIter->second == info::device_type::all) { | ||
// Set all configuration values if we got the device-type wildcard. | ||
Result.GPU = DeviceConfigValue; | ||
Result.CPU = DeviceConfigValue; | ||
Result.Accelerator = DeviceConfigValue; | ||
} else { | ||
// Try setting the corresponding configuration. | ||
getRefByDeviceType(Result, DeviceTypeIter->second) = DeviceConfigValue; | ||
} | ||
|
||
// Move to the start of the next configuration. If the start is outside | ||
// the full value string we are done. | ||
Start = End + 1; | ||
} while (Start < ValueStr.size()); | ||
return Result; | ||
} | ||
|
||
static ParsedValue getCachedValue(bool ResetCache = false) { | ||
static ParsedValue Val = parseValue(); | ||
if (ResetCache) | ||
Val = parseValue(); | ||
return Val; | ||
} | ||
}; | ||
|
||
#undef INVALID_CONFIG_EXCEPTION | ||
|
||
} // namespace detail | ||
} // __SYCL_INLINE_VER_NAMESPACE(_V1) | ||
} // namespace sycl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Final decision is based on both this new env config and
NumConcurrentWorkGroups
above, as we'll doDoes it match your intent? Or maybe we should uplift that piece of code from
reduction.hpp
to here and make the env. config have ultimate control?Up to you and not a blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that is in line with the expected behavior. The configuration is only intended as a preference, so if the implementation knows better it can make a more educated decision, like here where it will go for a smaller work-group size if there are fewer items than the setting.