-
Notifications
You must be signed in to change notification settings - Fork 13
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
Support of integer
and long double
types for CUDA math functions
#45
Comments
The CUDA Math library supports all C99 standard float and double math functions. Therefore I now implemented for each of those functions a wrapper, that either promotes integral types to float when the function is called in device code or calls the normal C++ function (which is already overloaded for integral types, effectively casting the argument to double) when in host code. For any function template <typename T>
__host__ __device__
float _brian_func(T value)
{
#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
return funcf((float)value);
#else
return func(value);
#endif
}
inline __host__ __device__
double _brian_func(double value)
{
return func(value);
} Tests for all functions are written as well (for both host and device code) and are passing. And it turns out Done. Closing. |
are integer types the only values that are used for the template parameter in instances of these code snippets? additionally: why not cast int to (64bit) double as in normal C++ (assuming what you write is correct) but (32bit) float which is not accurate enough for (32bit) int values due to the limited size of the mantissa. i therefore suggest to use double finally: for integer types with more than 32bit, e.g. 64bit even (64bit) double might be too small. therefore long int occurences should give rise to a warning that these values might be casted with loss of precision |
These snippets are used whenever one of the default functions is used in any string expression. This example covers some possible use cases at the moment: G = NeuronGroup(3, 'dv/dt = sin(v)') # will use the double version of sin, since v is double by default
G.v[0] = 'sin(i)' # i is int32_t type
G.v[1] = 'sin(1)' # 1 is int type
G.v[2] = 'sin(1.0)' # 1.0 is double type
Yeah right, I didn't think of the fact, that we can loose information when promoting integral to floating point types. I used float because of the faster single precision arithmetic on the GPU. But I think in many use cases, we won't loose information when casting integral types to float (the integer would have to be
As far as I can tell this case can only happen if the user specifically specifies a So what about template <typename T>
__host__ __device__
double _brian_func(T value)
{
#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
return func((double)value);
#else
return func(value);
#endif
}
inline __host__ __device__
float _brian_func(float value)
{
return funcf(value);
} and later we can just switch the |
This also adds a user preference to convert to float instead for performance reasons. (#45)
This warns for occurence of default functions and integer types in the same code line, does not check if the integer type is an argument fo the default function. (#45)
I added a preference prefs.codegen.generators.cuda.default_functions_integral_convertion The default is I have also added warnings. Just warning whenever a We warn in two cases:
I also added tests for setting the preference and the correct warnings. So, trying to close this again :) |
The CUDA math library functions are only overloaded for single (
float
) and double precision (double
) arguments. That means if we want to support integer arithmetics in device functions, we need to overload them ourselves. In C++ they are overloaded additionally forint
andlong double
arguments.Question is, do we create overloaded functions and add them all to
CUDACodeGenerator.universal_support_code
as is currently done with_brian_mod
(here)?Or would it be cleaner to do that in an extra header file that we add to
brianlib
? And instead of overloading it, we could also just template it like it is done forint_
in stdint_compat.h. We could then just cast everything to float, using the single precision math functions and have one specialisation fordouble
, using the double precision math function, like this:And since in some cases there is some easier way to calculate a function for integer types, we could instead do the integer calculation and only specialize for float and double. And we could even add
#ifdef __CUDA_ARCH ...
to have different code for host or device function calls.Another thing is, that
long double
arithmetics are not supported on CUDA at all. Question is, do we simply castlong double
todouble
or let it fail instead. Right now the_brian_mod
function just fails forlong double
arguments with something likeAnd since CUDA doesn't have any exception handling, the user wouldn't know whats happening.
@mstimberg Are there actual use cases where people use
long double
data types? And is there an easy way to check for it and give a warning or error when usingcuda_standalone
?And how does genn do it? From what I could see, genn just uses
fmod
, so I suppose it only supportsfloat
anddouble
?The text was updated successfully, but these errors were encountered: