Skip to content
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

Inconsistent use of integer types #430

Open
upsj opened this issue Dec 19, 2019 · 5 comments
Open

Inconsistent use of integer types #430

upsj opened this issue Dec 19, 2019 · 5 comments
Labels
is:help-wanted Need ideas on how to solve this. is:interface-breaking This change may break interface compatibility. Can only be done through a major release.

Comments

@upsj
Copy link
Member

upsj commented Dec 19, 2019

It seems to me Ginkgo is sometimes integer types inconsistently.
This includes both 32/64 bit as well as signed/unsigned. Examples:

  • dim returns a size_type for all dimensions, even though we internally use the IndexType template parameter in almost all kernels.
  • Most Reference and OMP kernels use size_type as loop variables.
  • SellP stores its slice lengths and slice sets as size_type

My opinion: I would consistently use signed types everywhere, because we use them for indexing anyways and overflows on signed types are way easier to recognize/catch when debugging. Only for allocation sizes and/or launch bounds, it might make sense to use size_type. But that is often a very fundamental debate, so I would like to start it here.

On the question what type dim should use, I am conflicted, since it would probably be annoying having to type dim<IndexType> everywhere, but in my opinion, it should at least return a signed type.

@upsj upsj added the is:help-wanted Need ideas on how to solve this. label Dec 19, 2019
@upsj upsj added the is:interface-breaking This change may break interface compatibility. Can only be done through a major release. label Dec 19, 2019
@upsj
Copy link
Member Author

upsj commented Jan 30, 2020

Coming back to this, since @thoasm and I discussed some related issues or questions about code style, I want to propose a few changes that might clean up our code base.

  1. Change all integer types to signed. The rationale for this is simple: We do this for all (but dense) kernels anyways and many popular style guides (Google and the C++ Core Guidelines) recommend avoiding unsigned types (except for bit patterns).
    This would also mean making the size type for Array signed and throwing an exception for size<0.
    Concerning overflows, this is of course a complex topic, but I would leave it at the following: In case you have a matrix with nnz >= MAX_INT, we would expect you to use int64 types, otherwise there is nothing we can do. Since the largest indices that occur in our kernels are limited by nnz (neglecting "cutoff" thread IDs for large block sizes and counts and/or the use of a whole subwarp per row/non-zero, which we could solve by using the y and z indices), I see no reason why we should expect our code to overflow. And if it does (and the numbers are not extraordinarily big), a negative number is a better indicator for overflown values than a small value where you would not expect it.
  2. Make sure all "external" functions we use have wrappers that return signed values in case they don't do that already. This includes the CUDA/HIP cooperative groups, intrinsics, but also the threadIdx and blockIdx/Dim variables, which we could wrap either in a get_thread_id<IndexType> function or use our cooperative_groups grid group everywhere.
  3. Minimize the use of static_cast and avoid functional-style casts for integers everywhere. This is also a recommendation from the C++ Core Guidelines, ES.48/49)

Comments?

@thoasm
Copy link
Member

thoasm commented Jan 30, 2020

I agree, making everything a signed value will definitively remove the need for a lot of casts. For sizes, I think you are correct that we can give up the "extra bit" we would have with unsigned and just move to 64 bit a bit earlier.
However, even if we change the size_type to int64, I am pretty sure it is considered interface breaking, so we have to wait for the 2.0 version to make that change.

@upsj
Copy link
Member Author

upsj commented Jan 30, 2020

Yes, that would be a long-term goal. Maybe we can collect other changes/issues that this switch would cause. Until then, I'll try to use static_cast where possible

@yhmtsai
Copy link
Member

yhmtsai commented Jan 30, 2020

Changing to signed is also easy to write the code in my experience.
In some cases, I just want to run the size - 1 or size - m loops. for i = 0: size-m is failed when size is less than m.

For nnz >= MAX_INT, (the num stored elements)
Add IndexType as Array template varaible to check whether the number of stored elements does not overflow such that we do not need to check it in each class. ( Maybe Dense also needs it)

@upsj
Copy link
Member Author

upsj commented Feb 28, 2020

After working on #464, I now have a slightly clearer picture of what the impact of the proposed changes would be, so my updated proposal would be:

  1. Make size_type signed and only use unsigned types for the internal allocation sizes. This could also provide better optimization opportunities (especially for reference and omp, where we use this types excessively), since the compiler can then assume that no overflows occur.
  2. Consistently use size_type for all matrix dimensions, even in kernel parameters. It seems to me that we only use these size parameters for bounds checking like if (tidx >= num_rows) return;, where it would not matter if tidx is a smaller IndexType. This would allow us to get rid of all num_rows = static_cast<IndexType>(...) and similar statements, since there is no more template parameter deduction mismatch between size_type and IndexType.
  3. Consistently use IndexType (or size_type for dense) for indexing within kernels, where the actual computations take place. There are of course places where we need to be careful to avoid overflows (multiplications etc.), but it looks to me like these mostly occur in the dense kernels anyways. This policy would probably allow us to get rid of all index casts (or hide them in calls such as get_thread_id_flat)

Comments?

@upsj upsj mentioned this issue Apr 3, 2024
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
is:help-wanted Need ideas on how to solve this. is:interface-breaking This change may break interface compatibility. Can only be done through a major release.
Projects
None yet
Development

No branches or pull requests

6 participants