Repository navigation
CUDA compare and swap with index #8790
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 1 commit
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 |
|---|---|---|
|
|
@@ -129,6 +129,7 @@ def array(self, shape, dtype): | |
| maxlock = threading.Lock() | ||
| minlock = threading.Lock() | ||
| caslock = threading.Lock() | ||
| casindexlock = threading.Lock() | ||
|
Member
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. For consistency I'd probably call this
Contributor
Author
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. Done. |
||
| inclock = threading.Lock() | ||
| declock = threading.Lock() | ||
| exchlock = threading.Lock() | ||
|
|
@@ -221,6 +222,13 @@ def compare_and_swap(self, array, old, val): | |
| array[index] = val | ||
| return loaded | ||
|
|
||
| def cas(self, array, index, old, val): | ||
| with casindexlock: | ||
| loaded = array[index] | ||
| if loaded == old: | ||
| array[index] = val | ||
| return loaded | ||
|
|
||
|
|
||
| class FakeCUDAFp16(object): | ||
| def hadd(self, a, b): | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -503,7 +503,18 @@ class compare_and_swap(Stub): | |||||
|
|
||||||
| Supported on int32, int64, uint32, uint64 operands only. | ||||||
|
|
||||||
| Returns the current value as if it is loaded atomically. | ||||||
| Returns the old value as if it is loaded atomically. | ||||||
| """ | ||||||
|
|
||||||
| class cas(Stub): | ||||||
| """cas(ary, idx, old, val) | ||||||
|
|
||||||
| Conditionally assign ``val`` to the element ary[idx] of an array | ||||||
|
Member
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. Should that refer to the position of the element rather than the value itself?:
Suggested change
Contributor
Author
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. Done. |
||||||
| ``ary`` if the current value of ary[idx] matches ``old``. | ||||||
|
Member
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. For formatting consistency:
Suggested change
Contributor
Author
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. Done. |
||||||
|
|
||||||
| Supported on int32, int64, uint32, uint64 operands only. | ||||||
|
|
||||||
| Returns the old value as if it is loaded atomically. | ||||||
| """ | ||||||
|
|
||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -456,6 +456,20 @@ def atomic_compare_and_swap(res, old, ary, fill_val): | |||||||
| old[gid] = out | ||||||||
|
|
||||||||
|
|
||||||||
| def atomic_cas_1dim(res, old, ary, fill_val): | ||||||||
| gid = cuda.grid(1) | ||||||||
| if gid < res.size: | ||||||||
| out = cuda.atomic.cas(res, gid, fill_val, ary[gid]) | ||||||||
| old[gid] = out | ||||||||
|
Member
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. Whilst the original
Suggested change
(and is it worth updating the original function too?)
Contributor
Author
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. Done, and on the other two functions too. |
||||||||
|
|
||||||||
|
|
||||||||
| def atomic_cas_2dim(res, old, ary, fill_val): | ||||||||
| gid = cuda.grid(2) | ||||||||
| if gid[0] < res.shape[0] and gid[1] < res.shape[1]: | ||||||||
| out = cuda.atomic.cas(res, gid, fill_val, ary[gid]) | ||||||||
| old[gid] = out | ||||||||
|
Member
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. Similar as above:
Suggested change
|
||||||||
|
|
||||||||
|
|
||||||||
| class TestCudaAtomics(CUDATestCase): | ||||||||
| def setUp(self): | ||||||||
| super().setUp() | ||||||||
|
|
@@ -1251,12 +1265,14 @@ def test_atomic_min_double_shared(self): | |||||||
| gold = np.min(vals) | ||||||||
| np.testing.assert_equal(res, gold) | ||||||||
|
|
||||||||
| def check_compare_and_swap(self, n, fill, unfill, dtype): | ||||||||
| def check_cas(self, n, fill, unfill, dtype, cas_func, ndim=1): | ||||||||
|
Contributor
Author
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. This function is used to test both |
||||||||
| res = [fill] * (n // 2) + [unfill] * (n // 2) | ||||||||
| np.random.shuffle(res) | ||||||||
| res = np.asarray(res, dtype=dtype) | ||||||||
| if ndim == 2: | ||||||||
| res.shape = (10, -1) | ||||||||
| out = np.zeros_like(res) | ||||||||
| ary = np.random.randint(1, 10, size=res.size).astype(res.dtype) | ||||||||
| ary = np.random.randint(1, 10, size=res.shape).astype(res.dtype) | ||||||||
|
|
||||||||
| fill_mask = res == fill | ||||||||
| unfill_mask = res == unfill | ||||||||
|
|
@@ -1265,33 +1281,76 @@ def check_compare_and_swap(self, n, fill, unfill, dtype): | |||||||
| expect_res[fill_mask] = ary[fill_mask] | ||||||||
| expect_res[unfill_mask] = unfill | ||||||||
|
|
||||||||
| expect_out = np.zeros_like(out) | ||||||||
| expect_out[fill_mask] = res[fill_mask] | ||||||||
| expect_out[unfill_mask] = unfill | ||||||||
| expect_out = res.copy() | ||||||||
|
|
||||||||
| cuda_func = cuda.jit(atomic_compare_and_swap) | ||||||||
| cuda_func[10, 10](res, out, ary, fill) | ||||||||
| cuda_func = cuda.jit(cas_func) | ||||||||
| if ndim == 1: | ||||||||
| cuda_func[10, 10](res, out, ary, fill) | ||||||||
| else: | ||||||||
| cuda_func[(10, 10), (10, 10)](res, out, ary, fill) | ||||||||
|
|
||||||||
| np.testing.assert_array_equal(expect_res, res) | ||||||||
| np.testing.assert_array_equal(expect_out, out) | ||||||||
|
|
||||||||
| def test_atomic_compare_and_swap(self): | ||||||||
| self.check_compare_and_swap(n=100, fill=-99, unfill=-1, dtype=np.int32) | ||||||||
| self.check_cas(n=100, fill=-99, unfill=-1, dtype=np.int32, | ||||||||
| cas_func=atomic_compare_and_swap) | ||||||||
|
|
||||||||
| def test_atomic_compare_and_swap2(self): | ||||||||
| self.check_compare_and_swap(n=100, fill=-45, unfill=-1, dtype=np.int64) | ||||||||
| self.check_cas(n=100, fill=-45, unfill=-1, dtype=np.int64, | ||||||||
| cas_func=atomic_compare_and_swap) | ||||||||
|
|
||||||||
| def test_atomic_compare_and_swap3(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint32) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint32) | ||||||||
| self.check_compare_and_swap(n=100, fill=rfill, unfill=runfill, | ||||||||
| dtype=np.uint32) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint32, | ||||||||
| cas_func=atomic_compare_and_swap) | ||||||||
|
|
||||||||
| def test_atomic_compare_and_swap4(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint64) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint64) | ||||||||
| self.check_compare_and_swap(n=100, fill=rfill, unfill=runfill, | ||||||||
| dtype=np.uint64) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint64, | ||||||||
| cas_func=atomic_compare_and_swap) | ||||||||
|
|
||||||||
| def test_atomic_cas_1dim(self): | ||||||||
| self.check_cas(n=100, fill=-99, unfill=-1, dtype=np.int32, | ||||||||
| cas_func=atomic_cas_1dim) | ||||||||
|
|
||||||||
| def test_atomic_cas_2dim(self): | ||||||||
| self.check_cas(n=100, fill=-99, unfill=-1, dtype=np.int32, | ||||||||
| cas_func=atomic_cas_2dim, ndim=2) | ||||||||
|
|
||||||||
| def test_atomic_cas2_1dim(self): | ||||||||
| self.check_cas(n=100, fill=-45, unfill=-1, dtype=np.int64, | ||||||||
| cas_func=atomic_cas_1dim) | ||||||||
|
|
||||||||
| def test_atomic_cas2_2dim(self): | ||||||||
| self.check_cas(n=100, fill=-45, unfill=-1, dtype=np.int64, | ||||||||
| cas_func=atomic_cas_2dim, ndim=2) | ||||||||
|
|
||||||||
| def test_atomic_cas3_1dim(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint32) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint32) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint32, | ||||||||
| cas_func=atomic_cas_1dim) | ||||||||
|
|
||||||||
| def test_atomic_cas3_2dim(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint32) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint32) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint32, | ||||||||
| cas_func=atomic_cas_2dim, ndim=2) | ||||||||
|
|
||||||||
| def test_atomic_cas4_1dim(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint64) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint64) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint64, | ||||||||
| cas_func=atomic_cas_1dim) | ||||||||
|
|
||||||||
| def test_atomic_cas4_2dim(self): | ||||||||
| rfill = np.random.randint(50, 500, dtype=np.uint64) | ||||||||
| runfill = np.random.randint(1, 25, dtype=np.uint64) | ||||||||
| self.check_cas(n=100, fill=rfill, unfill=runfill, dtype=np.uint64, | ||||||||
| cas_func=atomic_cas_2dim, ndim=2) | ||||||||
|
|
||||||||
| # Tests that the atomic add, min, and max operations return the old value - | ||||||||
| # in the simulator, they did not (see Issue #5458). The max and min have | ||||||||
|
|
||||||||
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.
This function could probably be combined with the previous one, but I haven't figured out how to do this yet.
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.
One way to combine them would be to modify the original implementation so that it inserts the type of the
idxparameter into the signature and a zero value into the args, then calls your new implementation - for example:(note also the change of name of the original function, which apparently made little sense anyway!)
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.
Thanks, that is much more concise.
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.
Done.