mirror of https://github.com/jlizier/jidt
Compare commits
548 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
fea2cb4bfa | |
|
|
cb12914e7a | |
|
|
d773508cf9 | |
|
|
333a00fc07 | |
|
|
2ab3ec347c | |
|
|
5dc9d00aff | |
|
|
c08aeb1783 | |
|
|
fa21f7343a | |
|
|
13251a0104 | |
|
|
283dcb3b94 | |
|
|
cab3de8b07 | |
|
|
efd3943697 | |
|
|
912bdf94e1 | |
|
|
5d4419d734 | |
|
|
a9b6b3e77c | |
|
|
3d1f3fec86 | |
|
|
2a18cd0e74 | |
|
|
2130985c34 | |
|
|
7d8b501127 | |
|
|
4b85a7fd46 | |
|
|
ec8e7f848f | |
|
|
5f87e2018b | |
|
|
938dd7c4fb | |
|
|
a554361de9 | |
|
|
4f5d8f894b | |
|
|
90baf68ee7 | |
|
|
817abd1de0 | |
|
|
f643632df4 | |
|
|
f55737c650 | |
|
|
107fabad32 | |
|
|
d920b1e9a9 | |
|
|
23bfeded27 | |
|
|
40d51ed542 | |
|
|
0cef502948 | |
|
|
28954585f4 | |
|
|
26e3cd8afb | |
|
|
6dc6b8fa11 | |
|
|
865d82630a | |
|
|
ee3ce5dd51 | |
|
|
8bc6d87d9b | |
|
|
348587ee59 | |
|
|
8c001e6ddd | |
|
|
65aa685d11 | |
|
|
2b0a5cb943 | |
|
|
72024c7299 | |
|
|
d750a737be | |
|
|
003a187a7a | |
|
|
fcfcca962b | |
|
|
27f9cccf65 | |
|
|
47be5df74b | |
|
|
e747e8aa93 | |
|
|
8db0c12c37 | |
|
|
05248ef105 | |
|
|
c233485c90 | |
|
|
9c2a25492f | |
|
|
5398785a67 | |
|
|
d250c6b24c | |
|
|
dc3874c72b | |
|
|
a040cde6da | |
|
|
0935806338 | |
|
|
bcefd900c4 | |
|
|
a398c925a8 | |
|
|
9dfafda555 | |
|
|
2714650cfa | |
|
|
2c72eb05f9 | |
|
|
25159009ca | |
|
|
e9fc39c4c2 | |
|
|
899a5607f8 | |
|
|
41361417ae | |
|
|
c068c8308d | |
|
|
285c95947a | |
|
|
b4e32727c9 | |
|
|
0e33393cdc | |
|
|
b02760ddd3 | |
|
|
8e55cf05d9 | |
|
|
7d7291fb9d | |
|
|
e5b223606c | |
|
|
a65db51b78 | |
|
|
9c31807283 | |
|
|
b4ab18f6c6 | |
|
|
6f43dfb620 | |
|
|
f8eb84e183 | |
|
|
21ae0bdeaa | |
|
|
d101a9f2ff | |
|
|
ccf920d3a8 | |
|
|
898a04904c | |
|
|
2caab8593e | |
|
|
5da70db3ae | |
|
|
650159661d | |
|
|
c8d17e9522 | |
|
|
aa8148eca9 | |
|
|
dd215fdfbb | |
|
|
ef7cfbb525 | |
|
|
dc85001ff7 | |
|
|
9a17361cca | |
|
|
dc226be60a | |
|
|
8d7c598022 | |
|
|
cf5f8edf5e | |
|
|
4ece80b5ef | |
|
|
bb51b09048 | |
|
|
0e530cdceb | |
|
|
deb9d97520 | |
|
|
fe0fb2ed49 | |
|
|
56dacc5232 | |
|
|
4f889df786 | |
|
|
70a7f5c33a | |
|
|
07883af734 | |
|
|
bc8c233e68 | |
|
|
a58a01fbfd | |
|
|
b3abd382aa | |
|
|
f67e274819 | |
|
|
403745800e | |
|
|
06cc6d649d | |
|
|
b3192e225f | |
|
|
3222b5a629 | |
|
|
0606f8ab39 | |
|
|
b5a6fa92c2 | |
|
|
44e53bc846 | |
|
|
8ecb512ce0 | |
|
|
4c95b4ee86 | |
|
|
34161a2e6d | |
|
|
bf207f866e | |
|
|
820f700cb8 | |
|
|
471ea59bb2 | |
|
|
d0a0fca40c | |
|
|
ccbccc6302 | |
|
|
cbcca8d0ff | |
|
|
510f999d2e | |
|
|
64a7a80e30 | |
|
|
1c2ccda1eb | |
|
|
edb8f997cb | |
|
|
502f7ac22c | |
|
|
2c2d012e20 | |
|
|
f53ecba635 | |
|
|
4966fdabc2 | |
|
|
c22e37d1ab | |
|
|
61ae6fb40b | |
|
|
2ab3ac01bb | |
|
|
1667b9fe7a | |
|
|
1dc2dc8b86 | |
|
|
d545793bc5 | |
|
|
0dae162d82 | |
|
|
17bf0e0be7 | |
|
|
fba941362d | |
|
|
1db8a2ac63 | |
|
|
681c9196ee | |
|
|
9f9c79bc95 | |
|
|
8542d217ea | |
|
|
fce1b34a5c | |
|
|
d9db7a15ab | |
|
|
b1437452e4 | |
|
|
ecec4d20e3 | |
|
|
988d32de70 | |
|
|
2a0024ad83 | |
|
|
66676a2692 | |
|
|
d58c393c53 | |
|
|
1d2c7c42b9 | |
|
|
9aed0d4590 | |
|
|
c4349140f1 | |
|
|
4985def021 | |
|
|
c693b5d57e | |
|
|
ea8a322b61 | |
|
|
fcbf5e7dac | |
|
|
8bcf41e572 | |
|
|
504c6262f0 | |
|
|
23abc021ac | |
|
|
064953c584 | |
|
|
b467ea22a1 | |
|
|
9f51dc284c | |
|
|
6f8ae62fee | |
|
|
267796dc83 | |
|
|
ec01683592 | |
|
|
fa9a45a9cf | |
|
|
2ba27f30ac | |
|
|
4908276333 | |
|
|
c616ba55d4 | |
|
|
bd6a9a2601 | |
|
|
eefe1e1a8b | |
|
|
ee1ef4909c | |
|
|
a3ec963618 | |
|
|
603445651c | |
|
|
7bd6496a40 | |
|
|
cd1d4c2684 | |
|
|
7910828ad7 | |
|
|
dc4e59f945 | |
|
|
b6a5285b9b | |
|
|
3c24debea5 | |
|
|
16015e0f12 | |
|
|
af7fbd6c37 | |
|
|
8909c23fcc | |
|
|
42f8d01e26 | |
|
|
e1bad39e4e | |
|
|
da418c31a0 | |
|
|
a34bce25ba | |
|
|
efb8243ed7 | |
|
|
2ff6d8aeec | |
|
|
3aa1493d7f | |
|
|
6cf43db7f5 | |
|
|
cf83a73bc0 | |
|
|
1f8a33b211 | |
|
|
19be59c118 | |
|
|
af1173ba5b | |
|
|
9dbe2f8578 | |
|
|
1c2082e74a | |
|
|
ad31af03a7 | |
|
|
f502497256 | |
|
|
7f9b1c0812 | |
|
|
fdec72aeff | |
|
|
c32b8de8b0 | |
|
|
b1a02be7db | |
|
|
dc08e1c372 | |
|
|
93c767999e | |
|
|
908cf675a8 | |
|
|
373ebbdd56 | |
|
|
7f4dfa7970 | |
|
|
323ef47475 | |
|
|
4b6fc39c72 | |
|
|
d69d503492 | |
|
|
a4b9f20801 | |
|
|
97e46c66a3 | |
|
|
093737951f | |
|
|
a278060056 | |
|
|
8c5aa51f57 | |
|
|
7fafabe451 | |
|
|
4170339426 | |
|
|
ca909dc60f | |
|
|
5bde6f295b | |
|
|
fd13b75be3 | |
|
|
de5661b1e3 | |
|
|
643212840a | |
|
|
fa9db58945 | |
|
|
4ff4e4bfc9 | |
|
|
dfe7ef0334 | |
|
|
fcd30c6716 | |
|
|
a649b38270 | |
|
|
e8603b08f8 | |
|
|
1c591925bd | |
|
|
48b48ab845 | |
|
|
9552091fca | |
|
|
0b70e92efc | |
|
|
c0b9d736d7 | |
|
|
46a9785da2 | |
|
|
28ba409ff6 | |
|
|
0e4455b2fd | |
|
|
860d43dbca | |
|
|
1f625b59f0 | |
|
|
d1bba0e4c1 | |
|
|
bf0062b902 | |
|
|
7d76f82bb2 | |
|
|
5b569c63dc | |
|
|
c15c643a09 | |
|
|
df8eec27cd | |
|
|
f8bca3275d | |
|
|
9c40d7fadc | |
|
|
02dad26a94 | |
|
|
03d5e2fb2f | |
|
|
5f6671aa2b | |
|
|
d4c9b28973 | |
|
|
550ff793eb | |
|
|
438a88c97c | |
|
|
8be6a037af | |
|
|
ce4b1599c6 | |
|
|
75bb2c814a | |
|
|
fa0bc96a6f | |
|
|
08f25beee6 | |
|
|
b0fc309d6a | |
|
|
b32d9443eb | |
|
|
9feff283c4 | |
|
|
0278f95ae9 | |
|
|
6b5e3237ed | |
|
|
92f1154e3d | |
|
|
64f1142b40 | |
|
|
65ac0e9ff8 | |
|
|
2c518ab436 | |
|
|
76e7277e95 | |
|
|
82593e1db2 | |
|
|
4949a1079d | |
|
|
2f1cd192b8 | |
|
|
39fb538945 | |
|
|
fc40c279cd | |
|
|
75a303875d | |
|
|
f1bd757dc0 | |
|
|
1fb4015df5 | |
|
|
a9dac705a8 | |
|
|
d0304ae0c4 | |
|
|
9248005de5 | |
|
|
6a13b80531 | |
|
|
b7dd2fca5f | |
|
|
f4ca442de4 | |
|
|
d010f32635 | |
|
|
eabd7f3701 | |
|
|
444fb70801 | |
|
|
e290040d8d | |
|
|
35df6ca3da | |
|
|
9c96ceb850 | |
|
|
9e669bceed | |
|
|
589d51674e | |
|
|
3bfbaa7ad3 | |
|
|
f902aa69dd | |
|
|
e3a71b0a7e | |
|
|
61f4bf3276 | |
|
|
afdcf02520 | |
|
|
ab6fa10fa7 | |
|
|
a4f9d9021d | |
|
|
0f8e447a52 | |
|
|
10cc5f25bf | |
|
|
db0503d763 | |
|
|
20e8fcef47 | |
|
|
df8724b872 | |
|
|
fd804d0d0c | |
|
|
69af7252cd | |
|
|
d09a9e804d | |
|
|
950ee70f9e | |
|
|
4fe82359ff | |
|
|
4881daadf2 | |
|
|
326e5fbe77 | |
|
|
7390c1d643 | |
|
|
ddf3b4de0a | |
|
|
072ee35b37 | |
|
|
142fbb4541 | |
|
|
e1212c0ce9 | |
|
|
8d0d0e23aa | |
|
|
9e77ebdadf | |
|
|
1bb67f0840 | |
|
|
446ac4ae59 | |
|
|
86cd661a9c | |
|
|
decbcd5445 | |
|
|
c5c38335ef | |
|
|
ecda70f8e9 | |
|
|
25df49fa5a | |
|
|
8f169171e1 | |
|
|
d40024e1cd | |
|
|
0c5bed62d6 | |
|
|
6a48112818 | |
|
|
0e27dbcbc2 | |
|
|
8c7e94b0fa | |
|
|
ed71502bc2 | |
|
|
83e77e8b3a | |
|
|
9b7d7c70c8 | |
|
|
f56ac6e69e | |
|
|
5961fb6246 | |
|
|
9aea9c4a02 | |
|
|
d36c803e41 | |
|
|
257ef285d3 | |
|
|
c2b1f7ef25 | |
|
|
626293b84d | |
|
|
e11218631e | |
|
|
49afa343fc | |
|
|
bba00f07fc | |
|
|
1f236dee49 | |
|
|
b7154fab62 | |
|
|
5196c204a9 | |
|
|
5dcb89bed7 | |
|
|
19f2c5abbc | |
|
|
9f4ce02363 | |
|
|
c59d932a44 | |
|
|
b2e3ae1ca0 | |
|
|
0e40ec2401 | |
|
|
4bc411654f | |
|
|
7c837bca68 | |
|
|
4db008ce33 | |
|
|
7972e03420 | |
|
|
26b15ccf2d | |
|
|
24c66cc460 | |
|
|
e71df111cf | |
|
|
4d6fc1a572 | |
|
|
e75eb761ca | |
|
|
086bee5945 | |
|
|
057f17e338 | |
|
|
65c7cd99cc | |
|
|
d6bd16546b | |
|
|
04351498aa | |
|
|
3e64fd28bd | |
|
|
60105e6d10 | |
|
|
d9e43d966a | |
|
|
d77a9f274e | |
|
|
70740c9753 | |
|
|
fa6307b4f2 | |
|
|
bfe8156584 | |
|
|
641d17ec24 | |
|
|
faa98a8d79 | |
|
|
7635cf8689 | |
|
|
1065662739 | |
|
|
2d42e6ebad | |
|
|
23bb573bb1 | |
|
|
8251bdc1cb | |
|
|
f55a7bd961 | |
|
|
bad50ce663 | |
|
|
b5d2b7e367 | |
|
|
8d1d1ab0eb | |
|
|
17e8ee6104 | |
|
|
9b42911c60 | |
|
|
dcd6314082 | |
|
|
437e606233 | |
|
|
39ad14d855 | |
|
|
abeb15cd6e | |
|
|
26137d12b1 | |
|
|
7b0f8b3d34 | |
|
|
e31cfc3939 | |
|
|
550bbae5c1 | |
|
|
bd94172d60 | |
|
|
e25364b84c | |
|
|
ea40f48bbc | |
|
|
a2b9939892 | |
|
|
77f4c1afd5 | |
|
|
4b8e26fc05 | |
|
|
f46fc94825 | |
|
|
028314cbab | |
|
|
d0b35b3db8 | |
|
|
1b69dfa3e7 | |
|
|
8233ad0d18 | |
|
|
82d751796e | |
|
|
834a3ec2c2 | |
|
|
c0a4a283fa | |
|
|
2a203faa23 | |
|
|
bc44e4c2fb | |
|
|
85dd28d13b | |
|
|
7a89dd7908 | |
|
|
6c25ea93ef | |
|
|
721c96c040 | |
|
|
c9d6a8a3bc | |
|
|
6645653ab3 | |
|
|
75a03341b9 | |
|
|
80b64eb4e6 | |
|
|
f4ef357a84 | |
|
|
180e5cbf45 | |
|
|
727a76fe94 | |
|
|
7eb91957ce | |
|
|
4b374de8c1 | |
|
|
34c9ebd6ad | |
|
|
a5e3d18e6a | |
|
|
fb01036127 | |
|
|
0845420fdf | |
|
|
502289d666 | |
|
|
0d296c854c | |
|
|
d94a373ca8 | |
|
|
ab393a6d1f | |
|
|
cb36236440 | |
|
|
1beb7a47fc | |
|
|
36d821979d | |
|
|
56771c94b4 | |
|
|
91b1235380 | |
|
|
d8779ee9f5 | |
|
|
2e4a58d2c9 | |
|
|
94bcfc1477 | |
|
|
6af0e22030 | |
|
|
6e8ed0b38f | |
|
|
a642c0a8c8 | |
|
|
79859e0832 | |
|
|
c8ceec9f71 | |
|
|
cce345b0ed | |
|
|
3fb9632439 | |
|
|
82c06abbeb | |
|
|
715050d4e6 | |
|
|
2d8d0271ca | |
|
|
afcfb61260 | |
|
|
20c019267c | |
|
|
ada9128389 | |
|
|
7fe8bf47cc | |
|
|
e39afcd5b4 | |
|
|
3a68e27af8 | |
|
|
3dd4f6b224 | |
|
|
b2c42563d0 | |
|
|
991ef30a1e | |
|
|
09548028e0 | |
|
|
3054461f7a | |
|
|
d17d8cad03 | |
|
|
8da0bfc17b | |
|
|
39a5cadf06 | |
|
|
4cb9a9bb16 | |
|
|
3c0ae81505 | |
|
|
1b1f4f9922 | |
|
|
bb241ae415 | |
|
|
63f26a60fa | |
|
|
ff868bf79f | |
|
|
cced321abb | |
|
|
169c35e990 | |
|
|
e6818493ca | |
|
|
7d9d7ad135 | |
|
|
3a64f2b926 | |
|
|
c33436e69a | |
|
|
492cfe8b53 | |
|
|
4a31d3d7f1 | |
|
|
0b66c3a3f8 | |
|
|
4cbeb681db | |
|
|
f2d6674a4f | |
|
|
a7cef7f87f | |
|
|
f7706f72bb | |
|
|
0d555a34ca | |
|
|
5ba6cd093e | |
|
|
7546dcfc06 | |
|
|
b2218a976d | |
|
|
b05399e40f | |
|
|
aedefd07bc | |
|
|
74e65b624c | |
|
|
9b455f7ef4 | |
|
|
001227c730 | |
|
|
118e32d9ad | |
|
|
71589d1150 | |
|
|
ad3454cce3 | |
|
|
a11c1aa2eb | |
|
|
cc47398d50 | |
|
|
6e9ade6542 | |
|
|
4b665d525f | |
|
|
b26695bf8a | |
|
|
f90ade60b8 | |
|
|
bdbe0ed659 | |
|
|
3e0cbc8a9e | |
|
|
ae3fdc9782 | |
|
|
8dc6eced45 | |
|
|
2fbe03f4ec | |
|
|
5a07352969 | |
|
|
d7fa6814dd | |
|
|
57aa4e95ee | |
|
|
1abd5a084e | |
|
|
dd83cd9ef1 | |
|
|
8675858e98 | |
|
|
3c14732762 | |
|
|
470090321e | |
|
|
55e74e6ede | |
|
|
6ec79dec55 | |
|
|
004a7c7f17 | |
|
|
22bae87123 | |
|
|
1dec066188 | |
|
|
d1c45866fb | |
|
|
0e407a784a | |
|
|
4e54fc6ce4 | |
|
|
f840f0ec72 | |
|
|
37954f6158 | |
|
|
c311067fe6 | |
|
|
62069dab16 | |
|
|
bf3f26cf18 | |
|
|
eb83262172 | |
|
|
0d940856a5 | |
|
|
8fd1e6ad2f | |
|
|
1e982ddbac | |
|
|
2294e81399 | |
|
|
ddf3088a5d | |
|
|
043af3febb | |
|
|
2654e848ae | |
|
|
a721f26734 | |
|
|
c40b8ae80c | |
|
|
08bb2dd609 | |
|
|
6f87aed980 | |
|
|
99a856cd8c | |
|
|
3da629a817 | |
|
|
493491d69c | |
|
|
5074c1b6f7 |
Binary file not shown.
76
README.md
76
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# Java Information Dynamics Toolkit (JIDT)
|
||||
|
||||
Copyright (C) 2012-2014 [Joseph T. Lizier](http://lizier.me/joseph/); 2014-2015 [Joseph T. Lizier](http://lizier.me/joseph/) and Ipek Özdemir
|
||||
Copyright (C) 2012- [Joseph T. Lizier](http://lizier.me/joseph/); 2014- Ipek Özdemir; 2017- [Pedro Mediano](https://www.doc.ic.ac.uk/~pam213/); 2019- Emanuele Crosato, Sooraj Sekhar, Oscar Huaigu Xu; 2022- [David Shorten](https://scholar.google.com/citations?user=ggF3Lt4AAAAJ&hl=en)
|
||||
|
||||
*JIDT* provides a stand-alone, open-source code Java implementation (also usable in [Matlab, Octave](../../wiki/UseInOctaveMatlab), [Python](../../wiki/UseInPython), [R](../../wiki/UseInR), [Julia](../../wiki/UseInJulia) and [Clojure](../../wiki/UseInClojure)) of information-theoretic measures of distributed computation in complex systems: i.e. information storage, transfer and modification.
|
||||
|
||||
|
|
@ -8,17 +8,25 @@ JIDT includes implementations:
|
|||
* principally for the measures **transfer entropy**, **mutual information**, and their conditional variants, as well as **active information storage**, entropy, etc;
|
||||
* for both _discrete_ and _continuous_-valued data;
|
||||
* using various types of estimators (e.g. _Kraskov-Stögbauer-Grassberger estimators_, _box-kernel estimation_, _linear-Gaussian_),
|
||||
as described in full at ImplementedMeasures.
|
||||
as described in full at [ImplementedMeasures](../../wiki/ImplementedMeasures).
|
||||
|
||||
JIDT is easy to use:
|
||||
* It ships with a **GUI application** -- the [AutoAnalyser](../../wiki/AutoAnalyser), see picture below -- to facilitate point-and-click analysis, as well as code template generation for more complex analysis.
|
||||
* We provide **short video lectures** and corresponding slides in a (beta) [Course](../../wiki/Course) on how to understand using information-theoretic tools to analyse complex systems, and to implement such analysis with JIDT.
|
||||
|
||||
JIDT is distributed under the [GNU GPL v3 license](http://www.gnu.org/licenses/gpl.html) (or later).
|
||||
|
||||
# Getting started
|
||||
|
||||
1. [Download](../../wiki/Downloads) and [Installation](../../wiki/Installation) is very easy!
|
||||
1. _Quick start_: download the latest [v1.3 full distribution](http://lizier.me/joseph/software/jidt/download.php?file=infodynamics-dist-1.3.zip) (suitable for all platforms) and see the readme.txt file therein.
|
||||
1. [Documentation](../../wiki/Documentation) including: the paper describing JIDT at [arXiv:1408.3270](http://arxiv.org/abs/1408.3270) (distributed with the toolkit), a [Tutorial](../../wiki/Tutorial), and [Javadocs (v1.3 here)](http://lizier.me/joseph/software/jidt/javadocs/v1.3/);
|
||||
1. [Demos](../../wiki/Demos) are included with the full distribution, including a [GUI app](../../wiki/AutoAnalyser) for automatic analysis and code generation, [simple java demos](../../wiki/SimpleJavaExamples) and [cellular automata (CA) demos](../../wiki/CellularAutomataDemos).
|
||||
1. These Java tools can easily be used in [Matlab/Octave](../../wiki/OctaveMatlabExamples), [Python](../../wiki/PythonExamples), [R](../../wiki/R_Examples), [Julia](../../wiki/JuliaExamples) and [Clojure](../../wiki/Clojure_Examples)! (click on each language here for examples)
|
||||
1. [Download](../../wiki/Downloads) and [Installation](../../wiki/Installation) is very easy!
|
||||
1. _Quick start_: take a `git clone` (then build via [AntScripts](../../wiki/AntScripts)) OR download the latest [v1.6.1 full distribution](https://lizier.me/joseph/software/jidt/download.php?file=infodynamics-dist-1.6.1.zip) (suitable for all platforms) and see the readme.txt file therein.
|
||||
1. [Documentation](../../wiki/Documentation) including: the paper describing JIDT at [arXiv:1408.3270](http://arxiv.org/abs/1408.3270) (distributed with the toolkit), a (beta) [Course](../../wiki/Course) including short video lectures and a shorter [Tutorial](../../wiki/Tutorial), and [Javadocs (v1.6.1 here)](https://lizier.me/joseph/software/jidt/javadocs/v1.6.1/);
|
||||
1. [Demos](../../wiki/Demos) are included with the full distribution, including a [GUI app](../../wiki/AutoAnalyser) for automatic analysis and code generation (see picture below), [simple java demos](../../wiki/SimpleJavaExamples) and [cellular automata (CA) demos](../../wiki/CellularAutomataDemos).
|
||||
1. These Java tools can easily be used in [Matlab/Octave](../../wiki/OctaveMatlabExamples), [Python](../../wiki/PythonExamples), [R](../../wiki/R_Examples), [Julia](../../wiki/JuliaExamples) and [Clojure](../../wiki/Clojure_Examples)! (click on each language here for examples)
|
||||
|
||||
[](../../wiki/AutoAnalyser)
|
||||
|
||||
[](../../wiki/Course)
|
||||
|
||||
For further information or announcements:
|
||||
* Join our discussion group: http://groups.google.com/d/forum/jidt-discuss
|
||||
|
|
@ -37,6 +45,52 @@ See other [PublicationsUsingThisToolkit](../../wiki/PublicationsUsingThisToolkit
|
|||
|
||||
# News
|
||||
|
||||
_22/08/2023_ - New full distribution files available for **release v1.6.1**; Changes for v1.6.1 include:
|
||||
Minor updates to supporting use in Python, including virtual environments;
|
||||
Minor tweaks to fish schooling examples (mostly comments).
|
||||
|
||||
_5/09/2022_ - New full distribution files available for **release v1.6**; Changes for v1.6 include:
|
||||
Adding Flocking/Schooling/Swarming demo;
|
||||
Included Pedro's code on IIT and O-/S-Information measures;
|
||||
Spiking TE estimator added from David;
|
||||
Fixed up AutoAnalyser to work well for Python3 and numpy;
|
||||
Links to lecture videos included in the beta wiki for the course;
|
||||
Added rudimentary effective network inference (simplified version of the IDTxl full algorithm) in demos/octave/EffectiveNetworkInference;
|
||||
|
||||
_26/11/2018_ - New jar and full distribution files available for **release v1.5**; Changes for v1.5 include:
|
||||
Added GPU (cuda) capability for KSG Conditional Mutual Information calculator (proper documentation to come), brief [wiki page](../../wiki/GPU) and unit tests included;
|
||||
Added auto-embedding for TE/AIS with multivariate KSG, and univariate and multivariate Gaussian estimator (plus unit tests), for Ragwitz criteria and Maximum bias-corrected AIS, and also added Maximum bias corrected AIS and TE to handle source embedding as well;
|
||||
Kozachenko entropy estimator adds noise to data by default;
|
||||
Added bias-correction property to Gaussian and Kernel estimators for MI and conditional MI, including with surrogates (only option for kernel);
|
||||
Enabled use of different bases for different variables in MI discrete estimator;
|
||||
All new above features enabled in AutoAnalyser;
|
||||
Added drop-down menus for parameters in AutoAnalyser;
|
||||
Included long-form lecture slides in course folder;
|
||||
|
||||
_26/11/2017_ - New jar and full distribution files available for **release v1.4**; Changes for v1.4 include:
|
||||
Major expansion of functionality for AutoAnalysers: adding Launcher applet and capability to double click jar to launch, added Entropy, CMI, CTE and AIS AutoAnalysers, also added binned estimator type, added all variables/pairs analysis, added statistical significance analysis, and ensured functionality of generated Python code with Python3;
|
||||
Added GPU (cuda) capability for KSG Mutual Information calculator (proper documentation and wiki page to come), including unit tests;
|
||||
Added fast neighbour search implementations for mixed discrete-continuous KSG MI estimators;
|
||||
Expanded Gaussian estimator for multi-information (integration);
|
||||
Made all demo/data files readable by Matlab.
|
||||
|
||||
_17/12/2016_ - New book out from J. Lizier et al., ["An Introduction to Transfer Entropy: Information Flow in Complex Systems"](http://bit.ly/te-book-2016) published by Springer, which contains various examples using JIDT (distributed in our releases)
|
||||
|
||||
_21/10/2016_ - New jar and full distribution files available for **release v1.3.1**; Changes for v1.3.1 include:
|
||||
Major update to TransferEntropyCalculatorDiscrete so as to implement arbitrary source and dest embeddings and source-dest delay;
|
||||
Conditional TE calculators (continuous) handle empty conditional variables;
|
||||
Added new auto-embedding method for AIS and TE which maximises bias corrected AIS;
|
||||
Added getNumSeparateObservations() method to TE calculators to make reconstructing/separating local values easier after multiple addObservations() calls;
|
||||
Fixed kernel estimator classes to return proper densities, not probabilities;
|
||||
Bug fix in mixed discrete-continuous MI (Kraskov) implementation;
|
||||
Added simple interface for adding joint observations for MultiInfoCalculatorDiscrete
|
||||
Including compiled class files for the AutoAnalyser demo in distribution;
|
||||
Updated Python demo 1 to show use of numpy arrays with ints;
|
||||
Added Python demo 7 and 9 for TE Kraskov with ensemble method and auto-embedding respectively;
|
||||
Added Matlab/Octave example 10 for conditional TE via Kraskov (KSG) algorithm;
|
||||
Added utilities to prepare for enhancing surrogate calculations with fast nearest neighbour search;
|
||||
Minor bug patch to Python readFloatsFile utility.
|
||||
|
||||
_19/7/2015_ - New jar and full distribution files available for **release v1.3**; Changes for v1.3 include:
|
||||
Added AutoAnalyser (Code Generator) GUI demo for MI and TE;
|
||||
Added auto-embedding capability via Ragwitz criteria for AIS and TE calculators (KSG estimators);
|
||||
|
|
@ -96,3 +150,11 @@ _19/11/2012_ - New jar and full distribution files available for v0.1.2, includi
|
|||
_31/10/2012_ - Jar and full distribution files available for v0.1.1 (first distribution)
|
||||
|
||||
_7/5/2012_ - JIDT project created and code uploaded
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
This project has been supported by funding through:
|
||||
* Australian Research Council Discovery Early Career Researcher Award (DECRA) "Relating function of complex networks to structure using information theory", J.T. Lizier, 2016-19 DE160100630
|
||||
* Universities Australia - Deutscher Akademischer Austauschdienst (German Academic Exchange Service) UA-DAAD Australia-Germany Joint Research Co-operation grant "Measuring neural information synthesis and its impairment", Wibral, Lizier, Priesemann, Wollstadt, Finn, 2016-17
|
||||
* University of Sydney Research Accelerator (SOAR) Fellowship 2019 Scheme, J.T. Lizier (CI), 2019-2020
|
||||
* Australian Research Council Discovery Project "Large-scale computational modelling of epidemics in Australia: analysis, prediction and mitigation", M. Prokopenko, P. Pattison, M. Gambhir, J.T. Lizier, M. Piraveenan, 2016-19 DP160102742
|
||||
|
|
|
|||
192
build.xml
192
build.xml
|
|
@ -1,72 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="build" name="Java Information Dynamics Toolkit">
|
||||
<project basedir="." default="build" name="Java Information Dynamics Toolkit" xmlns:if="ant:if">
|
||||
<description>
|
||||
Build file for the Java Information Dynamics Toolkit
|
||||
</description>
|
||||
|
||||
<!-- set global properties for this build -->
|
||||
<property name="version" value="1.3.1"/>
|
||||
<property name="version" value="1.6.1"/>
|
||||
<property name="mainfilename" value="infodynamics"/>
|
||||
<property name="jarplainname" value="${mainfilename}.jar" />
|
||||
<property name="jarversiondistnamezip" value="${mainfilename}-jar-${version}.zip" />
|
||||
<property name="distname" value="${mainfilename}-dist-${version}" />
|
||||
<property name="distnamezip" value="${distname}.zip" />
|
||||
<property name="distnametargz" value="${distname}.tar.gz" />
|
||||
<property name="src" location="java/source"/>
|
||||
<property name="cudasrc" location="cuda"/>
|
||||
<property name="bin" location="bin"/>
|
||||
<property name="unittestsouttoplevel" location="unittests"/>
|
||||
<property name="unittestssrc" location="java/unittests"/>
|
||||
<property name="unittestsbin" location="${unittestsouttoplevel}/bin"/>
|
||||
<property name="autoanalyserdemo" location="demos/java"/>
|
||||
<property name="reports.tests" location="${unittestsouttoplevel}/reports"/>
|
||||
<property name="javadocsdir" location="javadocs"/>
|
||||
<property name="versionfile" value="version-${version}.txt"/>
|
||||
<!-- To enable GPU code, set the following variable to true -->
|
||||
<property name="enablegpu" value="false"/>
|
||||
|
||||
<path id="project.classpath">
|
||||
<pathelement location="bin"/>
|
||||
</path>
|
||||
|
||||
|
||||
<path id="apache-classpath">
|
||||
<pathelement path="./share/commons-math3-3.5.jar"/>
|
||||
</path>
|
||||
|
||||
<!-- Make required directories -->
|
||||
<target name="init" description="Create the compiled code directories">
|
||||
<mkdir dir="${bin}"/>
|
||||
<mkdir dir="${bin}/cuda"/>
|
||||
<mkdir dir="${unittestsbin}"/>
|
||||
<mkdir dir="${unittestsbin}/cuda"/>
|
||||
<mkdir dir="${reports.tests}"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Compile the java toolkit -->
|
||||
<target name="compile" depends="init" description="compile the source and unittests">
|
||||
<!-- Compile to Java 6 to provide compatibility for users with older JREs.
|
||||
<target name="compile" depends="init" description="compile the source">
|
||||
<!-- Compile to Java 8 to provide compatibility for users with older JREs.
|
||||
Caveat: The flags here only check the language compatibility, but
|
||||
may still use newer libraries which may cause issues for users with JDK 6.
|
||||
Indeed, one gets the warning: "bootstrap class path not set in conjunction with -source 1.6"
|
||||
may still use newer libraries which may cause issues for users with JDK 8.
|
||||
Indeed, one gets the warning: "bootstrap class path not set in conjunction with -source 1.8"
|
||||
To fix this, one would use the bootstrap classpath to point our JDK to an rt.jar
|
||||
for Java 6.
|
||||
At this stage, I'm sure I'm not using new library calls from Java 7, so we can
|
||||
ignore the warning, and I don't want to bother installing Java 6 just to compile
|
||||
like this. I'll endeavour not to use JDK 7 libraries so as not to cause
|
||||
for Java 8.
|
||||
At this stage, I'm sure I'm not using new library calls from Java 9+, so we can
|
||||
ignore the warning, and I don't want to bother installing newer Java just to compile
|
||||
like this. I'll endeavour not to use JDK 9+ libraries so as not to cause
|
||||
any issues here ... -->
|
||||
<javac srcdir="${src}" destdir="${bin}" includeAntRuntime="false" target="1.6" source="1.6"/>
|
||||
<javac srcdir="${src}" destdir="${bin}" includeAntRuntime="false" target="1.8" source="1.8" encoding="UTF8">
|
||||
<classpath refid="apache-classpath"/>
|
||||
</javac>
|
||||
|
||||
<!-- Compiling Cpp code -->
|
||||
<antcall target="gpu" if:true="${enablegpu}">
|
||||
<param name="DEBUG" value="0"/>
|
||||
</antcall>
|
||||
|
||||
</target>
|
||||
|
||||
<!-- Jar the toolkit -->
|
||||
<target name="jar" depends="compile" description="Create the jar for distribution">
|
||||
<!-- Put everything in ${bin} into the infodynamics-${version}.jar file -->
|
||||
<jar jarfile="${jarplainname}" basedir="${bin}"/>
|
||||
<jar jarfile="${jarplainname}" basedir="${bin}" excludes="cuda/*.o,cuda/*.a,cuda/findComputeCapability">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="infodynamics.demos.autoanalysis.AutoAnalyserLauncher"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
<!-- Set the jar to be runnable: (only has effect on unix/linux)
|
||||
so we can double click to run the AutoAnalyser -->
|
||||
<chmod file="${jarplainname}" perm="u+x"/>
|
||||
</target>
|
||||
|
||||
<!-- Compile and run the JUnit tests -->
|
||||
<target name="junit" depends="compile" description="Run the junit tests and make sure they compile">
|
||||
|
||||
<!-- Compile the junit tests first -->
|
||||
<javac destdir="${unittestsbin}" includeAntRuntime="true">
|
||||
<javac destdir="${unittestsbin}" includeAntRuntime="true" debug="true">
|
||||
<!-- Need includeAntRuntime=true if you want to pick up junit.jar and ant-junit.jar in ANT_HOME/lib;
|
||||
Otherwise you will need to set the classpath to include these here. -->
|
||||
<src path="${unittestssrc}"/>
|
||||
<classpath refid="project.classpath"/>
|
||||
<classpath refid="apache-classpath"/>
|
||||
</javac>
|
||||
|
||||
<!-- Run the junit tests and make sure they complete ok -->
|
||||
<junit printsummary="yes" showoutput="yes" haltonfailure="yes" haltonerror="yes" includeantruntime="true">
|
||||
<junit printsummary="yes" showoutput="yes" fork="true" forkmode="once" haltonfailure="yes" haltonerror="yes" includeantruntime="true">
|
||||
<classpath>
|
||||
<path refid="project.classpath"/>
|
||||
<pathelement path="${unittestsbin}"/>
|
||||
<pathelement location="${basedir}/clover.jar"/>
|
||||
</classpath>
|
||||
<formatter type="plain"/>
|
||||
<batchtest todir="${reports.tests}"> <!-- Writes full reports with stdout and stderr to ${reports.tests} -->
|
||||
|
|
@ -75,37 +103,25 @@
|
|||
<exclude name="**/*AbstractTester.class"/>
|
||||
<exclude name="**/ActiveInfoStorageCalculatorCorrelationIntegrals.class"/>
|
||||
<exclude name="**/ActiveInfoStorageCalculatorKernelDirect.class"/>
|
||||
<exclude name="**/*GPU*.class"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
</junit>
|
||||
|
||||
<!-- If applicable, run also GPU tests -->
|
||||
<antcall target="gputest" if:true="${enablegpu}"/>
|
||||
</target>
|
||||
|
||||
<!-- Compile the autoanalyser demo -->
|
||||
<target name="autoanalyser" depends="jar" description="compile the autoanalyser demo">
|
||||
<!-- Compile to Java 6 to provide compatibility for users with older JREs.
|
||||
Caveat: The flags here only check the language compatibility, but
|
||||
may still use newer libraries which may cause issues for users with JDK 6.
|
||||
Indeed, one gets the warning: "bootstrap class path not set in conjunction with -source 1.6"
|
||||
To fix this, one would use the bootstrap classpath to point our JDK to an rt.jar
|
||||
for Java 6.
|
||||
At this stage, I'm sure I'm not using new library calls from Java 7, so we can
|
||||
ignore the warning, and I don't want to bother installing Java 6 just to compile
|
||||
like this. I'll endeavour not to use JDK 7 libraries so as not to cause
|
||||
any issues here ... -->
|
||||
<javac srcdir="${autoanalyserdemo}" includes="infodynamics/demos/autoanalysis/*.java" excludes="infodynamics/demos/autoanalysis/GeneratedCalculator.java" includeAntRuntime="false" target="1.6" source="1.6">
|
||||
<classpath refid="project.classpath"/>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- Make javadocs, excluding the classes we've derived from Apache Commons Math -->
|
||||
<!-- Make javadocs, excluding the AutoAnalyser and classes we've derived from Apache Commons Math -->
|
||||
<target name="javadocs" depends="compile" description="Make the javadocs for the toolkit">
|
||||
<delete dir="${javadocsdir}"/>
|
||||
<javadoc destdir="${javadocsdir}">
|
||||
<fileset dir="${src}">
|
||||
<include name="**/*.java"/>
|
||||
<exclude name="**/commonsmath3/*.java"/>
|
||||
<exclude name="**/commonsmath3/**/*.java"/>
|
||||
</fileset>
|
||||
<packageset dir="${src}">
|
||||
<include name="**"/>
|
||||
<exclude name="infodynamics/demos/**"/>
|
||||
<exclude name="**/commonsmath3/*"/>
|
||||
<exclude name="**/commonsmath3/**"/>
|
||||
</packageset>
|
||||
</javadoc>
|
||||
<!-- Change some of the style in the javadocs css for our lists: -->
|
||||
<concat destfile="${javadocsdir}/stylesheet.css" append="true">
|
||||
|
|
@ -133,6 +149,7 @@
|
|||
<delete file="demos/clojure/project.clj"/>
|
||||
<delete file="${jarversiondistnamezip}"/>
|
||||
<delete file="${distnamezip}"/>
|
||||
<delete file="${distnametargz}"/>
|
||||
<delete file="${jarplainname}"/>
|
||||
<delete>
|
||||
<fileset dir="demos/AutoAnalyser" includes="GeneratedCalculator.*"/>
|
||||
|
|
@ -140,6 +157,7 @@
|
|||
<delete>
|
||||
<fileset dir="demos/java/infodynamics/demos/autoanalysis" includes="GeneratedCalculator.*"/>
|
||||
</delete>
|
||||
<antcall target="gpuclean"/>
|
||||
<!-- Don't delete the readme and version files - the user may not have the template to recreate them from -->
|
||||
</target>
|
||||
|
||||
|
|
@ -156,6 +174,83 @@
|
|||
***********************************
|
||||
-->
|
||||
|
||||
<!-- Compile and jar the toolkit with debug symbols -->
|
||||
<target name="debug" depends="init" description="compile and jar with debug symbols">
|
||||
<echo message="Compiling for debug"/>
|
||||
<javac srcdir="${src}" destdir="${bin}" includeAntRuntime="false" target="1.7" source="1.7" debug="true">
|
||||
<classpath refid="apache-classpath"/>
|
||||
</javac>
|
||||
|
||||
<!-- Compiling Cpp code -->
|
||||
<antcall target="gpu" if:true="${enablegpu}">
|
||||
<param name="DEBUG" value="1"/>
|
||||
</antcall>
|
||||
|
||||
<jar jarfile="${jarplainname}" basedir="${bin}"/>
|
||||
</target>
|
||||
|
||||
<!-- Compile GPU code -->
|
||||
<target name="gpu" depends="init" description="compile only C/C++ GPU code">
|
||||
<exec executable="make" dir="${cudasrc}" failonerror="true" resultproperty="return.code">
|
||||
<env key="DEBUG" value="${DEBUG}"/>
|
||||
</exec>
|
||||
<fail>
|
||||
<condition>
|
||||
<isfailure code="${return.code}"/>
|
||||
</condition>
|
||||
</fail>
|
||||
</target>
|
||||
|
||||
<!-- Clean GPU code -->
|
||||
<target name="gpuclean" description="clean only C/C++ GPU code">
|
||||
<exec executable="make" dir="${cudasrc}" failonerror="true">
|
||||
<arg value="clean"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Test GPU code -->
|
||||
<target name="gputest" depends="gpu, compile" description="compile and run C unit tests for GPU code">
|
||||
|
||||
<!-- Compile and run the C-only GPU unit tests -->
|
||||
<exec executable="make" dir="${cudasrc}" failonerror="true" resultproperty="return.code">
|
||||
<arg value="test"/>
|
||||
</exec>
|
||||
<fail>
|
||||
<condition>
|
||||
<isfailure code="${return.code}"/>
|
||||
</condition>
|
||||
</fail>
|
||||
|
||||
<exec executable="./unittest" dir="${unittestsbin}/cuda/" failonerror="true" resultproperty="return.code"/>
|
||||
<fail>
|
||||
<condition>
|
||||
<isfailure code="${return.code}"/>
|
||||
</condition>
|
||||
</fail>
|
||||
|
||||
<!-- Compile and run the Java+C GPU tests -->
|
||||
<javac destdir="${unittestsbin}" includeAntRuntime="true" debug="true">
|
||||
<src path="${unittestssrc}"/>
|
||||
<classpath refid="project.classpath"/>
|
||||
<classpath refid="apache-classpath"/>
|
||||
</javac>
|
||||
|
||||
<junit printsummary="yes" showoutput="yes" fork="true" forkmode="once" haltonfailure="yes" haltonerror="yes" includeantruntime="true">
|
||||
<classpath>
|
||||
<path refid="project.classpath"/>
|
||||
<pathelement path="${unittestsbin}"/>
|
||||
<pathelement location="${basedir}/clover.jar"/>
|
||||
</classpath>
|
||||
<formatter type="plain"/>
|
||||
<batchtest todir="${reports.tests}"> <!-- Writes full reports with stdout and stderr to ${reports.tests} -->
|
||||
<fileset dir="${unittestsbin}">
|
||||
<include name="**/*GPU*.class"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
</junit>
|
||||
|
||||
</target>
|
||||
|
||||
<!-- Developer build - creates readme and version files -->
|
||||
<target name="readmefiles" description="developer: create the readme and version files from templates">
|
||||
<tstamp>
|
||||
|
|
@ -200,23 +295,30 @@
|
|||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- Developer build - builds everything and makes the full distribution file -->
|
||||
<target name="dist" depends="jar,junit,autoanalyser,javadocs,readmefiles" description="developer: generate the full distribution">
|
||||
<!-- Developer build - builds everything and makes the full distribution file in zip and tar.gz -->
|
||||
<target name="dist" depends="jar,junit,javadocs,readmefiles" description="developer: generate the full distribution">
|
||||
<echo message="${ant.project.name}: ${ant.file}"/>
|
||||
<zip destfile="${distnamezip}">
|
||||
<fileset file="build.xml"/>
|
||||
<fileset file="${jarplainname}"/>
|
||||
<zipfileset file="${jarplainname}" filemode="755"/>
|
||||
<fileset file="license-gplv3.txt"/>
|
||||
<fileset file="readme.txt"/>
|
||||
<fileset file="InfoDynamicsToolkit.pdf"/>
|
||||
<fileset file="JIDT-logo.png" erroronmissingdir="false"/> <!-- This file is missing in full repository versions -->
|
||||
<fileset file="${versionfile}"/>
|
||||
<zipfileset dir="java" includes="**/*.java" prefix="java"/>
|
||||
<zipfileset dir="demos" includes="**/*.*,**/*" excludes="clojure/deploy,clojure/deploy/*.*,python/*.pyc" prefix="demos"/>
|
||||
<zipfileset dir="demos" includes="**/*.*,**/*" excludes="clojure/deploy,clojure/deploy/*.*,python/*.pyc,**/*.sh,**/*.bat" prefix="demos"/>
|
||||
<zipfileset dir="demos" includes="**/*.sh,**/*.bat" prefix="demos" filemode="755"/> <!-- Do these separately to get executable permissions -->
|
||||
<zipfileset dir="javadocs" includes="**/*.*,**/*" prefix="javadocs"/>
|
||||
<zipfileset dir="notices" includes="**/*.*,**/*" prefix="notices"/>
|
||||
<zipfileset dir="tutorial" prefix="tutorial"/>
|
||||
<zipfileset dir="cuda" prefix="cuda" excludes="benchmark.sh"/>
|
||||
<zipfileset dir="cuda" prefix="cuda" includes="benchmark.sh" filemode="755"/> <!-- Do this separately to get executable permissions -->
|
||||
<zipfileset dir="course" prefix="course"/>
|
||||
<zipfileset dir="tutorial" prefix="tutorial"/> <!-- Get rid of this when tutorial is subsumed in course... -->
|
||||
<zipfileset dir="web" includes="JIDT-logo.png" prefix="" erroronmissingdir="false"/> <!-- This file is missing in zip dist versions -->
|
||||
</zip>
|
||||
<tar destfile="${distnametargz}" compression="gzip" longfile="posix"> <!-- for longfiles could also use "gnu" but apparently is slightly less widely supported -->
|
||||
<zipfileset src="${distnamezip}"/>
|
||||
</tar>
|
||||
</target>
|
||||
</project>
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
|||
# Course
|
||||
|
||||
Please see the [Course page](../../../wiki/Course) on our wiki for full information about the course, including lecture slides and videos.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
ifeq ($(JAVA_HOME),)
|
||||
JAVAC ?= $(realpath $(call which,javac))
|
||||
JAVA_HOME = $(abspath $(dir $(JAVAC))..)
|
||||
endif
|
||||
|
||||
ifneq ($(JAVA_HOME),)
|
||||
JNI_INCLUDE ?= $(JAVA_HOME)/include
|
||||
endif
|
||||
|
||||
ifeq ($(JNI_INCLUDE),)
|
||||
$(error could not determine JNI include dir, try specifying either \
|
||||
JAVA_HOME or JNI_INCLUDE)
|
||||
endif
|
||||
|
||||
TARGETTRIPLET := $(shell $(CC) -dumpmachine)
|
||||
ifeq ($(findstring mingw,$(TARGETTRIPLET)),mingw)
|
||||
JNI_PLATFORM:= win32
|
||||
else
|
||||
ifeq ($(findstring linux,$(TARGETTRIPLET)),linux)
|
||||
JNI_PLATFORM:= linux
|
||||
endif
|
||||
endif
|
||||
|
||||
JNI_PLATFORM_INCLUDE ?= $(JNI_INCLUDE)/$(JNI_PLATFORM)
|
||||
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
####################################################
|
||||
#
|
||||
# Makefile for GPU Kraskov Mutual Information code
|
||||
# inside the JIDT library.
|
||||
#
|
||||
# (C) Pedro Mediano, 2017
|
||||
#
|
||||
####################################################
|
||||
|
||||
## Common binaries and flags
|
||||
#---------------------------
|
||||
NVCC ?= nvcc
|
||||
GCC ?= g++
|
||||
CCFLAGS ?= -O3 -Wall -Wextra -Wno-unused-parameter
|
||||
NVCCFLAGS ?= -O3 -D_FORCE_INLINES -Xcompiler -Wall
|
||||
BIN =../bin/cuda
|
||||
UNITBIN =../unittests/bin/cuda
|
||||
|
||||
|
||||
## Quick check to find 'which' function
|
||||
#--------------------------------------
|
||||
ifeq ($(OS),Windows_NT)
|
||||
which = $(shell where $1)
|
||||
else
|
||||
which = $(shell which $1)
|
||||
endif
|
||||
|
||||
|
||||
## Check that NVCC is actually available
|
||||
#---------------------------------------
|
||||
ifeq ($(shell which ${NVCC}),)
|
||||
$(error No NVCC found. Check that CUDA is installed and that CUDA_PATH is set)
|
||||
endif
|
||||
|
||||
|
||||
## Find CUDA compute capability for target GPU
|
||||
#---------------------------------------------
|
||||
# Set this variable if you know your target compute capability(ies). Example:
|
||||
# COMPUTE_CAPABILITY = -arch=sm_30
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifeq (${COMPUTE_CAPABILITY},)
|
||||
ifeq ($(wildcard $(BIN)/findComputeCapability),)
|
||||
$(info Compiling diagnostics script)
|
||||
$(shell ${NVCC} -Wno-deprecated-gpu-targets -o $(BIN)/findComputeCapability findComputeCapability.cu)
|
||||
endif
|
||||
COMPUTE_CAPABILITY ?= $(shell $(BIN)/findComputeCapability)
|
||||
$(info CUDA compute capability found: ${COMPUTE_CAPABILITY})
|
||||
endif
|
||||
endif
|
||||
NVCCFLAGS += ${COMPUTE_CAPABILITY}
|
||||
|
||||
|
||||
## Find path to JNI header files
|
||||
#-------------------------------
|
||||
# Set these variables if you know where your JNI header files are
|
||||
# JNI_INCLUDE ?=
|
||||
# JNI_PLATFORM_INCLUDE ?=
|
||||
ifeq ($(and $(JNI_INCLUDE),$(JNI_PLATFORM_INCLUDE)),)
|
||||
include FindJNI.mk
|
||||
endif
|
||||
|
||||
|
||||
## Set more flags depending on make arguments
|
||||
#--------------------------------------------
|
||||
ifdef DEBUG
|
||||
CCFLAGS += -g
|
||||
NVCCFLAGS += -g -G
|
||||
endif
|
||||
|
||||
ifdef CUSTOMFLAGS
|
||||
CCFLAGS += ${CUSTOMFLAGS}
|
||||
NVCCFLAGS += ${CUSTOMFLAGS}
|
||||
endif
|
||||
|
||||
# Common includes and paths for CUDA. This assumes the CUDA toolkit is in PATH
|
||||
INCLUDES := -I. -I./cub -I${JNI_INCLUDE} -I${JNI_PLATFORM_INCLUDE}
|
||||
NVCCLDFLAGS := -L. -L$(BIN) -lcuda -lcudart
|
||||
|
||||
.PHONY: all clean test
|
||||
|
||||
# Main target rule
|
||||
all: $(BIN)/libKraskov.so
|
||||
|
||||
|
||||
## Compile device code
|
||||
#---------------------
|
||||
$(BIN)/gpuKnnLibrary.o: gpuKnnLibrary.c helperfunctions.cu gpuKnnBF_kernel.cu
|
||||
${NVCC} ${NVCCFLAGS} ${INCLUDES} -x cu -Xcompiler -fPIC -c gpuKnnLibrary.c -o $@
|
||||
|
||||
$(BIN)/libgpuKnnLibrary.a: $(BIN)/gpuKnnLibrary.o
|
||||
${AR} -r $@ $<
|
||||
|
||||
|
||||
## Compile host code
|
||||
#-------------------
|
||||
c_objects = $(addprefix $(BIN)/,digamma.o gpuMILibrary.o gpuCMILibrary.o kraskovCuda.o)
|
||||
|
||||
$(BIN)/%.o: %.c
|
||||
${GCC} ${INCLUDES} ${CCFLAGS} -x c -std=c99 -fPIC -c $< -o $@
|
||||
|
||||
|
||||
## Final shared library linking
|
||||
#------------------------------
|
||||
$(BIN)/libKraskov.so: $(BIN)/libgpuKnnLibrary.a $(c_objects)
|
||||
${NVCC} ${NVCCFLAGS} ${INCLUDES} -Xcompiler -fPIC -shared -o $@ $(c_objects) ${NVCCLDFLAGS} -lgpuKnnLibrary
|
||||
|
||||
|
||||
## Test binary targets
|
||||
#------------------------------
|
||||
test: $(UNITBIN)/unittest $(UNITBIN)/perftest
|
||||
|
||||
$(UNITBIN)/unittest: unittest.cpp $(BIN)/libKraskov.so
|
||||
${GCC} ${CCFLAGS} -std=c++11 $< -Wl,-rpath,"$(abspath $(BIN))" -L$(BIN) -lKraskov -o $@
|
||||
|
||||
$(UNITBIN)/perftest: perftest.cpp $(BIN)/libKraskov.so
|
||||
${GCC} ${CCFLAGS} -DTIMER -std=c++11 $< -Wl,-rpath,"$(abspath $(BIN))" -L$(BIN) -lKraskov -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/* $(UNITBIN)/*
|
||||
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
Memory management in KSG CUDA functions
|
||||
=======================================
|
||||
|
||||
This file contains information about how memory is allocated and managed in the
|
||||
CUDA KSG calculators. It is meant to act as documentation and as a tool for
|
||||
future developers.
|
||||
|
||||
All memory is allocated (in a single cudaMalloc call) and distributed in the
|
||||
function allocateDeviceMemory. The location of all pointers in memory is as
|
||||
follows (diagram not to scale):
|
||||
|
||||
_____
|
||||
float *pointset -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
float *distances -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
int *indexes -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
int *npoints_x -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
int *npoints_y -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
float *digammas -> |
|
||||
|
|
||||
|
|
||||
|_____
|
||||
|
||||
|
||||
|
||||
The pointset contains both the source and the target data. Source data is
|
||||
represented by a tensor X[i,j,k], where k runs across data samples, j across
|
||||
source dimensions and i across different surrogate shufflings (or different
|
||||
realisations of the process). Assume we have R realisations of a process with M
|
||||
variables, of N samples each. Then the arrangement of X in memory is:
|
||||
|
||||
_____ _
|
||||
float *pointset, *source -> | X[0,0,0] |
|
||||
| X[0,0,1] |
|
||||
| . |
|
||||
| . | Shuffle 0, dimension 0
|
||||
| . |
|
||||
| X[0,0,N] _
|
||||
| X[1,0,0] |
|
||||
| X[1,0,1] |
|
||||
| . |
|
||||
| . | Shuffle 1, dimension 0
|
||||
| . |
|
||||
| X[1,0,N] _|
|
||||
| .
|
||||
| . ... Dimension 0 of all other shuffles
|
||||
| .
|
||||
| X[R,0,N] _
|
||||
| X[0,1,0] |
|
||||
| X[0,1,1] |
|
||||
| . |
|
||||
| . | Shuffle 0, dimension 1
|
||||
| . |
|
||||
| X[0,1,N] _|
|
||||
| X[1,1,0] |
|
||||
| X[1,1,1] |
|
||||
| . | Shuffle 1, dimension 1
|
||||
| . |
|
||||
| . |
|
||||
| X[1,1,N] _|
|
||||
| .
|
||||
| . ... All other dimensions of all other shuffles
|
||||
| .
|
||||
| X[R,M,N]
|
||||
float *dest -> |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|_____
|
||||
|
||||
|
||||
dest follows the same structure as source.
|
||||
|
||||
Distances contains the distances between each point and its K nearest
|
||||
neighbours. D[p,q,r] represents the distance from point r to its q'th
|
||||
neighbour, for each shuffling p. Assume we have R realisations of a process of
|
||||
N samples each, and we are finding the K nearest neighbours of each point. Then
|
||||
the arrangement of D in memory is as follows:
|
||||
|
||||
_____ _
|
||||
float *distances -> | D[0,0,0] |
|
||||
| D[0,0,1] |
|
||||
| . |
|
||||
| . | Shuffle 0, distances to 1st neighbour
|
||||
| . |
|
||||
| D[0,0,N] _|
|
||||
| D[1,0,0] |
|
||||
| D[1,0,1] |
|
||||
| . |
|
||||
| . | Shuffle 1, distances to 1st neighbour
|
||||
| . |
|
||||
| D[1,0,N] _|
|
||||
| .
|
||||
| . ... Distances to 1st neighbour of all other surrogates
|
||||
| .
|
||||
| D[R,0,N] _
|
||||
| D[0,1,0] |
|
||||
| D[0,1,1] |
|
||||
| . |
|
||||
| . | Shuffle 0, distances to 2nd neighbour
|
||||
| . |
|
||||
| D[0,1,N] _|
|
||||
| .
|
||||
| . ... Distances to all other neighbours of all other surrogates
|
||||
| .
|
||||
| D[R,K-1,N]_
|
||||
float *radii -> | D[0,K,0] |
|
||||
| D[0,K,1] |
|
||||
| . |
|
||||
| . | Shuffle 0, distances to K-th neighbour
|
||||
| . |
|
||||
| D[1,K,N] _|
|
||||
| .
|
||||
| . ... Distances to K-th neighbour of all other surrogates
|
||||
| .
|
||||
|_____ D[R,K,N]
|
||||
|
||||
|
||||
The distances from each point to its K-th nearest neighbour are particularly
|
||||
important since they are used in other parts of the algorithm, and they are
|
||||
called the range search radii.
|
||||
|
||||
Indexes contain the index of each nearest neighbour of each point in the main
|
||||
pointset, and follow the same structure as distances.
|
||||
|
||||
Npoints contains the count of points -- in either the source (nx) or the dest
|
||||
(ny) -- that lie within the range search radius of each point. NX[i,j] represents
|
||||
the number of points in source which are closer to $j$ than $j$'s radii.
|
||||
|
||||
_____
|
||||
int *npoints, *nx -> | NX[0,0]
|
||||
| NX[0,1]
|
||||
| .
|
||||
| .
|
||||
| .
|
||||
| NX[0,N]
|
||||
| NX[1,0]
|
||||
| NX[1,1]
|
||||
| .
|
||||
| .
|
||||
| .
|
||||
| NX[R,N]
|
||||
int *ny -> | NY[0,0]
|
||||
| .
|
||||
| .
|
||||
| .
|
||||
|_____ NY[R,N]
|
||||
|
||||
The array ny contains the equivalent point count in dest, and follows the same
|
||||
structure as nx.
|
||||
|
||||
Digammas also follow the same structure as nx and ny, and are calculated as
|
||||
|
||||
digammas[i] = digamma(nx[i]+1) + digamma(ny[i]+1)
|
||||
|
||||
Reference: Kraskov, A., Stoegbauer, H., Grassberger, P., "Estimating mutual
|
||||
information", Physical Review E 69, (2004) 066138.
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ "$#" -lt "2" ]; then
|
||||
echo "Usage:\n\t./benchmark.sh tag iterator\n\nExample:\n\t./benchmark.sh \"kNN kernel\" \"seq 1 10\"\n";
|
||||
exit
|
||||
fi
|
||||
|
||||
# Very convenient functions to plot the results of this script
|
||||
# function plot { gnuplot -e "plot '$1' using 1:2; pause -1" ; }
|
||||
# function fitline { gnuplot -e "set fit quiet; f(x)=a*x+b; fit f(x) '$1' u 1:2 via a, b; plot '$1' u 1:2, f(x) t sprintf('f(x) = %.2fx + %.2f', a, b); pause -1" ;}
|
||||
|
||||
tag=$1
|
||||
vals=$(eval "$2")
|
||||
echo $vals
|
||||
|
||||
# Warm-up GPU before taking measurements
|
||||
warmup=5
|
||||
for n in $warmup ; do
|
||||
./perftest 10 >/dev/null
|
||||
done
|
||||
|
||||
for n in $vals ; do
|
||||
dur=$(./perftest $n | grep -i "$tag" | cut -f2 -d: | awk '{print $1}') ;
|
||||
echo "$n\t$dur" >> gpu_perftimes.txt ;
|
||||
done
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Lean performance timer structure.
|
||||
*/
|
||||
typedef struct CPerfTimer {
|
||||
struct timeval tv;
|
||||
unsigned long us;
|
||||
const char *tag;
|
||||
} CPerfTimer;
|
||||
|
||||
/**
|
||||
* Initialise and return a performance timer with a given tag.
|
||||
*
|
||||
* @param s tag string to be attached to the timer
|
||||
* @return new CPerfTimer
|
||||
*/
|
||||
static inline CPerfTimer startTimer(const char *s) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long us = 1000000 * tv.tv_sec + tv.tv_usec;
|
||||
CPerfTimer pf = { .tv = tv, .us = us, .tag = s};
|
||||
return pf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print time since creation of pt, together with pt's tag. Only prints
|
||||
* results if compiled with -DTIMER flag.
|
||||
*
|
||||
* @param pt CPerfTimer (usually initialised by startTimer())
|
||||
*/
|
||||
static inline void stopTimer(CPerfTimer pt) {
|
||||
#ifdef TIMER
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long us = 1000000 * tv.tv_sec + tv.tv_usec;
|
||||
if (strlen(pt.tag) < 50) {
|
||||
char buf[50] = " ";
|
||||
memcpy(buf, pt.tag, strlen(pt.tag));
|
||||
printf("%s: %.3fms\n", buf, (us - pt.us)/((double) 1000.0));
|
||||
} else {
|
||||
printf("%s: %.3fms\n", pt.tag, (us - pt.us)/((double) 1000.0));
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2010-2011, Duane Merrill. All rights reserved.
|
||||
Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the NVIDIA CORPORATION nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
<hr>
|
||||
<h3>About CUB</h3>
|
||||
|
||||
Current release: v1.6.4 (12/06/2016)
|
||||
|
||||
We recommend the [CUB Project Website](http://nvlabs.github.com/cub) and the [cub-users discussion forum](http://groups.google.com/group/cub-users) for further information and examples.
|
||||
|
||||
CUB provides state-of-the-art, reusable software components for every layer
|
||||
of the CUDA programming model:
|
||||
- [<b><em>Device-wide primitives</em></b>] (https://nvlabs.github.com/cub/group___device_module.html)
|
||||
- Sort, prefix scan, reduction, histogram, etc.
|
||||
- Compatible with CUDA dynamic parallelism
|
||||
- [<b><em>Block-wide "collective" primitives</em></b>] (https://nvlabs.github.com/cub/group___block_module.html)
|
||||
- I/O, sort, prefix scan, reduction, histogram, etc.
|
||||
- Compatible with arbitrary thread block sizes and types
|
||||
- [<b><em>Warp-wide "collective" primitives</em></b>] (https://nvlabs.github.com/cub/group___warp_module.html)
|
||||
- Warp-wide prefix scan, reduction, etc.
|
||||
- Safe and architecture-specific
|
||||
- [<b><em>Thread and resource utilities</em></b>](https://nvlabs.github.com/cub/group___thread_module.html)
|
||||
- PTX intrinsics, device reflection, texture-caching iterators, caching memory allocators, etc.
|
||||
|
||||

|
||||
|
||||
<br><hr>
|
||||
<h3>A Simple Example</h3>
|
||||
|
||||
```C++
|
||||
#include <cub/cub.cuh>
|
||||
|
||||
// Block-sorting CUDA kernel
|
||||
__global__ void BlockSortKernel(int *d_in, int *d_out)
|
||||
{
|
||||
using namespace cub;
|
||||
|
||||
// Specialize BlockRadixSort, BlockLoad, and BlockStore for 128 threads
|
||||
// owning 16 integer items each
|
||||
typedef BlockRadixSort<int, 128, 16> BlockRadixSort;
|
||||
typedef BlockLoad<int, 128, 16, BLOCK_LOAD_TRANSPOSE> BlockLoad;
|
||||
typedef BlockStore<int, 128, 16, BLOCK_STORE_TRANSPOSE> BlockStore;
|
||||
|
||||
// Allocate shared memory
|
||||
__shared__ union {
|
||||
typename BlockRadixSort::TempStorage sort;
|
||||
typename BlockLoad::TempStorage load;
|
||||
typename BlockStore::TempStorage store;
|
||||
} temp_storage;
|
||||
|
||||
int block_offset = blockIdx.x * (128 * 16); // OffsetT for this block's ment
|
||||
|
||||
// Obtain a segment of 2048 consecutive keys that are blocked across threads
|
||||
int thread_keys[16];
|
||||
BlockLoad(temp_storage.load).Load(d_in + block_offset, thread_keys);
|
||||
__syncthreads();
|
||||
|
||||
// Collectively sort the keys
|
||||
BlockRadixSort(temp_storage.sort).Sort(thread_keys);
|
||||
__syncthreads();
|
||||
|
||||
// Store the sorted segment
|
||||
BlockStore(temp_storage.store).Store(d_out + block_offset, thread_keys);
|
||||
}
|
||||
```
|
||||
|
||||
Each thread block uses cub::BlockRadixSort to collectively sort
|
||||
its own input segment. The class is specialized by the
|
||||
data type being sorted, by the number of threads per block, by the number of
|
||||
keys per thread, and implicitly by the targeted compilation architecture.
|
||||
|
||||
The cub::BlockLoad and cub::BlockStore classes are similarly specialized.
|
||||
Furthermore, to provide coalesced accesses to device memory, these primitives are
|
||||
configured to access memory using a striped access pattern (where consecutive threads
|
||||
simultaneously access consecutive items) and then <em>transpose</em> the keys into
|
||||
a [<em>blocked arrangement</em>](index.html#sec4sec3) of elements across threads.
|
||||
|
||||
Once specialized, these classes expose opaque \p TempStorage member types.
|
||||
The thread block uses these storage types to statically allocate the union of
|
||||
shared memory needed by the thread block. (Alternatively these storage types
|
||||
could be aliased to global memory allocations).
|
||||
|
||||
<br><hr>
|
||||
<h3>Stable Releases</h3>
|
||||
|
||||
CUB releases are labeled using version identifiers having three fields:
|
||||
*epoch.feature.update*. The *epoch* field corresponds to support for
|
||||
a major change in the CUDA programming model. The *feature* field
|
||||
corresponds to a stable set of features, functionality, and interface. The
|
||||
*update* field corresponds to a bug-fix or performance update for that
|
||||
feature set. At the moment, we do not publicly provide non-stable releases
|
||||
such as development snapshots, beta releases or rolling releases. (Feel free
|
||||
to contact us if you would like such things.) See the
|
||||
[CUB Project Website](http://nvlabs.github.com/cub) for more information.
|
||||
|
||||
<br><hr>
|
||||
<h3>Contributors</h3>
|
||||
|
||||
CUB is developed as an open-source project by [NVIDIA Research](http://research.nvidia.com). The primary contributor is [Duane Merrill](http://github.com/dumerrill).
|
||||
|
||||
<br><hr>
|
||||
<h3>Open Source License</h3>
|
||||
|
||||
CUB is available under the "New BSD" open-source license:
|
||||
|
||||
```
|
||||
Copyright (c) 2010-2011, Duane Merrill. All rights reserved.
|
||||
Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the NVIDIA CORPORATION nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
#/******************************************************************************
|
||||
# * Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
# * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
# *
|
||||
# * Redistribution and use in source and binary forms, with or without
|
||||
# * modification, are permitted provided that the following conditions are met:
|
||||
# * * Redistributions of source code must retain the above copyright
|
||||
# * notice, this list of conditions and the following disclaimer.
|
||||
# * * Redistributions in binary form must reproduce the above copyright
|
||||
# * notice, this list of conditions and the following disclaimer in the
|
||||
# * documentation and/or other materials provided with the distribution.
|
||||
# * * Neither the name of the NVIDIA CORPORATION nor the
|
||||
# * names of its contributors may be used to endorse or promote products
|
||||
# * derived from this software without specific prior written permission.
|
||||
# *
|
||||
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
# * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# *
|
||||
#******************************************************************************/
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Commandline Options
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# [sm=<XXX,...>] Compute-capability to compile for, e.g., "sm=200,300,350" (SM20 by default).
|
||||
|
||||
COMMA = ,
|
||||
ifdef sm
|
||||
SM_ARCH = $(subst $(COMMA),-,$(sm))
|
||||
else
|
||||
SM_ARCH = 200
|
||||
endif
|
||||
|
||||
ifeq (620, $(findstring 620, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_62,code=\"sm_62,compute_62\"
|
||||
SM_DEF += -DSM620
|
||||
TEST_ARCH = 620
|
||||
endif
|
||||
ifeq (610, $(findstring 610, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_61,code=\"sm_61,compute_61\"
|
||||
SM_DEF += -DSM610
|
||||
TEST_ARCH = 610
|
||||
endif
|
||||
ifeq (600, $(findstring 600, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_60,code=\"sm_60,compute_60\"
|
||||
SM_DEF += -DSM600
|
||||
TEST_ARCH = 600
|
||||
endif
|
||||
ifeq (520, $(findstring 520, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_52,code=\"sm_52,compute_52\"
|
||||
SM_DEF += -DSM520
|
||||
TEST_ARCH = 520
|
||||
endif
|
||||
ifeq (370, $(findstring 370, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_37,code=\"sm_37,compute_37\"
|
||||
SM_DEF += -DSM370
|
||||
TEST_ARCH = 370
|
||||
endif
|
||||
ifeq (350, $(findstring 350, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_35,code=\"sm_35,compute_35\"
|
||||
SM_DEF += -DSM350
|
||||
TEST_ARCH = 350
|
||||
endif
|
||||
ifeq (300, $(findstring 300, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_30,code=\"sm_30,compute_30\"
|
||||
SM_DEF += -DSM300
|
||||
TEST_ARCH = 300
|
||||
endif
|
||||
ifeq (210, $(findstring 210, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_20,code=\"sm_21,compute_20\"
|
||||
SM_DEF += -DSM210
|
||||
TEST_ARCH = 210
|
||||
endif
|
||||
ifeq (200, $(findstring 200, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_20,code=\"sm_20,compute_20\"
|
||||
SM_DEF += -DSM200
|
||||
TEST_ARCH = 200
|
||||
endif
|
||||
ifeq (130, $(findstring 130, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_13,code=\"sm_13,compute_13\"
|
||||
SM_DEF += -DSM130
|
||||
TEST_ARCH = 130
|
||||
endif
|
||||
ifeq (120, $(findstring 120, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_12,code=\"sm_12,compute_12\"
|
||||
SM_DEF += -DSM120
|
||||
TEST_ARCH = 120
|
||||
endif
|
||||
ifeq (110, $(findstring 110, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_11,code=\"sm_11,compute_11\"
|
||||
SM_DEF += -DSM110
|
||||
TEST_ARCH = 110
|
||||
endif
|
||||
ifeq (100, $(findstring 100, $(SM_ARCH)))
|
||||
SM_TARGETS += -gencode=arch=compute_10,code=\"sm_10,compute_10\"
|
||||
SM_DEF += -DSM100
|
||||
TEST_ARCH = 100
|
||||
endif
|
||||
|
||||
|
||||
# [cdp=<0|1>] CDP enable option (default: no)
|
||||
ifeq ($(cdp), 1)
|
||||
DEFINES += -DCUB_CDP
|
||||
CDP_SUFFIX = cdp
|
||||
NVCCFLAGS += -rdc=true -lcudadevrt
|
||||
else
|
||||
CDP_SUFFIX = nocdp
|
||||
endif
|
||||
|
||||
|
||||
# [force32=<0|1>] Device addressing mode option (64-bit device pointers by default)
|
||||
ifeq ($(force32), 1)
|
||||
CPU_ARCH = -m32
|
||||
CPU_ARCH_SUFFIX = i386
|
||||
else
|
||||
CPU_ARCH = -m64
|
||||
CPU_ARCH_SUFFIX = x86_64
|
||||
NPPI = -lnppi
|
||||
endif
|
||||
|
||||
|
||||
# [abi=<0|1>] CUDA ABI option (enabled by default)
|
||||
ifneq ($(abi), 0)
|
||||
ABI_SUFFIX = abi
|
||||
else
|
||||
NVCCFLAGS += -Xptxas -abi=no
|
||||
ABI_SUFFIX = noabi
|
||||
endif
|
||||
|
||||
|
||||
# [open64=<0|1>] Middle-end compiler option (nvvm by default)
|
||||
ifeq ($(open64), 1)
|
||||
NVCCFLAGS += -open64
|
||||
PTX_SUFFIX = open64
|
||||
else
|
||||
PTX_SUFFIX = nvvm
|
||||
endif
|
||||
|
||||
|
||||
# [verbose=<0|1>] Verbose toolchain output from nvcc option
|
||||
ifeq ($(verbose), 1)
|
||||
NVCCFLAGS += -v
|
||||
endif
|
||||
|
||||
|
||||
# [keep=<0|1>] Keep intermediate compilation artifacts option
|
||||
ifeq ($(keep), 1)
|
||||
NVCCFLAGS += -keep
|
||||
endif
|
||||
|
||||
# [debug=<0|1>] Generate debug mode code
|
||||
ifeq ($(debug), 1)
|
||||
NVCCFLAGS += -G
|
||||
endif
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Compiler and compilation platform
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
CUB_DIR = $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
NVCC = "$(shell which nvcc)"
|
||||
ifdef nvccver
|
||||
NVCC_VERSION = $(nvccver)
|
||||
else
|
||||
NVCC_VERSION = $(strip $(shell nvcc --version | grep release | sed 's/.*release //' | sed 's/,.*//'))
|
||||
endif
|
||||
|
||||
# detect OS
|
||||
OSUPPER = $(shell uname -s 2>/dev/null | tr [:lower:] [:upper:])
|
||||
|
||||
# Default flags: verbose kernel properties (regs, smem, cmem, etc.); runtimes for compilation phases
|
||||
NVCCFLAGS += $(SM_DEF) -Xptxas -v -Xcudafe -\#
|
||||
|
||||
ifeq (WIN_NT, $(findstring WIN_NT, $(OSUPPER)))
|
||||
# For MSVC
|
||||
# Enable more warnings and treat as errors
|
||||
NVCCFLAGS += -Xcompiler /W3 -Xcompiler /WX
|
||||
# Disable excess x86 floating point precision that can lead to results being labeled incorrectly
|
||||
NVCCFLAGS += -Xcompiler /fp:strict
|
||||
# Help the compiler/linker work with huge numbers of kernels on Windows
|
||||
NVCCFLAGS += -Xcompiler /bigobj -Xcompiler /Zm500
|
||||
CC = cl
|
||||
|
||||
# Multithreaded runtime
|
||||
NVCCFLAGS += -Xcompiler /MT
|
||||
|
||||
ifneq ($(force32), 1)
|
||||
CUDART_CYG = "$(shell dirname $(NVCC))/../lib/Win32/cudart.lib"
|
||||
else
|
||||
CUDART_CYG = "$(shell dirname $(NVCC))/../lib/x64/cudart.lib"
|
||||
endif
|
||||
CUDART = "$(shell cygpath -w $(CUDART_CYG))"
|
||||
else
|
||||
# For g++
|
||||
# Disable excess x86 floating point precision that can lead to results being labeled incorrectly
|
||||
NVCCFLAGS += -Xcompiler -ffloat-store
|
||||
CC = g++
|
||||
ifneq ($(force32), 1)
|
||||
CUDART = "$(shell dirname $(NVCC))/../lib/libcudart_static.a"
|
||||
else
|
||||
CUDART = "$(shell dirname $(NVCC))/../lib64/libcudart_static.a"
|
||||
endif
|
||||
endif
|
||||
|
||||
# Suffix to append to each binary
|
||||
BIN_SUFFIX = sm$(SM_ARCH)_$(PTX_SUFFIX)_$(NVCC_VERSION)_$(ABI_SUFFIX)_$(CDP_SUFFIX)_$(CPU_ARCH_SUFFIX)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Dependency Lists
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||
|
||||
CUB_DEPS = $(call rwildcard, $(CUB_DIR),*.cuh) \
|
||||
$(CUB_DIR)common.mk
|
||||
|
||||
|
|
@ -0,0 +1,783 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentHistogram implements a stateful abstraction of CUDA thread blocks for participating in device-wide histogram .
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../util_type.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../grid/grid_queue.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
enum BlockHistogramMemoryPreference
|
||||
{
|
||||
GMEM,
|
||||
SMEM,
|
||||
BLEND
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentHistogram
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _PIXELS_PER_THREAD, ///< Pixels per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
bool _RLE_COMPRESS, ///< Whether to perform localized RLE to compress samples before histogramming
|
||||
BlockHistogramMemoryPreference _MEM_PREFERENCE, ///< Whether to prefer privatized shared-memory bins (versus privatized global-memory bins)
|
||||
bool _WORK_STEALING> ///< Whether to dequeue tiles from a global work queue
|
||||
struct AgentHistogramPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
PIXELS_PER_THREAD = _PIXELS_PER_THREAD, ///< Pixels per thread (per tile of input)
|
||||
IS_RLE_COMPRESS = _RLE_COMPRESS, ///< Whether to perform localized RLE to compress samples before histogramming
|
||||
MEM_PREFERENCE = _MEM_PREFERENCE, ///< Whether to prefer privatized shared-memory bins (versus privatized global-memory bins)
|
||||
IS_WORK_STEALING = _WORK_STEALING, ///< Whether to dequeue tiles from a global work queue
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentHistogram implements a stateful abstraction of CUDA thread blocks for participating in device-wide histogram .
|
||||
*/
|
||||
template <
|
||||
typename AgentHistogramPolicyT, ///< Parameterized AgentHistogramPolicy tuning policy type
|
||||
int PRIVATIZED_SMEM_BINS, ///< Number of privatized shared-memory histogram bins of any channel. Zero indicates privatized counters to be maintained in device-accessible memory.
|
||||
int NUM_CHANNELS, ///< Number of channels interleaved in the input data. Supports up to four channels.
|
||||
int NUM_ACTIVE_CHANNELS, ///< Number of channels actively being histogrammed
|
||||
typename SampleIteratorT, ///< Random-access input iterator type for reading samples
|
||||
typename CounterT, ///< Integer type for counting sample occurrences per histogram bin
|
||||
typename PrivatizedDecodeOpT, ///< The transform operator type for determining privatized counter indices from samples, one for each channel
|
||||
typename OutputDecodeOpT, ///< The transform operator type for determining output bin-ids from privatized counter indices, one for each channel
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
int PTX_ARCH = CUB_PTX_ARCH> ///< PTX compute capability
|
||||
struct AgentHistogram
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// The sample type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
|
||||
/// The pixel type of SampleT
|
||||
typedef typename CubVector<SampleT, NUM_CHANNELS>::Type PixelT;
|
||||
|
||||
/// The quad type of SampleT
|
||||
typedef typename CubVector<SampleT, 4>::Type QuadT;
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentHistogramPolicyT::BLOCK_THREADS,
|
||||
|
||||
PIXELS_PER_THREAD = AgentHistogramPolicyT::PIXELS_PER_THREAD,
|
||||
SAMPLES_PER_THREAD = PIXELS_PER_THREAD * NUM_CHANNELS,
|
||||
QUADS_PER_THREAD = SAMPLES_PER_THREAD / 4,
|
||||
|
||||
TILE_PIXELS = PIXELS_PER_THREAD * BLOCK_THREADS,
|
||||
TILE_SAMPLES = SAMPLES_PER_THREAD * BLOCK_THREADS,
|
||||
|
||||
IS_RLE_COMPRESS = AgentHistogramPolicyT::IS_RLE_COMPRESS,
|
||||
|
||||
MEM_PREFERENCE = (PRIVATIZED_SMEM_BINS > 0) ?
|
||||
AgentHistogramPolicyT::MEM_PREFERENCE :
|
||||
GMEM,
|
||||
|
||||
IS_WORK_STEALING = AgentHistogramPolicyT::IS_WORK_STEALING,
|
||||
};
|
||||
|
||||
/// Cache load modifier for reading input elements
|
||||
static const CacheLoadModifier LOAD_MODIFIER = AgentHistogramPolicyT::LOAD_MODIFIER;
|
||||
|
||||
|
||||
/// Input iterator wrapper type (for applying cache modifier)
|
||||
typedef typename If<IsPointer<SampleIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<LOAD_MODIFIER, SampleT, OffsetT>, // Wrap the native input pointer with CacheModifiedInputIterator
|
||||
SampleIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedSampleIteratorT;
|
||||
|
||||
/// Pixel input iterator type (for applying cache modifier)
|
||||
typedef CacheModifiedInputIterator<LOAD_MODIFIER, PixelT, OffsetT>
|
||||
WrappedPixelIteratorT;
|
||||
|
||||
/// Qaud input iterator type (for applying cache modifier)
|
||||
typedef CacheModifiedInputIterator<LOAD_MODIFIER, QuadT, OffsetT>
|
||||
WrappedQuadIteratorT;
|
||||
|
||||
/// Parameterized BlockLoad type for samples
|
||||
typedef BlockLoad<
|
||||
SampleT,
|
||||
BLOCK_THREADS,
|
||||
SAMPLES_PER_THREAD,
|
||||
AgentHistogramPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadSampleT;
|
||||
|
||||
/// Parameterized BlockLoad type for pixels
|
||||
typedef BlockLoad<
|
||||
PixelT,
|
||||
BLOCK_THREADS,
|
||||
PIXELS_PER_THREAD,
|
||||
AgentHistogramPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadPixelT;
|
||||
|
||||
/// Parameterized BlockLoad type for quads
|
||||
typedef BlockLoad<
|
||||
QuadT,
|
||||
BLOCK_THREADS,
|
||||
QUADS_PER_THREAD,
|
||||
AgentHistogramPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadQuadT;
|
||||
|
||||
/// Shared memory type required by this thread block
|
||||
struct _TempStorage
|
||||
{
|
||||
CounterT histograms[NUM_ACTIVE_CHANNELS][PRIVATIZED_SMEM_BINS + 1]; // Smem needed for block-privatized smem histogram (with 1 word of padding)
|
||||
|
||||
int tile_idx;
|
||||
|
||||
union
|
||||
{
|
||||
typename BlockLoadSampleT::TempStorage sample_load; // Smem needed for loading a tile of samples
|
||||
typename BlockLoadPixelT::TempStorage pixel_load; // Smem needed for loading a tile of pixels
|
||||
typename BlockLoadQuadT::TempStorage quad_load; // Smem needed for loading a tile of quads
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/// Temporary storage type (unionable)
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Reference to temp_storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Sample input iterator (with cache modifier applied, if possible)
|
||||
WrappedSampleIteratorT d_wrapped_samples;
|
||||
|
||||
/// Native pointer for input samples (possibly NULL if unavailable)
|
||||
SampleT* d_native_samples;
|
||||
|
||||
/// The number of output bins for each channel
|
||||
int (&num_output_bins)[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// The number of privatized bins for each channel
|
||||
int (&num_privatized_bins)[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// Reference to gmem privatized histograms for each channel
|
||||
CounterT* d_privatized_histograms[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// Reference to final output histograms (gmem)
|
||||
CounterT* (&d_output_histograms)[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// The transform operator for determining output bin-ids from privatized counter indices, one for each channel
|
||||
OutputDecodeOpT (&output_decode_op)[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// The transform operator for determining privatized counter indices from samples, one for each channel
|
||||
PrivatizedDecodeOpT (&privatized_decode_op)[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
/// Whether to prefer privatized smem counters vs privatized global counters
|
||||
bool prefer_smem;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Initialize privatized bin counters
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Initialize privatized bin counters
|
||||
__device__ __forceinline__ void InitBinCounters(CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS])
|
||||
{
|
||||
// Initialize histogram bin counts to zeros
|
||||
#pragma unroll
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
{
|
||||
for (int privatized_bin = threadIdx.x; privatized_bin < num_privatized_bins[CHANNEL]; privatized_bin += BLOCK_THREADS)
|
||||
{
|
||||
privatized_histograms[CHANNEL][privatized_bin] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Barrier to make sure all threads are done updating counters
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
|
||||
// Initialize privatized bin counters. Specialized for privatized shared-memory counters
|
||||
__device__ __forceinline__ void InitSmemBinCounters()
|
||||
{
|
||||
CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
privatized_histograms[CHANNEL] = temp_storage.histograms[CHANNEL];
|
||||
|
||||
InitBinCounters(privatized_histograms);
|
||||
}
|
||||
|
||||
|
||||
// Initialize privatized bin counters. Specialized for privatized global-memory counters
|
||||
__device__ __forceinline__ void InitGmemBinCounters()
|
||||
{
|
||||
InitBinCounters(d_privatized_histograms);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Update final output histograms
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Update final output histograms from privatized histograms
|
||||
__device__ __forceinline__ void StoreOutput(CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS])
|
||||
{
|
||||
// Barrier to make sure all threads are done updating counters
|
||||
__syncthreads();
|
||||
|
||||
// Apply privatized bin counts to output bin counts
|
||||
#pragma unroll
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
{
|
||||
int channel_bins = num_privatized_bins[CHANNEL];
|
||||
for (int privatized_bin = threadIdx.x;
|
||||
privatized_bin < channel_bins;
|
||||
privatized_bin += BLOCK_THREADS)
|
||||
{
|
||||
int output_bin = -1;
|
||||
CounterT count = privatized_histograms[CHANNEL][privatized_bin];
|
||||
bool is_valid = count > 0;
|
||||
|
||||
output_decode_op[CHANNEL].BinSelect<LOAD_MODIFIER>((SampleT) privatized_bin, output_bin, is_valid);
|
||||
|
||||
if (output_bin >= 0)
|
||||
{
|
||||
atomicAdd(&d_output_histograms[CHANNEL][output_bin], count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update final output histograms from privatized histograms. Specialized for privatized shared-memory counters
|
||||
__device__ __forceinline__ void StoreSmemOutput()
|
||||
{
|
||||
CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS];
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
privatized_histograms[CHANNEL] = temp_storage.histograms[CHANNEL];
|
||||
|
||||
StoreOutput(privatized_histograms);
|
||||
}
|
||||
|
||||
|
||||
// Update final output histograms from privatized histograms. Specialized for privatized global-memory counters
|
||||
__device__ __forceinline__ void StoreGmemOutput()
|
||||
{
|
||||
StoreOutput(d_privatized_histograms);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tile accumulation
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Accumulate pixels. Specialized for RLE compression.
|
||||
__device__ __forceinline__ void AccumulatePixels(
|
||||
SampleT samples[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
bool is_valid[PIXELS_PER_THREAD],
|
||||
CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS],
|
||||
Int2Type<true> is_rle_compress)
|
||||
{
|
||||
|
||||
#pragma unroll
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
{
|
||||
// Bin pixels
|
||||
int bins[PIXELS_PER_THREAD];
|
||||
|
||||
#pragma unroll
|
||||
for (int PIXEL = 0; PIXEL < PIXELS_PER_THREAD; ++PIXEL)
|
||||
{
|
||||
bins[PIXEL] = -1;
|
||||
privatized_decode_op[CHANNEL].BinSelect<LOAD_MODIFIER>(samples[PIXEL][CHANNEL], bins[PIXEL], is_valid[PIXEL]);
|
||||
}
|
||||
|
||||
CounterT accumulator = 1;
|
||||
|
||||
#pragma unroll
|
||||
for (int PIXEL = 0; PIXEL < PIXELS_PER_THREAD - 1; ++PIXEL)
|
||||
{
|
||||
if (bins[PIXEL] == bins[PIXEL + 1])
|
||||
{
|
||||
accumulator++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bins[PIXEL] >= 0)
|
||||
atomicAdd(privatized_histograms[CHANNEL] + bins[PIXEL], accumulator);
|
||||
|
||||
accumulator = 1;
|
||||
}
|
||||
}
|
||||
// Last pixel
|
||||
if (bins[PIXELS_PER_THREAD - 1] >= 0)
|
||||
atomicAdd(privatized_histograms[CHANNEL] + bins[PIXELS_PER_THREAD - 1], accumulator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Accumulate pixels. Specialized for individual accumulation of each pixel.
|
||||
__device__ __forceinline__ void AccumulatePixels(
|
||||
SampleT samples[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
bool is_valid[PIXELS_PER_THREAD],
|
||||
CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS],
|
||||
Int2Type<false> is_rle_compress)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int PIXEL = 0; PIXEL < PIXELS_PER_THREAD; ++PIXEL)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
{
|
||||
int bin = -1;
|
||||
privatized_decode_op[CHANNEL].BinSelect<LOAD_MODIFIER>(samples[PIXEL][CHANNEL], bin, is_valid[PIXEL]);
|
||||
if (bin >= 0)
|
||||
atomicAdd(privatized_histograms[CHANNEL] + bin, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accumulate pixel, specialized for smem privatized histogram
|
||||
*/
|
||||
__device__ __forceinline__ void AccumulateSmemPixels(
|
||||
SampleT samples[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
bool is_valid[PIXELS_PER_THREAD])
|
||||
{
|
||||
CounterT* privatized_histograms[NUM_ACTIVE_CHANNELS];
|
||||
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
privatized_histograms[CHANNEL] = temp_storage.histograms[CHANNEL];
|
||||
|
||||
AccumulatePixels(samples, is_valid, privatized_histograms, Int2Type<IS_RLE_COMPRESS>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accumulate pixel, specialized for gmem privatized histogram
|
||||
*/
|
||||
__device__ __forceinline__ void AccumulateGmemPixels(
|
||||
SampleT samples[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
bool is_valid[PIXELS_PER_THREAD])
|
||||
{
|
||||
AccumulatePixels(samples, is_valid, d_privatized_histograms, Int2Type<IS_RLE_COMPRESS>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tile loading
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Load full, aligned tile using pixel iterator (multi-channel)
|
||||
template <int _NUM_ACTIVE_CHANNELS>
|
||||
__device__ __forceinline__ void LoadFullAlignedTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<_NUM_ACTIVE_CHANNELS> num_active_channels)
|
||||
{
|
||||
typedef PixelT AliasedPixels[PIXELS_PER_THREAD];
|
||||
|
||||
WrappedPixelIteratorT d_wrapped_pixels((PixelT*) (d_native_samples + block_offset));
|
||||
|
||||
// Load using a wrapped pixel iterator
|
||||
BlockLoadPixelT(temp_storage.pixel_load).Load(
|
||||
d_wrapped_pixels,
|
||||
reinterpret_cast<AliasedPixels&>(samples));
|
||||
}
|
||||
|
||||
// Load full, aligned tile using quad iterator (single-channel)
|
||||
__device__ __forceinline__ void LoadFullAlignedTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<1> num_active_channels)
|
||||
{
|
||||
typedef QuadT AliasedQuads[QUADS_PER_THREAD];
|
||||
|
||||
WrappedQuadIteratorT d_wrapped_quads((QuadT*) (d_native_samples + block_offset));
|
||||
|
||||
// Load using a wrapped quad iterator
|
||||
BlockLoadQuadT(temp_storage.quad_load).Load(
|
||||
d_wrapped_quads,
|
||||
reinterpret_cast<AliasedQuads&>(samples));
|
||||
}
|
||||
|
||||
// Load full, aligned tile
|
||||
__device__ __forceinline__ void LoadTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<true> is_full_tile,
|
||||
Int2Type<true> is_aligned)
|
||||
{
|
||||
LoadFullAlignedTile(block_offset, valid_samples, samples, Int2Type<NUM_ACTIVE_CHANNELS>());
|
||||
}
|
||||
|
||||
// Load full, mis-aligned tile using sample iterator
|
||||
__device__ __forceinline__ void LoadTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<true> is_full_tile,
|
||||
Int2Type<false> is_aligned)
|
||||
{
|
||||
typedef SampleT AliasedSamples[SAMPLES_PER_THREAD];
|
||||
|
||||
// Load using sample iterator
|
||||
BlockLoadSampleT(temp_storage.sample_load).Load(
|
||||
d_wrapped_samples + block_offset,
|
||||
reinterpret_cast<AliasedSamples&>(samples));
|
||||
}
|
||||
|
||||
// Load partially-full, aligned tile using the pixel iterator
|
||||
__device__ __forceinline__ void LoadTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<false> is_full_tile,
|
||||
Int2Type<true> is_aligned)
|
||||
{
|
||||
typedef PixelT AliasedPixels[PIXELS_PER_THREAD];
|
||||
|
||||
WrappedPixelIteratorT d_wrapped_pixels((PixelT*) (d_native_samples + block_offset));
|
||||
|
||||
int valid_pixels = valid_samples / NUM_CHANNELS;
|
||||
|
||||
// Load using a wrapped pixel iterator
|
||||
BlockLoadPixelT(temp_storage.pixel_load).Load(
|
||||
d_wrapped_pixels,
|
||||
reinterpret_cast<AliasedPixels&>(samples),
|
||||
valid_pixels);
|
||||
}
|
||||
|
||||
// Load partially-full, mis-aligned tile using sample iterator
|
||||
__device__ __forceinline__ void LoadTile(
|
||||
OffsetT block_offset,
|
||||
int valid_samples,
|
||||
SampleT (&samples)[PIXELS_PER_THREAD][NUM_CHANNELS],
|
||||
Int2Type<false> is_full_tile,
|
||||
Int2Type<false> is_aligned)
|
||||
{
|
||||
typedef SampleT AliasedSamples[SAMPLES_PER_THREAD];
|
||||
|
||||
BlockLoadSampleT(temp_storage.sample_load).Load(
|
||||
d_wrapped_samples + block_offset,
|
||||
reinterpret_cast<AliasedSamples&>(samples),
|
||||
valid_samples);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tile processing
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Consume a tile of data samples
|
||||
template <
|
||||
bool IS_ALIGNED, // Whether the tile offset is aligned (quad-aligned for single-channel, pixel-aligned for multi-channel)
|
||||
bool IS_FULL_TILE> // Whether the tile is full
|
||||
__device__ __forceinline__ void ConsumeTile(OffsetT block_offset, int valid_samples)
|
||||
{
|
||||
SampleT samples[PIXELS_PER_THREAD][NUM_CHANNELS];
|
||||
bool is_valid[PIXELS_PER_THREAD];
|
||||
|
||||
// Load tile
|
||||
LoadTile(
|
||||
block_offset,
|
||||
valid_samples,
|
||||
samples,
|
||||
Int2Type<IS_FULL_TILE>(),
|
||||
Int2Type<IS_ALIGNED>());
|
||||
|
||||
// Set valid flags
|
||||
#pragma unroll
|
||||
for (int PIXEL = 0; PIXEL < PIXELS_PER_THREAD; ++PIXEL)
|
||||
is_valid[PIXEL] = IS_FULL_TILE || (((threadIdx.x * PIXELS_PER_THREAD + PIXEL) * NUM_CHANNELS) < valid_samples);
|
||||
|
||||
// Accumulate samples
|
||||
#if CUB_PTX_ARCH >= 120
|
||||
if (prefer_smem)
|
||||
AccumulateSmemPixels(samples, is_valid);
|
||||
else
|
||||
AccumulateGmemPixels(samples, is_valid);
|
||||
#else
|
||||
AccumulateGmemPixels(samples, is_valid);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Consume row tiles. Specialized for work-stealing from queue
|
||||
template <bool IS_ALIGNED>
|
||||
__device__ __forceinline__ void ConsumeTiles(
|
||||
OffsetT num_row_pixels, ///< The number of multi-channel pixels per row in the region of interest
|
||||
OffsetT num_rows, ///< The number of rows in the region of interest
|
||||
OffsetT row_stride_samples, ///< The number of samples between starts of consecutive rows in the region of interest
|
||||
int tiles_per_row, ///< Number of image tiles per row
|
||||
GridQueue<int> tile_queue,
|
||||
Int2Type<true> is_work_stealing)
|
||||
{
|
||||
|
||||
int num_tiles = num_rows * tiles_per_row;
|
||||
int tile_idx = (blockIdx.y * gridDim.x) + blockIdx.x;
|
||||
OffsetT num_even_share_tiles = gridDim.x * gridDim.y;
|
||||
|
||||
while (tile_idx < num_tiles)
|
||||
{
|
||||
int row = tile_idx / tiles_per_row;
|
||||
int col = tile_idx - (row * tiles_per_row);
|
||||
OffsetT row_offset = row * row_stride_samples;
|
||||
OffsetT col_offset = (col * TILE_SAMPLES);
|
||||
OffsetT tile_offset = row_offset + col_offset;
|
||||
|
||||
if (col == tiles_per_row - 1)
|
||||
{
|
||||
// Consume a partially-full tile at the end of the row
|
||||
OffsetT num_remaining = (num_row_pixels * NUM_CHANNELS) - col_offset;
|
||||
ConsumeTile<IS_ALIGNED, false>(tile_offset, num_remaining);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume full tile
|
||||
ConsumeTile<IS_ALIGNED, true>(tile_offset, TILE_SAMPLES);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Get next tile
|
||||
if (threadIdx.x == 0)
|
||||
temp_storage.tile_idx = tile_queue.Drain(1) + num_even_share_tiles;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
tile_idx = temp_storage.tile_idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Consume row tiles. Specialized for even-share (striped across thread blocks)
|
||||
template <bool IS_ALIGNED>
|
||||
__device__ __forceinline__ void ConsumeTiles(
|
||||
OffsetT num_row_pixels, ///< The number of multi-channel pixels per row in the region of interest
|
||||
OffsetT num_rows, ///< The number of rows in the region of interest
|
||||
OffsetT row_stride_samples, ///< The number of samples between starts of consecutive rows in the region of interest
|
||||
int tiles_per_row, ///< Number of image tiles per row
|
||||
GridQueue<int> tile_queue,
|
||||
Int2Type<false> is_work_stealing)
|
||||
{
|
||||
for (int row = blockIdx.y; row < num_rows; row += gridDim.y)
|
||||
{
|
||||
OffsetT row_begin = row * row_stride_samples;
|
||||
OffsetT row_end = row_begin + (num_row_pixels * NUM_CHANNELS);
|
||||
OffsetT tile_offset = row_begin + (blockIdx.x * TILE_SAMPLES);
|
||||
|
||||
while (tile_offset < row_end)
|
||||
{
|
||||
OffsetT num_remaining = row_end - tile_offset;
|
||||
|
||||
if (num_remaining < TILE_SAMPLES)
|
||||
{
|
||||
// Consume partial tile
|
||||
ConsumeTile<IS_ALIGNED, false>(tile_offset, num_remaining);
|
||||
break;
|
||||
}
|
||||
|
||||
// Consume full tile
|
||||
ConsumeTile<IS_ALIGNED, true>(tile_offset, TILE_SAMPLES);
|
||||
tile_offset += gridDim.x * TILE_SAMPLES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Parameter extraction
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Return a native pixel pointer (specialized for CacheModifiedInputIterator types)
|
||||
template <
|
||||
CacheLoadModifier _MODIFIER,
|
||||
typename _ValueT,
|
||||
typename _OffsetT>
|
||||
__device__ __forceinline__ SampleT* NativePointer(CacheModifiedInputIterator<_MODIFIER, _ValueT, _OffsetT> itr)
|
||||
{
|
||||
return itr.ptr;
|
||||
}
|
||||
|
||||
// Return a native pixel pointer (specialized for other types)
|
||||
template <typename IteratorT>
|
||||
__device__ __forceinline__ SampleT* NativePointer(IteratorT itr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentHistogram(
|
||||
TempStorage &temp_storage, ///< Reference to temp_storage
|
||||
SampleIteratorT d_samples, ///< Input data to reduce
|
||||
int (&num_output_bins)[NUM_ACTIVE_CHANNELS], ///< The number bins per final output histogram
|
||||
int (&num_privatized_bins)[NUM_ACTIVE_CHANNELS], ///< The number bins per privatized histogram
|
||||
CounterT* (&d_output_histograms)[NUM_ACTIVE_CHANNELS], ///< Reference to final output histograms
|
||||
CounterT* (&d_privatized_histograms)[NUM_ACTIVE_CHANNELS], ///< Reference to privatized histograms
|
||||
OutputDecodeOpT (&output_decode_op)[NUM_ACTIVE_CHANNELS], ///< The transform operator for determining output bin-ids from privatized counter indices, one for each channel
|
||||
PrivatizedDecodeOpT (&privatized_decode_op)[NUM_ACTIVE_CHANNELS]) ///< The transform operator for determining privatized counter indices from samples, one for each channel
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_wrapped_samples(d_samples),
|
||||
num_output_bins(num_output_bins),
|
||||
num_privatized_bins(num_privatized_bins),
|
||||
d_output_histograms(d_output_histograms),
|
||||
privatized_decode_op(privatized_decode_op),
|
||||
output_decode_op(output_decode_op),
|
||||
d_native_samples(NativePointer(d_wrapped_samples)),
|
||||
prefer_smem((MEM_PREFERENCE == SMEM) ?
|
||||
true : // prefer smem privatized histograms
|
||||
(MEM_PREFERENCE == GMEM) ?
|
||||
false : // prefer gmem privatized histograms
|
||||
blockIdx.x & 1) // prefer blended privatized histograms
|
||||
{
|
||||
int blockId = (blockIdx.y * gridDim.x) + blockIdx.x;
|
||||
|
||||
// Initialize the locations of this block's privatized histograms
|
||||
for (int CHANNEL = 0; CHANNEL < NUM_ACTIVE_CHANNELS; ++CHANNEL)
|
||||
this->d_privatized_histograms[CHANNEL] = d_privatized_histograms[CHANNEL] + (blockId * num_privatized_bins[CHANNEL]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume image
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeTiles(
|
||||
OffsetT num_row_pixels, ///< The number of multi-channel pixels per row in the region of interest
|
||||
OffsetT num_rows, ///< The number of rows in the region of interest
|
||||
OffsetT row_stride_samples, ///< The number of samples between starts of consecutive rows in the region of interest
|
||||
int tiles_per_row, ///< Number of image tiles per row
|
||||
GridQueue<int> tile_queue) ///< Queue descriptor for assigning tiles of work to thread blocks
|
||||
{
|
||||
// Check whether all row starting offsets are quad-aligned (in single-channel) or pixel-aligned (in multi-channel)
|
||||
size_t row_bytes = sizeof(SampleT) * row_stride_samples;
|
||||
size_t offset_mask = size_t(d_native_samples) | row_bytes;
|
||||
int quad_mask = sizeof(SampleT) * 4 - 1;
|
||||
int pixel_mask = AlignBytes<PixelT>::ALIGN_BYTES - 1;
|
||||
bool quad_aligned_rows = (NUM_CHANNELS == 1) && ((offset_mask & quad_mask) == 0);
|
||||
bool pixel_aligned_rows = (NUM_CHANNELS > 1) && ((offset_mask & pixel_mask) == 0);
|
||||
|
||||
// Whether rows are aligned and can be vectorized
|
||||
if (quad_aligned_rows || pixel_aligned_rows)
|
||||
ConsumeTiles<true>(num_row_pixels, num_rows, row_stride_samples, tiles_per_row, tile_queue, Int2Type<IS_WORK_STEALING>());
|
||||
else
|
||||
ConsumeTiles<false>(num_row_pixels, num_rows, row_stride_samples, tiles_per_row, tile_queue, Int2Type<IS_WORK_STEALING>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize privatized bin counters. Specialized for privatized shared-memory counters
|
||||
*/
|
||||
__device__ __forceinline__ void InitBinCounters()
|
||||
{
|
||||
if (prefer_smem)
|
||||
InitSmemBinCounters();
|
||||
else
|
||||
InitGmemBinCounters();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store privatized histogram to device-accessible memory. Specialized for privatized shared-memory counters
|
||||
*/
|
||||
__device__ __forceinline__ void StoreOutput()
|
||||
{
|
||||
if (prefer_smem)
|
||||
StoreSmemOutput();
|
||||
else
|
||||
StoreGmemOutput();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,751 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* AgentRadixSortDownsweep implements a stateful abstraction of CUDA thread blocks for participating in device-wide radix sort downsweep .
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_radix_rank.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Types of scattering strategies
|
||||
*/
|
||||
enum RadixSortScatterAlgorithm
|
||||
{
|
||||
RADIX_SORT_SCATTER_DIRECT, ///< Scatter directly from registers to global bins
|
||||
RADIX_SORT_SCATTER_TWO_PHASE, ///< First scatter from registers into shared memory bins, then into global bins
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentRadixSortDownsweep
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading keys (and values)
|
||||
bool _MEMOIZE_OUTER_SCAN, ///< Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure. See BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE for more details.
|
||||
BlockScanAlgorithm _INNER_SCAN_ALGORITHM, ///< The BlockScan algorithm algorithm to use
|
||||
RadixSortScatterAlgorithm _SCATTER_ALGORITHM, ///< The scattering strategy to use
|
||||
int _RADIX_BITS> ///< The number of radix bits, i.e., log2(bins)
|
||||
struct AgentRadixSortDownsweepPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
RADIX_BITS = _RADIX_BITS, ///< The number of radix bits, i.e., log2(bins)
|
||||
MEMOIZE_OUTER_SCAN = _MEMOIZE_OUTER_SCAN, ///< Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure. See BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE for more details.
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading keys (and values)
|
||||
static const BlockScanAlgorithm INNER_SCAN_ALGORITHM = _INNER_SCAN_ALGORITHM; ///< The BlockScan algorithm algorithm to use
|
||||
static const RadixSortScatterAlgorithm SCATTER_ALGORITHM = _SCATTER_ALGORITHM; ///< The scattering strategy to use
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentRadixSortDownsweep implements a stateful abstraction of CUDA thread blocks for participating in device-wide radix sort downsweep .
|
||||
*/
|
||||
template <
|
||||
typename AgentRadixSortDownsweepPolicy, ///< Parameterized AgentRadixSortDownsweepPolicy tuning policy type
|
||||
bool IS_DESCENDING, ///< Whether or not the sorted-order is high-to-low
|
||||
typename KeyT, ///< KeyT type
|
||||
typename ValueT, ///< ValueT type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentRadixSortDownsweep
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Type definitions and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Appropriate unsigned-bits representation of KeyT
|
||||
typedef typename Traits<KeyT>::UnsignedBits UnsignedBits;
|
||||
|
||||
static const UnsignedBits LOWEST_KEY = Traits<KeyT>::LOWEST_KEY;
|
||||
static const UnsignedBits MAX_KEY = Traits<KeyT>::MAX_KEY;
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = AgentRadixSortDownsweepPolicy::LOAD_ALGORITHM;
|
||||
static const CacheLoadModifier LOAD_MODIFIER = AgentRadixSortDownsweepPolicy::LOAD_MODIFIER;
|
||||
static const BlockScanAlgorithm INNER_SCAN_ALGORITHM = AgentRadixSortDownsweepPolicy::INNER_SCAN_ALGORITHM;
|
||||
static const RadixSortScatterAlgorithm SCATTER_ALGORITHM = AgentRadixSortDownsweepPolicy::SCATTER_ALGORITHM;
|
||||
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentRadixSortDownsweepPolicy::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentRadixSortDownsweepPolicy::ITEMS_PER_THREAD,
|
||||
RADIX_BITS = AgentRadixSortDownsweepPolicy::RADIX_BITS,
|
||||
MEMOIZE_OUTER_SCAN = AgentRadixSortDownsweepPolicy::MEMOIZE_OUTER_SCAN,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
|
||||
RADIX_DIGITS = 1 << RADIX_BITS,
|
||||
KEYS_ONLY = Equals<ValueT, NullType>::VALUE,
|
||||
|
||||
WARP_THREADS = CUB_PTX_LOG_WARP_THREADS,
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
|
||||
BYTES_PER_SIZET = sizeof(OffsetT),
|
||||
LOG_BYTES_PER_SIZET = Log2<BYTES_PER_SIZET>::VALUE,
|
||||
|
||||
LOG_SMEM_BANKS = CUB_PTX_LOG_SMEM_BANKS,
|
||||
SMEM_BANKS = 1 << LOG_SMEM_BANKS,
|
||||
|
||||
DIGITS_PER_SCATTER_PASS = BLOCK_THREADS / SMEM_BANKS,
|
||||
SCATTER_PASSES = RADIX_DIGITS / DIGITS_PER_SCATTER_PASS,
|
||||
|
||||
LOG_STORE_TXN_THREADS = LOG_SMEM_BANKS,
|
||||
STORE_TXN_THREADS = 1 << LOG_STORE_TXN_THREADS,
|
||||
};
|
||||
|
||||
// Input iterator wrapper type (for applying cache modifier)s
|
||||
typedef CacheModifiedInputIterator<LOAD_MODIFIER, UnsignedBits, OffsetT> KeysItr;
|
||||
typedef CacheModifiedInputIterator<LOAD_MODIFIER, ValueT, OffsetT> ValuesItr;
|
||||
|
||||
// BlockRadixRank type
|
||||
typedef BlockRadixRank<
|
||||
BLOCK_THREADS,
|
||||
RADIX_BITS,
|
||||
IS_DESCENDING,
|
||||
MEMOIZE_OUTER_SCAN,
|
||||
INNER_SCAN_ALGORITHM> BlockRadixRank;
|
||||
|
||||
// BlockLoad type (keys)
|
||||
typedef BlockLoad<
|
||||
UnsignedBits,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
LOAD_ALGORITHM> BlockLoadKeys;
|
||||
|
||||
// BlockLoad type (values)
|
||||
typedef BlockLoad<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
LOAD_ALGORITHM> BlockLoadValues;
|
||||
|
||||
// BlockExchange type (keys)
|
||||
typedef BlockExchange<
|
||||
UnsignedBits,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD> BlockExchangeKeys;
|
||||
|
||||
// BlockExchange type (values)
|
||||
typedef BlockExchange<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD> BlockExchangeValues;
|
||||
|
||||
|
||||
/**
|
||||
* Shared memory storage layout
|
||||
*/
|
||||
union __align__(16) _TempStorage
|
||||
{
|
||||
typename BlockLoadKeys::TempStorage load_keys;
|
||||
typename BlockRadixRank::TempStorage ranking;
|
||||
typename BlockLoadValues::TempStorage load_values;
|
||||
typename BlockExchangeValues::TempStorage exchange_values;
|
||||
|
||||
OffsetT exclusive_digit_prefix[RADIX_DIGITS];
|
||||
|
||||
struct
|
||||
{
|
||||
typename BlockExchangeKeys::TempStorage exchange_keys;
|
||||
OffsetT relative_bin_offsets[RADIX_DIGITS + 1];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Shared storage for this CTA
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
// Input and output device pointers
|
||||
KeysItr d_keys_in;
|
||||
ValuesItr d_values_in;
|
||||
UnsignedBits *d_keys_out;
|
||||
ValueT *d_values_out;
|
||||
|
||||
// The global scatter base offset for each digit (valid in the first RADIX_DIGITS threads)
|
||||
OffsetT bin_offset;
|
||||
|
||||
// The least-significant bit position of the current digit to extract
|
||||
int current_bit;
|
||||
|
||||
// Number of bits in current digit
|
||||
int num_bits;
|
||||
|
||||
// Whether to short-cirucit
|
||||
int short_circuit;
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scatter ranked keys directly to device-accessible memory
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void ScatterKeys(
|
||||
UnsignedBits (&twiddled_keys)[ITEMS_PER_THREAD],
|
||||
OffsetT (&relative_bin_offsets)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
OffsetT valid_items,
|
||||
Int2Type<RADIX_SORT_SCATTER_DIRECT> /*scatter_algorithm*/)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
UnsignedBits digit = BFE(twiddled_keys[ITEM], current_bit, num_bits);
|
||||
relative_bin_offsets[ITEM] = temp_storage.relative_bin_offsets[digit];
|
||||
|
||||
// Un-twiddle
|
||||
UnsignedBits key = Traits<KeyT>::TwiddleOut(twiddled_keys[ITEM]);
|
||||
|
||||
if (FULL_TILE || (ranks[ITEM] < valid_items))
|
||||
{
|
||||
d_keys_out[relative_bin_offsets[ITEM] + ranks[ITEM]] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter ranked keys through shared memory, then to device-accessible memory
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void ScatterKeys(
|
||||
UnsignedBits (&twiddled_keys)[ITEMS_PER_THREAD],
|
||||
OffsetT (&relative_bin_offsets)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
OffsetT valid_items,
|
||||
Int2Type<RADIX_SORT_SCATTER_TWO_PHASE> /*scatter_algorithm*/)
|
||||
{
|
||||
UnsignedBits *smem = reinterpret_cast<UnsignedBits*>(&temp_storage.exchange_keys);
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
smem[ranks[ITEM]] = twiddled_keys[ITEM];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
UnsignedBits key = smem[threadIdx.x + (ITEM * BLOCK_THREADS)];
|
||||
|
||||
UnsignedBits digit = BFE(key, current_bit, num_bits);
|
||||
|
||||
relative_bin_offsets[ITEM] = temp_storage.relative_bin_offsets[digit];
|
||||
|
||||
// Un-twiddle
|
||||
key = Traits<KeyT>::TwiddleOut(key);
|
||||
|
||||
if (FULL_TILE || (threadIdx.x + (ITEM * BLOCK_THREADS) < valid_items))
|
||||
{
|
||||
d_keys_out[relative_bin_offsets[ITEM] + threadIdx.x + (ITEM * BLOCK_THREADS)] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scatter ranked values directly to device-accessible memory
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void ScatterValues(
|
||||
ValueT (&values)[ITEMS_PER_THREAD],
|
||||
OffsetT (&relative_bin_offsets)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
OffsetT valid_items,
|
||||
Int2Type<RADIX_SORT_SCATTER_DIRECT> /*scatter_algorithm*/)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (FULL_TILE || (ranks[ITEM] < valid_items))
|
||||
{
|
||||
d_values_out[relative_bin_offsets[ITEM] + ranks[ITEM]] = values[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter ranked values through shared memory, then to device-accessible memory
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void ScatterValues(
|
||||
ValueT (&values)[ITEMS_PER_THREAD],
|
||||
OffsetT (&relative_bin_offsets)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
OffsetT valid_items,
|
||||
Int2Type<RADIX_SORT_SCATTER_TWO_PHASE> /*scatter_algorithm*/)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
ValueT *smem = reinterpret_cast<ValueT*>(&temp_storage.exchange_values);
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
smem[ranks[ITEM]] = values[ITEM];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
ValueT value = smem[threadIdx.x + (ITEM * BLOCK_THREADS)];
|
||||
|
||||
if (FULL_TILE || (threadIdx.x + (ITEM * BLOCK_THREADS) < valid_items))
|
||||
{
|
||||
d_values_out[relative_bin_offsets[ITEM] + threadIdx.x + (ITEM * BLOCK_THREADS)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a tile of items (specialized for full tile)
|
||||
*/
|
||||
template <typename BlockLoadT, typename T, typename InputIteratorT>
|
||||
__device__ __forceinline__ void LoadItems(
|
||||
BlockLoadT &block_loader,
|
||||
T (&items)[ITEMS_PER_THREAD],
|
||||
InputIteratorT d_in,
|
||||
OffsetT /*valid_items*/,
|
||||
Int2Type<true> /*is_full_tile*/)
|
||||
{
|
||||
block_loader.Load(d_in, items);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a tile of items (specialized for full tile)
|
||||
*/
|
||||
template <typename BlockLoadT, typename T, typename InputIteratorT>
|
||||
__device__ __forceinline__ void LoadItems(
|
||||
BlockLoadT &block_loader,
|
||||
T (&items)[ITEMS_PER_THREAD],
|
||||
InputIteratorT d_in,
|
||||
OffsetT /*valid_items*/,
|
||||
T /*oob_item*/,
|
||||
Int2Type<true> /*is_full_tile*/)
|
||||
{
|
||||
block_loader.Load(d_in, items);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a tile of items (specialized for partial tile)
|
||||
*/
|
||||
template <typename BlockLoadT, typename T, typename InputIteratorT>
|
||||
__device__ __forceinline__ void LoadItems(
|
||||
BlockLoadT &block_loader,
|
||||
T (&items)[ITEMS_PER_THREAD],
|
||||
InputIteratorT d_in,
|
||||
OffsetT valid_items,
|
||||
Int2Type<false> /*is_full_tile*/)
|
||||
{
|
||||
block_loader.Load(d_in, items, valid_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a tile of items (specialized for partial tile)
|
||||
*/
|
||||
template <typename BlockLoadT, typename T, typename InputIteratorT>
|
||||
__device__ __forceinline__ void LoadItems(
|
||||
BlockLoadT &block_loader,
|
||||
T (&items)[ITEMS_PER_THREAD],
|
||||
InputIteratorT d_in,
|
||||
OffsetT valid_items,
|
||||
T oob_item,
|
||||
Int2Type<false> /*is_full_tile*/)
|
||||
{
|
||||
block_loader.Load(d_in, items, valid_items, oob_item);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Truck along associated values
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void GatherScatterValues(
|
||||
OffsetT (&relative_bin_offsets)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
OffsetT block_offset,
|
||||
OffsetT valid_items,
|
||||
Int2Type<false> /*is_keys_only*/)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
ValueT values[ITEMS_PER_THREAD];
|
||||
|
||||
BlockLoadValues loader(temp_storage.load_values);
|
||||
LoadItems(
|
||||
loader,
|
||||
values,
|
||||
d_values_in + block_offset,
|
||||
valid_items,
|
||||
Int2Type<FULL_TILE>());
|
||||
|
||||
ScatterValues<FULL_TILE>(
|
||||
values,
|
||||
relative_bin_offsets,
|
||||
ranks,
|
||||
valid_items,
|
||||
Int2Type<SCATTER_ALGORITHM>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Truck along associated values (specialized for key-only sorting)
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void GatherScatterValues(
|
||||
OffsetT (&/*relative_bin_offsets*/)[ITEMS_PER_THREAD],
|
||||
int (&/*ranks*/)[ITEMS_PER_THREAD],
|
||||
OffsetT /*block_offset*/,
|
||||
OffsetT /*valid_items*/,
|
||||
Int2Type<true> /*is_keys_only*/)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Process tile
|
||||
*/
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ void ProcessTile(
|
||||
OffsetT block_offset,
|
||||
const OffsetT &valid_items = TILE_ITEMS)
|
||||
{
|
||||
// Per-thread tile data
|
||||
UnsignedBits keys[ITEMS_PER_THREAD]; // Keys
|
||||
UnsignedBits twiddled_keys[ITEMS_PER_THREAD]; // Twiddled keys
|
||||
int ranks[ITEMS_PER_THREAD]; // For each key, the local rank within the CTA
|
||||
OffsetT relative_bin_offsets[ITEMS_PER_THREAD]; // For each key, the global scatter base offset of the corresponding digit
|
||||
|
||||
// Assign default (min/max) value to all keys
|
||||
UnsignedBits default_key = (IS_DESCENDING) ? LOWEST_KEY : MAX_KEY;
|
||||
|
||||
// Load tile of keys
|
||||
BlockLoadKeys loader(temp_storage.load_keys);
|
||||
LoadItems(
|
||||
loader,
|
||||
keys,
|
||||
d_keys_in + block_offset,
|
||||
valid_items,
|
||||
default_key,
|
||||
Int2Type<FULL_TILE>());
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Twiddle key bits if necessary
|
||||
#pragma unroll
|
||||
for (int KEY = 0; KEY < ITEMS_PER_THREAD; KEY++)
|
||||
{
|
||||
twiddled_keys[KEY] = Traits<KeyT>::TwiddleIn(keys[KEY]);
|
||||
}
|
||||
|
||||
// Rank the twiddled keys
|
||||
int exclusive_digit_prefix;
|
||||
BlockRadixRank(temp_storage.ranking).RankKeys(
|
||||
twiddled_keys,
|
||||
ranks,
|
||||
current_bit,
|
||||
num_bits,
|
||||
exclusive_digit_prefix);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Share exclusive digit prefix
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
// Store exclusive prefix
|
||||
temp_storage.exclusive_digit_prefix[threadIdx.x] = exclusive_digit_prefix;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Get inclusive digit prefix
|
||||
int inclusive_digit_prefix;
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
if (IS_DESCENDING)
|
||||
{
|
||||
// Get inclusive digit prefix from exclusive prefix (higher bins come first)
|
||||
inclusive_digit_prefix = (threadIdx.x == 0) ?
|
||||
(BLOCK_THREADS * ITEMS_PER_THREAD) :
|
||||
temp_storage.exclusive_digit_prefix[threadIdx.x - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get inclusive digit prefix from exclusive prefix (lower bins come first)
|
||||
inclusive_digit_prefix = (threadIdx.x == RADIX_DIGITS - 1) ?
|
||||
(BLOCK_THREADS * ITEMS_PER_THREAD) :
|
||||
temp_storage.exclusive_digit_prefix[threadIdx.x + 1];
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Update global scatter base offsets for each digit
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
|
||||
|
||||
bin_offset -= exclusive_digit_prefix;
|
||||
temp_storage.relative_bin_offsets[threadIdx.x] = bin_offset;
|
||||
bin_offset += inclusive_digit_prefix;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Scatter keys
|
||||
ScatterKeys<FULL_TILE>(twiddled_keys, relative_bin_offsets, ranks, valid_items, Int2Type<SCATTER_ALGORITHM>());
|
||||
|
||||
// Gather/scatter values
|
||||
GatherScatterValues<FULL_TILE>(relative_bin_offsets , ranks, block_offset, valid_items, Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Copy shortcut
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Copy tiles within the range of input
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename T>
|
||||
__device__ __forceinline__ void Copy(
|
||||
InputIteratorT d_in,
|
||||
T *d_out,
|
||||
OffsetT block_offset,
|
||||
OffsetT block_end)
|
||||
{
|
||||
// Simply copy the input
|
||||
while (block_offset + TILE_ITEMS <= block_end)
|
||||
{
|
||||
T items[ITEMS_PER_THREAD];
|
||||
|
||||
LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in + block_offset, items);
|
||||
__syncthreads();
|
||||
StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items);
|
||||
|
||||
block_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
// Clean up last partial tile with guarded-I/O
|
||||
if (block_offset < block_end)
|
||||
{
|
||||
OffsetT valid_items = block_end - block_offset;
|
||||
|
||||
T items[ITEMS_PER_THREAD];
|
||||
|
||||
LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_in + block_offset, items, valid_items);
|
||||
__syncthreads();
|
||||
StoreDirectStriped<BLOCK_THREADS>(threadIdx.x, d_out + block_offset, items, valid_items);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy tiles within the range of input (specialized for NullType)
|
||||
*/
|
||||
template <typename InputIteratorT>
|
||||
__device__ __forceinline__ void Copy(
|
||||
InputIteratorT /*d_in*/,
|
||||
NullType * /*d_out*/,
|
||||
OffsetT /*block_offset*/,
|
||||
OffsetT /*block_end*/)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentRadixSortDownsweep(
|
||||
TempStorage &temp_storage,
|
||||
OffsetT num_items,
|
||||
OffsetT bin_offset,
|
||||
const KeyT *d_keys_in,
|
||||
KeyT *d_keys_out,
|
||||
const ValueT *d_values_in,
|
||||
ValueT *d_values_out,
|
||||
int current_bit,
|
||||
int num_bits)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
bin_offset(bin_offset),
|
||||
d_keys_in(reinterpret_cast<const UnsignedBits*>(d_keys_in)),
|
||||
d_keys_out(reinterpret_cast<UnsignedBits*>(d_keys_out)),
|
||||
d_values_in(d_values_in),
|
||||
d_values_out(d_values_out),
|
||||
current_bit(current_bit),
|
||||
num_bits(num_bits),
|
||||
short_circuit(1)
|
||||
{
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
// Short circuit if the histogram has only bin counts of only zeros or problem-size
|
||||
short_circuit = ((bin_offset == 0) || (bin_offset == num_items));
|
||||
}
|
||||
|
||||
short_circuit = __syncthreads_and(short_circuit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentRadixSortDownsweep(
|
||||
TempStorage &temp_storage,
|
||||
OffsetT num_items,
|
||||
OffsetT *d_spine,
|
||||
const KeyT *d_keys_in,
|
||||
KeyT *d_keys_out,
|
||||
const ValueT *d_values_in,
|
||||
ValueT *d_values_out,
|
||||
int current_bit,
|
||||
int num_bits)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_keys_in(reinterpret_cast<const UnsignedBits*>(d_keys_in)),
|
||||
d_keys_out(reinterpret_cast<UnsignedBits*>(d_keys_out)),
|
||||
d_values_in(d_values_in),
|
||||
d_values_out(d_values_out),
|
||||
current_bit(current_bit),
|
||||
num_bits(num_bits),
|
||||
short_circuit(1)
|
||||
{
|
||||
// Load digit bin offsets (each of the first RADIX_DIGITS threads will load an offset for that digit)
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
int bin_idx = (IS_DESCENDING) ?
|
||||
RADIX_DIGITS - threadIdx.x - 1 :
|
||||
threadIdx.x;
|
||||
|
||||
// Short circuit if the first block's histogram has only bin counts of only zeros or problem-size
|
||||
OffsetT first_block_bin_offset = d_spine[gridDim.x * bin_idx];
|
||||
short_circuit = ((first_block_bin_offset == 0) || (first_block_bin_offset == num_items));
|
||||
|
||||
// Load my block's bin offset for my bin
|
||||
bin_offset = d_spine[(gridDim.x * bin_idx) + blockIdx.x];
|
||||
}
|
||||
|
||||
short_circuit = __syncthreads_and(short_circuit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Distribute keys from a segment of input tiles.
|
||||
*/
|
||||
__device__ __forceinline__ void ProcessRegion(
|
||||
OffsetT block_offset,
|
||||
OffsetT block_end)
|
||||
{
|
||||
if (short_circuit)
|
||||
{
|
||||
// Copy keys
|
||||
Copy(d_keys_in, d_keys_out, block_offset, block_end);
|
||||
|
||||
// Copy values
|
||||
Copy(d_values_in, d_values_out, block_offset, block_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process full tiles of tile_items
|
||||
while (block_offset + TILE_ITEMS <= block_end)
|
||||
{
|
||||
ProcessTile<true>(block_offset);
|
||||
block_offset += TILE_ITEMS;
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Clean up last partial tile with guarded-I/O
|
||||
if (block_offset < block_end)
|
||||
{
|
||||
ProcessTile<false>(block_offset, block_end - block_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* AgentRadixSortUpsweep implements a stateful abstraction of CUDA thread blocks for participating in device-wide radix sort upsweep .
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../thread/thread_reduce.cuh"
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentRadixSortUpsweep
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading keys
|
||||
int _RADIX_BITS> ///< The number of radix bits, i.e., log2(bins)
|
||||
struct AgentRadixSortUpsweepPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
RADIX_BITS = _RADIX_BITS, ///< The number of radix bits, i.e., log2(bins)
|
||||
};
|
||||
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading keys
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentRadixSortUpsweep implements a stateful abstraction of CUDA thread blocks for participating in device-wide radix sort upsweep .
|
||||
*/
|
||||
template <
|
||||
typename AgentRadixSortUpsweepPolicy, ///< Parameterized AgentRadixSortUpsweepPolicy tuning policy type
|
||||
typename KeyT, ///< KeyT type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentRadixSortUpsweep
|
||||
{
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Type definitions and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
typedef typename Traits<KeyT>::UnsignedBits UnsignedBits;
|
||||
|
||||
// Integer type for digit counters (to be packed into words of PackedCounters)
|
||||
typedef unsigned char DigitCounter;
|
||||
|
||||
// Integer type for packing DigitCounters into columns of shared memory banks
|
||||
typedef unsigned int PackedCounter;
|
||||
|
||||
static const CacheLoadModifier LOAD_MODIFIER = AgentRadixSortUpsweepPolicy::LOAD_MODIFIER;
|
||||
|
||||
enum
|
||||
{
|
||||
RADIX_BITS = AgentRadixSortUpsweepPolicy::RADIX_BITS,
|
||||
BLOCK_THREADS = AgentRadixSortUpsweepPolicy::BLOCK_THREADS,
|
||||
KEYS_PER_THREAD = AgentRadixSortUpsweepPolicy::ITEMS_PER_THREAD,
|
||||
|
||||
RADIX_DIGITS = 1 << RADIX_BITS,
|
||||
|
||||
LOG_WARP_THREADS = CUB_PTX_LOG_WARP_THREADS,
|
||||
WARP_THREADS = 1 << LOG_WARP_THREADS,
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
|
||||
TILE_ITEMS = BLOCK_THREADS * KEYS_PER_THREAD,
|
||||
|
||||
BYTES_PER_COUNTER = sizeof(DigitCounter),
|
||||
LOG_BYTES_PER_COUNTER = Log2<BYTES_PER_COUNTER>::VALUE,
|
||||
|
||||
PACKING_RATIO = sizeof(PackedCounter) / sizeof(DigitCounter),
|
||||
LOG_PACKING_RATIO = Log2<PACKING_RATIO>::VALUE,
|
||||
|
||||
LOG_COUNTER_LANES = CUB_MAX(0, RADIX_BITS - LOG_PACKING_RATIO),
|
||||
COUNTER_LANES = 1 << LOG_COUNTER_LANES,
|
||||
|
||||
// To prevent counter overflow, we must periodically unpack and aggregate the
|
||||
// digit counters back into registers. Each counter lane is assigned to a
|
||||
// warp for aggregation.
|
||||
|
||||
LANES_PER_WARP = CUB_MAX(1, (COUNTER_LANES + WARPS - 1) / WARPS),
|
||||
|
||||
// Unroll tiles in batches without risk of counter overflow
|
||||
UNROLL_COUNT = CUB_MIN(64, 255 / KEYS_PER_THREAD),
|
||||
UNROLLED_ELEMENTS = UNROLL_COUNT * TILE_ITEMS,
|
||||
};
|
||||
|
||||
|
||||
// Input iterator wrapper type (for applying cache modifier)s
|
||||
typedef CacheModifiedInputIterator<LOAD_MODIFIER, UnsignedBits, OffsetT> KeysItr;
|
||||
|
||||
/**
|
||||
* Shared memory storage layout
|
||||
*/
|
||||
struct _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
DigitCounter digit_counters[COUNTER_LANES][BLOCK_THREADS][PACKING_RATIO];
|
||||
PackedCounter packed_counters[COUNTER_LANES][BLOCK_THREADS];
|
||||
OffsetT digit_partials[RADIX_DIGITS][WARP_THREADS + 1];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Thread fields (aggregate state bundle)
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Shared storage for this CTA
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
// Thread-local counters for periodically aggregating composite-counter lanes
|
||||
OffsetT local_counts[LANES_PER_WARP][PACKING_RATIO];
|
||||
|
||||
// Input and output device pointers
|
||||
KeysItr d_keys_in;
|
||||
|
||||
// The least-significant bit position of the current digit to extract
|
||||
int current_bit;
|
||||
|
||||
// Number of bits in current digit
|
||||
int num_bits;
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Helper structure for templated iteration
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Iterate
|
||||
template <int COUNT, int MAX>
|
||||
struct Iterate
|
||||
{
|
||||
// BucketKeys
|
||||
static __device__ __forceinline__ void BucketKeys(
|
||||
AgentRadixSortUpsweep &cta,
|
||||
UnsignedBits keys[KEYS_PER_THREAD])
|
||||
{
|
||||
cta.Bucket(keys[COUNT]);
|
||||
|
||||
// Next
|
||||
Iterate<COUNT + 1, MAX>::BucketKeys(cta, keys);
|
||||
}
|
||||
};
|
||||
|
||||
// Terminate
|
||||
template <int MAX>
|
||||
struct Iterate<MAX, MAX>
|
||||
{
|
||||
// BucketKeys
|
||||
static __device__ __forceinline__ void BucketKeys(AgentRadixSortUpsweep &/*cta*/, UnsignedBits /*keys*/[KEYS_PER_THREAD]) {}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Decode a key and increment corresponding smem digit counter
|
||||
*/
|
||||
__device__ __forceinline__ void Bucket(UnsignedBits key)
|
||||
{
|
||||
// Perform transform op
|
||||
UnsignedBits converted_key = Traits<KeyT>::TwiddleIn(key);
|
||||
|
||||
// Extract current digit bits
|
||||
UnsignedBits digit = BFE(converted_key, current_bit, num_bits);
|
||||
|
||||
// Get sub-counter offset
|
||||
UnsignedBits sub_counter = digit & (PACKING_RATIO - 1);
|
||||
|
||||
// Get row offset
|
||||
UnsignedBits row_offset = digit >> LOG_PACKING_RATIO;
|
||||
|
||||
// Increment counter
|
||||
temp_storage.digit_counters[row_offset][threadIdx.x][sub_counter]++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset composite counters
|
||||
*/
|
||||
__device__ __forceinline__ void ResetDigitCounters()
|
||||
{
|
||||
#pragma unroll
|
||||
for (int LANE = 0; LANE < COUNTER_LANES; LANE++)
|
||||
{
|
||||
temp_storage.packed_counters[LANE][threadIdx.x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset the unpacked counters in each thread
|
||||
*/
|
||||
__device__ __forceinline__ void ResetUnpackedCounters()
|
||||
{
|
||||
#pragma unroll
|
||||
for (int LANE = 0; LANE < LANES_PER_WARP; LANE++)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int UNPACKED_COUNTER = 0; UNPACKED_COUNTER < PACKING_RATIO; UNPACKED_COUNTER++)
|
||||
{
|
||||
local_counts[LANE][UNPACKED_COUNTER] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts and aggregates the digit counters for each counter lane
|
||||
* owned by this warp
|
||||
*/
|
||||
__device__ __forceinline__ void UnpackDigitCounts()
|
||||
{
|
||||
unsigned int warp_id = threadIdx.x >> LOG_WARP_THREADS;
|
||||
unsigned int warp_tid = threadIdx.x & (WARP_THREADS - 1);
|
||||
|
||||
#pragma unroll
|
||||
for (int LANE = 0; LANE < LANES_PER_WARP; LANE++)
|
||||
{
|
||||
const int counter_lane = (LANE * WARPS) + warp_id;
|
||||
if (counter_lane < COUNTER_LANES)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int PACKED_COUNTER = 0; PACKED_COUNTER < BLOCK_THREADS; PACKED_COUNTER += WARP_THREADS)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int UNPACKED_COUNTER = 0; UNPACKED_COUNTER < PACKING_RATIO; UNPACKED_COUNTER++)
|
||||
{
|
||||
OffsetT counter = temp_storage.digit_counters[counter_lane][warp_tid + PACKED_COUNTER][UNPACKED_COUNTER];
|
||||
local_counts[LANE][UNPACKED_COUNTER] += counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Places unpacked counters into smem for final digit reduction
|
||||
*/
|
||||
__device__ __forceinline__ void ReduceUnpackedCounts(OffsetT &bin_count)
|
||||
{
|
||||
unsigned int warp_id = threadIdx.x >> LOG_WARP_THREADS;
|
||||
unsigned int warp_tid = threadIdx.x & (WARP_THREADS - 1);
|
||||
|
||||
// Place unpacked digit counters in shared memory
|
||||
#pragma unroll
|
||||
for (int LANE = 0; LANE < LANES_PER_WARP; LANE++)
|
||||
{
|
||||
int counter_lane = (LANE * WARPS) + warp_id;
|
||||
if (counter_lane < COUNTER_LANES)
|
||||
{
|
||||
int digit_row = counter_lane << LOG_PACKING_RATIO;
|
||||
|
||||
#pragma unroll
|
||||
for (int UNPACKED_COUNTER = 0; UNPACKED_COUNTER < PACKING_RATIO; UNPACKED_COUNTER++)
|
||||
{
|
||||
temp_storage.digit_partials[digit_row + UNPACKED_COUNTER][warp_tid] =
|
||||
local_counts[LANE][UNPACKED_COUNTER];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Rake-reduce bin_count reductions
|
||||
if (threadIdx.x < RADIX_DIGITS)
|
||||
{
|
||||
bin_count = ThreadReduce<WARP_THREADS>(
|
||||
temp_storage.digit_partials[threadIdx.x],
|
||||
Sum());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes a single, full tile
|
||||
*/
|
||||
__device__ __forceinline__ void ProcessFullTile(OffsetT block_offset)
|
||||
{
|
||||
// Tile of keys
|
||||
UnsignedBits keys[KEYS_PER_THREAD];
|
||||
|
||||
LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_keys_in + block_offset, keys);
|
||||
|
||||
// Prevent hoisting
|
||||
__syncthreads();
|
||||
|
||||
// Bucket tile of keys
|
||||
Iterate<0, KEYS_PER_THREAD>::BucketKeys(*this, keys);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes a single load (may have some threads masked off)
|
||||
*/
|
||||
__device__ __forceinline__ void ProcessPartialTile(
|
||||
OffsetT block_offset,
|
||||
const OffsetT &block_end)
|
||||
{
|
||||
// Process partial tile if necessary using single loads
|
||||
block_offset += threadIdx.x;
|
||||
while (block_offset < block_end)
|
||||
{
|
||||
// Load and bucket key
|
||||
UnsignedBits key = d_keys_in[block_offset];
|
||||
Bucket(key);
|
||||
block_offset += BLOCK_THREADS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentRadixSortUpsweep(
|
||||
TempStorage &temp_storage,
|
||||
const KeyT *d_keys_in,
|
||||
int current_bit,
|
||||
int num_bits)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_keys_in(reinterpret_cast<const UnsignedBits*>(d_keys_in)),
|
||||
current_bit(current_bit),
|
||||
num_bits(num_bits)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Compute radix digit histograms from a segment of input tiles.
|
||||
*/
|
||||
__device__ __forceinline__ void ProcessRegion(
|
||||
OffsetT block_offset,
|
||||
const OffsetT &block_end,
|
||||
OffsetT &bin_count) ///< [out] The digit count for tid'th bin (output param, valid in the first RADIX_DIGITS threads)
|
||||
{
|
||||
// Reset digit counters in smem and unpacked counters in registers
|
||||
ResetDigitCounters();
|
||||
ResetUnpackedCounters();
|
||||
|
||||
// Unroll batches of full tiles
|
||||
while (block_offset + UNROLLED_ELEMENTS <= block_end)
|
||||
{
|
||||
for (int i = 0; i < UNROLL_COUNT; ++i)
|
||||
{
|
||||
ProcessFullTile(block_offset);
|
||||
block_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Aggregate back into local_count registers to prevent overflow
|
||||
UnpackDigitCounts();
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reset composite counters in lanes
|
||||
ResetDigitCounters();
|
||||
}
|
||||
|
||||
// Unroll single full tiles
|
||||
while (block_offset + TILE_ITEMS <= block_end)
|
||||
{
|
||||
ProcessFullTile(block_offset);
|
||||
block_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
// Process partial tile if necessary
|
||||
ProcessPartialTile(
|
||||
block_offset,
|
||||
block_end);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Aggregate back into local_count registers
|
||||
UnpackDigitCounts();
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Final raking reduction of counts by bin
|
||||
ReduceUnpackedCounts(bin_count);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,475 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentReduce implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduction .
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_reduce.cuh"
|
||||
#include "../grid/grid_mapping.cuh"
|
||||
#include "../grid/grid_queue.cuh"
|
||||
#include "../grid/grid_even_share.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentReduce
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
int _VECTOR_LOAD_LENGTH, ///< Number of items per vectorized load
|
||||
BlockReduceAlgorithm _BLOCK_ALGORITHM, ///< Cooperative block-wide reduction algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
GridMappingStrategy _GRID_MAPPING> ///< How to map tiles of input onto thread blocks
|
||||
struct AgentReducePolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
VECTOR_LOAD_LENGTH = _VECTOR_LOAD_LENGTH, ///< Number of items per vectorized load
|
||||
};
|
||||
|
||||
static const BlockReduceAlgorithm BLOCK_ALGORITHM = _BLOCK_ALGORITHM; ///< Cooperative block-wide reduction algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const GridMappingStrategy GRID_MAPPING = _GRID_MAPPING; ///< How to map tiles of input onto thread blocks
|
||||
};
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentReduce implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduction .
|
||||
*
|
||||
* Each thread reduces only the values it loads. If \p FIRST_TILE, this
|
||||
* partial reduction is stored into \p thread_aggregate. Otherwise it is
|
||||
* accumulated into \p thread_aggregate.
|
||||
*/
|
||||
template <
|
||||
typename AgentReducePolicy, ///< Parameterized AgentReducePolicy tuning policy type
|
||||
typename InputIteratorT, ///< Random-access iterator type for input
|
||||
typename OutputIteratorT, ///< Random-access iterator type for output
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOp> ///< Binary reduction operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
struct AgentReduce
|
||||
{
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
/// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
/// Vector type of InputT for data movement
|
||||
typedef typename CubVector<InputT, AgentReducePolicy::VECTOR_LOAD_LENGTH>::Type VectorT;
|
||||
|
||||
/// Input iterator wrapper type (for applying cache modifier)
|
||||
typedef typename If<IsPointer<InputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentReducePolicy::LOAD_MODIFIER, InputT, OffsetT>, // Wrap the native input pointer with CacheModifiedInputIterator
|
||||
InputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedInputIteratorT;
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentReducePolicy::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentReducePolicy::ITEMS_PER_THREAD,
|
||||
VECTOR_LOAD_LENGTH = CUB_MIN(ITEMS_PER_THREAD, AgentReducePolicy::VECTOR_LOAD_LENGTH),
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
|
||||
// Can vectorize according to the policy if the input iterator is a native pointer to a primitive type
|
||||
ATTEMPT_VECTORIZATION = (VECTOR_LOAD_LENGTH > 1) &&
|
||||
(ITEMS_PER_THREAD % VECTOR_LOAD_LENGTH == 0) &&
|
||||
(IsPointer<InputIteratorT>::VALUE) && Traits<InputT>::PRIMITIVE,
|
||||
|
||||
};
|
||||
|
||||
static const CacheLoadModifier LOAD_MODIFIER = AgentReducePolicy::LOAD_MODIFIER;
|
||||
static const BlockReduceAlgorithm BLOCK_ALGORITHM = AgentReducePolicy::BLOCK_ALGORITHM;
|
||||
|
||||
/// Parameterized BlockReduce primitive
|
||||
typedef BlockReduce<OutputT, BLOCK_THREADS, AgentReducePolicy::BLOCK_ALGORITHM> BlockReduceT;
|
||||
|
||||
/// Shared memory type required by this thread block
|
||||
struct _TempStorage
|
||||
{
|
||||
typename BlockReduceT::TempStorage reduce;
|
||||
OffsetT dequeue_offset;
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
InputIteratorT d_in; ///< Input data to reduce
|
||||
WrappedInputIteratorT d_wrapped_in; ///< Wrapped input data to reduce
|
||||
ReductionOp reduction_op; ///< Binary reduction operator
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
// Whether or not the input is aligned with the vector type (specialized for types we can vectorize)
|
||||
template <typename Iterator>
|
||||
static __device__ __forceinline__ bool IsAligned(
|
||||
Iterator d_in,
|
||||
Int2Type<true> /*can_vectorize*/)
|
||||
{
|
||||
return (size_t(d_in) & (sizeof(VectorT) - 1)) == 0;
|
||||
}
|
||||
|
||||
// Whether or not the input is aligned with the vector type (specialized for types we cannot vectorize)
|
||||
template <typename Iterator>
|
||||
static __device__ __forceinline__ bool IsAligned(
|
||||
Iterator /*d_in*/,
|
||||
Int2Type<false> /*can_vectorize*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentReduce(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
InputIteratorT d_in, ///< Input data to reduce
|
||||
ReductionOp reduction_op) ///< Binary reduction operator
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_in(d_in),
|
||||
d_wrapped_in(d_in),
|
||||
reduction_op(reduction_op)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tile consumption
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Consume a full tile of input (non-vectorized)
|
||||
*/
|
||||
template <int IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OutputT &thread_aggregate,
|
||||
OffsetT block_offset, ///< The offset the tile to consume
|
||||
int /*valid_items*/, ///< The number of valid items in the tile
|
||||
Int2Type<true> /*is_full_tile*/, ///< Whether or not this is a full tile
|
||||
Int2Type<false> /*can_vectorize*/) ///< Whether or not we can vectorize loads
|
||||
{
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
|
||||
// Load items in striped fashion
|
||||
LoadDirectStriped<BLOCK_THREADS>(threadIdx.x, d_wrapped_in + block_offset, items);
|
||||
|
||||
// Reduce items within each thread stripe
|
||||
thread_aggregate = (IS_FIRST_TILE) ?
|
||||
ThreadReduce(items, reduction_op) :
|
||||
ThreadReduce(items, reduction_op, thread_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume a full tile of input (vectorized)
|
||||
*/
|
||||
template <int IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OutputT &thread_aggregate,
|
||||
OffsetT block_offset, ///< The offset the tile to consume
|
||||
int /*valid_items*/, ///< The number of valid items in the tile
|
||||
Int2Type<true> /*is_full_tile*/, ///< Whether or not this is a full tile
|
||||
Int2Type<true> /*can_vectorize*/) ///< Whether or not we can vectorize loads
|
||||
{
|
||||
// Alias items as an array of VectorT and load it in striped fashion
|
||||
enum { WORDS = ITEMS_PER_THREAD / VECTOR_LOAD_LENGTH };
|
||||
|
||||
// Fabricate a vectorized input iterator
|
||||
InputT *d_in_unqualified = const_cast<InputT*>(d_in) + block_offset + (threadIdx.x * VECTOR_LOAD_LENGTH);
|
||||
CacheModifiedInputIterator<AgentReducePolicy::LOAD_MODIFIER, VectorT, OffsetT> d_vec_in(
|
||||
reinterpret_cast<VectorT*>(d_in_unqualified));
|
||||
|
||||
// Load items as vector items
|
||||
InputT input_items[ITEMS_PER_THREAD];
|
||||
VectorT *vec_items = reinterpret_cast<VectorT*>(input_items);
|
||||
#pragma unroll
|
||||
for (int i = 0; i < WORDS; ++i)
|
||||
vec_items[i] = d_vec_in[BLOCK_THREADS * i];
|
||||
|
||||
// Convert from input type to output type
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
#pragma unroll
|
||||
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
|
||||
items[i] = input_items[i];
|
||||
|
||||
// Reduce items within each thread stripe
|
||||
thread_aggregate = (IS_FIRST_TILE) ?
|
||||
ThreadReduce(items, reduction_op) :
|
||||
ThreadReduce(items, reduction_op, thread_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume a partial tile of input
|
||||
*/
|
||||
template <int IS_FIRST_TILE, int CAN_VECTORIZE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OutputT &thread_aggregate,
|
||||
OffsetT block_offset, ///< The offset the tile to consume
|
||||
int valid_items, ///< The number of valid items in the tile
|
||||
Int2Type<false> /*is_full_tile*/, ///< Whether or not this is a full tile
|
||||
Int2Type<CAN_VECTORIZE> /*can_vectorize*/) ///< Whether or not we can vectorize loads
|
||||
{
|
||||
// Partial tile
|
||||
int thread_offset = threadIdx.x;
|
||||
|
||||
// Read first item
|
||||
if ((IS_FIRST_TILE) && (thread_offset < valid_items))
|
||||
{
|
||||
thread_aggregate = d_wrapped_in[block_offset + thread_offset];
|
||||
thread_offset += BLOCK_THREADS;
|
||||
}
|
||||
|
||||
// Continue reading items (block-striped)
|
||||
while (thread_offset < valid_items)
|
||||
{
|
||||
OutputT item = d_wrapped_in[block_offset + thread_offset];
|
||||
thread_aggregate = reduction_op(thread_aggregate, item);
|
||||
thread_offset += BLOCK_THREADS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Consume a contiguous segment of tiles
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* \brief Reduce a contiguous segment of input tiles
|
||||
*/
|
||||
template <int CAN_VECTORIZE>
|
||||
__device__ __forceinline__ OutputT ConsumeRange(
|
||||
OffsetT block_offset, ///< [in] Threadblock begin offset (inclusive)
|
||||
OffsetT block_end, ///< [in] Threadblock end offset (exclusive)
|
||||
Int2Type<CAN_VECTORIZE> can_vectorize) ///< Whether or not we can vectorize loads
|
||||
{
|
||||
OutputT thread_aggregate;
|
||||
|
||||
if (block_offset + TILE_ITEMS > block_end)
|
||||
{
|
||||
// First tile isn't full (not all threads have valid items)
|
||||
int valid_items = block_end - block_offset;
|
||||
ConsumeTile<true>(thread_aggregate, block_offset, valid_items, Int2Type<false>(), can_vectorize);
|
||||
return BlockReduceT(temp_storage.reduce).Reduce(thread_aggregate, reduction_op, valid_items);
|
||||
}
|
||||
|
||||
// At least one full block
|
||||
ConsumeTile<true>(thread_aggregate, block_offset, TILE_ITEMS, Int2Type<true>(), can_vectorize);
|
||||
block_offset += TILE_ITEMS;
|
||||
|
||||
// Consume subsequent full tiles of input
|
||||
while (block_offset + TILE_ITEMS <= block_end)
|
||||
{
|
||||
ConsumeTile<false>(thread_aggregate, block_offset, TILE_ITEMS, Int2Type<true>(), can_vectorize);
|
||||
block_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
// Consume a partially-full tile
|
||||
if (block_offset < block_end)
|
||||
{
|
||||
int valid_items = block_end - block_offset;
|
||||
ConsumeTile<false>(thread_aggregate, block_offset, valid_items, Int2Type<false>(), can_vectorize);
|
||||
}
|
||||
|
||||
// Compute block-wide reduction (all threads have valid items)
|
||||
return BlockReduceT(temp_storage.reduce).Reduce(thread_aggregate, reduction_op);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reduce a contiguous segment of input tiles
|
||||
*/
|
||||
__device__ __forceinline__ OutputT ConsumeRange(
|
||||
OffsetT block_offset, ///< [in] Threadblock begin offset (inclusive)
|
||||
OffsetT block_end) ///< [in] Threadblock end offset (exclusive)
|
||||
{
|
||||
return (IsAligned(d_in + block_offset, Int2Type<ATTEMPT_VECTORIZATION>())) ?
|
||||
ConsumeRange(block_offset, block_end, Int2Type<true && ATTEMPT_VECTORIZATION>()) :
|
||||
ConsumeRange(block_offset, block_end, Int2Type<false && ATTEMPT_VECTORIZATION>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce a contiguous segment of input tiles
|
||||
*/
|
||||
__device__ __forceinline__ OutputT ConsumeTiles(
|
||||
OffsetT /*num_items*/, ///< [in] Total number of global input items
|
||||
GridEvenShare<OffsetT> &even_share, ///< [in] GridEvenShare descriptor
|
||||
GridQueue<OffsetT> &/*queue*/, ///< [in,out] GridQueue descriptor
|
||||
Int2Type<GRID_MAPPING_EVEN_SHARE> /*is_even_share*/) ///< [in] Marker type indicating this is an even-share mapping
|
||||
{
|
||||
// Initialize even-share descriptor for this thread block
|
||||
even_share.BlockInit();
|
||||
|
||||
return (IsAligned(d_in, Int2Type<ATTEMPT_VECTORIZATION>())) ?
|
||||
ConsumeRange(even_share.block_offset, even_share.block_end, Int2Type<true && ATTEMPT_VECTORIZATION>()) :
|
||||
ConsumeRange(even_share.block_offset, even_share.block_end, Int2Type<false && ATTEMPT_VECTORIZATION>());
|
||||
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dynamically consume tiles
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Dequeue and reduce tiles of items as part of a inter-block reduction
|
||||
*/
|
||||
template <int CAN_VECTORIZE>
|
||||
__device__ __forceinline__ OutputT ConsumeTiles(
|
||||
int num_items, ///< Total number of input items
|
||||
GridQueue<OffsetT> queue, ///< Queue descriptor for assigning tiles of work to thread blocks
|
||||
Int2Type<CAN_VECTORIZE> can_vectorize) ///< Whether or not we can vectorize loads
|
||||
{
|
||||
// We give each thread block at least one tile of input.
|
||||
OutputT thread_aggregate;
|
||||
OffsetT block_offset = blockIdx.x * TILE_ITEMS;
|
||||
OffsetT even_share_base = gridDim.x * TILE_ITEMS;
|
||||
|
||||
if (block_offset + TILE_ITEMS > num_items)
|
||||
{
|
||||
// First tile isn't full (not all threads have valid items)
|
||||
int valid_items = num_items - block_offset;
|
||||
ConsumeTile<true>(thread_aggregate, block_offset, valid_items, Int2Type<false>(), can_vectorize);
|
||||
return BlockReduceT(temp_storage.reduce).Reduce(thread_aggregate, reduction_op, valid_items);
|
||||
}
|
||||
|
||||
// Consume first full tile of input
|
||||
ConsumeTile<true>(thread_aggregate, block_offset, TILE_ITEMS, Int2Type<true>(), can_vectorize);
|
||||
|
||||
if (num_items > even_share_base)
|
||||
{
|
||||
// Dequeue a tile of items
|
||||
if (threadIdx.x == 0)
|
||||
temp_storage.dequeue_offset = queue.Drain(TILE_ITEMS) + even_share_base;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab tile offset and check if we're done with full tiles
|
||||
block_offset = temp_storage.dequeue_offset;
|
||||
|
||||
// Consume more full tiles
|
||||
while (block_offset + TILE_ITEMS <= num_items)
|
||||
{
|
||||
ConsumeTile<false>(thread_aggregate, block_offset, TILE_ITEMS, Int2Type<true>(), can_vectorize);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Dequeue a tile of items
|
||||
if (threadIdx.x == 0)
|
||||
temp_storage.dequeue_offset = queue.Drain(TILE_ITEMS) + even_share_base;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab tile offset and check if we're done with full tiles
|
||||
block_offset = temp_storage.dequeue_offset;
|
||||
}
|
||||
|
||||
// Consume partial tile
|
||||
if (block_offset < num_items)
|
||||
{
|
||||
int valid_items = num_items - block_offset;
|
||||
ConsumeTile<false>(thread_aggregate, block_offset, valid_items, Int2Type<false>(), can_vectorize);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute block-wide reduction (all threads have valid items)
|
||||
return BlockReduceT(temp_storage.reduce).Reduce(thread_aggregate, reduction_op);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue and reduce tiles of items as part of a inter-block reduction
|
||||
*/
|
||||
__device__ __forceinline__ OutputT ConsumeTiles(
|
||||
OffsetT num_items, ///< [in] Total number of global input items
|
||||
GridEvenShare<OffsetT> &/*even_share*/, ///< [in] GridEvenShare descriptor
|
||||
GridQueue<OffsetT> &queue, ///< [in,out] GridQueue descriptor
|
||||
Int2Type<GRID_MAPPING_DYNAMIC> /*is_dynamic*/) ///< [in] Marker type indicating this is a dynamic mapping
|
||||
{
|
||||
return (IsAligned(d_in, Int2Type<ATTEMPT_VECTORIZATION>())) ?
|
||||
ConsumeTiles(num_items, queue, Int2Type<true && ATTEMPT_VECTORIZATION>()) :
|
||||
ConsumeTiles(num_items, queue, Int2Type<false && ATTEMPT_VECTORIZATION>());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,544 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentReduceByKey implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduce-value-by-key.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "single_pass_scan_operators.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_discontinuity.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/constant_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentReduceByKey
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentReduceByKeyPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentReduceByKey implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduce-value-by-key
|
||||
*/
|
||||
template <
|
||||
typename AgentReduceByKeyPolicyT, ///< Parameterized AgentReduceByKeyPolicy tuning policy type
|
||||
typename KeysInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename UniqueOutputIteratorT, ///< Random-access output iterator type for keys
|
||||
typename ValuesInputIteratorT, ///< Random-access input iterator type for values
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename NumRunsOutputIteratorT, ///< Output iterator type for recording number of items selected
|
||||
typename EqualityOpT, ///< KeyT equality operator type
|
||||
typename ReductionOpT, ///< ValueT reduction operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentReduceByKey
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// The input keys type
|
||||
typedef typename std::iterator_traits<KeysInputIteratorT>::value_type KeyInputT;
|
||||
|
||||
// The output keys type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<UniqueOutputIteratorT>::value_type, void>::VALUE), // KeyOutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<KeysInputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<UniqueOutputIteratorT>::value_type>::Type KeyOutputT; // ... else the output iterator's value type
|
||||
|
||||
// The input values type
|
||||
typedef typename std::iterator_traits<ValuesInputIteratorT>::value_type ValueInputT;
|
||||
|
||||
// The output values type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<AggregatesOutputIteratorT>::value_type, void>::VALUE), // ValueOutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<ValuesInputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<AggregatesOutputIteratorT>::value_type>::Type ValueOutputT; // ... else the output iterator's value type
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueOutputT> OffsetValuePairT;
|
||||
|
||||
// Tuple type for pairing keys and values
|
||||
typedef KeyValuePair<KeyOutputT, ValueOutputT> KeyValuePairT;
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<ValueOutputT, OffsetT> ScanTileStateT;
|
||||
|
||||
// Guarded inequality functor
|
||||
template <typename _EqualityOpT>
|
||||
struct GuardedInequalityWrapper
|
||||
{
|
||||
_EqualityOpT op; ///< Wrapped equality operator
|
||||
int num_remaining; ///< Items remaining
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
GuardedInequalityWrapper(_EqualityOpT op, int num_remaining) : op(op), num_remaining(num_remaining) {}
|
||||
|
||||
/// Boolean inequality operator, returns <tt>(a != b)</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ bool operator()(const T &a, const T &b, int idx) const
|
||||
{
|
||||
if (idx < num_remaining)
|
||||
return !op(a, b); // In bounds
|
||||
|
||||
// Return true if first out-of-bounds item, false otherwise
|
||||
return (idx == num_remaining);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentReduceByKeyPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentReduceByKeyPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
TWO_PHASE_SCATTER = (ITEMS_PER_THREAD > 1),
|
||||
|
||||
// Whether or not the scan operation has a zero-valued identity value (true if we're performing addition on a primitive type)
|
||||
HAS_IDENTITY_ZERO = (Equals<ReductionOpT, cub::Sum>::VALUE) && (Traits<ValueOutputT>::PRIMITIVE),
|
||||
};
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for keys
|
||||
typedef typename If<IsPointer<KeysInputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentReduceByKeyPolicyT::LOAD_MODIFIER, KeyInputT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
KeysInputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedKeysInputIteratorT;
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for values
|
||||
typedef typename If<IsPointer<ValuesInputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentReduceByKeyPolicyT::LOAD_MODIFIER, ValueInputT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
ValuesInputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedValuesInputIteratorT;
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for fixup values
|
||||
typedef typename If<IsPointer<AggregatesOutputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentReduceByKeyPolicyT::LOAD_MODIFIER, ValueInputT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
AggregatesOutputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedFixupInputIteratorT;
|
||||
|
||||
// Reduce-value-by-segment scan operator
|
||||
typedef ReduceBySegmentOp<ReductionOpT> ReduceBySegmentOpT;
|
||||
|
||||
// Parameterized BlockLoad type for keys
|
||||
typedef BlockLoad<
|
||||
KeyOutputT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
AgentReduceByKeyPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadKeysT;
|
||||
|
||||
// Parameterized BlockLoad type for values
|
||||
typedef BlockLoad<
|
||||
ValueOutputT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
AgentReduceByKeyPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadValuesT;
|
||||
|
||||
// Parameterized BlockDiscontinuity type for keys
|
||||
typedef BlockDiscontinuity<
|
||||
KeyOutputT,
|
||||
BLOCK_THREADS>
|
||||
BlockDiscontinuityKeys;
|
||||
|
||||
// Parameterized BlockScan type
|
||||
typedef BlockScan<
|
||||
OffsetValuePairT,
|
||||
BLOCK_THREADS,
|
||||
AgentReduceByKeyPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
// Callback type for obtaining tile prefix during block scan
|
||||
typedef TilePrefixCallbackOp<
|
||||
OffsetValuePairT,
|
||||
ReduceBySegmentOpT,
|
||||
ScanTileStateT>
|
||||
TilePrefixCallbackOpT;
|
||||
|
||||
// Key and value exchange types
|
||||
typedef KeyOutputT KeyExchangeT[TILE_ITEMS + 1];
|
||||
typedef ValueOutputT ValueExchangeT[TILE_ITEMS + 1];
|
||||
|
||||
// Shared memory type for this threadblock
|
||||
union _TempStorage
|
||||
{
|
||||
struct
|
||||
{
|
||||
typename BlockScanT::TempStorage scan; // Smem needed for tile scanning
|
||||
typename TilePrefixCallbackOpT::TempStorage prefix; // Smem needed for cooperative prefix callback
|
||||
typename BlockDiscontinuityKeys::TempStorage discontinuity; // Smem needed for discontinuity detection
|
||||
};
|
||||
|
||||
// Smem needed for loading keys
|
||||
typename BlockLoadKeysT::TempStorage load_keys;
|
||||
|
||||
// Smem needed for loading values
|
||||
typename BlockLoadValuesT::TempStorage load_values;
|
||||
|
||||
// Smem needed for compacting key value pairs(allows non POD items in this union)
|
||||
Uninitialized<KeyValuePairT[TILE_ITEMS + 1]> raw_exchange;
|
||||
};
|
||||
|
||||
// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
WrappedKeysInputIteratorT d_keys_in; ///< Input keys
|
||||
UniqueOutputIteratorT d_unique_out; ///< Unique output keys
|
||||
WrappedValuesInputIteratorT d_values_in; ///< Input values
|
||||
AggregatesOutputIteratorT d_aggregates_out; ///< Output value aggregates
|
||||
NumRunsOutputIteratorT d_num_runs_out; ///< Output pointer for total number of segments identified
|
||||
EqualityOpT equality_op; ///< KeyT equality operator
|
||||
ReductionOpT reduction_op; ///< Reduction operator
|
||||
ReduceBySegmentOpT scan_op; ///< Reduce-by-segment scan operator
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
AgentReduceByKey(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
KeysInputIteratorT d_keys_in, ///< Input keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< Unique output keys
|
||||
ValuesInputIteratorT d_values_in, ///< Input values
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< Output value aggregates
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< Output pointer for total number of segments identified
|
||||
EqualityOpT equality_op, ///< KeyT equality operator
|
||||
ReductionOpT reduction_op) ///< ValueT reduction operator
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_keys_in(d_keys_in),
|
||||
d_unique_out(d_unique_out),
|
||||
d_values_in(d_values_in),
|
||||
d_aggregates_out(d_aggregates_out),
|
||||
d_num_runs_out(d_num_runs_out),
|
||||
equality_op(equality_op),
|
||||
reduction_op(reduction_op),
|
||||
scan_op(reduction_op)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Scatter utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Directly scatter flagged items to output offsets
|
||||
*/
|
||||
__device__ __forceinline__ void ScatterDirect(
|
||||
KeyValuePairT (&scatter_items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_indices)[ITEMS_PER_THREAD])
|
||||
{
|
||||
// Scatter flagged keys and values
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (segment_flags[ITEM])
|
||||
{
|
||||
d_unique_out[segment_indices[ITEM]] = scatter_items[ITEM].key;
|
||||
d_aggregates_out[segment_indices[ITEM]] = scatter_items[ITEM].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 2-phase scatter flagged items to output offsets
|
||||
*
|
||||
* The exclusive scan causes each head flag to be paired with the previous
|
||||
* value aggregate: the scatter offsets must be decremented for value aggregates
|
||||
*/
|
||||
__device__ __forceinline__ void ScatterTwoPhase(
|
||||
KeyValuePairT (&scatter_items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_indices)[ITEMS_PER_THREAD],
|
||||
OffsetT num_tile_segments,
|
||||
OffsetT num_tile_segments_prefix)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Compact and scatter pairs
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (segment_flags[ITEM])
|
||||
{
|
||||
temp_storage.raw_exchange.Alias()[segment_indices[ITEM] - num_tile_segments_prefix] = scatter_items[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int item = threadIdx.x; item < num_tile_segments; item += BLOCK_THREADS)
|
||||
{
|
||||
KeyValuePairT pair = temp_storage.raw_exchange.Alias()[item];
|
||||
d_unique_out[num_tile_segments_prefix + item] = pair.key;
|
||||
d_aggregates_out[num_tile_segments_prefix + item] = pair.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter flagged items
|
||||
*/
|
||||
__device__ __forceinline__ void Scatter(
|
||||
KeyValuePairT (&scatter_items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&segment_indices)[ITEMS_PER_THREAD],
|
||||
OffsetT num_tile_segments,
|
||||
OffsetT num_tile_segments_prefix)
|
||||
{
|
||||
// Do a one-phase scatter if (a) two-phase is disabled or (b) the average number of selected items per thread is less than one
|
||||
if (TWO_PHASE_SCATTER && (num_tile_segments > BLOCK_THREADS))
|
||||
{
|
||||
ScatterTwoPhase(
|
||||
scatter_items,
|
||||
segment_flags,
|
||||
segment_indices,
|
||||
num_tile_segments,
|
||||
num_tile_segments_prefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScatterDirect(
|
||||
scatter_items,
|
||||
segment_flags,
|
||||
segment_indices);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Cooperatively scan a device-wide sequence of tiles with other CTAs
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process a tile of input (dynamic chained scan)
|
||||
*/
|
||||
template <bool IS_LAST_TILE> ///< Whether the current tile is the last tile
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OffsetT num_remaining, ///< Number of global input items remaining (including this tile)
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
KeyOutputT keys[ITEMS_PER_THREAD]; // Tile keys
|
||||
KeyOutputT prev_keys[ITEMS_PER_THREAD]; // Tile keys shuffled up
|
||||
ValueOutputT values[ITEMS_PER_THREAD]; // Tile values
|
||||
OffsetT head_flags[ITEMS_PER_THREAD]; // Segment head flags
|
||||
OffsetT segment_indices[ITEMS_PER_THREAD]; // Segment indices
|
||||
OffsetValuePairT scan_items[ITEMS_PER_THREAD]; // Zipped values and segment flags|indices
|
||||
KeyValuePairT scatter_items[ITEMS_PER_THREAD]; // Zipped key value pairs for scattering
|
||||
|
||||
// Load keys
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadKeysT(temp_storage.load_keys).Load(d_keys_in + tile_offset, keys, num_remaining);
|
||||
else
|
||||
BlockLoadKeysT(temp_storage.load_keys).Load(d_keys_in + tile_offset, keys);
|
||||
|
||||
// Load tile predecessor key in first thread
|
||||
KeyOutputT tile_predecessor;
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
tile_predecessor = (tile_idx == 0) ?
|
||||
keys[0] : // First tile gets repeat of first item (thus first item will not be flagged as a head)
|
||||
d_keys_in[tile_offset - 1]; // Subsequent tiles get last key from previous tile
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Load values
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadValuesT(temp_storage.load_values).Load(d_values_in + tile_offset, values, num_remaining);
|
||||
else
|
||||
BlockLoadValuesT(temp_storage.load_values).Load(d_values_in + tile_offset, values);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Initialize head-flags and shuffle up the previous keys
|
||||
if (IS_LAST_TILE)
|
||||
{
|
||||
// Use custom flag operator to additionally flag the first out-of-bounds item
|
||||
GuardedInequalityWrapper<EqualityOpT> flag_op(equality_op, num_remaining);
|
||||
BlockDiscontinuityKeys(temp_storage.discontinuity).FlagHeads(
|
||||
head_flags, keys, prev_keys, flag_op, tile_predecessor);
|
||||
}
|
||||
else
|
||||
{
|
||||
InequalityWrapper<EqualityOpT> flag_op(equality_op);
|
||||
BlockDiscontinuityKeys(temp_storage.discontinuity).FlagHeads(
|
||||
head_flags, keys, prev_keys, flag_op, tile_predecessor);
|
||||
}
|
||||
|
||||
// Zip values and head flags
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
scan_items[ITEM].value = values[ITEM];
|
||||
scan_items[ITEM].key = head_flags[ITEM];
|
||||
}
|
||||
|
||||
// Perform exclusive tile scan
|
||||
OffsetValuePairT block_aggregate; // Inclusive block-wide scan aggregate
|
||||
OffsetT num_segments_prefix; // Number of segments prior to this tile
|
||||
if (tile_idx == 0)
|
||||
{
|
||||
// Scan first tile
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_items, scan_items, scan_op, block_aggregate);
|
||||
num_segments_prefix = 0;
|
||||
|
||||
// Update tile status if there are successor tiles
|
||||
if ((!IS_LAST_TILE) && (threadIdx.x == 0))
|
||||
tile_state.SetInclusive(0, block_aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scan non-first tile
|
||||
TilePrefixCallbackOpT prefix_op(tile_state, temp_storage.prefix, scan_op, tile_idx);
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_items, scan_items, scan_op, prefix_op);
|
||||
|
||||
num_segments_prefix = prefix_op.GetExclusivePrefix().key;
|
||||
block_aggregate = prefix_op.GetBlockAggregate();
|
||||
}
|
||||
|
||||
// Rezip scatter items and segment indices
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
scatter_items[ITEM].key = prev_keys[ITEM];
|
||||
scatter_items[ITEM].value = scan_items[ITEM].value;
|
||||
segment_indices[ITEM] = scan_items[ITEM].key;
|
||||
}
|
||||
|
||||
// At this point, each flagged segment head has:
|
||||
// - The key for the previous segment
|
||||
// - The reduced value from the previous segment
|
||||
// - The segment index for the reduced value
|
||||
|
||||
// Scatter flagged keys and values
|
||||
OffsetT num_tile_segments = block_aggregate.key;
|
||||
Scatter(scatter_items, head_flags, segment_indices, num_tile_segments, num_segments_prefix);
|
||||
|
||||
// Last thread in last tile will output final count (and last pair, if necessary)
|
||||
if ((IS_LAST_TILE) && (threadIdx.x == BLOCK_THREADS - 1))
|
||||
{
|
||||
OffsetT num_segments = num_segments_prefix + num_tile_segments;
|
||||
|
||||
// If the last tile is a whole tile, the block-wide aggregate contains the value for the last segment
|
||||
if (num_remaining == TILE_ITEMS)
|
||||
{
|
||||
d_unique_out[num_segments] = keys[ITEMS_PER_THREAD - 1];
|
||||
d_aggregates_out[num_segments] = block_aggregate.value;
|
||||
num_segments++;
|
||||
}
|
||||
|
||||
// Output the total number of items selected
|
||||
*d_num_runs_out = num_segments;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan tiles of items as part of a dynamic chained scan
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
int num_items, ///< Total number of input items
|
||||
ScanTileStateT& tile_state, ///< Global tile state descriptor
|
||||
int start_tile) ///< The starting tile for the current grid
|
||||
{
|
||||
// Blocks are launched in increasing order, so just assign one tile per block
|
||||
int tile_idx = start_tile + blockIdx.x; // Current tile index
|
||||
OffsetT tile_offset = OffsetT(TILE_ITEMS) * tile_idx; // Global offset for the current tile
|
||||
OffsetT num_remaining = num_items - tile_offset; // Remaining items (including this tile)
|
||||
|
||||
if (num_remaining > TILE_ITEMS)
|
||||
{
|
||||
// Not last tile
|
||||
ConsumeTile<false>(num_remaining, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
else if (num_remaining > 0)
|
||||
{
|
||||
// Last tile
|
||||
ConsumeTile<true>(num_remaining, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,833 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentRle implements a stateful abstraction of CUDA thread blocks for participating in device-wide run-length-encode.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "single_pass_scan_operators.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../block/block_discontinuity.cuh"
|
||||
#include "../grid/grid_queue.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/constant_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentRle
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
bool _STORE_WARP_TIME_SLICING, ///< Whether or not only one warp's worth of shared memory should be allocated and time-sliced among block-warps during any store-related data transpositions (versus each warp having its own storage)
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentRlePolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
STORE_WARP_TIME_SLICING = _STORE_WARP_TIME_SLICING, ///< Whether or not only one warp's worth of shared memory should be allocated and time-sliced among block-warps during any store-related data transpositions (versus each warp having its own storage)
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentRle implements a stateful abstraction of CUDA thread blocks for participating in device-wide run-length-encode
|
||||
*/
|
||||
template <
|
||||
typename AgentRlePolicyT, ///< Parameterized AgentRlePolicyT tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type for data
|
||||
typename OffsetsOutputIteratorT, ///< Random-access output iterator type for offset values
|
||||
typename LengthsOutputIteratorT, ///< Random-access output iterator type for length values
|
||||
typename EqualityOpT, ///< T equality operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentRle
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type T;
|
||||
|
||||
/// The lengths output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<LengthsOutputIteratorT>::value_type, void>::VALUE), // LengthT = (if output iterator's value type is void) ?
|
||||
OffsetT, // ... then the OffsetT type,
|
||||
typename std::iterator_traits<LengthsOutputIteratorT>::value_type>::Type LengthT; // ... else the output iterator's value type
|
||||
|
||||
/// Tuple type for scanning (pairs run-length and run-index)
|
||||
typedef KeyValuePair<OffsetT, LengthT> LengthOffsetPair;
|
||||
|
||||
/// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<LengthT, OffsetT> ScanTileStateT;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
BLOCK_THREADS = AgentRlePolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentRlePolicyT::ITEMS_PER_THREAD,
|
||||
WARP_ITEMS = WARP_THREADS * ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
|
||||
/// Whether or not to sync after loading data
|
||||
SYNC_AFTER_LOAD = (AgentRlePolicyT::LOAD_ALGORITHM != BLOCK_LOAD_DIRECT),
|
||||
|
||||
/// Whether or not only one warp's worth of shared memory should be allocated and time-sliced among block-warps during any store-related data transpositions (versus each warp having its own storage)
|
||||
STORE_WARP_TIME_SLICING = AgentRlePolicyT::STORE_WARP_TIME_SLICING,
|
||||
ACTIVE_EXCHANGE_WARPS = (STORE_WARP_TIME_SLICING) ? 1 : WARPS,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Special operator that signals all out-of-bounds items are not equal to everything else,
|
||||
* forcing both (1) the last item to be tail-flagged and (2) all oob items to be marked
|
||||
* trivial.
|
||||
*/
|
||||
template <bool LAST_TILE>
|
||||
struct OobInequalityOp
|
||||
{
|
||||
OffsetT num_remaining;
|
||||
EqualityOpT equality_op;
|
||||
|
||||
__device__ __forceinline__ OobInequalityOp(
|
||||
OffsetT num_remaining,
|
||||
EqualityOpT equality_op)
|
||||
:
|
||||
num_remaining(num_remaining),
|
||||
equality_op(equality_op)
|
||||
{}
|
||||
|
||||
template <typename Index>
|
||||
__device__ __forceinline__ bool operator()(T first, T second, Index idx)
|
||||
{
|
||||
if (!LAST_TILE || (idx < num_remaining))
|
||||
return !equality_op(first, second);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for data
|
||||
typedef typename If<IsPointer<InputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentRlePolicyT::LOAD_MODIFIER, T, OffsetT>, // Wrap the native input pointer with CacheModifiedVLengthnputIterator
|
||||
InputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedInputIteratorT;
|
||||
|
||||
// Parameterized BlockLoad type for data
|
||||
typedef BlockLoad<
|
||||
T,
|
||||
AgentRlePolicyT::BLOCK_THREADS,
|
||||
AgentRlePolicyT::ITEMS_PER_THREAD,
|
||||
AgentRlePolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadT;
|
||||
|
||||
// Parameterized BlockDiscontinuity type for data
|
||||
typedef BlockDiscontinuity<T, BLOCK_THREADS> BlockDiscontinuityT;
|
||||
|
||||
// Parameterized WarpScan type
|
||||
typedef WarpScan<LengthOffsetPair> WarpScanPairs;
|
||||
|
||||
// Reduce-length-by-run scan operator
|
||||
typedef ReduceBySegmentOp<cub::Sum> ReduceBySegmentOpT;
|
||||
|
||||
// Callback type for obtaining tile prefix during block scan
|
||||
typedef TilePrefixCallbackOp<
|
||||
LengthOffsetPair,
|
||||
ReduceBySegmentOpT,
|
||||
ScanTileStateT>
|
||||
TilePrefixCallbackOpT;
|
||||
|
||||
// Warp exchange types
|
||||
typedef WarpExchange<LengthOffsetPair, ITEMS_PER_THREAD> WarpExchangePairs;
|
||||
|
||||
typedef typename If<STORE_WARP_TIME_SLICING, typename WarpExchangePairs::TempStorage, NullType>::Type WarpExchangePairsStorage;
|
||||
|
||||
typedef WarpExchange<OffsetT, ITEMS_PER_THREAD> WarpExchangeOffsets;
|
||||
typedef WarpExchange<LengthT, ITEMS_PER_THREAD> WarpExchangeLengths;
|
||||
|
||||
typedef LengthOffsetPair WarpAggregates[WARPS];
|
||||
|
||||
// Shared memory type for this threadblock
|
||||
struct _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
typename BlockDiscontinuityT::TempStorage discontinuity; // Smem needed for discontinuity detection
|
||||
typename WarpScanPairs::TempStorage warp_scan[WARPS]; // Smem needed for warp-synchronous scans
|
||||
Uninitialized<LengthOffsetPair[WARPS]> warp_aggregates; // Smem needed for sharing warp-wide aggregates
|
||||
typename TilePrefixCallbackOpT::TempStorage prefix; // Smem needed for cooperative prefix callback
|
||||
};
|
||||
|
||||
// Smem needed for input loading
|
||||
typename BlockLoadT::TempStorage load;
|
||||
|
||||
// Smem needed for two-phase scatter
|
||||
union
|
||||
{
|
||||
unsigned long long align;
|
||||
WarpExchangePairsStorage exchange_pairs[ACTIVE_EXCHANGE_WARPS];
|
||||
typename WarpExchangeOffsets::TempStorage exchange_offsets[ACTIVE_EXCHANGE_WARPS];
|
||||
typename WarpExchangeLengths::TempStorage exchange_lengths[ACTIVE_EXCHANGE_WARPS];
|
||||
};
|
||||
};
|
||||
|
||||
OffsetT tile_idx; // Shared tile index
|
||||
LengthOffsetPair tile_inclusive; // Inclusive tile prefix
|
||||
LengthOffsetPair tile_exclusive; // Exclusive tile prefix
|
||||
};
|
||||
|
||||
// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
|
||||
WrappedInputIteratorT d_in; ///< Pointer to input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out; ///< Input run offsets
|
||||
LengthsOutputIteratorT d_lengths_out; ///< Output run lengths
|
||||
|
||||
EqualityOpT equality_op; ///< T equality operator
|
||||
ReduceBySegmentOpT scan_op; ///< Reduce-length-by-flag scan operator
|
||||
OffsetT num_items; ///< Total number of input items
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
AgentRle(
|
||||
TempStorage &temp_storage, ///< [in] Reference to temp_storage
|
||||
InputIteratorT d_in, ///< [in] Pointer to input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out, ///< [out] Pointer to output sequence of run offsets
|
||||
LengthsOutputIteratorT d_lengths_out, ///< [out] Pointer to output sequence of run lengths
|
||||
EqualityOpT equality_op, ///< [in] T equality operator
|
||||
OffsetT num_items) ///< [in] Total number of input items
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_in(d_in),
|
||||
d_offsets_out(d_offsets_out),
|
||||
d_lengths_out(d_lengths_out),
|
||||
equality_op(equality_op),
|
||||
scan_op(cub::Sum()),
|
||||
num_items(num_items)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods for initializing the selections
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template <bool FIRST_TILE, bool LAST_TILE>
|
||||
__device__ __forceinline__ void InitializeSelections(
|
||||
OffsetT tile_offset,
|
||||
OffsetT num_remaining,
|
||||
T (&items)[ITEMS_PER_THREAD],
|
||||
LengthOffsetPair (&lengths_and_num_runs)[ITEMS_PER_THREAD])
|
||||
{
|
||||
bool head_flags[ITEMS_PER_THREAD];
|
||||
bool tail_flags[ITEMS_PER_THREAD];
|
||||
|
||||
OobInequalityOp<LAST_TILE> inequality_op(num_remaining, equality_op);
|
||||
|
||||
if (FIRST_TILE && LAST_TILE)
|
||||
{
|
||||
// First-and-last-tile always head-flags the first item and tail-flags the last item
|
||||
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeadsAndTails(
|
||||
head_flags, tail_flags, items, inequality_op);
|
||||
}
|
||||
else if (FIRST_TILE)
|
||||
{
|
||||
// First-tile always head-flags the first item
|
||||
|
||||
// Get the first item from the next tile
|
||||
T tile_successor_item;
|
||||
if (threadIdx.x == BLOCK_THREADS - 1)
|
||||
tile_successor_item = d_in[tile_offset + TILE_ITEMS];
|
||||
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeadsAndTails(
|
||||
head_flags, tail_flags, tile_successor_item, items, inequality_op);
|
||||
}
|
||||
else if (LAST_TILE)
|
||||
{
|
||||
// Last-tile always flags the last item
|
||||
|
||||
// Get the last item from the previous tile
|
||||
T tile_predecessor_item;
|
||||
if (threadIdx.x == 0)
|
||||
tile_predecessor_item = d_in[tile_offset - 1];
|
||||
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeadsAndTails(
|
||||
head_flags, tile_predecessor_item, tail_flags, items, inequality_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the first item from the next tile
|
||||
T tile_successor_item;
|
||||
if (threadIdx.x == BLOCK_THREADS - 1)
|
||||
tile_successor_item = d_in[tile_offset + TILE_ITEMS];
|
||||
|
||||
// Get the last item from the previous tile
|
||||
T tile_predecessor_item;
|
||||
if (threadIdx.x == 0)
|
||||
tile_predecessor_item = d_in[tile_offset - 1];
|
||||
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeadsAndTails(
|
||||
head_flags, tile_predecessor_item, tail_flags, tile_successor_item, items, inequality_op);
|
||||
}
|
||||
|
||||
// Zip counts and runs
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
lengths_and_num_runs[ITEM].key = head_flags[ITEM] && (!tail_flags[ITEM]);
|
||||
lengths_and_num_runs[ITEM].value = ((!head_flags[ITEM]) || (!tail_flags[ITEM]));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Scan utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scan of allocations
|
||||
*/
|
||||
__device__ __forceinline__ void WarpScanAllocations(
|
||||
LengthOffsetPair &tile_aggregate,
|
||||
LengthOffsetPair &warp_aggregate,
|
||||
LengthOffsetPair &warp_exclusive_in_tile,
|
||||
LengthOffsetPair &thread_exclusive_in_warp,
|
||||
LengthOffsetPair (&lengths_and_num_runs)[ITEMS_PER_THREAD])
|
||||
{
|
||||
// Perform warpscans
|
||||
unsigned int warp_id = ((WARPS == 1) ? 0 : threadIdx.x / WARP_THREADS);
|
||||
int lane_id = LaneId();
|
||||
|
||||
LengthOffsetPair identity;
|
||||
identity.key = 0;
|
||||
identity.value = 0;
|
||||
|
||||
LengthOffsetPair thread_inclusive;
|
||||
LengthOffsetPair thread_aggregate = ThreadReduce(lengths_and_num_runs, scan_op);
|
||||
WarpScanPairs(temp_storage.warp_scan[warp_id]).Scan(
|
||||
thread_aggregate,
|
||||
thread_inclusive,
|
||||
thread_exclusive_in_warp,
|
||||
identity,
|
||||
scan_op);
|
||||
|
||||
// Last lane in each warp shares its warp-aggregate
|
||||
if (lane_id == WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates.Alias()[warp_id] = thread_inclusive;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Accumulate total selected and the warp-wide prefix
|
||||
warp_exclusive_in_tile = identity;
|
||||
warp_aggregate = temp_storage.warp_aggregates.Alias()[warp_id];
|
||||
tile_aggregate = temp_storage.warp_aggregates.Alias()[0];
|
||||
|
||||
#pragma unroll
|
||||
for (int WARP = 1; WARP < WARPS; ++WARP)
|
||||
{
|
||||
if (warp_id == WARP)
|
||||
warp_exclusive_in_tile = tile_aggregate;
|
||||
|
||||
tile_aggregate = scan_op(tile_aggregate, temp_storage.warp_aggregates.Alias()[WARP]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods for scattering selections
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Two-phase scatter, specialized for warp time-slicing
|
||||
*/
|
||||
template <bool FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterTwoPhase(
|
||||
OffsetT tile_num_runs_exclusive_in_global,
|
||||
OffsetT warp_num_runs_aggregate,
|
||||
OffsetT warp_num_runs_exclusive_in_tile,
|
||||
OffsetT (&thread_num_runs_exclusive_in_warp)[ITEMS_PER_THREAD],
|
||||
LengthOffsetPair (&lengths_and_offsets)[ITEMS_PER_THREAD],
|
||||
Int2Type<true> is_warp_time_slice)
|
||||
{
|
||||
unsigned int warp_id = ((WARPS == 1) ? 0 : threadIdx.x / WARP_THREADS);
|
||||
int lane_id = LaneId();
|
||||
|
||||
// Locally compact items within the warp (first warp)
|
||||
if (warp_id == 0)
|
||||
{
|
||||
WarpExchangePairs(temp_storage.exchange_pairs[0]).ScatterToStriped(lengths_and_offsets, thread_num_runs_exclusive_in_warp);
|
||||
}
|
||||
|
||||
// Locally compact items within the warp (remaining warps)
|
||||
#pragma unroll
|
||||
for (int SLICE = 1; SLICE < WARPS; ++SLICE)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
if (warp_id == SLICE)
|
||||
{
|
||||
WarpExchangePairs(temp_storage.exchange_pairs[0]).ScatterToStriped(lengths_and_offsets, thread_num_runs_exclusive_in_warp);
|
||||
}
|
||||
}
|
||||
|
||||
// Global scatter
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
if ((ITEM * WARP_THREADS) < warp_num_runs_aggregate - lane_id)
|
||||
{
|
||||
OffsetT item_offset =
|
||||
tile_num_runs_exclusive_in_global +
|
||||
warp_num_runs_exclusive_in_tile +
|
||||
(ITEM * WARP_THREADS) + lane_id;
|
||||
|
||||
// Scatter offset
|
||||
d_offsets_out[item_offset] = lengths_and_offsets[ITEM].key;
|
||||
|
||||
// Scatter length if not the first (global) length
|
||||
if ((!FIRST_TILE) || (ITEM != 0) || (threadIdx.x > 0))
|
||||
{
|
||||
d_lengths_out[item_offset - 1] = lengths_and_offsets[ITEM].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two-phase scatter
|
||||
*/
|
||||
template <bool FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterTwoPhase(
|
||||
OffsetT tile_num_runs_exclusive_in_global,
|
||||
OffsetT warp_num_runs_aggregate,
|
||||
OffsetT warp_num_runs_exclusive_in_tile,
|
||||
OffsetT (&thread_num_runs_exclusive_in_warp)[ITEMS_PER_THREAD],
|
||||
LengthOffsetPair (&lengths_and_offsets)[ITEMS_PER_THREAD],
|
||||
Int2Type<false> is_warp_time_slice)
|
||||
{
|
||||
unsigned int warp_id = ((WARPS == 1) ? 0 : threadIdx.x / WARP_THREADS);
|
||||
int lane_id = LaneId();
|
||||
|
||||
// Unzip
|
||||
OffsetT run_offsets[ITEMS_PER_THREAD];
|
||||
LengthT run_lengths[ITEMS_PER_THREAD];
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
run_offsets[ITEM] = lengths_and_offsets[ITEM].key;
|
||||
run_lengths[ITEM] = lengths_and_offsets[ITEM].value;
|
||||
}
|
||||
|
||||
WarpExchangeOffsets(temp_storage.exchange_offsets[warp_id]).ScatterToStriped(run_offsets, thread_num_runs_exclusive_in_warp);
|
||||
|
||||
if (sizeof(LengthT) == sizeof(OffsetT))
|
||||
__threadfence_block();
|
||||
else
|
||||
__syncthreads();
|
||||
|
||||
WarpExchangeLengths(temp_storage.exchange_lengths[warp_id]).ScatterToStriped(run_lengths, thread_num_runs_exclusive_in_warp);
|
||||
|
||||
// Global scatter
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
if ((ITEM * WARP_THREADS) + lane_id < warp_num_runs_aggregate)
|
||||
{
|
||||
OffsetT item_offset =
|
||||
tile_num_runs_exclusive_in_global +
|
||||
warp_num_runs_exclusive_in_tile +
|
||||
(ITEM * WARP_THREADS) + lane_id;
|
||||
|
||||
// Scatter offset
|
||||
d_offsets_out[item_offset] = run_offsets[ITEM];
|
||||
|
||||
// Scatter length if not the first (global) length
|
||||
if ((!FIRST_TILE) || (ITEM != 0) || (threadIdx.x > 0))
|
||||
{
|
||||
d_lengths_out[item_offset - 1] = run_lengths[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Direct scatter
|
||||
*/
|
||||
template <bool FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterDirect(
|
||||
OffsetT tile_num_runs_exclusive_in_global,
|
||||
OffsetT warp_num_runs_aggregate,
|
||||
OffsetT warp_num_runs_exclusive_in_tile,
|
||||
OffsetT (&thread_num_runs_exclusive_in_warp)[ITEMS_PER_THREAD],
|
||||
LengthOffsetPair (&lengths_and_offsets)[ITEMS_PER_THREAD])
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (thread_num_runs_exclusive_in_warp[ITEM] < warp_num_runs_aggregate)
|
||||
{
|
||||
OffsetT item_offset =
|
||||
tile_num_runs_exclusive_in_global +
|
||||
warp_num_runs_exclusive_in_tile +
|
||||
thread_num_runs_exclusive_in_warp[ITEM];
|
||||
|
||||
// Scatter offset
|
||||
d_offsets_out[item_offset] = lengths_and_offsets[ITEM].key;
|
||||
|
||||
// Scatter length if not the first (global) length
|
||||
if (item_offset >= 1)
|
||||
{
|
||||
d_lengths_out[item_offset - 1] = lengths_and_offsets[ITEM].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter
|
||||
*/
|
||||
template <bool FIRST_TILE>
|
||||
__device__ __forceinline__ void Scatter(
|
||||
OffsetT tile_num_runs_aggregate,
|
||||
OffsetT tile_num_runs_exclusive_in_global,
|
||||
OffsetT warp_num_runs_aggregate,
|
||||
OffsetT warp_num_runs_exclusive_in_tile,
|
||||
OffsetT (&thread_num_runs_exclusive_in_warp)[ITEMS_PER_THREAD],
|
||||
LengthOffsetPair (&lengths_and_offsets)[ITEMS_PER_THREAD])
|
||||
{
|
||||
if ((ITEMS_PER_THREAD == 1) || (tile_num_runs_aggregate < BLOCK_THREADS))
|
||||
{
|
||||
// Direct scatter if the warp has any items
|
||||
if (warp_num_runs_aggregate)
|
||||
{
|
||||
ScatterDirect<FIRST_TILE>(
|
||||
tile_num_runs_exclusive_in_global,
|
||||
warp_num_runs_aggregate,
|
||||
warp_num_runs_exclusive_in_tile,
|
||||
thread_num_runs_exclusive_in_warp,
|
||||
lengths_and_offsets);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scatter two phase
|
||||
ScatterTwoPhase<FIRST_TILE>(
|
||||
tile_num_runs_exclusive_in_global,
|
||||
warp_num_runs_aggregate,
|
||||
warp_num_runs_exclusive_in_tile,
|
||||
thread_num_runs_exclusive_in_warp,
|
||||
lengths_and_offsets,
|
||||
Int2Type<STORE_WARP_TIME_SLICING>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Cooperatively scan a device-wide sequence of tiles with other CTAs
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process a tile of input (dynamic chained scan)
|
||||
*/
|
||||
template <
|
||||
bool LAST_TILE>
|
||||
__device__ __forceinline__ LengthOffsetPair ConsumeTile(
|
||||
OffsetT num_items, ///< Total number of global input items
|
||||
OffsetT num_remaining, ///< Number of global input items remaining (including this tile)
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT &tile_status) ///< Global list of tile status
|
||||
{
|
||||
if (tile_idx == 0)
|
||||
{
|
||||
// First tile
|
||||
|
||||
// Load items
|
||||
T items[ITEMS_PER_THREAD];
|
||||
if (LAST_TILE)
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items, num_remaining, T());
|
||||
else
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items);
|
||||
|
||||
if (SYNC_AFTER_LOAD)
|
||||
__syncthreads();
|
||||
|
||||
// Set flags
|
||||
LengthOffsetPair lengths_and_num_runs[ITEMS_PER_THREAD];
|
||||
|
||||
InitializeSelections<true, LAST_TILE>(
|
||||
tile_offset,
|
||||
num_remaining,
|
||||
items,
|
||||
lengths_and_num_runs);
|
||||
|
||||
// Exclusive scan of lengths and runs
|
||||
LengthOffsetPair tile_aggregate;
|
||||
LengthOffsetPair warp_aggregate;
|
||||
LengthOffsetPair warp_exclusive_in_tile;
|
||||
LengthOffsetPair thread_exclusive_in_warp;
|
||||
|
||||
WarpScanAllocations(
|
||||
tile_aggregate,
|
||||
warp_aggregate,
|
||||
warp_exclusive_in_tile,
|
||||
thread_exclusive_in_warp,
|
||||
lengths_and_num_runs);
|
||||
|
||||
// Update tile status if this is not the last tile
|
||||
if (!LAST_TILE && (threadIdx.x == 0))
|
||||
tile_status.SetInclusive(0, tile_aggregate);
|
||||
|
||||
// Update thread_exclusive_in_warp to fold in warp run-length
|
||||
if (thread_exclusive_in_warp.key == 0)
|
||||
thread_exclusive_in_warp.value += warp_exclusive_in_tile.value;
|
||||
|
||||
LengthOffsetPair lengths_and_offsets[ITEMS_PER_THREAD];
|
||||
OffsetT thread_num_runs_exclusive_in_warp[ITEMS_PER_THREAD];
|
||||
LengthOffsetPair lengths_and_num_runs2[ITEMS_PER_THREAD];
|
||||
|
||||
// Downsweep scan through lengths_and_num_runs
|
||||
ThreadScanExclusive(lengths_and_num_runs, lengths_and_num_runs2, scan_op, thread_exclusive_in_warp);
|
||||
|
||||
// Zip
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
lengths_and_offsets[ITEM].value = lengths_and_num_runs2[ITEM].value;
|
||||
lengths_and_offsets[ITEM].key = tile_offset + (threadIdx.x * ITEMS_PER_THREAD) + ITEM;
|
||||
thread_num_runs_exclusive_in_warp[ITEM] = (lengths_and_num_runs[ITEM].key) ?
|
||||
lengths_and_num_runs2[ITEM].key : // keep
|
||||
WARP_THREADS * ITEMS_PER_THREAD; // discard
|
||||
}
|
||||
|
||||
OffsetT tile_num_runs_aggregate = tile_aggregate.key;
|
||||
OffsetT tile_num_runs_exclusive_in_global = 0;
|
||||
OffsetT warp_num_runs_aggregate = warp_aggregate.key;
|
||||
OffsetT warp_num_runs_exclusive_in_tile = warp_exclusive_in_tile.key;
|
||||
|
||||
// Scatter
|
||||
Scatter<true>(
|
||||
tile_num_runs_aggregate,
|
||||
tile_num_runs_exclusive_in_global,
|
||||
warp_num_runs_aggregate,
|
||||
warp_num_runs_exclusive_in_tile,
|
||||
thread_num_runs_exclusive_in_warp,
|
||||
lengths_and_offsets);
|
||||
|
||||
// Return running total (inclusive of this tile)
|
||||
return tile_aggregate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not first tile
|
||||
|
||||
// Load items
|
||||
T items[ITEMS_PER_THREAD];
|
||||
if (LAST_TILE)
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items, num_remaining, T());
|
||||
else
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items);
|
||||
|
||||
if (SYNC_AFTER_LOAD)
|
||||
__syncthreads();
|
||||
|
||||
// Set flags
|
||||
LengthOffsetPair lengths_and_num_runs[ITEMS_PER_THREAD];
|
||||
|
||||
InitializeSelections<false, LAST_TILE>(
|
||||
tile_offset,
|
||||
num_remaining,
|
||||
items,
|
||||
lengths_and_num_runs);
|
||||
|
||||
// Exclusive scan of lengths and runs
|
||||
LengthOffsetPair tile_aggregate;
|
||||
LengthOffsetPair warp_aggregate;
|
||||
LengthOffsetPair warp_exclusive_in_tile;
|
||||
LengthOffsetPair thread_exclusive_in_warp;
|
||||
|
||||
WarpScanAllocations(
|
||||
tile_aggregate,
|
||||
warp_aggregate,
|
||||
warp_exclusive_in_tile,
|
||||
thread_exclusive_in_warp,
|
||||
lengths_and_num_runs);
|
||||
|
||||
// First warp computes tile prefix in lane 0
|
||||
TilePrefixCallbackOpT prefix_op(tile_status, temp_storage.prefix, Sum(), tile_idx);
|
||||
unsigned int warp_id = ((WARPS == 1) ? 0 : threadIdx.x / WARP_THREADS);
|
||||
if (warp_id == 0)
|
||||
{
|
||||
prefix_op(tile_aggregate);
|
||||
if (threadIdx.x == 0)
|
||||
temp_storage.tile_exclusive = prefix_op.exclusive_prefix;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
LengthOffsetPair tile_exclusive_in_global = temp_storage.tile_exclusive;
|
||||
|
||||
// Update thread_exclusive_in_warp to fold in warp and tile run-lengths
|
||||
LengthOffsetPair thread_exclusive = scan_op(tile_exclusive_in_global, warp_exclusive_in_tile);
|
||||
if (thread_exclusive_in_warp.key == 0)
|
||||
thread_exclusive_in_warp.value += thread_exclusive.value;
|
||||
|
||||
// Downsweep scan through lengths_and_num_runs
|
||||
LengthOffsetPair lengths_and_num_runs2[ITEMS_PER_THREAD];
|
||||
LengthOffsetPair lengths_and_offsets[ITEMS_PER_THREAD];
|
||||
OffsetT thread_num_runs_exclusive_in_warp[ITEMS_PER_THREAD];
|
||||
|
||||
ThreadScanExclusive(lengths_and_num_runs, lengths_and_num_runs2, scan_op, thread_exclusive_in_warp);
|
||||
|
||||
// Zip
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
lengths_and_offsets[ITEM].value = lengths_and_num_runs2[ITEM].value;
|
||||
lengths_and_offsets[ITEM].key = tile_offset + (threadIdx.x * ITEMS_PER_THREAD) + ITEM;
|
||||
thread_num_runs_exclusive_in_warp[ITEM] = (lengths_and_num_runs[ITEM].key) ?
|
||||
lengths_and_num_runs2[ITEM].key : // keep
|
||||
WARP_THREADS * ITEMS_PER_THREAD; // discard
|
||||
}
|
||||
|
||||
OffsetT tile_num_runs_aggregate = tile_aggregate.key;
|
||||
OffsetT tile_num_runs_exclusive_in_global = tile_exclusive_in_global.key;
|
||||
OffsetT warp_num_runs_aggregate = warp_aggregate.key;
|
||||
OffsetT warp_num_runs_exclusive_in_tile = warp_exclusive_in_tile.key;
|
||||
|
||||
// Scatter
|
||||
Scatter<false>(
|
||||
tile_num_runs_aggregate,
|
||||
tile_num_runs_exclusive_in_global,
|
||||
warp_num_runs_aggregate,
|
||||
warp_num_runs_exclusive_in_tile,
|
||||
thread_num_runs_exclusive_in_warp,
|
||||
lengths_and_offsets);
|
||||
|
||||
// Return running total (inclusive of this tile)
|
||||
return prefix_op.inclusive_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan tiles of items as part of a dynamic chained scan
|
||||
*/
|
||||
template <typename NumRunsIteratorT> ///< Output iterator type for recording number of items selected
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
int num_tiles, ///< Total number of input tiles
|
||||
ScanTileStateT& tile_status, ///< Global list of tile status
|
||||
NumRunsIteratorT d_num_runs_out) ///< Output pointer for total number of runs identified
|
||||
{
|
||||
// Blocks are launched in increasing order, so just assign one tile per block
|
||||
int tile_idx = (blockIdx.x * gridDim.y) + blockIdx.y; // Current tile index
|
||||
OffsetT tile_offset = tile_idx * TILE_ITEMS; // Global offset for the current tile
|
||||
OffsetT num_remaining = num_items - tile_offset; // Remaining items (including this tile)
|
||||
|
||||
if (tile_idx < num_tiles - 1)
|
||||
{
|
||||
// Not the last tile (full)
|
||||
ConsumeTile<false>(num_items, num_remaining, tile_idx, tile_offset, tile_status);
|
||||
}
|
||||
else if (num_remaining > 0)
|
||||
{
|
||||
// The last tile (possibly partially-full)
|
||||
LengthOffsetPair running_total = ConsumeTile<true>(num_items, num_remaining, tile_idx, tile_offset, tile_status);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Output the total number of items selected
|
||||
*d_num_runs_out = running_total.key;
|
||||
|
||||
// The inclusive prefix contains accumulated length reduction for the last run
|
||||
if (running_total.key > 0)
|
||||
d_lengths_out[running_total.key - 1] = running_total.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentScan implements a stateful abstraction of CUDA thread blocks for participating in device-wide prefix scan .
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "single_pass_scan_operators.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../grid/grid_queue.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentScan
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
BlockStoreAlgorithm _STORE_ALGORITHM, ///< The BlockStore algorithm to use
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentScanPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const BlockStoreAlgorithm STORE_ALGORITHM = _STORE_ALGORITHM; ///< The BlockStore algorithm to use
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentScan implements a stateful abstraction of CUDA thread blocks for participating in device-wide prefix scan .
|
||||
*/
|
||||
template <
|
||||
typename AgentScanPolicyT, ///< Parameterized AgentScanPolicyT tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type
|
||||
typename OutputIteratorT, ///< Random-access output iterator type
|
||||
typename ScanOpT, ///< Scan functor type
|
||||
typename InitValueT, ///< The init_value element for ScanOpT type (cub::NullType for inclusive scan)
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentScan
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ScanTileState<OutputT> ScanTileStateT;
|
||||
|
||||
// Input iterator wrapper type (for applying cache modifier)
|
||||
typedef typename If<IsPointer<InputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentScanPolicyT::LOAD_MODIFIER, InputT, OffsetT>, // Wrap the native input pointer with CacheModifiedInputIterator
|
||||
InputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedInputIteratorT;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
IS_INCLUSIVE = Equals<InitValueT, NullType>::VALUE, // Inclusive scan if no init_value type is provided
|
||||
BLOCK_THREADS = AgentScanPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentScanPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
// Parameterized BlockLoad type
|
||||
typedef BlockLoad<
|
||||
OutputT,
|
||||
AgentScanPolicyT::BLOCK_THREADS,
|
||||
AgentScanPolicyT::ITEMS_PER_THREAD,
|
||||
AgentScanPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadT;
|
||||
|
||||
// Parameterized BlockStore type
|
||||
typedef BlockStore<
|
||||
OutputT,
|
||||
AgentScanPolicyT::BLOCK_THREADS,
|
||||
AgentScanPolicyT::ITEMS_PER_THREAD,
|
||||
AgentScanPolicyT::STORE_ALGORITHM>
|
||||
BlockStoreT;
|
||||
|
||||
// Parameterized BlockScan type
|
||||
typedef BlockScan<
|
||||
OutputT,
|
||||
AgentScanPolicyT::BLOCK_THREADS,
|
||||
AgentScanPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
// Callback type for obtaining tile prefix during block scan
|
||||
typedef TilePrefixCallbackOp<
|
||||
OutputT,
|
||||
ScanOpT,
|
||||
ScanTileStateT>
|
||||
TilePrefixCallbackOpT;
|
||||
|
||||
// Stateful BlockScan prefix callback type for managing a running total while scanning consecutive tiles
|
||||
typedef BlockScanRunningPrefixOp<
|
||||
OutputT,
|
||||
ScanOpT>
|
||||
RunningPrefixCallbackOp;
|
||||
|
||||
// Shared memory type for this threadblock
|
||||
union _TempStorage
|
||||
{
|
||||
typename BlockLoadT::TempStorage load; // Smem needed for tile loading
|
||||
typename BlockStoreT::TempStorage store; // Smem needed for tile storing
|
||||
|
||||
struct
|
||||
{
|
||||
typename TilePrefixCallbackOpT::TempStorage prefix; // Smem needed for cooperative prefix callback
|
||||
typename BlockScanT::TempStorage scan; // Smem needed for tile scanning
|
||||
};
|
||||
};
|
||||
|
||||
// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
WrappedInputIteratorT d_in; ///< Input data
|
||||
OutputIteratorT d_out; ///< Output data
|
||||
ScanOpT scan_op; ///< Binary scan operator
|
||||
InitValueT init_value; ///< The init_value element for ScanOpT
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Block scan utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Exclusive scan specialization (first tile)
|
||||
*/
|
||||
__device__ __forceinline__
|
||||
void ScanTile(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OutputT init_value,
|
||||
ScanOpT scan_op,
|
||||
OutputT &block_aggregate,
|
||||
Int2Type<false> /*is_inclusive*/)
|
||||
{
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(items, items, init_value, scan_op, block_aggregate);
|
||||
block_aggregate = scan_op(init_value, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inclusive scan specialization (first tile)
|
||||
*/
|
||||
__device__ __forceinline__
|
||||
void ScanTile(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
InitValueT /*init_value*/,
|
||||
ScanOpT scan_op,
|
||||
OutputT &block_aggregate,
|
||||
Int2Type<true> /*is_inclusive*/)
|
||||
{
|
||||
BlockScanT(temp_storage.scan).InclusiveScan(items, items, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exclusive scan specialization (subsequent tiles)
|
||||
*/
|
||||
template <typename PrefixCallback>
|
||||
__device__ __forceinline__
|
||||
void ScanTile(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
ScanOpT scan_op,
|
||||
PrefixCallback &prefix_op,
|
||||
Int2Type<false> /*is_inclusive*/)
|
||||
{
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(items, items, scan_op, prefix_op);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inclusive scan specialization (subsequent tiles)
|
||||
*/
|
||||
template <typename PrefixCallback>
|
||||
__device__ __forceinline__
|
||||
void ScanTile(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
ScanOpT scan_op,
|
||||
PrefixCallback &prefix_op,
|
||||
Int2Type<true> /*is_inclusive*/)
|
||||
{
|
||||
BlockScanT(temp_storage.scan).InclusiveScan(items, items, scan_op, prefix_op);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
AgentScan(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
InputIteratorT d_in, ///< Input data
|
||||
OutputIteratorT d_out, ///< Output data
|
||||
ScanOpT scan_op, ///< Binary scan operator
|
||||
InitValueT init_value) ///< Initial value to seed the exclusive scan
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_in(d_in),
|
||||
d_out(d_out),
|
||||
scan_op(scan_op),
|
||||
init_value(init_value)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Cooperatively scan a device-wide sequence of tiles with other CTAs
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process a tile of input (dynamic chained scan)
|
||||
*/
|
||||
template <bool IS_LAST_TILE> ///< Whether the current tile is the last tile
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OffsetT num_remaining, ///< Number of global input items remaining (including this tile)
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
// Load items
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items, num_remaining);
|
||||
else
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Perform tile scan
|
||||
if (tile_idx == 0)
|
||||
{
|
||||
// Scan first tile
|
||||
OutputT block_aggregate;
|
||||
ScanTile(items, init_value, scan_op, block_aggregate, Int2Type<IS_INCLUSIVE>());
|
||||
if ((!IS_LAST_TILE) && (threadIdx.x == 0))
|
||||
tile_state.SetInclusive(0, block_aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scan non-first tile
|
||||
TilePrefixCallbackOpT prefix_op(tile_state, temp_storage.prefix, scan_op, tile_idx);
|
||||
ScanTile(items, scan_op, prefix_op, Int2Type<IS_INCLUSIVE>());
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Store items
|
||||
if (IS_LAST_TILE)
|
||||
BlockStoreT(temp_storage.store).Store(d_out + tile_offset, items, num_remaining);
|
||||
else
|
||||
BlockStoreT(temp_storage.store).Store(d_out + tile_offset, items);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan tiles of items as part of a dynamic chained scan
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
int num_items, ///< Total number of input items
|
||||
ScanTileStateT& tile_state, ///< Global tile state descriptor
|
||||
int start_tile) ///< The starting tile for the current grid
|
||||
{
|
||||
// Blocks are launched in increasing order, so just assign one tile per block
|
||||
int tile_idx = start_tile + blockIdx.x; // Current tile index
|
||||
OffsetT tile_offset = OffsetT(TILE_ITEMS) * tile_idx; // Global offset for the current tile
|
||||
OffsetT num_remaining = num_items - tile_offset; // Remaining items (including this tile)
|
||||
|
||||
if (num_remaining > TILE_ITEMS)
|
||||
{
|
||||
// Not last tile
|
||||
ConsumeTile<false>(num_remaining, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
else if (num_remaining > 0)
|
||||
{
|
||||
// Last tile
|
||||
ConsumeTile<true>(num_remaining, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Scan an sequence of consecutive tiles (independent of other thread blocks)
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Process a tile of input
|
||||
*/
|
||||
template <
|
||||
bool IS_FIRST_TILE,
|
||||
bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
RunningPrefixCallbackOp& prefix_op, ///< Running prefix operator
|
||||
int valid_items = TILE_ITEMS) ///< Number of valid items in the tile
|
||||
{
|
||||
// Load items
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items, valid_items);
|
||||
else
|
||||
BlockLoadT(temp_storage.load).Load(d_in + tile_offset, items);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Block scan
|
||||
if (IS_FIRST_TILE)
|
||||
{
|
||||
OutputT block_aggregate;
|
||||
ScanTile(items, init_value, scan_op, block_aggregate, Int2Type<IS_INCLUSIVE>());
|
||||
prefix_op.running_total = block_aggregate;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScanTile(items, scan_op, prefix_op, Int2Type<IS_INCLUSIVE>());
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Store items
|
||||
if (IS_LAST_TILE)
|
||||
BlockStoreT(temp_storage.store).Store(d_out + tile_offset, items, valid_items);
|
||||
else
|
||||
BlockStoreT(temp_storage.store).Store(d_out + tile_offset, items);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan a consecutive share of input tiles
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
OffsetT range_offset, ///< [in] Threadblock begin offset (inclusive)
|
||||
OffsetT range_end) ///< [in] Threadblock end offset (exclusive)
|
||||
{
|
||||
BlockScanRunningPrefixOp<OutputT, ScanOpT> prefix_op(scan_op);
|
||||
|
||||
if (range_offset + TILE_ITEMS <= range_end)
|
||||
{
|
||||
// Consume first tile of input (full)
|
||||
ConsumeTile<true, true>(range_offset, prefix_op);
|
||||
range_offset += TILE_ITEMS;
|
||||
|
||||
// Consume subsequent full tiles of input
|
||||
while (range_offset + TILE_ITEMS <= range_end)
|
||||
{
|
||||
ConsumeTile<false, true>(range_offset, prefix_op);
|
||||
range_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
// Consume a partially-full tile
|
||||
if (range_offset < range_end)
|
||||
{
|
||||
int valid_items = range_end - range_offset;
|
||||
ConsumeTile<false, false>(range_offset, prefix_op, valid_items);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume the first tile of input (partially-full)
|
||||
int valid_items = range_end - range_offset;
|
||||
ConsumeTile<true, false>(range_offset, prefix_op, valid_items);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan a consecutive share of input tiles, seeded with the specified prefix value
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
OffsetT range_offset, ///< [in] Threadblock begin offset (inclusive)
|
||||
OffsetT range_end, ///< [in] Threadblock end offset (exclusive)
|
||||
OutputT prefix) ///< [in] The prefix to apply to the scan segment
|
||||
{
|
||||
BlockScanRunningPrefixOp<OutputT, ScanOpT> prefix_op(prefix, scan_op);
|
||||
|
||||
// Consume full tiles of input
|
||||
while (range_offset + TILE_ITEMS <= range_end)
|
||||
{
|
||||
ConsumeTile<true, false>(range_offset, prefix_op);
|
||||
range_offset += TILE_ITEMS;
|
||||
}
|
||||
|
||||
// Consume a partially-full tile
|
||||
if (range_offset < range_end)
|
||||
{
|
||||
int valid_items = range_end - range_offset;
|
||||
ConsumeTile<false, false>(range_offset, prefix_op, valid_items);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentSegmentFixup implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduce-value-by-key.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "single_pass_scan_operators.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_discontinuity.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/constant_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentSegmentFixup
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentSegmentFixupPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief AgentSegmentFixup implements a stateful abstraction of CUDA thread blocks for participating in device-wide reduce-value-by-key
|
||||
*/
|
||||
template <
|
||||
typename AgentSegmentFixupPolicyT, ///< Parameterized AgentSegmentFixupPolicy tuning policy type
|
||||
typename PairsInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename EqualityOpT, ///< KeyT equality operator type
|
||||
typename ReductionOpT, ///< ValueT reduction operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct AgentSegmentFixup
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Data type of key-value input iterator
|
||||
typedef typename std::iterator_traits<PairsInputIteratorT>::value_type KeyValuePairT;
|
||||
|
||||
// Value type
|
||||
typedef typename KeyValuePairT::Value ValueT;
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<ValueT, OffsetT> ScanTileStateT;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentSegmentFixupPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentSegmentFixupPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
|
||||
// Whether or not do fixup using RLE + global atomics
|
||||
USE_ATOMIC_FIXUP = (CUB_PTX_ARCH >= 350) &&
|
||||
(Equals<ValueT, float>::VALUE ||
|
||||
Equals<ValueT, int>::VALUE ||
|
||||
Equals<ValueT, unsigned int>::VALUE ||
|
||||
Equals<ValueT, unsigned long long>::VALUE),
|
||||
|
||||
// Whether or not the scan operation has a zero-valued identity value (true if we're performing addition on a primitive type)
|
||||
HAS_IDENTITY_ZERO = (Equals<ReductionOpT, cub::Sum>::VALUE) && (Traits<ValueT>::PRIMITIVE),
|
||||
};
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for keys
|
||||
typedef typename If<IsPointer<PairsInputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentSegmentFixupPolicyT::LOAD_MODIFIER, KeyValuePairT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
PairsInputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedPairsInputIteratorT;
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for fixup values
|
||||
typedef typename If<IsPointer<AggregatesOutputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentSegmentFixupPolicyT::LOAD_MODIFIER, ValueT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
AggregatesOutputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedFixupInputIteratorT;
|
||||
|
||||
// Reduce-value-by-segment scan operator
|
||||
typedef ReduceByKeyOp<cub::Sum> ReduceBySegmentOpT;
|
||||
|
||||
// Parameterized BlockLoad type for pairs
|
||||
typedef BlockLoad<
|
||||
KeyValuePairT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
AgentSegmentFixupPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadPairs;
|
||||
|
||||
// Parameterized BlockScan type
|
||||
typedef BlockScan<
|
||||
KeyValuePairT,
|
||||
BLOCK_THREADS,
|
||||
AgentSegmentFixupPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
// Callback type for obtaining tile prefix during block scan
|
||||
typedef TilePrefixCallbackOp<
|
||||
KeyValuePairT,
|
||||
ReduceBySegmentOpT,
|
||||
ScanTileStateT>
|
||||
TilePrefixCallbackOpT;
|
||||
|
||||
// Shared memory type for this threadblock
|
||||
union _TempStorage
|
||||
{
|
||||
struct
|
||||
{
|
||||
typename BlockScanT::TempStorage scan; // Smem needed for tile scanning
|
||||
typename TilePrefixCallbackOpT::TempStorage prefix; // Smem needed for cooperative prefix callback
|
||||
};
|
||||
|
||||
// Smem needed for loading keys
|
||||
typename BlockLoadPairs::TempStorage load_pairs;
|
||||
};
|
||||
|
||||
// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
WrappedPairsInputIteratorT d_pairs_in; ///< Input keys
|
||||
AggregatesOutputIteratorT d_aggregates_out; ///< Output value aggregates
|
||||
WrappedFixupInputIteratorT d_fixup_in; ///< Fixup input values
|
||||
InequalityWrapper<EqualityOpT> inequality_op; ///< KeyT inequality operator
|
||||
ReductionOpT reduction_op; ///< Reduction operator
|
||||
ReduceBySegmentOpT scan_op; ///< Reduce-by-segment scan operator
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
AgentSegmentFixup(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
PairsInputIteratorT d_pairs_in, ///< Input keys
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< Output value aggregates
|
||||
EqualityOpT equality_op, ///< KeyT equality operator
|
||||
ReductionOpT reduction_op) ///< ValueT reduction operator
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_pairs_in(d_pairs_in),
|
||||
d_aggregates_out(d_aggregates_out),
|
||||
d_fixup_in(d_aggregates_out),
|
||||
inequality_op(equality_op),
|
||||
reduction_op(reduction_op),
|
||||
scan_op(reduction_op)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Cooperatively scan a device-wide sequence of tiles with other CTAs
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Process input tile. Specialized for atomic-fixup
|
||||
*/
|
||||
template <bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OffsetT num_remaining, ///< Number of global input items remaining (including this tile)
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state, ///< Global tile state descriptor
|
||||
Int2Type<true> use_atomic_fixup) ///< Marker whether to use atomicAdd (instead of reduce-by-key)
|
||||
{
|
||||
KeyValuePairT pairs[ITEMS_PER_THREAD];
|
||||
|
||||
// Load pairs
|
||||
KeyValuePairT oob_pair;
|
||||
oob_pair.key = -1;
|
||||
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadPairs(temp_storage.load_pairs).Load(d_pairs_in + tile_offset, pairs, num_remaining, oob_pair);
|
||||
else
|
||||
BlockLoadPairs(temp_storage.load_pairs).Load(d_pairs_in + tile_offset, pairs);
|
||||
|
||||
// RLE
|
||||
#pragma unroll
|
||||
for (int ITEM = 1; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
ValueT* d_scatter = d_aggregates_out + pairs[ITEM - 1].key;
|
||||
if (pairs[ITEM].key != pairs[ITEM - 1].key)
|
||||
atomicAdd(d_scatter, pairs[ITEM - 1].value);
|
||||
else
|
||||
pairs[ITEM].value = reduction_op(pairs[ITEM - 1].value, pairs[ITEM].value);
|
||||
}
|
||||
|
||||
// Flush last item if valid
|
||||
ValueT* d_scatter = d_aggregates_out + pairs[ITEMS_PER_THREAD - 1].key;
|
||||
if ((!IS_LAST_TILE) || (pairs[ITEMS_PER_THREAD - 1].key >= 0))
|
||||
atomicAdd(d_scatter, pairs[ITEMS_PER_THREAD - 1].value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process input tile. Specialized for reduce-by-key fixup
|
||||
*/
|
||||
template <bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
OffsetT num_remaining, ///< Number of global input items remaining (including this tile)
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state, ///< Global tile state descriptor
|
||||
Int2Type<false> use_atomic_fixup) ///< Marker whether to use atomicAdd (instead of reduce-by-key)
|
||||
{
|
||||
KeyValuePairT pairs[ITEMS_PER_THREAD];
|
||||
KeyValuePairT scatter_pairs[ITEMS_PER_THREAD];
|
||||
|
||||
// Load pairs
|
||||
KeyValuePairT oob_pair;
|
||||
oob_pair.key = -1;
|
||||
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadPairs(temp_storage.load_pairs).Load(d_pairs_in + tile_offset, pairs, num_remaining, oob_pair);
|
||||
else
|
||||
BlockLoadPairs(temp_storage.load_pairs).Load(d_pairs_in + tile_offset, pairs);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
KeyValuePairT tile_aggregate;
|
||||
if (tile_idx == 0)
|
||||
{
|
||||
// Exclusive scan of values and segment_flags
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(pairs, scatter_pairs, scan_op, tile_aggregate);
|
||||
|
||||
// Update tile status if this is not the last tile
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Set first segment id to not trigger a flush (invalid from exclusive scan)
|
||||
scatter_pairs[0].key = pairs[0].key;
|
||||
|
||||
if (!IS_LAST_TILE)
|
||||
tile_state.SetInclusive(0, tile_aggregate);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exclusive scan of values and segment_flags
|
||||
TilePrefixCallbackOpT prefix_op(tile_state, temp_storage.prefix, scan_op, tile_idx);
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(pairs, scatter_pairs, scan_op, prefix_op);
|
||||
tile_aggregate = prefix_op.GetBlockAggregate();
|
||||
}
|
||||
|
||||
// Scatter updated values
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (scatter_pairs[ITEM].key != pairs[ITEM].key)
|
||||
{
|
||||
// Update the value at the key location
|
||||
ValueT value = d_fixup_in[scatter_pairs[ITEM].key];
|
||||
value = reduction_op(value, scatter_pairs[ITEM].value);
|
||||
|
||||
d_aggregates_out[scatter_pairs[ITEM].key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the last item
|
||||
if (IS_LAST_TILE)
|
||||
{
|
||||
// Last thread will output final count and last item, if necessary
|
||||
if (threadIdx.x == BLOCK_THREADS - 1)
|
||||
{
|
||||
// If the last tile is a whole tile, the inclusive prefix contains accumulated value reduction for the last segment
|
||||
if (num_remaining == TILE_ITEMS)
|
||||
{
|
||||
// Update the value at the key location
|
||||
OffsetT last_key = pairs[ITEMS_PER_THREAD - 1].key;
|
||||
d_aggregates_out[last_key] = reduction_op(tile_aggregate.value, d_fixup_in[last_key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan tiles of items as part of a dynamic chained scan
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
int num_items, ///< Total number of input items
|
||||
int num_tiles, ///< Total number of input tiles
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
// Blocks are launched in increasing order, so just assign one tile per block
|
||||
int tile_idx = (blockIdx.x * gridDim.y) + blockIdx.y; // Current tile index
|
||||
OffsetT tile_offset = tile_idx * TILE_ITEMS; // Global offset for the current tile
|
||||
OffsetT num_remaining = num_items - tile_offset; // Remaining items (including this tile)
|
||||
|
||||
if (num_remaining > TILE_ITEMS)
|
||||
{
|
||||
// Not the last tile (full)
|
||||
ConsumeTile<false>(num_remaining, tile_idx, tile_offset, tile_state, Int2Type<USE_ATOMIC_FIXUP>());
|
||||
}
|
||||
else if (num_remaining > 0)
|
||||
{
|
||||
// The last tile (possibly partially-full)
|
||||
ConsumeTile<true>(num_remaining, tile_idx, tile_offset, tile_state, Int2Type<USE_ATOMIC_FIXUP>());
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,703 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentSelectIf implements a stateful abstraction of CUDA thread blocks for participating in device-wide select.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "single_pass_scan_operators.cuh"
|
||||
#include "../block/block_load.cuh"
|
||||
#include "../block/block_store.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../block/block_discontinuity.cuh"
|
||||
#include "../grid/grid_queue.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy types
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentSelectIf
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
BlockLoadAlgorithm _LOAD_ALGORITHM, ///< The BlockLoad algorithm to use
|
||||
CacheLoadModifier _LOAD_MODIFIER, ///< Cache load modifier for reading input elements
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentSelectIfPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
};
|
||||
|
||||
static const BlockLoadAlgorithm LOAD_ALGORITHM = _LOAD_ALGORITHM; ///< The BlockLoad algorithm to use
|
||||
static const CacheLoadModifier LOAD_MODIFIER = _LOAD_MODIFIER; ///< Cache load modifier for reading input elements
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* \brief AgentSelectIf implements a stateful abstraction of CUDA thread blocks for participating in device-wide selection
|
||||
*
|
||||
* Performs functor-based selection if SelectOpT functor type != NullType
|
||||
* Otherwise performs flag-based selection if FlagsInputIterator's value type != NullType
|
||||
* Otherwise performs discontinuity selection (keep unique)
|
||||
*/
|
||||
template <
|
||||
typename AgentSelectIfPolicyT, ///< Parameterized AgentSelectIfPolicy tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type for selection items
|
||||
typename FlagsInputIteratorT, ///< Random-access input iterator type for selections (NullType* if a selection functor or discontinuity flagging is to be used for selection)
|
||||
typename SelectedOutputIteratorT, ///< Random-access input iterator type for selection_flags items
|
||||
typename SelectOpT, ///< Selection operator type (NullType if selections or discontinuity flagging is to be used for selection)
|
||||
typename EqualityOpT, ///< Equality operator type (NullType if selection functor or selections is to be used for selection)
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
bool KEEP_REJECTS> ///< Whether or not we push rejected items to the back of the output
|
||||
struct AgentSelectIf
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<SelectedOutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<SelectedOutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// The flag value type
|
||||
typedef typename std::iterator_traits<FlagsInputIteratorT>::value_type FlagT;
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ScanTileState<OffsetT> ScanTileStateT;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
USE_SELECT_OP,
|
||||
USE_SELECT_FLAGS,
|
||||
USE_DISCONTINUITY,
|
||||
|
||||
BLOCK_THREADS = AgentSelectIfPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentSelectIfPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
TWO_PHASE_SCATTER = (ITEMS_PER_THREAD > 1),
|
||||
|
||||
SELECT_METHOD = (!Equals<SelectOpT, NullType>::VALUE) ?
|
||||
USE_SELECT_OP :
|
||||
(!Equals<FlagT, NullType>::VALUE) ?
|
||||
USE_SELECT_FLAGS :
|
||||
USE_DISCONTINUITY
|
||||
};
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for items
|
||||
typedef typename If<IsPointer<InputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentSelectIfPolicyT::LOAD_MODIFIER, InputT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
InputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedInputIteratorT;
|
||||
|
||||
// Cache-modified Input iterator wrapper type (for applying cache modifier) for values
|
||||
typedef typename If<IsPointer<FlagsInputIteratorT>::VALUE,
|
||||
CacheModifiedInputIterator<AgentSelectIfPolicyT::LOAD_MODIFIER, FlagT, OffsetT>, // Wrap the native input pointer with CacheModifiedValuesInputIterator
|
||||
FlagsInputIteratorT>::Type // Directly use the supplied input iterator type
|
||||
WrappedFlagsInputIteratorT;
|
||||
|
||||
// Parameterized BlockLoad type for input data
|
||||
typedef BlockLoad<
|
||||
OutputT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
AgentSelectIfPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadT;
|
||||
|
||||
// Parameterized BlockLoad type for flags
|
||||
typedef BlockLoad<
|
||||
FlagT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD,
|
||||
AgentSelectIfPolicyT::LOAD_ALGORITHM>
|
||||
BlockLoadFlags;
|
||||
|
||||
// Parameterized BlockDiscontinuity type for items
|
||||
typedef BlockDiscontinuity<
|
||||
OutputT,
|
||||
BLOCK_THREADS>
|
||||
BlockDiscontinuityT;
|
||||
|
||||
// Parameterized BlockScan type
|
||||
typedef BlockScan<
|
||||
OffsetT,
|
||||
BLOCK_THREADS,
|
||||
AgentSelectIfPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
// Callback type for obtaining tile prefix during block scan
|
||||
typedef TilePrefixCallbackOp<
|
||||
OffsetT,
|
||||
cub::Sum,
|
||||
ScanTileStateT>
|
||||
TilePrefixCallbackOpT;
|
||||
|
||||
// Item exchange type
|
||||
typedef OutputT ItemExchangeT[TILE_ITEMS];
|
||||
|
||||
// Shared memory type for this threadblock
|
||||
union _TempStorage
|
||||
{
|
||||
struct
|
||||
{
|
||||
typename BlockScanT::TempStorage scan; // Smem needed for tile scanning
|
||||
typename TilePrefixCallbackOpT::TempStorage prefix; // Smem needed for cooperative prefix callback
|
||||
typename BlockDiscontinuityT::TempStorage discontinuity; // Smem needed for discontinuity detection
|
||||
};
|
||||
|
||||
// Smem needed for loading items
|
||||
typename BlockLoadT::TempStorage load_items;
|
||||
|
||||
// Smem needed for loading values
|
||||
typename BlockLoadFlags::TempStorage load_flags;
|
||||
|
||||
// Smem needed for compacting items (allows non POD items in this union)
|
||||
Uninitialized<ItemExchangeT> raw_exchange;
|
||||
};
|
||||
|
||||
// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
_TempStorage& temp_storage; ///< Reference to temp_storage
|
||||
WrappedInputIteratorT d_in; ///< Input items
|
||||
SelectedOutputIteratorT d_selected_out; ///< Unique output items
|
||||
WrappedFlagsInputIteratorT d_flags_in; ///< Input selection flags (if applicable)
|
||||
InequalityWrapper<EqualityOpT> inequality_op; ///< T inequality operator
|
||||
SelectOpT select_op; ///< Selection operator
|
||||
OffsetT num_items; ///< Total number of input items
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructor
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
AgentSelectIf(
|
||||
TempStorage &temp_storage, ///< Reference to temp_storage
|
||||
InputIteratorT d_in, ///< Input data
|
||||
FlagsInputIteratorT d_flags_in, ///< Input selection flags (if applicable)
|
||||
SelectedOutputIteratorT d_selected_out, ///< Output data
|
||||
SelectOpT select_op, ///< Selection operator
|
||||
EqualityOpT equality_op, ///< Equality operator
|
||||
OffsetT num_items) ///< Total number of input items
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
d_in(d_in),
|
||||
d_flags_in(d_flags_in),
|
||||
d_selected_out(d_selected_out),
|
||||
select_op(select_op),
|
||||
inequality_op(equality_op),
|
||||
num_items(num_items)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods for initializing the selections
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize selections (specialized for selection operator)
|
||||
*/
|
||||
template <bool IS_FIRST_TILE, bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void InitializeSelections(
|
||||
OffsetT /*tile_offset*/,
|
||||
OffsetT num_tile_items,
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
Int2Type<USE_SELECT_OP> /*select_method*/)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
// Out-of-bounds items are selection_flags
|
||||
selection_flags[ITEM] = 1;
|
||||
|
||||
if (!IS_LAST_TILE || (OffsetT(threadIdx.x * ITEMS_PER_THREAD) + ITEM < num_tile_items))
|
||||
selection_flags[ITEM] = select_op(items[ITEM]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize selections (specialized for valid flags)
|
||||
*/
|
||||
template <bool IS_FIRST_TILE, bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void InitializeSelections(
|
||||
OffsetT tile_offset,
|
||||
OffsetT num_tile_items,
|
||||
OutputT (&/*items*/)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
Int2Type<USE_SELECT_FLAGS> /*select_method*/)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
FlagT flags[ITEMS_PER_THREAD];
|
||||
|
||||
if (IS_LAST_TILE)
|
||||
{
|
||||
// Out-of-bounds items are selection_flags
|
||||
BlockLoadFlags(temp_storage.load_flags).Load(d_flags_in + tile_offset, flags, num_tile_items, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockLoadFlags(temp_storage.load_flags).Load(d_flags_in + tile_offset, flags);
|
||||
}
|
||||
|
||||
// Convert flag type to selection_flags type
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
selection_flags[ITEM] = flags[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize selections (specialized for discontinuity detection)
|
||||
*/
|
||||
template <bool IS_FIRST_TILE, bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ void InitializeSelections(
|
||||
OffsetT tile_offset,
|
||||
OffsetT num_tile_items,
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
Int2Type<USE_DISCONTINUITY> /*select_method*/)
|
||||
{
|
||||
if (IS_FIRST_TILE)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Set head selection_flags. First tile sets the first flag for the first item
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeads(selection_flags, items, inequality_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputT tile_predecessor;
|
||||
if (threadIdx.x == 0)
|
||||
tile_predecessor = d_in[tile_offset - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
BlockDiscontinuityT(temp_storage.discontinuity).FlagHeads(selection_flags, items, inequality_op, tile_predecessor);
|
||||
}
|
||||
|
||||
// Set selection flags for out-of-bounds items
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
// Set selection_flags for out-of-bounds items
|
||||
if ((IS_LAST_TILE) && (OffsetT(threadIdx.x * ITEMS_PER_THREAD) + ITEM >= num_tile_items))
|
||||
selection_flags[ITEM] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Scatter utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scatter flagged items to output offsets (specialized for direct scattering)
|
||||
*/
|
||||
template <bool IS_LAST_TILE, bool IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterDirect(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_indices)[ITEMS_PER_THREAD],
|
||||
OffsetT num_selections)
|
||||
{
|
||||
// Scatter flagged items
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (selection_flags[ITEM])
|
||||
{
|
||||
if ((!IS_LAST_TILE) || selection_indices[ITEM] < num_selections)
|
||||
{
|
||||
d_selected_out[selection_indices[ITEM]] = items[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter flagged items to output offsets (specialized for two-phase scattering)
|
||||
*/
|
||||
template <bool IS_LAST_TILE, bool IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterTwoPhase(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_indices)[ITEMS_PER_THREAD],
|
||||
int /*num_tile_items*/, ///< Number of valid items in this tile
|
||||
int num_tile_selections, ///< Number of selections in this tile
|
||||
OffsetT num_selections_prefix, ///< Total number of selections prior to this tile
|
||||
OffsetT /*num_rejected_prefix*/, ///< Total number of rejections prior to this tile
|
||||
Int2Type<false> /*is_keep_rejects*/) ///< Marker type indicating whether to keep rejected items in the second partition
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Compact and scatter items
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int local_scatter_offset = selection_indices[ITEM] - num_selections_prefix;
|
||||
if (selection_flags[ITEM])
|
||||
{
|
||||
temp_storage.raw_exchange.Alias()[local_scatter_offset] = items[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
for (int item = threadIdx.x; item < num_tile_selections; item += BLOCK_THREADS)
|
||||
{
|
||||
d_selected_out[num_selections_prefix + item] = temp_storage.raw_exchange.Alias()[item];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter flagged items to output offsets (specialized for two-phase scattering)
|
||||
*/
|
||||
template <bool IS_LAST_TILE, bool IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void ScatterTwoPhase(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_indices)[ITEMS_PER_THREAD],
|
||||
int num_tile_items, ///< Number of valid items in this tile
|
||||
int num_tile_selections, ///< Number of selections in this tile
|
||||
OffsetT num_selections_prefix, ///< Total number of selections prior to this tile
|
||||
OffsetT num_rejected_prefix, ///< Total number of rejections prior to this tile
|
||||
Int2Type<true> /*is_keep_rejects*/) ///< Marker type indicating whether to keep rejected items in the second partition
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
int tile_num_rejections = num_tile_items - num_tile_selections;
|
||||
|
||||
// Scatter items to shared memory (rejections first)
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int item_idx = (threadIdx.x * ITEMS_PER_THREAD) + ITEM;
|
||||
int local_selection_idx = selection_indices[ITEM] - num_selections_prefix;
|
||||
int local_rejection_idx = item_idx - local_selection_idx;
|
||||
int local_scatter_offset = (selection_flags[ITEM]) ?
|
||||
tile_num_rejections + local_selection_idx :
|
||||
local_rejection_idx;
|
||||
|
||||
temp_storage.raw_exchange.Alias()[local_scatter_offset] = items[ITEM];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Gather items from shared memory and scatter to global
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int item_idx = (ITEM * BLOCK_THREADS) + threadIdx.x;
|
||||
int rejection_idx = item_idx;
|
||||
int selection_idx = item_idx - tile_num_rejections;
|
||||
OffsetT scatter_offset = (item_idx < tile_num_rejections) ?
|
||||
num_items - num_rejected_prefix - rejection_idx - 1 :
|
||||
num_selections_prefix + selection_idx;
|
||||
|
||||
OutputT item = temp_storage.raw_exchange.Alias()[item_idx];
|
||||
|
||||
if (!IS_LAST_TILE || (item_idx < num_tile_items))
|
||||
{
|
||||
d_selected_out[scatter_offset] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scatter flagged items
|
||||
*/
|
||||
template <bool IS_LAST_TILE, bool IS_FIRST_TILE>
|
||||
__device__ __forceinline__ void Scatter(
|
||||
OutputT (&items)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_flags)[ITEMS_PER_THREAD],
|
||||
OffsetT (&selection_indices)[ITEMS_PER_THREAD],
|
||||
int num_tile_items, ///< Number of valid items in this tile
|
||||
int num_tile_selections, ///< Number of selections in this tile
|
||||
OffsetT num_selections_prefix, ///< Total number of selections prior to this tile
|
||||
OffsetT num_rejected_prefix, ///< Total number of rejections prior to this tile
|
||||
OffsetT num_selections) ///< Total number of selections including this tile
|
||||
{
|
||||
// Do a two-phase scatter if (a) keeping both partitions or (b) two-phase is enabled and the average number of selection_flags items per thread is greater than one
|
||||
if (KEEP_REJECTS || (TWO_PHASE_SCATTER && (num_tile_selections > BLOCK_THREADS)))
|
||||
{
|
||||
ScatterTwoPhase<IS_LAST_TILE, IS_FIRST_TILE>(
|
||||
items,
|
||||
selection_flags,
|
||||
selection_indices,
|
||||
num_tile_items,
|
||||
num_tile_selections,
|
||||
num_selections_prefix,
|
||||
num_rejected_prefix,
|
||||
Int2Type<KEEP_REJECTS>());
|
||||
}
|
||||
else
|
||||
{
|
||||
ScatterDirect<IS_LAST_TILE, IS_FIRST_TILE>(
|
||||
items,
|
||||
selection_flags,
|
||||
selection_indices,
|
||||
num_selections);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Cooperatively scan a device-wide sequence of tiles with other CTAs
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Process first tile of input (dynamic chained scan). Returns the running count of selections (including this tile)
|
||||
*/
|
||||
template <bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ OffsetT ConsumeFirstTile(
|
||||
int num_tile_items, ///< Number of input items comprising this tile
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
OffsetT selection_flags[ITEMS_PER_THREAD];
|
||||
OffsetT selection_indices[ITEMS_PER_THREAD];
|
||||
|
||||
// Load items
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadT(temp_storage.load_items).Load(d_in + tile_offset, items, num_tile_items);
|
||||
else
|
||||
BlockLoadT(temp_storage.load_items).Load(d_in + tile_offset, items);
|
||||
|
||||
// Initialize selection_flags
|
||||
InitializeSelections<true, IS_LAST_TILE>(
|
||||
tile_offset,
|
||||
num_tile_items,
|
||||
items,
|
||||
selection_flags,
|
||||
Int2Type<SELECT_METHOD>());
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Exclusive scan of selection_flags
|
||||
OffsetT num_tile_selections;
|
||||
BlockScanT(temp_storage.scan).ExclusiveSum(selection_flags, selection_indices, num_tile_selections);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Update tile status if this is not the last tile
|
||||
if (!IS_LAST_TILE)
|
||||
tile_state.SetInclusive(0, num_tile_selections);
|
||||
}
|
||||
|
||||
// Discount any out-of-bounds selections
|
||||
if (IS_LAST_TILE)
|
||||
num_tile_selections -= (TILE_ITEMS - num_tile_items);
|
||||
|
||||
// Scatter flagged items
|
||||
Scatter<IS_LAST_TILE, true>(
|
||||
items,
|
||||
selection_flags,
|
||||
selection_indices,
|
||||
num_tile_items,
|
||||
num_tile_selections,
|
||||
0,
|
||||
0,
|
||||
num_tile_selections);
|
||||
|
||||
return num_tile_selections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process subsequent tile of input (dynamic chained scan). Returns the running count of selections (including this tile)
|
||||
*/
|
||||
template <bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ OffsetT ConsumeSubsequentTile(
|
||||
int num_tile_items, ///< Number of input items comprising this tile
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
OutputT items[ITEMS_PER_THREAD];
|
||||
OffsetT selection_flags[ITEMS_PER_THREAD];
|
||||
OffsetT selection_indices[ITEMS_PER_THREAD];
|
||||
|
||||
// Load items
|
||||
if (IS_LAST_TILE)
|
||||
BlockLoadT(temp_storage.load_items).Load(d_in + tile_offset, items, num_tile_items);
|
||||
else
|
||||
BlockLoadT(temp_storage.load_items).Load(d_in + tile_offset, items);
|
||||
|
||||
// Initialize selection_flags
|
||||
InitializeSelections<false, IS_LAST_TILE>(
|
||||
tile_offset,
|
||||
num_tile_items,
|
||||
items,
|
||||
selection_flags,
|
||||
Int2Type<SELECT_METHOD>());
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Exclusive scan of values and selection_flags
|
||||
TilePrefixCallbackOpT prefix_op(tile_state, temp_storage.prefix, cub::Sum(), tile_idx);
|
||||
BlockScanT(temp_storage.scan).ExclusiveSum(selection_flags, selection_indices, prefix_op);
|
||||
|
||||
OffsetT num_tile_selections = prefix_op.GetBlockAggregate();
|
||||
OffsetT num_selections = prefix_op.GetInclusivePrefix();
|
||||
OffsetT num_selections_prefix = prefix_op.GetExclusivePrefix();
|
||||
OffsetT num_rejected_prefix = (tile_idx * TILE_ITEMS) - num_selections_prefix;
|
||||
|
||||
// Discount any out-of-bounds selections
|
||||
if (IS_LAST_TILE)
|
||||
{
|
||||
int num_discount = TILE_ITEMS - num_tile_items;
|
||||
num_selections -= num_discount;
|
||||
num_tile_selections -= num_discount;
|
||||
}
|
||||
|
||||
// Scatter flagged items
|
||||
Scatter<IS_LAST_TILE, false>(
|
||||
items,
|
||||
selection_flags,
|
||||
selection_indices,
|
||||
num_tile_items,
|
||||
num_tile_selections,
|
||||
num_selections_prefix,
|
||||
num_rejected_prefix,
|
||||
num_selections);
|
||||
|
||||
return num_selections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a tile of input
|
||||
*/
|
||||
template <bool IS_LAST_TILE>
|
||||
__device__ __forceinline__ OffsetT ConsumeTile(
|
||||
int num_tile_items, ///< Number of input items comprising this tile
|
||||
int tile_idx, ///< Tile index
|
||||
OffsetT tile_offset, ///< Tile offset
|
||||
ScanTileStateT& tile_state) ///< Global tile state descriptor
|
||||
{
|
||||
OffsetT num_selections;
|
||||
if (tile_idx == 0)
|
||||
{
|
||||
num_selections = ConsumeFirstTile<IS_LAST_TILE>(num_tile_items, tile_offset, tile_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
num_selections = ConsumeSubsequentTile<IS_LAST_TILE>(num_tile_items, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
|
||||
return num_selections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan tiles of items as part of a dynamic chained scan
|
||||
*/
|
||||
template <typename NumSelectedIteratorT> ///< Output iterator type for recording number of items selection_flags
|
||||
__device__ __forceinline__ void ConsumeRange(
|
||||
int num_tiles, ///< Total number of input tiles
|
||||
ScanTileStateT& tile_state, ///< Global tile state descriptor
|
||||
NumSelectedIteratorT d_num_selected_out) ///< Output total number selection_flags
|
||||
{
|
||||
// Blocks are launched in increasing order, so just assign one tile per block
|
||||
int tile_idx = (blockIdx.x * gridDim.y) + blockIdx.y; // Current tile index
|
||||
OffsetT tile_offset = tile_idx * TILE_ITEMS; // Global offset for the current tile
|
||||
|
||||
if (tile_idx < num_tiles - 1)
|
||||
{
|
||||
// Not the last tile (full)
|
||||
ConsumeTile<false>(TILE_ITEMS, tile_idx, tile_offset, tile_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The last tile (possibly partially-full)
|
||||
OffsetT num_remaining = num_items - tile_offset;
|
||||
OffsetT num_selections = ConsumeTile<true>(num_remaining, tile_idx, tile_offset, tile_state);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Output the total number of items selection_flags
|
||||
*d_num_selected_out = num_selections;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,638 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../util_type.cuh"
|
||||
#include "../block/block_reduce.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../thread/thread_search.cuh"
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/counting_input_iterator.cuh"
|
||||
#include "../iterator/tex_ref_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentSpmv
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
CacheLoadModifier _ROW_OFFSETS_SEARCH_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets during search
|
||||
CacheLoadModifier _ROW_OFFSETS_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets
|
||||
CacheLoadModifier _COLUMN_INDICES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR column-indices
|
||||
CacheLoadModifier _VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR values
|
||||
CacheLoadModifier _VECTOR_VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading vector values
|
||||
bool _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (vs. pre-staged through shared memory)
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentSpmvPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
DIRECT_LOAD_NONZEROS = _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (pre-staged through shared memory)
|
||||
};
|
||||
|
||||
static const CacheLoadModifier ROW_OFFSETS_SEARCH_LOAD_MODIFIER = _ROW_OFFSETS_SEARCH_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier ROW_OFFSETS_LOAD_MODIFIER = _ROW_OFFSETS_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier COLUMN_INDICES_LOAD_MODIFIER = _COLUMN_INDICES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR column-indices
|
||||
static const CacheLoadModifier VALUES_LOAD_MODIFIER = _VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR values
|
||||
static const CacheLoadModifier VECTOR_VALUES_LOAD_MODIFIER = _VECTOR_VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading vector values
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for sequence offsets
|
||||
struct SpmvParams
|
||||
{
|
||||
ValueT* d_values; ///< Pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
OffsetT* d_row_end_offsets; ///< Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
OffsetT* d_column_indices; ///< Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
ValueT* d_vector_x; ///< Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
ValueT* d_vector_y; ///< Pointer to the array of \p num_rows values corresponding to the dense output vector <em>y</em>
|
||||
int num_rows; ///< Number of rows of matrix <b>A</b>.
|
||||
int num_cols; ///< Number of columns of matrix <b>A</b>.
|
||||
int num_nonzeros; ///< Number of nonzero elements of matrix <b>A</b>.
|
||||
ValueT alpha; ///< Alpha multiplicand
|
||||
ValueT beta; ///< Beta addend-multiplicand
|
||||
|
||||
TexRefInputIterator<ValueT, 66778899, OffsetT> t_vector_x;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
template <
|
||||
typename AgentSpmvPolicyT, ///< Parameterized AgentSpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
bool HAS_ALPHA, ///< Whether the input parameter \p alpha is 1
|
||||
bool HAS_BETA, ///< Whether the input parameter \p beta is 0
|
||||
int PTX_ARCH = CUB_PTX_ARCH> ///< PTX compute capability
|
||||
struct AgentSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentSpmvPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentSpmvPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
/// 2D merge path coordinate type
|
||||
typedef typename CubVector<OffsetT, 2>::Type CoordinateT;
|
||||
|
||||
/// Input iterator wrapper types (for applying cache modifiers)
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_SEARCH_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsSearchIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::COLUMN_INDICES_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
ColumnIndicesIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
ValueIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VECTOR_VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
VectorValueIteratorT;
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
// Reduce-value-by-key scan operator
|
||||
typedef ReduceByKeyOp<cub::Sum> ReduceBySegmentOpT;
|
||||
|
||||
// BlockReduce specialization
|
||||
typedef BlockReduce<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS>
|
||||
BlockReduceT;
|
||||
|
||||
// BlockScan specialization
|
||||
typedef BlockScan<
|
||||
KeyValuePairT,
|
||||
BLOCK_THREADS,
|
||||
AgentSpmvPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
/// Merge item type (either a non-zero value or a row-end offset)
|
||||
union MergeItem
|
||||
{
|
||||
// Value type to pair with index type OffsetT (NullType if loading values directly during merge)
|
||||
typedef typename If<AgentSpmvPolicyT::DIRECT_LOAD_NONZEROS, NullType, ValueT>::Type MergeValueT;
|
||||
|
||||
OffsetT row_end_offset;
|
||||
MergeValueT nonzero;
|
||||
};
|
||||
|
||||
/// Shared memory type required by this thread block
|
||||
struct _TempStorage
|
||||
{
|
||||
union {
|
||||
CoordinateT tile_coord;
|
||||
OffsetT turnstile;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
// Smem needed for tile of merge items
|
||||
MergeItem merge_items[ITEMS_PER_THREAD + TILE_ITEMS + 1];
|
||||
|
||||
// Smem needed for block-wide reduction
|
||||
typename BlockReduceT::TempStorage reduce;
|
||||
|
||||
// Smem needed for tile scanning
|
||||
typename BlockScanT::TempStorage scan;
|
||||
};
|
||||
};
|
||||
|
||||
/// Temporary storage type (unionable)
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
_TempStorage& temp_storage; /// Reference to temp_storage
|
||||
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params;
|
||||
|
||||
ValueIteratorT wd_values; ///< Wrapped pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
RowOffsetsIteratorT wd_row_end_offsets; ///< Wrapped Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
ColumnIndicesIteratorT wd_column_indices; ///< Wrapped Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
VectorValueIteratorT wd_vector_x; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
VectorValueIteratorT wd_vector_y; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentSpmv(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params) ///< SpMV input parameter bundle
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
spmv_params(spmv_params),
|
||||
wd_values(spmv_params.d_values),
|
||||
wd_row_end_offsets(spmv_params.d_row_end_offsets),
|
||||
wd_column_indices(spmv_params.d_column_indices),
|
||||
wd_vector_x(spmv_params.d_vector_x),
|
||||
wd_vector_y(spmv_params.d_vector_y)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for direct-load of nonzeros
|
||||
* /
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
Int2Type<true> is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
for (int item = threadIdx.x; item <= tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
s_tile_row_end_offsets[item] = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Search for the thread's starting coordinate within the merge tile
|
||||
CountingInputIterator<OffsetT> tile_nonzero_indices(tile_start_coord.y);
|
||||
CoordinateT thread_start_coord;
|
||||
|
||||
MergePathSearch(
|
||||
OffsetT(threadIdx.x * ITEMS_PER_THREAD), // Diagonal
|
||||
s_tile_row_end_offsets, // List A
|
||||
tile_nonzero_indices, // List B
|
||||
tile_num_rows,
|
||||
tile_num_nonzeros,
|
||||
thread_start_coord);
|
||||
|
||||
__syncthreads(); // Perf-sync
|
||||
|
||||
// Compute the thread's merge path segment
|
||||
CoordinateT thread_current_coord = thread_start_coord;
|
||||
KeyValuePairT scan_segment[ITEMS_PER_THREAD];
|
||||
|
||||
ValueT running_total = 0.0;
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
OffsetT nonzero_idx = CUB_MIN(tile_nonzero_indices[thread_current_coord.y], spmv_params.num_nonzeros - 1);
|
||||
OffsetT column_idx = wd_column_indices[nonzero_idx];
|
||||
ValueT value = wd_values[nonzero_idx];
|
||||
ValueT vector_value = wd_vector_x[column_idx];
|
||||
ValueT nonzero = value * vector_value;
|
||||
|
||||
OffsetT row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
|
||||
if (tile_nonzero_indices[thread_current_coord.y] < row_end_offset)
|
||||
{
|
||||
// Move down (accumulate)
|
||||
running_total += nonzero;
|
||||
scan_segment[ITEM].value = running_total;
|
||||
scan_segment[ITEM].key = tile_num_rows;
|
||||
++thread_current_coord.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move right (reset)
|
||||
scan_segment[ITEM].value = running_total;
|
||||
scan_segment[ITEM].key = thread_current_coord.x;
|
||||
running_total = 0.0;
|
||||
++thread_current_coord.x;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Block-wide reduce-value-by-segment
|
||||
KeyValuePairT tile_carry;
|
||||
ReduceBySegmentOpT scan_op;
|
||||
KeyValuePairT scan_item;
|
||||
|
||||
scan_item.value = running_total;
|
||||
scan_item.key = thread_current_coord.x;
|
||||
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_item, scan_item, scan_op, tile_carry);
|
||||
|
||||
if (tile_num_rows > 0)
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
scan_item.key = -1;
|
||||
|
||||
// Direct scatter
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (scan_segment[ITEM].key < tile_num_rows)
|
||||
{
|
||||
if (scan_item.key == scan_segment[ITEM].key)
|
||||
scan_segment[ITEM].value = scan_item.value + scan_segment[ITEM].value;
|
||||
|
||||
if (HAS_ALPHA)
|
||||
{
|
||||
scan_segment[ITEM].value *= spmv_params.alpha;
|
||||
}
|
||||
|
||||
if (HAS_BETA)
|
||||
{
|
||||
// Update the output vector element
|
||||
ValueT addend = spmv_params.beta * wd_vector_y[tile_start_coord.x + scan_segment[ITEM].key];
|
||||
scan_segment[ITEM].value += addend;
|
||||
}
|
||||
|
||||
// Set the output vector element
|
||||
spmv_params.d_vector_y[tile_start_coord.x + scan_segment[ITEM].key] = scan_segment[ITEM].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
return tile_carry;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for indirect load of nonzeros
|
||||
* /
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
Int2Type<false> is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
|
||||
#if (CUB_PTX_ARCH >= 520)
|
||||
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[tile_num_rows + ITEMS_PER_THREAD].nonzero;
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int nonzero_idx = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
|
||||
ValueIteratorT a = wd_values + tile_start_coord.y + nonzero_idx;
|
||||
ColumnIndicesIteratorT ci = wd_column_indices + tile_start_coord.y + nonzero_idx;
|
||||
ValueT* s = s_tile_nonzeros + nonzero_idx;
|
||||
|
||||
if (nonzero_idx < tile_num_nonzeros)
|
||||
{
|
||||
|
||||
OffsetT column_idx = *ci;
|
||||
ValueT value = *a;
|
||||
ValueT vector_value = spmv_params.t_vector_x[column_idx];
|
||||
vector_value = wd_vector_x[column_idx];
|
||||
ValueT nonzero = value * vector_value;
|
||||
*s = nonzero;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[tile_num_rows + ITEMS_PER_THREAD].nonzero;
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
if (tile_num_nonzeros > 0)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int nonzero_idx = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
nonzero_idx = CUB_MIN(nonzero_idx, tile_num_nonzeros - 1);
|
||||
|
||||
OffsetT column_idx = wd_column_indices[tile_start_coord.y + nonzero_idx];
|
||||
ValueT value = wd_values[tile_start_coord.y + nonzero_idx];
|
||||
|
||||
ValueT vector_value = wd_vector_x[column_idx];
|
||||
ValueT nonzero = value * vector_value;
|
||||
|
||||
s_tile_nonzeros[nonzero_idx] = nonzero;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item <= tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
s_tile_row_end_offsets[item] = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Search for the thread's starting coordinate within the merge tile
|
||||
CountingInputIterator<OffsetT> tile_nonzero_indices(tile_start_coord.y);
|
||||
CoordinateT thread_start_coord;
|
||||
|
||||
MergePathSearch(
|
||||
OffsetT(threadIdx.x * ITEMS_PER_THREAD), // Diagonal
|
||||
s_tile_row_end_offsets, // List A
|
||||
tile_nonzero_indices, // List B
|
||||
tile_num_rows,
|
||||
tile_num_nonzeros,
|
||||
thread_start_coord);
|
||||
|
||||
__syncthreads(); // Perf-sync
|
||||
|
||||
// Compute the thread's merge path segment
|
||||
CoordinateT thread_current_coord = thread_start_coord;
|
||||
KeyValuePairT scan_segment[ITEMS_PER_THREAD];
|
||||
ValueT running_total = 0.0;
|
||||
|
||||
OffsetT row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
ValueT nonzero = s_tile_nonzeros[thread_current_coord.y];
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (tile_nonzero_indices[thread_current_coord.y] < row_end_offset)
|
||||
{
|
||||
// Move down (accumulate)
|
||||
scan_segment[ITEM].value = nonzero;
|
||||
running_total += nonzero;
|
||||
++thread_current_coord.y;
|
||||
nonzero = s_tile_nonzeros[thread_current_coord.y];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move right (reset)
|
||||
scan_segment[ITEM].value = 0.0;
|
||||
running_total = 0.0;
|
||||
++thread_current_coord.x;
|
||||
row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
}
|
||||
|
||||
scan_segment[ITEM].key = thread_current_coord.x;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Block-wide reduce-value-by-segment
|
||||
KeyValuePairT tile_carry;
|
||||
ReduceBySegmentOpT scan_op;
|
||||
KeyValuePairT scan_item;
|
||||
|
||||
scan_item.value = running_total;
|
||||
scan_item.key = thread_current_coord.x;
|
||||
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_item, scan_item, scan_op, tile_carry);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
scan_item.key = thread_start_coord.x;
|
||||
scan_item.value = 0.0;
|
||||
}
|
||||
|
||||
if (tile_num_rows > 0)
|
||||
{
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Scan downsweep and scatter
|
||||
ValueT* s_partials = &temp_storage.merge_items[0].nonzero;
|
||||
|
||||
if (scan_item.key != scan_segment[0].key)
|
||||
{
|
||||
s_partials[scan_item.key] = scan_item.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_segment[0].value += scan_item.value;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 1; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (scan_segment[ITEM - 1].key != scan_segment[ITEM].key)
|
||||
{
|
||||
s_partials[scan_segment[ITEM - 1].key] = scan_segment[ITEM - 1].value;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_segment[ITEM].value += scan_segment[ITEM - 1].value;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item < tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
spmv_params.d_vector_y[tile_start_coord.x + item] = s_partials[item];
|
||||
}
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
return tile_carry;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Consume input tile
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
int merge_items_per_block, ///< [in] Number of merge tiles per block
|
||||
KeyValuePairT* d_tile_carry_pairs) ///< [out] Pointer to the temporary array carry-out dot product row-ids, one per block
|
||||
{
|
||||
// Read our starting coordinates
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Search our starting coordinates
|
||||
OffsetT diagonal = blockIdx.x * merge_items_per_block;
|
||||
CoordinateT tile_coord;
|
||||
CountingInputIterator<OffsetT> nonzero_indices(0);
|
||||
|
||||
// Search the merge path
|
||||
MergePathSearch(
|
||||
diagonal,
|
||||
RowOffsetsSearchIteratorT(spmv_params.d_row_end_offsets),
|
||||
nonzero_indices,
|
||||
spmv_params.num_rows,
|
||||
spmv_params.num_nonzeros,
|
||||
tile_coord);
|
||||
|
||||
temp_storage.tile_coord = tile_coord;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
CoordinateT tile_start_coord = temp_storage.tile_coord;
|
||||
|
||||
|
||||
// Mooch
|
||||
__shared__ volatile OffsetT x;
|
||||
x = tile_start_coord.x;
|
||||
|
||||
|
||||
// Turnstile
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
__threadfence();
|
||||
temp_storage.turnstile = atomicAdd(spmv_params.d_row_end_offsets - 1, 1);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Last block through turnstile does fixup
|
||||
if (temp_storage.turnstile == gridDim.x - 1)
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
spmv_params.d_row_end_offsets[-1] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,924 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../util_type.cuh"
|
||||
#include "../block/block_reduce.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../thread/thread_search.cuh"
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/counting_input_iterator.cuh"
|
||||
#include "../iterator/tex_ref_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentSpmv
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
CacheLoadModifier _ROW_OFFSETS_SEARCH_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets during search
|
||||
CacheLoadModifier _ROW_OFFSETS_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets
|
||||
CacheLoadModifier _COLUMN_INDICES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR column-indices
|
||||
CacheLoadModifier _VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR values
|
||||
CacheLoadModifier _VECTOR_VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading vector values
|
||||
bool _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (vs. pre-staged through shared memory)
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentSpmvPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
DIRECT_LOAD_NONZEROS = _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (pre-staged through shared memory)
|
||||
};
|
||||
|
||||
static const CacheLoadModifier ROW_OFFSETS_SEARCH_LOAD_MODIFIER = _ROW_OFFSETS_SEARCH_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier ROW_OFFSETS_LOAD_MODIFIER = _ROW_OFFSETS_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier COLUMN_INDICES_LOAD_MODIFIER = _COLUMN_INDICES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR column-indices
|
||||
static const CacheLoadModifier VALUES_LOAD_MODIFIER = _VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR values
|
||||
static const CacheLoadModifier VECTOR_VALUES_LOAD_MODIFIER = _VECTOR_VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading vector values
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for sequence offsets
|
||||
struct SpmvParams
|
||||
{
|
||||
ValueT* d_values; ///< Pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
OffsetT* d_row_end_offsets; ///< Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
OffsetT* d_column_indices; ///< Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
ValueT* d_vector_x; ///< Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
ValueT* d_vector_y; ///< Pointer to the array of \p num_rows values corresponding to the dense output vector <em>y</em>
|
||||
int num_rows; ///< Number of rows of matrix <b>A</b>.
|
||||
int num_cols; ///< Number of columns of matrix <b>A</b>.
|
||||
int num_nonzeros; ///< Number of nonzero elements of matrix <b>A</b>.
|
||||
ValueT alpha; ///< Alpha multiplicand
|
||||
ValueT beta; ///< Beta addend-multiplicand
|
||||
|
||||
TexRefInputIterator<ValueT, 66778899, OffsetT> t_vector_x;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
template <
|
||||
typename AgentSpmvPolicyT, ///< Parameterized AgentSpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
bool HAS_ALPHA, ///< Whether the input parameter \p alpha is 1
|
||||
bool HAS_BETA, ///< Whether the input parameter \p beta is 0
|
||||
int PTX_ARCH = CUB_PTX_ARCH> ///< PTX compute capability
|
||||
struct AgentSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentSpmvPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentSpmvPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
/// 2D merge path coordinate type
|
||||
typedef typename CubVector<OffsetT, 2>::Type CoordinateT;
|
||||
|
||||
/// Input iterator wrapper types (for applying cache modifiers)
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_SEARCH_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsSearchIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::COLUMN_INDICES_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
ColumnIndicesIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
ValueIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VECTOR_VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
VectorValueIteratorT;
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
// Reduce-value-by-segment scan operator
|
||||
typedef ReduceByKeyOp<cub::Sum> ReduceBySegmentOpT;
|
||||
|
||||
// BlockReduce specialization
|
||||
typedef BlockReduce<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS>
|
||||
BlockReduceT;
|
||||
|
||||
// BlockScan specialization
|
||||
typedef BlockScan<
|
||||
KeyValuePairT,
|
||||
BLOCK_THREADS,
|
||||
AgentSpmvPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
// BlockScan specialization
|
||||
typedef BlockScan<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
AgentSpmvPolicyT::SCAN_ALGORITHM>
|
||||
BlockPrefixSumT;
|
||||
|
||||
// BlockExchange specialization
|
||||
typedef BlockExchange<
|
||||
ValueT,
|
||||
BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD>
|
||||
BlockExchangeT;
|
||||
|
||||
/// Merge item type (either a non-zero value or a row-end offset)
|
||||
union MergeItem
|
||||
{
|
||||
// Value type to pair with index type OffsetT (NullType if loading values directly during merge)
|
||||
typedef typename If<AgentSpmvPolicyT::DIRECT_LOAD_NONZEROS, NullType, ValueT>::Type MergeValueT;
|
||||
|
||||
OffsetT row_end_offset;
|
||||
MergeValueT nonzero;
|
||||
};
|
||||
|
||||
/// Shared memory type required by this thread block
|
||||
struct _TempStorage
|
||||
{
|
||||
CoordinateT tile_coords[2];
|
||||
|
||||
union
|
||||
{
|
||||
// Smem needed for tile of merge items
|
||||
MergeItem merge_items[ITEMS_PER_THREAD + TILE_ITEMS + 1];
|
||||
|
||||
// Smem needed for block exchange
|
||||
typename BlockExchangeT::TempStorage exchange;
|
||||
|
||||
// Smem needed for block-wide reduction
|
||||
typename BlockReduceT::TempStorage reduce;
|
||||
|
||||
// Smem needed for tile scanning
|
||||
typename BlockScanT::TempStorage scan;
|
||||
|
||||
// Smem needed for tile prefix sum
|
||||
typename BlockPrefixSumT::TempStorage prefix_sum;
|
||||
};
|
||||
};
|
||||
|
||||
/// Temporary storage type (unionable)
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
_TempStorage& temp_storage; /// Reference to temp_storage
|
||||
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params;
|
||||
|
||||
ValueIteratorT wd_values; ///< Wrapped pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
RowOffsetsIteratorT wd_row_end_offsets; ///< Wrapped Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
ColumnIndicesIteratorT wd_column_indices; ///< Wrapped Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
VectorValueIteratorT wd_vector_x; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
VectorValueIteratorT wd_vector_y; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentSpmv(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params) ///< SpMV input parameter bundle
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
spmv_params(spmv_params),
|
||||
wd_values(spmv_params.d_values),
|
||||
wd_row_end_offsets(spmv_params.d_row_end_offsets),
|
||||
wd_column_indices(spmv_params.d_column_indices),
|
||||
wd_vector_x(spmv_params.d_vector_x),
|
||||
wd_vector_y(spmv_params.d_vector_y)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for direct-load of nonzeros
|
||||
*/
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
Int2Type<true> is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
for (int item = threadIdx.x; item <= tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
s_tile_row_end_offsets[item] = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Search for the thread's starting coordinate within the merge tile
|
||||
CountingInputIterator<OffsetT> tile_nonzero_indices(tile_start_coord.y);
|
||||
CoordinateT thread_start_coord;
|
||||
|
||||
MergePathSearch(
|
||||
OffsetT(threadIdx.x * ITEMS_PER_THREAD), // Diagonal
|
||||
s_tile_row_end_offsets, // List A
|
||||
tile_nonzero_indices, // List B
|
||||
tile_num_rows,
|
||||
tile_num_nonzeros,
|
||||
thread_start_coord);
|
||||
|
||||
__syncthreads(); // Perf-sync
|
||||
|
||||
// Compute the thread's merge path segment
|
||||
CoordinateT thread_current_coord = thread_start_coord;
|
||||
KeyValuePairT scan_segment[ITEMS_PER_THREAD];
|
||||
|
||||
ValueT running_total = 0.0;
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
OffsetT nonzero_idx = CUB_MIN(tile_nonzero_indices[thread_current_coord.y], spmv_params.num_nonzeros - 1);
|
||||
OffsetT column_idx = wd_column_indices[nonzero_idx];
|
||||
ValueT value = wd_values[nonzero_idx];
|
||||
|
||||
ValueT vector_value = spmv_params.t_vector_x[column_idx];
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
vector_value = wd_vector_x[column_idx];
|
||||
#endif
|
||||
ValueT nonzero = value * vector_value;
|
||||
|
||||
OffsetT row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
|
||||
if (tile_nonzero_indices[thread_current_coord.y] < row_end_offset)
|
||||
{
|
||||
// Move down (accumulate)
|
||||
running_total += nonzero;
|
||||
scan_segment[ITEM].value = running_total;
|
||||
scan_segment[ITEM].key = tile_num_rows;
|
||||
++thread_current_coord.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move right (reset)
|
||||
scan_segment[ITEM].value = running_total;
|
||||
scan_segment[ITEM].key = thread_current_coord.x;
|
||||
running_total = 0.0;
|
||||
++thread_current_coord.x;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Block-wide reduce-value-by-segment
|
||||
KeyValuePairT tile_carry;
|
||||
ReduceBySegmentOpT scan_op;
|
||||
KeyValuePairT scan_item;
|
||||
|
||||
scan_item.value = running_total;
|
||||
scan_item.key = thread_current_coord.x;
|
||||
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_item, scan_item, scan_op, tile_carry);
|
||||
|
||||
if (tile_num_rows > 0)
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
scan_item.key = -1;
|
||||
|
||||
// Direct scatter
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (scan_segment[ITEM].key < tile_num_rows)
|
||||
{
|
||||
if (scan_item.key == scan_segment[ITEM].key)
|
||||
scan_segment[ITEM].value = scan_item.value + scan_segment[ITEM].value;
|
||||
|
||||
if (HAS_ALPHA)
|
||||
{
|
||||
scan_segment[ITEM].value *= spmv_params.alpha;
|
||||
}
|
||||
|
||||
if (HAS_BETA)
|
||||
{
|
||||
// Update the output vector element
|
||||
ValueT addend = spmv_params.beta * wd_vector_y[tile_start_coord.x + scan_segment[ITEM].key];
|
||||
scan_segment[ITEM].value += addend;
|
||||
}
|
||||
|
||||
// Set the output vector element
|
||||
spmv_params.d_vector_y[tile_start_coord.x + scan_segment[ITEM].key] = scan_segment[ITEM].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
return tile_carry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for indirect load of nonzeros
|
||||
*/
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
Int2Type<false> is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
|
||||
#if (CUB_PTX_ARCH >= 520)
|
||||
|
||||
/*
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[tile_num_nonzeros].row_end_offset;
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[0].nonzero;
|
||||
|
||||
OffsetT col_indices[ITEMS_PER_THREAD];
|
||||
ValueT mat_values[ITEMS_PER_THREAD];
|
||||
int nonzero_indices[ITEMS_PER_THREAD];
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
nonzero_indices[ITEM] = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
|
||||
ValueIteratorT a = wd_values + tile_start_coord.y + nonzero_indices[ITEM];
|
||||
ColumnIndicesIteratorT ci = wd_column_indices + tile_start_coord.y + nonzero_indices[ITEM];
|
||||
|
||||
col_indices[ITEM] = (nonzero_indices[ITEM] < tile_num_nonzeros) ? *ci : 0;
|
||||
mat_values[ITEM] = (nonzero_indices[ITEM] < tile_num_nonzeros) ? *a : 0.0;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
VectorValueIteratorT x = wd_vector_x + col_indices[ITEM];
|
||||
mat_values[ITEM] *= *x;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
ValueT *s = s_tile_nonzeros + nonzero_indices[ITEM];
|
||||
|
||||
*s = mat_values[ITEM];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
*/
|
||||
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[tile_num_rows + ITEMS_PER_THREAD].nonzero;
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int nonzero_idx = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
|
||||
ValueIteratorT a = wd_values + tile_start_coord.y + nonzero_idx;
|
||||
ColumnIndicesIteratorT ci = wd_column_indices + tile_start_coord.y + nonzero_idx;
|
||||
ValueT* s = s_tile_nonzeros + nonzero_idx;
|
||||
|
||||
if (nonzero_idx < tile_num_nonzeros)
|
||||
{
|
||||
|
||||
OffsetT column_idx = *ci;
|
||||
ValueT value = *a;
|
||||
|
||||
ValueT vector_value = spmv_params.t_vector_x[column_idx];
|
||||
vector_value = wd_vector_x[column_idx];
|
||||
|
||||
ValueT nonzero = value * vector_value;
|
||||
|
||||
*s = nonzero;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[tile_num_rows + ITEMS_PER_THREAD].nonzero;
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
if (tile_num_nonzeros > 0)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int nonzero_idx = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
nonzero_idx = CUB_MIN(nonzero_idx, tile_num_nonzeros - 1);
|
||||
|
||||
OffsetT column_idx = wd_column_indices[tile_start_coord.y + nonzero_idx];
|
||||
ValueT value = wd_values[tile_start_coord.y + nonzero_idx];
|
||||
|
||||
ValueT vector_value = spmv_params.t_vector_x[column_idx];
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
vector_value = wd_vector_x[column_idx];
|
||||
#endif
|
||||
ValueT nonzero = value * vector_value;
|
||||
|
||||
s_tile_nonzeros[nonzero_idx] = nonzero;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item <= tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
s_tile_row_end_offsets[item] = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Search for the thread's starting coordinate within the merge tile
|
||||
CountingInputIterator<OffsetT> tile_nonzero_indices(tile_start_coord.y);
|
||||
CoordinateT thread_start_coord;
|
||||
|
||||
MergePathSearch(
|
||||
OffsetT(threadIdx.x * ITEMS_PER_THREAD), // Diagonal
|
||||
s_tile_row_end_offsets, // List A
|
||||
tile_nonzero_indices, // List B
|
||||
tile_num_rows,
|
||||
tile_num_nonzeros,
|
||||
thread_start_coord);
|
||||
|
||||
__syncthreads(); // Perf-sync
|
||||
|
||||
// Compute the thread's merge path segment
|
||||
CoordinateT thread_current_coord = thread_start_coord;
|
||||
KeyValuePairT scan_segment[ITEMS_PER_THREAD];
|
||||
ValueT running_total = 0.0;
|
||||
|
||||
OffsetT row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
ValueT nonzero = s_tile_nonzeros[thread_current_coord.y];
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (tile_nonzero_indices[thread_current_coord.y] < row_end_offset)
|
||||
{
|
||||
// Move down (accumulate)
|
||||
scan_segment[ITEM].value = nonzero;
|
||||
running_total += nonzero;
|
||||
++thread_current_coord.y;
|
||||
nonzero = s_tile_nonzeros[thread_current_coord.y];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move right (reset)
|
||||
scan_segment[ITEM].value = 0.0;
|
||||
running_total = 0.0;
|
||||
++thread_current_coord.x;
|
||||
row_end_offset = s_tile_row_end_offsets[thread_current_coord.x];
|
||||
}
|
||||
|
||||
scan_segment[ITEM].key = thread_current_coord.x;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Block-wide reduce-value-by-segment
|
||||
KeyValuePairT tile_carry;
|
||||
ReduceBySegmentOpT scan_op;
|
||||
KeyValuePairT scan_item;
|
||||
|
||||
scan_item.value = running_total;
|
||||
scan_item.key = thread_current_coord.x;
|
||||
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_item, scan_item, scan_op, tile_carry);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
scan_item.key = thread_start_coord.x;
|
||||
scan_item.value = 0.0;
|
||||
}
|
||||
|
||||
if (tile_num_rows > 0)
|
||||
{
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Scan downsweep and scatter
|
||||
ValueT* s_partials = &temp_storage.merge_items[0].nonzero;
|
||||
|
||||
if (scan_item.key != scan_segment[0].key)
|
||||
{
|
||||
s_partials[scan_item.key] = scan_item.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_segment[0].value += scan_item.value;
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 1; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
if (scan_segment[ITEM - 1].key != scan_segment[ITEM].key)
|
||||
{
|
||||
s_partials[scan_segment[ITEM - 1].key] = scan_segment[ITEM - 1].value;
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_segment[ITEM].value += scan_segment[ITEM - 1].value;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item < tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
spmv_params.d_vector_y[tile_start_coord.x + item] = s_partials[item];
|
||||
}
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
return tile_carry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for indirect load of nonzeros
|
||||
* /
|
||||
template <typename IsDirectLoadT>
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile1(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
IsDirectLoadT is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
|
||||
OffsetT* s_tile_row_end_offsets = &temp_storage.merge_items[0].row_end_offset;
|
||||
|
||||
int warp_idx = threadIdx.x / WARP_THREADS;
|
||||
int lane_idx = LaneId();
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item <= tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
s_tile_row_end_offsets[item] = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Search for warp start/end coords
|
||||
if (lane_idx == 0)
|
||||
{
|
||||
MergePathSearch(
|
||||
OffsetT(warp_idx * ITEMS_PER_WARP), // Diagonal
|
||||
s_tile_row_end_offsets, // List A
|
||||
CountingInputIterator<OffsetT>(tile_start_coord.y), // List B
|
||||
tile_num_rows,
|
||||
tile_num_nonzeros,
|
||||
temp_storage.warp_coords[warp_idx]);
|
||||
|
||||
CoordinateT last = {tile_num_rows, tile_num_nonzeros};
|
||||
temp_storage.warp_coords[WARPS] = last;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
CoordinateT warp_coord = temp_storage.warp_coords[warp_idx];
|
||||
CoordinateT warp_end_coord = temp_storage.warp_coords[warp_idx + 1];
|
||||
OffsetT warp_nonzero_idx = tile_start_coord.y + warp_coord.y;
|
||||
|
||||
// Consume whole rows
|
||||
#pragma unroll 1
|
||||
for (; warp_coord.x < warp_end_coord.x; ++warp_coord.x)
|
||||
{
|
||||
ValueT row_total = 0.0;
|
||||
OffsetT row_end_offset = s_tile_row_end_offsets[warp_coord.x];
|
||||
|
||||
#pragma unroll 1
|
||||
for (OffsetT nonzero_idx = warp_nonzero_idx + lane_idx;
|
||||
nonzero_idx < row_end_offset;
|
||||
nonzero_idx += WARP_THREADS)
|
||||
{
|
||||
OffsetT column_idx = wd_column_indices[nonzero_idx];
|
||||
ValueT value = wd_values[nonzero_idx];
|
||||
ValueT vector_value = wd_vector_x[column_idx];
|
||||
row_total += value * vector_value;
|
||||
}
|
||||
|
||||
// Warp reduce
|
||||
row_total = WarpReduceT(temp_storage.warp_reduce[warp_idx]).Sum(row_total);
|
||||
|
||||
// Output
|
||||
if (lane_idx == 0)
|
||||
{
|
||||
spmv_params.d_vector_y[tile_start_coord.x + warp_coord.x] = row_total;
|
||||
}
|
||||
|
||||
warp_nonzero_idx = row_end_offset;
|
||||
}
|
||||
|
||||
// Consume partial portion of thread's last row
|
||||
if (warp_nonzero_idx < tile_start_coord.y + warp_end_coord.y)
|
||||
{
|
||||
ValueT row_total = 0.0;
|
||||
for (OffsetT nonzero_idx = warp_nonzero_idx + lane_idx;
|
||||
nonzero_idx < tile_start_coord.y + warp_end_coord.y;
|
||||
nonzero_idx += WARP_THREADS)
|
||||
{
|
||||
|
||||
OffsetT column_idx = wd_column_indices[nonzero_idx];
|
||||
ValueT value = wd_values[nonzero_idx];
|
||||
ValueT vector_value = wd_vector_x[column_idx];
|
||||
row_total += value * vector_value;
|
||||
}
|
||||
|
||||
// Warp reduce
|
||||
row_total = WarpReduceT(temp_storage.warp_reduce[warp_idx]).Sum(row_total);
|
||||
|
||||
// Output
|
||||
if (lane_idx == 0)
|
||||
{
|
||||
spmv_params.d_vector_y[tile_start_coord.x + warp_coord.x] = row_total;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
KeyValuePairT tile_carry(tile_num_rows, 0.0);
|
||||
return tile_carry;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume a merge tile, specialized for indirect load of nonzeros
|
||||
* /
|
||||
__device__ __forceinline__ KeyValuePairT ConsumeTile2(
|
||||
int tile_idx,
|
||||
CoordinateT tile_start_coord,
|
||||
CoordinateT tile_end_coord,
|
||||
Int2Type<false> is_direct_load) ///< Marker type indicating whether to load nonzeros directly during path-discovery or beforehand in batch
|
||||
{
|
||||
int tile_num_rows = tile_end_coord.x - tile_start_coord.x;
|
||||
int tile_num_nonzeros = tile_end_coord.y - tile_start_coord.y;
|
||||
|
||||
ValueT* s_tile_nonzeros = &temp_storage.merge_items[0].nonzero;
|
||||
|
||||
ValueT nonzeros[ITEMS_PER_THREAD];
|
||||
|
||||
// Gather the nonzeros for the merge tile into shared memory
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int nonzero_idx = threadIdx.x + (ITEM * BLOCK_THREADS);
|
||||
nonzero_idx = CUB_MIN(nonzero_idx, tile_num_nonzeros - 1);
|
||||
|
||||
OffsetT column_idx = wd_column_indices[tile_start_coord.y + nonzero_idx];
|
||||
ValueT value = wd_values[tile_start_coord.y + nonzero_idx];
|
||||
|
||||
ValueT vector_value = spmv_params.t_vector_x[column_idx];
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
vector_value = wd_vector_x[column_idx];
|
||||
#endif
|
||||
|
||||
nonzeros[ITEM] = value * vector_value;
|
||||
}
|
||||
|
||||
// Exchange striped->blocked
|
||||
BlockExchangeT(temp_storage.exchange).StripedToBlocked(nonzeros);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Compute an inclusive prefix sum
|
||||
BlockPrefixSumT(temp_storage.prefix_sum).InclusiveSum(nonzeros, nonzeros);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
s_tile_nonzeros[0] = 0.0;
|
||||
|
||||
// Scatter back to smem
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int item_idx = (threadIdx.x * ITEMS_PER_THREAD) + ITEM + 1;
|
||||
s_tile_nonzeros[item_idx] = nonzeros[ITEM];
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Gather the row end-offsets for the merge tile into shared memory
|
||||
#pragma unroll 1
|
||||
for (int item = threadIdx.x; item < tile_num_rows; item += BLOCK_THREADS)
|
||||
{
|
||||
OffsetT start = CUB_MAX(wd_row_end_offsets[tile_start_coord.x + item - 1], tile_start_coord.y);
|
||||
OffsetT end = wd_row_end_offsets[tile_start_coord.x + item];
|
||||
|
||||
start -= tile_start_coord.y;
|
||||
end -= tile_start_coord.y;
|
||||
|
||||
ValueT row_partial = s_tile_nonzeros[end] - s_tile_nonzeros[start];
|
||||
|
||||
spmv_params.d_vector_y[tile_start_coord.x + item] = row_partial;
|
||||
}
|
||||
|
||||
// Get the tile's carry-out
|
||||
KeyValuePairT tile_carry;
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
tile_carry.key = tile_num_rows;
|
||||
|
||||
OffsetT start = CUB_MAX(wd_row_end_offsets[tile_end_coord.x - 1], tile_start_coord.y);
|
||||
start -= tile_start_coord.y;
|
||||
OffsetT end = tile_num_nonzeros;
|
||||
|
||||
tile_carry.value = s_tile_nonzeros[end] - s_tile_nonzeros[start];
|
||||
}
|
||||
|
||||
// Return the tile's running carry-out
|
||||
return tile_carry;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Consume input tile
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
CoordinateT* d_tile_coordinates, ///< [in] Pointer to the temporary array of tile starting coordinates
|
||||
KeyValuePairT* d_tile_carry_pairs, ///< [out] Pointer to the temporary array carry-out dot product row-ids, one per block
|
||||
int num_merge_tiles) ///< [in] Number of merge tiles
|
||||
{
|
||||
int tile_idx = (blockIdx.x * gridDim.y) + blockIdx.y; // Current tile index
|
||||
|
||||
if (tile_idx >= num_merge_tiles)
|
||||
return;
|
||||
|
||||
// Read our starting coordinates
|
||||
if (threadIdx.x < 2)
|
||||
{
|
||||
if (d_tile_coordinates == NULL)
|
||||
{
|
||||
// Search our starting coordinates
|
||||
OffsetT diagonal = (tile_idx + threadIdx.x) * TILE_ITEMS;
|
||||
CoordinateT tile_coord;
|
||||
CountingInputIterator<OffsetT> nonzero_indices(0);
|
||||
|
||||
// Search the merge path
|
||||
MergePathSearch(
|
||||
diagonal,
|
||||
RowOffsetsSearchIteratorT(spmv_params.d_row_end_offsets),
|
||||
nonzero_indices,
|
||||
spmv_params.num_rows,
|
||||
spmv_params.num_nonzeros,
|
||||
tile_coord);
|
||||
|
||||
temp_storage.tile_coords[threadIdx.x] = tile_coord;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_storage.tile_coords[threadIdx.x] = d_tile_coordinates[tile_idx + threadIdx.x];
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
CoordinateT tile_start_coord = temp_storage.tile_coords[0];
|
||||
CoordinateT tile_end_coord = temp_storage.tile_coords[1];
|
||||
|
||||
// Consume multi-segment tile
|
||||
KeyValuePairT tile_carry = ConsumeTile(
|
||||
tile_idx,
|
||||
tile_start_coord,
|
||||
tile_end_coord,
|
||||
Int2Type<AgentSpmvPolicyT::DIRECT_LOAD_NONZEROS>());
|
||||
|
||||
// Output the tile's carry-out
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
if (HAS_ALPHA)
|
||||
tile_carry.value *= spmv_params.alpha;
|
||||
|
||||
tile_carry.key += tile_start_coord.x;
|
||||
d_tile_carry_pairs[tile_idx] = tile_carry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../util_type.cuh"
|
||||
#include "../block/block_reduce.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../block/block_exchange.cuh"
|
||||
#include "../thread/thread_search.cuh"
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../iterator/cache_modified_input_iterator.cuh"
|
||||
#include "../iterator/counting_input_iterator.cuh"
|
||||
#include "../iterator/tex_ref_input_iterator.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policy
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Parameterizable tuning policy type for AgentSpmv
|
||||
*/
|
||||
template <
|
||||
int _BLOCK_THREADS, ///< Threads per thread block
|
||||
int _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
CacheLoadModifier _ROW_OFFSETS_SEARCH_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets during search
|
||||
CacheLoadModifier _ROW_OFFSETS_LOAD_MODIFIER, ///< Cache load modifier for reading CSR row-offsets
|
||||
CacheLoadModifier _COLUMN_INDICES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR column-indices
|
||||
CacheLoadModifier _VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading CSR values
|
||||
CacheLoadModifier _VECTOR_VALUES_LOAD_MODIFIER, ///< Cache load modifier for reading vector values
|
||||
bool _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (vs. pre-staged through shared memory)
|
||||
BlockScanAlgorithm _SCAN_ALGORITHM> ///< The BlockScan algorithm to use
|
||||
struct AgentSpmvPolicy
|
||||
{
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = _BLOCK_THREADS, ///< Threads per thread block
|
||||
ITEMS_PER_THREAD = _ITEMS_PER_THREAD, ///< Items per thread (per tile of input)
|
||||
DIRECT_LOAD_NONZEROS = _DIRECT_LOAD_NONZEROS, ///< Whether to load nonzeros directly from global during sequential merging (pre-staged through shared memory)
|
||||
};
|
||||
|
||||
static const CacheLoadModifier ROW_OFFSETS_SEARCH_LOAD_MODIFIER = _ROW_OFFSETS_SEARCH_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier ROW_OFFSETS_LOAD_MODIFIER = _ROW_OFFSETS_LOAD_MODIFIER; ///< Cache load modifier for reading CSR row-offsets
|
||||
static const CacheLoadModifier COLUMN_INDICES_LOAD_MODIFIER = _COLUMN_INDICES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR column-indices
|
||||
static const CacheLoadModifier VALUES_LOAD_MODIFIER = _VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading CSR values
|
||||
static const CacheLoadModifier VECTOR_VALUES_LOAD_MODIFIER = _VECTOR_VALUES_LOAD_MODIFIER; ///< Cache load modifier for reading vector values
|
||||
static const BlockScanAlgorithm SCAN_ALGORITHM = _SCAN_ALGORITHM; ///< The BlockScan algorithm to use
|
||||
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread block abstractions
|
||||
******************************************************************************/
|
||||
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for sequence offsets
|
||||
struct SpmvParams
|
||||
{
|
||||
ValueT* d_values; ///< Pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
OffsetT* d_row_end_offsets; ///< Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
OffsetT* d_column_indices; ///< Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
ValueT* d_vector_x; ///< Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
ValueT* d_vector_y; ///< Pointer to the array of \p num_rows values corresponding to the dense output vector <em>y</em>
|
||||
int num_rows; ///< Number of rows of matrix <b>A</b>.
|
||||
int num_cols; ///< Number of columns of matrix <b>A</b>.
|
||||
int num_nonzeros; ///< Number of nonzero elements of matrix <b>A</b>.
|
||||
ValueT alpha; ///< Alpha multiplicand
|
||||
ValueT beta; ///< Beta addend-multiplicand
|
||||
|
||||
TexRefInputIterator<ValueT, 66778899, OffsetT> t_vector_x;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief AgentSpmv implements a stateful abstraction of CUDA thread blocks for participating in device-wide SpMV.
|
||||
*/
|
||||
template <
|
||||
typename AgentSpmvPolicyT, ///< Parameterized AgentSpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
bool HAS_ALPHA, ///< Whether the input parameter \p alpha is 1
|
||||
bool HAS_BETA, ///< Whether the input parameter \p beta is 0
|
||||
int PTX_ARCH = CUB_PTX_ARCH> ///< PTX compute capability
|
||||
struct AgentSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = AgentSpmvPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = AgentSpmvPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
/// 2D merge path coordinate type
|
||||
typedef typename CubVector<OffsetT, 2>::Type CoordinateT;
|
||||
|
||||
/// Input iterator wrapper types (for applying cache modifiers)
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_SEARCH_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsSearchIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::ROW_OFFSETS_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::COLUMN_INDICES_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
ColumnIndicesIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
ValueIteratorT;
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VECTOR_VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
VectorValueIteratorT;
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
// Reduce-value-by-segment scan operator
|
||||
typedef ReduceBySegmentOp<cub::Sum> ReduceBySegmentOpT;
|
||||
|
||||
// Prefix functor type
|
||||
typedef BlockScanRunningPrefixOp<KeyValuePairT, ReduceBySegmentOpT> PrefixOpT;
|
||||
|
||||
// BlockScan specialization
|
||||
typedef BlockScan<
|
||||
KeyValuePairT,
|
||||
BLOCK_THREADS,
|
||||
AgentSpmvPolicyT::SCAN_ALGORITHM>
|
||||
BlockScanT;
|
||||
|
||||
/// Shared memory type required by this thread block
|
||||
struct _TempStorage
|
||||
{
|
||||
OffsetT tile_nonzero_idx;
|
||||
OffsetT tile_nonzero_idx_end;
|
||||
|
||||
// Smem needed for tile scanning
|
||||
typename BlockScanT::TempStorage scan;
|
||||
|
||||
// Smem needed for tile of merge items
|
||||
ValueT nonzeros[TILE_ITEMS + 1];
|
||||
|
||||
};
|
||||
|
||||
/// Temporary storage type (unionable)
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
_TempStorage& temp_storage; /// Reference to temp_storage
|
||||
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params;
|
||||
|
||||
ValueIteratorT wd_values; ///< Wrapped pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
RowOffsetsIteratorT wd_row_end_offsets; ///< Wrapped Pointer to the array of \p m offsets demarcating the end of every row in \p d_column_indices and \p d_values
|
||||
ColumnIndicesIteratorT wd_column_indices; ///< Wrapped Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
VectorValueIteratorT wd_vector_x; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
VectorValueIteratorT wd_vector_y; ///< Wrapped Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
__device__ __forceinline__ AgentSpmv(
|
||||
TempStorage& temp_storage, ///< Reference to temp_storage
|
||||
SpmvParams<ValueT, OffsetT>& spmv_params) ///< SpMV input parameter bundle
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
spmv_params(spmv_params),
|
||||
wd_values(spmv_params.d_values),
|
||||
wd_row_end_offsets(spmv_params.d_row_end_offsets),
|
||||
wd_column_indices(spmv_params.d_column_indices),
|
||||
wd_vector_x(spmv_params.d_vector_x),
|
||||
wd_vector_y(spmv_params.d_vector_y)
|
||||
{}
|
||||
|
||||
|
||||
__device__ __forceinline__ void InitNan(double& nan_token)
|
||||
{
|
||||
long long NAN_BITS = 0xFFF0000000000001;
|
||||
nan_token = reinterpret_cast<ValueT&>(NAN_BITS); // ValueT(0.0) / ValueT(0.0);
|
||||
}
|
||||
|
||||
|
||||
__device__ __forceinline__ void InitNan(float& nan_token)
|
||||
{
|
||||
int NAN_BITS = 0xFF800001;
|
||||
nan_token = reinterpret_cast<ValueT&>(NAN_BITS); // ValueT(0.0) / ValueT(0.0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template <int NNZ_PER_THREAD>
|
||||
__device__ __forceinline__ void ConsumeStrip(
|
||||
PrefixOpT& prefix_op,
|
||||
ReduceBySegmentOpT& scan_op,
|
||||
ValueT& row_total,
|
||||
ValueT& row_start,
|
||||
OffsetT& tile_nonzero_idx,
|
||||
OffsetT tile_nonzero_idx_end,
|
||||
OffsetT row_nonzero_idx,
|
||||
OffsetT row_nonzero_idx_end)
|
||||
{
|
||||
ValueT NAN_TOKEN;
|
||||
InitNan(NAN_TOKEN);
|
||||
|
||||
|
||||
//
|
||||
// Gather a strip of nonzeros into shared memory
|
||||
//
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < NNZ_PER_THREAD; ++ITEM)
|
||||
{
|
||||
|
||||
ValueT nonzero = 0.0;
|
||||
|
||||
OffsetT local_nonzero_idx = (ITEM * BLOCK_THREADS) + threadIdx.x;
|
||||
OffsetT nonzero_idx = tile_nonzero_idx + local_nonzero_idx;
|
||||
|
||||
bool in_range = nonzero_idx < tile_nonzero_idx_end;
|
||||
|
||||
OffsetT nonzero_idx2 = (in_range) ?
|
||||
nonzero_idx :
|
||||
tile_nonzero_idx_end - 1;
|
||||
|
||||
OffsetT column_idx = wd_column_indices[nonzero_idx2];
|
||||
ValueT value = wd_values[nonzero_idx2];
|
||||
ValueT vector_value = wd_vector_x[column_idx];
|
||||
nonzero = value * vector_value;
|
||||
|
||||
if (!in_range)
|
||||
nonzero = 0.0;
|
||||
|
||||
temp_storage.nonzeros[local_nonzero_idx] = nonzero;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Swap in NANs at local row start offsets
|
||||
//
|
||||
|
||||
OffsetT local_row_nonzero_idx = row_nonzero_idx - tile_nonzero_idx;
|
||||
if ((local_row_nonzero_idx >= 0) && (local_row_nonzero_idx < TILE_ITEMS))
|
||||
{
|
||||
// Thread's row starts in this strip
|
||||
row_start = temp_storage.nonzeros[local_row_nonzero_idx];
|
||||
temp_storage.nonzeros[local_row_nonzero_idx] = NAN_TOKEN;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Segmented scan
|
||||
//
|
||||
|
||||
// Read strip of nonzeros into thread-blocked order, setup segment flags
|
||||
KeyValuePairT scan_items[NNZ_PER_THREAD];
|
||||
for (int ITEM = 0; ITEM < NNZ_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int local_nonzero_idx = (threadIdx.x * NNZ_PER_THREAD) + ITEM;
|
||||
ValueT value = temp_storage.nonzeros[local_nonzero_idx];
|
||||
bool is_nan = (value != value);
|
||||
|
||||
scan_items[ITEM].value = (is_nan) ? 0.0 : value;
|
||||
scan_items[ITEM].key = is_nan;
|
||||
}
|
||||
|
||||
KeyValuePairT tile_aggregate;
|
||||
KeyValuePairT scan_items_out[NNZ_PER_THREAD];
|
||||
|
||||
BlockScanT(temp_storage.scan).ExclusiveScan(scan_items, scan_items_out, scan_op, tile_aggregate, prefix_op);
|
||||
|
||||
// Save the inclusive sum for the last row
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
temp_storage.nonzeros[TILE_ITEMS] = prefix_op.running_total.value;
|
||||
}
|
||||
|
||||
// Store segment totals
|
||||
for (int ITEM = 0; ITEM < NNZ_PER_THREAD; ++ITEM)
|
||||
{
|
||||
int local_nonzero_idx = (threadIdx.x * NNZ_PER_THREAD) + ITEM;
|
||||
|
||||
if (scan_items[ITEM].key)
|
||||
temp_storage.nonzeros[local_nonzero_idx] = scan_items_out[ITEM].value;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Update row totals
|
||||
//
|
||||
|
||||
OffsetT local_row_nonzero_idx_end = row_nonzero_idx_end - tile_nonzero_idx;
|
||||
if ((local_row_nonzero_idx_end >= 0) && (local_row_nonzero_idx_end < TILE_ITEMS))
|
||||
{
|
||||
// Thread's row ends in this strip
|
||||
row_total = temp_storage.nonzeros[local_row_nonzero_idx_end];
|
||||
}
|
||||
|
||||
tile_nonzero_idx += NNZ_PER_THREAD * BLOCK_THREADS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume input tile
|
||||
*/
|
||||
__device__ __forceinline__ void ConsumeTile(
|
||||
int tile_idx,
|
||||
int rows_per_tile)
|
||||
{
|
||||
//
|
||||
// Read in tile of row ranges
|
||||
//
|
||||
|
||||
// Row range for the thread block
|
||||
OffsetT tile_row_idx = tile_idx * rows_per_tile;
|
||||
OffsetT tile_row_idx_end = CUB_MIN(tile_row_idx + rows_per_tile, spmv_params.num_rows);
|
||||
|
||||
// Thread's row
|
||||
OffsetT row_idx = tile_row_idx + threadIdx.x;
|
||||
ValueT row_total = 0.0;
|
||||
ValueT row_start = 0.0;
|
||||
|
||||
// Nonzero range for the thread's row
|
||||
OffsetT row_nonzero_idx = -1;
|
||||
OffsetT row_nonzero_idx_end = -1;
|
||||
|
||||
if (row_idx < tile_row_idx_end)
|
||||
{
|
||||
row_nonzero_idx = wd_row_end_offsets[row_idx - 1];
|
||||
row_nonzero_idx_end = wd_row_end_offsets[row_idx];
|
||||
|
||||
// Share block's starting nonzero offset
|
||||
if (threadIdx.x == 0)
|
||||
temp_storage.tile_nonzero_idx = row_nonzero_idx;
|
||||
|
||||
// Share block's ending nonzero offset
|
||||
if (row_idx == tile_row_idx_end - 1)
|
||||
temp_storage.tile_nonzero_idx_end = row_nonzero_idx_end;
|
||||
|
||||
// Zero-length rows don't participate
|
||||
if (row_nonzero_idx == row_nonzero_idx_end)
|
||||
{
|
||||
row_nonzero_idx = -1;
|
||||
row_nonzero_idx_end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
//
|
||||
// Process strips of nonzeros
|
||||
//
|
||||
|
||||
// Nonzero range for the thread block
|
||||
OffsetT tile_nonzero_idx = temp_storage.tile_nonzero_idx;
|
||||
OffsetT tile_nonzero_idx_end = temp_storage.tile_nonzero_idx_end;
|
||||
|
||||
KeyValuePairT tile_prefix(0, 0.0);
|
||||
ReduceBySegmentOpT scan_op;
|
||||
PrefixOpT prefix_op(tile_prefix, scan_op);
|
||||
|
||||
#pragma unroll 1
|
||||
while (tile_nonzero_idx < tile_nonzero_idx_end)
|
||||
{
|
||||
ConsumeStrip<ITEMS_PER_THREAD>(prefix_op, scan_op, row_total, row_start,
|
||||
tile_nonzero_idx, tile_nonzero_idx_end, row_nonzero_idx, row_nonzero_idx_end);
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
//
|
||||
// Output to y
|
||||
//
|
||||
|
||||
if (row_idx < tile_row_idx_end)
|
||||
{
|
||||
if (row_nonzero_idx_end == tile_nonzero_idx_end)
|
||||
{
|
||||
// Last row grabs the inclusive sum
|
||||
row_total = temp_storage.nonzeros[TILE_ITEMS];
|
||||
}
|
||||
|
||||
spmv_params.d_vector_y[row_idx] = row_start + row_total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,792 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Callback operator types for supplying BlockScan prefixes
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../warp/warp_reduce.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Prefix functor type for maintaining a running prefix while scanning a
|
||||
* region independent of other thread blocks
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Stateful callback operator type for supplying BlockScan prefixes.
|
||||
* Maintains a running prefix that can be applied to consecutive
|
||||
* BlockScan operations.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< BlockScan value type
|
||||
typename ScanOpT> ///< Wrapped scan operator type
|
||||
struct BlockScanRunningPrefixOp
|
||||
{
|
||||
ScanOpT op; ///< Wrapped scan operator
|
||||
T running_total; ///< Running block-wide prefix
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanRunningPrefixOp(ScanOpT op)
|
||||
:
|
||||
op(op)
|
||||
{}
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanRunningPrefixOp(
|
||||
T starting_prefix,
|
||||
ScanOpT op)
|
||||
:
|
||||
op(op),
|
||||
running_total(starting_prefix)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Prefix callback operator. Returns the block-wide running_total in thread-0.
|
||||
*/
|
||||
__device__ __forceinline__ T operator()(
|
||||
const T &block_aggregate) ///< The aggregate sum of the BlockScan inputs
|
||||
{
|
||||
T retval = running_total;
|
||||
running_total = op(running_total, block_aggregate);
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Generic tile status interface types for block-cooperative scans
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Enumerations of tile status
|
||||
*/
|
||||
enum ScanTileStatus
|
||||
{
|
||||
SCAN_TILE_OOB, // Out-of-bounds (e.g., padding)
|
||||
SCAN_TILE_INVALID = 99, // Not yet processed
|
||||
SCAN_TILE_PARTIAL, // Tile aggregate is available
|
||||
SCAN_TILE_INCLUSIVE, // Inclusive tile prefix is available
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tile status interface.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
bool SINGLE_WORD = Traits<T>::PRIMITIVE>
|
||||
struct ScanTileState;
|
||||
|
||||
|
||||
/**
|
||||
* Tile status interface specialized for scan status and value types
|
||||
* that can be combined into one machine word that can be
|
||||
* read/written coherently in a single access.
|
||||
*/
|
||||
template <typename T>
|
||||
struct ScanTileState<T, true>
|
||||
{
|
||||
// Status word type
|
||||
typedef typename If<(sizeof(T) == 8),
|
||||
long long,
|
||||
typename If<(sizeof(T) == 4),
|
||||
int,
|
||||
typename If<(sizeof(T) == 2),
|
||||
short,
|
||||
char>::Type>::Type>::Type StatusWord;
|
||||
|
||||
|
||||
// Unit word type
|
||||
typedef typename If<(sizeof(T) == 8),
|
||||
longlong2,
|
||||
typename If<(sizeof(T) == 4),
|
||||
int2,
|
||||
typename If<(sizeof(T) == 2),
|
||||
int,
|
||||
uchar2>::Type>::Type>::Type TxnWord;
|
||||
|
||||
|
||||
// Device word type
|
||||
struct TileDescriptor
|
||||
{
|
||||
StatusWord status;
|
||||
T value;
|
||||
};
|
||||
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
TILE_STATUS_PADDING = CUB_PTX_WARP_THREADS,
|
||||
};
|
||||
|
||||
|
||||
// Device storage
|
||||
TileDescriptor *d_tile_status;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
ScanTileState()
|
||||
:
|
||||
d_tile_status(NULL)
|
||||
{}
|
||||
|
||||
|
||||
/// Initializer
|
||||
__host__ __device__ __forceinline__
|
||||
cudaError_t Init(
|
||||
int /*num_tiles*/, ///< [in] Number of tiles
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t /*temp_storage_bytes*/) ///< [in] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
d_tile_status = reinterpret_cast<TileDescriptor*>(d_temp_storage);
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute device memory needed for tile status
|
||||
*/
|
||||
__host__ __device__ __forceinline__
|
||||
static cudaError_t AllocationSize(
|
||||
int num_tiles, ///< [in] Number of tiles
|
||||
size_t &temp_storage_bytes) ///< [out] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
temp_storage_bytes = (num_tiles + TILE_STATUS_PADDING) * sizeof(TileDescriptor); // bytes needed for tile status descriptors
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize (from device)
|
||||
*/
|
||||
__device__ __forceinline__ void InitializeStatus(int num_tiles)
|
||||
{
|
||||
int tile_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (tile_idx < num_tiles)
|
||||
{
|
||||
// Not-yet-set
|
||||
d_tile_status[TILE_STATUS_PADDING + tile_idx].status = StatusWord(SCAN_TILE_INVALID);
|
||||
}
|
||||
|
||||
if ((blockIdx.x == 0) && (threadIdx.x < TILE_STATUS_PADDING))
|
||||
{
|
||||
// Padding
|
||||
d_tile_status[threadIdx.x].status = StatusWord(SCAN_TILE_OOB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's inclusive value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetInclusive(int tile_idx, T tile_inclusive)
|
||||
{
|
||||
TileDescriptor tile_descriptor;
|
||||
tile_descriptor.status = SCAN_TILE_INCLUSIVE;
|
||||
tile_descriptor.value = tile_inclusive;
|
||||
|
||||
TxnWord alias;
|
||||
*reinterpret_cast<TileDescriptor*>(&alias) = tile_descriptor;
|
||||
ThreadStore<STORE_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx), alias);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's partial value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetPartial(int tile_idx, T tile_partial)
|
||||
{
|
||||
TileDescriptor tile_descriptor;
|
||||
tile_descriptor.status = SCAN_TILE_PARTIAL;
|
||||
tile_descriptor.value = tile_partial;
|
||||
|
||||
TxnWord alias;
|
||||
*reinterpret_cast<TileDescriptor*>(&alias) = tile_descriptor;
|
||||
ThreadStore<STORE_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx), alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the corresponding tile to become non-invalid
|
||||
*/
|
||||
__device__ __forceinline__ void WaitForValid(
|
||||
int tile_idx,
|
||||
StatusWord &status,
|
||||
T &value)
|
||||
{
|
||||
TileDescriptor tile_descriptor;
|
||||
do
|
||||
{
|
||||
__threadfence_block(); // prevent hoisting loads from loop
|
||||
TxnWord alias = ThreadLoad<LOAD_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx));
|
||||
tile_descriptor = reinterpret_cast<TileDescriptor&>(alias);
|
||||
|
||||
} while (WarpAny(tile_descriptor.status == SCAN_TILE_INVALID));
|
||||
|
||||
status = tile_descriptor.status;
|
||||
value = tile_descriptor.value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tile status interface specialized for scan status and value types that
|
||||
* cannot be combined into one machine word.
|
||||
*/
|
||||
template <typename T>
|
||||
struct ScanTileState<T, false>
|
||||
{
|
||||
// Status word type
|
||||
typedef char StatusWord;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
TILE_STATUS_PADDING = CUB_PTX_WARP_THREADS,
|
||||
};
|
||||
|
||||
// Device storage
|
||||
StatusWord *d_tile_status;
|
||||
T *d_tile_partial;
|
||||
T *d_tile_inclusive;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
ScanTileState()
|
||||
:
|
||||
d_tile_status(NULL),
|
||||
d_tile_partial(NULL),
|
||||
d_tile_inclusive(NULL)
|
||||
{}
|
||||
|
||||
|
||||
/// Initializer
|
||||
__host__ __device__ __forceinline__
|
||||
cudaError_t Init(
|
||||
int num_tiles, ///< [in] Number of tiles
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t temp_storage_bytes) ///< [in] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
cudaError_t error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
void* allocations[3];
|
||||
size_t allocation_sizes[3];
|
||||
|
||||
allocation_sizes[0] = (num_tiles + TILE_STATUS_PADDING) * sizeof(StatusWord); // bytes needed for tile status descriptors
|
||||
allocation_sizes[1] = (num_tiles + TILE_STATUS_PADDING) * sizeof(Uninitialized<T>); // bytes needed for partials
|
||||
allocation_sizes[2] = (num_tiles + TILE_STATUS_PADDING) * sizeof(Uninitialized<T>); // bytes needed for inclusives
|
||||
|
||||
// Compute allocation pointers into the single storage blob
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
|
||||
// Alias the offsets
|
||||
d_tile_status = reinterpret_cast<StatusWord*>(allocations[0]);
|
||||
d_tile_partial = reinterpret_cast<T*>(allocations[1]);
|
||||
d_tile_inclusive = reinterpret_cast<T*>(allocations[2]);
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute device memory needed for tile status
|
||||
*/
|
||||
__host__ __device__ __forceinline__
|
||||
static cudaError_t AllocationSize(
|
||||
int num_tiles, ///< [in] Number of tiles
|
||||
size_t &temp_storage_bytes) ///< [out] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
// Specify storage allocation requirements
|
||||
size_t allocation_sizes[3];
|
||||
allocation_sizes[0] = (num_tiles + TILE_STATUS_PADDING) * sizeof(StatusWord); // bytes needed for tile status descriptors
|
||||
allocation_sizes[1] = (num_tiles + TILE_STATUS_PADDING) * sizeof(Uninitialized<T>); // bytes needed for partials
|
||||
allocation_sizes[2] = (num_tiles + TILE_STATUS_PADDING) * sizeof(Uninitialized<T>); // bytes needed for inclusives
|
||||
|
||||
// Set the necessary size of the blob
|
||||
void* allocations[3];
|
||||
return CubDebug(AliasTemporaries(NULL, temp_storage_bytes, allocations, allocation_sizes));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize (from device)
|
||||
*/
|
||||
__device__ __forceinline__ void InitializeStatus(int num_tiles)
|
||||
{
|
||||
int tile_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (tile_idx < num_tiles)
|
||||
{
|
||||
// Not-yet-set
|
||||
d_tile_status[TILE_STATUS_PADDING + tile_idx] = StatusWord(SCAN_TILE_INVALID);
|
||||
}
|
||||
|
||||
if ((blockIdx.x == 0) && (threadIdx.x < TILE_STATUS_PADDING))
|
||||
{
|
||||
// Padding
|
||||
d_tile_status[threadIdx.x] = StatusWord(SCAN_TILE_OOB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's inclusive value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetInclusive(int tile_idx, T tile_inclusive)
|
||||
{
|
||||
// Update tile inclusive value
|
||||
ThreadStore<STORE_CG>(d_tile_inclusive + TILE_STATUS_PADDING + tile_idx, tile_inclusive);
|
||||
|
||||
// Fence
|
||||
__threadfence();
|
||||
|
||||
// Update tile status
|
||||
ThreadStore<STORE_CG>(d_tile_status + TILE_STATUS_PADDING + tile_idx, StatusWord(SCAN_TILE_INCLUSIVE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's partial value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetPartial(int tile_idx, T tile_partial)
|
||||
{
|
||||
// Update tile partial value
|
||||
ThreadStore<STORE_CG>(d_tile_partial + TILE_STATUS_PADDING + tile_idx, tile_partial);
|
||||
|
||||
// Fence
|
||||
__threadfence();
|
||||
|
||||
// Update tile status
|
||||
ThreadStore<STORE_CG>(d_tile_status + TILE_STATUS_PADDING + tile_idx, StatusWord(SCAN_TILE_PARTIAL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the corresponding tile to become non-invalid
|
||||
*/
|
||||
__device__ __forceinline__ void WaitForValid(
|
||||
int tile_idx,
|
||||
StatusWord &status,
|
||||
T &value)
|
||||
{
|
||||
do {
|
||||
status = ThreadLoad<LOAD_CG>(d_tile_status + TILE_STATUS_PADDING + tile_idx);
|
||||
|
||||
__threadfence(); // prevent hoisting loads from loop or loads below above this one
|
||||
|
||||
} while (status == SCAN_TILE_INVALID);
|
||||
|
||||
if (status == StatusWord(SCAN_TILE_PARTIAL))
|
||||
value = ThreadLoad<LOAD_CG>(d_tile_partial + TILE_STATUS_PADDING + tile_idx);
|
||||
else
|
||||
value = ThreadLoad<LOAD_CG>(d_tile_inclusive + TILE_STATUS_PADDING + tile_idx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* ReduceByKey tile status interface types for block-cooperative scans
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Tile status interface for reduction by key.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename ValueT,
|
||||
typename KeyT,
|
||||
bool SINGLE_WORD = (Traits<ValueT>::PRIMITIVE) && (sizeof(ValueT) + sizeof(KeyT) < 16)>
|
||||
struct ReduceByKeyScanTileState;
|
||||
|
||||
|
||||
/**
|
||||
* Tile status interface for reduction by key, specialized for scan status and value types that
|
||||
* cannot be combined into one machine word.
|
||||
*/
|
||||
template <
|
||||
typename ValueT,
|
||||
typename KeyT>
|
||||
struct ReduceByKeyScanTileState<ValueT, KeyT, false> :
|
||||
ScanTileState<KeyValuePair<KeyT, ValueT> >
|
||||
{
|
||||
typedef ScanTileState<KeyValuePair<KeyT, ValueT> > SuperClass;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
ReduceByKeyScanTileState() : SuperClass() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tile status interface for reduction by key, specialized for scan status and value types that
|
||||
* can be combined into one machine word that can be read/written coherently in a single access.
|
||||
*/
|
||||
template <
|
||||
typename ValueT,
|
||||
typename KeyT>
|
||||
struct ReduceByKeyScanTileState<ValueT, KeyT, true>
|
||||
{
|
||||
typedef KeyValuePair<KeyT, ValueT>KeyValuePairT;
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
PAIR_SIZE = sizeof(ValueT) + sizeof(KeyT),
|
||||
TXN_WORD_SIZE = 1 << Log2<PAIR_SIZE + 1>::VALUE,
|
||||
STATUS_WORD_SIZE = TXN_WORD_SIZE - PAIR_SIZE,
|
||||
|
||||
TILE_STATUS_PADDING = CUB_PTX_WARP_THREADS,
|
||||
};
|
||||
|
||||
// Status word type
|
||||
typedef typename If<(STATUS_WORD_SIZE == 8),
|
||||
long long,
|
||||
typename If<(STATUS_WORD_SIZE == 4),
|
||||
int,
|
||||
typename If<(STATUS_WORD_SIZE == 2),
|
||||
short,
|
||||
char>::Type>::Type>::Type StatusWord;
|
||||
|
||||
// Status word type
|
||||
typedef typename If<(TXN_WORD_SIZE == 16),
|
||||
longlong2,
|
||||
typename If<(TXN_WORD_SIZE == 8),
|
||||
long long,
|
||||
int>::Type>::Type TxnWord;
|
||||
|
||||
// Device word type (for when sizeof(ValueT) == sizeof(KeyT))
|
||||
struct TileDescriptorBigStatus
|
||||
{
|
||||
KeyT key;
|
||||
ValueT value;
|
||||
StatusWord status;
|
||||
};
|
||||
|
||||
// Device word type (for when sizeof(ValueT) != sizeof(KeyT))
|
||||
struct TileDescriptorLittleStatus
|
||||
{
|
||||
ValueT value;
|
||||
StatusWord status;
|
||||
KeyT key;
|
||||
};
|
||||
|
||||
// Device word type
|
||||
typedef typename If<
|
||||
(sizeof(ValueT) == sizeof(KeyT)),
|
||||
TileDescriptorBigStatus,
|
||||
TileDescriptorLittleStatus>::Type
|
||||
TileDescriptor;
|
||||
|
||||
|
||||
// Device storage
|
||||
TileDescriptor *d_tile_status;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
ReduceByKeyScanTileState()
|
||||
:
|
||||
d_tile_status(NULL)
|
||||
{}
|
||||
|
||||
|
||||
/// Initializer
|
||||
__host__ __device__ __forceinline__
|
||||
cudaError_t Init(
|
||||
int /*num_tiles*/, ///< [in] Number of tiles
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t /*temp_storage_bytes*/) ///< [in] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
d_tile_status = reinterpret_cast<TileDescriptor*>(d_temp_storage);
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute device memory needed for tile status
|
||||
*/
|
||||
__host__ __device__ __forceinline__
|
||||
static cudaError_t AllocationSize(
|
||||
int num_tiles, ///< [in] Number of tiles
|
||||
size_t &temp_storage_bytes) ///< [out] Size in bytes of \t d_temp_storage allocation
|
||||
{
|
||||
temp_storage_bytes = (num_tiles + TILE_STATUS_PADDING) * sizeof(TileDescriptor); // bytes needed for tile status descriptors
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize (from device)
|
||||
*/
|
||||
__device__ __forceinline__ void InitializeStatus(int num_tiles)
|
||||
{
|
||||
int tile_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (tile_idx < num_tiles)
|
||||
{
|
||||
// Not-yet-set
|
||||
d_tile_status[TILE_STATUS_PADDING + tile_idx].status = StatusWord(SCAN_TILE_INVALID);
|
||||
}
|
||||
|
||||
if ((blockIdx.x == 0) && (threadIdx.x < TILE_STATUS_PADDING))
|
||||
{
|
||||
// Padding
|
||||
d_tile_status[threadIdx.x].status = StatusWord(SCAN_TILE_OOB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's inclusive value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetInclusive(int tile_idx, KeyValuePairT tile_inclusive)
|
||||
{
|
||||
TileDescriptor tile_descriptor;
|
||||
tile_descriptor.status = SCAN_TILE_INCLUSIVE;
|
||||
tile_descriptor.value = tile_inclusive.value;
|
||||
tile_descriptor.key = tile_inclusive.key;
|
||||
|
||||
TxnWord alias;
|
||||
*reinterpret_cast<TileDescriptor*>(&alias) = tile_descriptor;
|
||||
ThreadStore<STORE_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx), alias);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the specified tile's partial value and corresponding status
|
||||
*/
|
||||
__device__ __forceinline__ void SetPartial(int tile_idx, KeyValuePairT tile_partial)
|
||||
{
|
||||
TileDescriptor tile_descriptor;
|
||||
tile_descriptor.status = SCAN_TILE_PARTIAL;
|
||||
tile_descriptor.value = tile_partial.value;
|
||||
tile_descriptor.key = tile_partial.key;
|
||||
|
||||
TxnWord alias;
|
||||
*reinterpret_cast<TileDescriptor*>(&alias) = tile_descriptor;
|
||||
ThreadStore<STORE_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx), alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the corresponding tile to become non-invalid
|
||||
*/
|
||||
__device__ __forceinline__ void WaitForValid(
|
||||
int tile_idx,
|
||||
StatusWord &status,
|
||||
KeyValuePairT &value)
|
||||
{
|
||||
TxnWord alias = ThreadLoad<LOAD_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx));
|
||||
TileDescriptor tile_descriptor = reinterpret_cast<TileDescriptor&>(alias);
|
||||
|
||||
while (tile_descriptor.status == SCAN_TILE_INVALID)
|
||||
{
|
||||
__threadfence_block(); // prevent hoisting loads from loop
|
||||
|
||||
alias = ThreadLoad<LOAD_CG>(reinterpret_cast<TxnWord*>(d_tile_status + TILE_STATUS_PADDING + tile_idx));
|
||||
tile_descriptor = reinterpret_cast<TileDescriptor&>(alias);
|
||||
}
|
||||
|
||||
status = tile_descriptor.status;
|
||||
value.value = tile_descriptor.value;
|
||||
value.key = tile_descriptor.key;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Prefix call-back operator for coupling local block scan within a
|
||||
* block-cooperative scan
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Stateful block-scan prefix functor. Provides the the running prefix for
|
||||
* the current tile by using the call-back warp to wait on on
|
||||
* aggregates/prefixes from predecessor tiles to become available.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
typename ScanOpT,
|
||||
typename ScanTileStateT,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
struct TilePrefixCallbackOp
|
||||
{
|
||||
// Parameterized warp reduce
|
||||
typedef WarpReduce<T, CUB_PTX_WARP_THREADS, PTX_ARCH> WarpReduceT;
|
||||
|
||||
// Temporary storage type
|
||||
struct _TempStorage
|
||||
{
|
||||
typename WarpReduceT::TempStorage warp_reduce;
|
||||
T exclusive_prefix;
|
||||
T inclusive_prefix;
|
||||
T block_aggregate;
|
||||
};
|
||||
|
||||
// Alias wrapper allowing temporary storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
// Type of status word
|
||||
typedef typename ScanTileStateT::StatusWord StatusWord;
|
||||
|
||||
// Fields
|
||||
_TempStorage& temp_storage; ///< Reference to a warp-reduction instance
|
||||
ScanTileStateT& tile_status; ///< Interface to tile status
|
||||
ScanOpT scan_op; ///< Binary scan operator
|
||||
int tile_idx; ///< The current tile index
|
||||
T exclusive_prefix; ///< Exclusive prefix for the tile
|
||||
T inclusive_prefix; ///< Inclusive prefix for the tile
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__
|
||||
TilePrefixCallbackOp(
|
||||
ScanTileStateT &tile_status,
|
||||
TempStorage &temp_storage,
|
||||
ScanOpT scan_op,
|
||||
int tile_idx)
|
||||
:
|
||||
tile_status(tile_status),
|
||||
temp_storage(temp_storage.Alias()),
|
||||
scan_op(scan_op),
|
||||
tile_idx(tile_idx) {}
|
||||
|
||||
|
||||
// Block until all predecessors within the warp-wide window have non-invalid status
|
||||
__device__ __forceinline__
|
||||
void ProcessWindow(
|
||||
int predecessor_idx, ///< Preceding tile index to inspect
|
||||
StatusWord &predecessor_status, ///< [out] Preceding tile status
|
||||
T &window_aggregate) ///< [out] Relevant partial reduction from this window of preceding tiles
|
||||
{
|
||||
T value;
|
||||
tile_status.WaitForValid(predecessor_idx, predecessor_status, value);
|
||||
|
||||
// Perform a segmented reduction to get the prefix for the current window.
|
||||
// Use the swizzled scan operator because we are now scanning *down* towards thread0.
|
||||
|
||||
int tail_flag = (predecessor_status == StatusWord(SCAN_TILE_INCLUSIVE));
|
||||
window_aggregate = WarpReduceT(temp_storage.warp_reduce).TailSegmentedReduce(
|
||||
value,
|
||||
tail_flag,
|
||||
SwizzleScanOp<ScanOpT>(scan_op));
|
||||
}
|
||||
|
||||
|
||||
// BlockScan prefix callback functor (called by the first warp)
|
||||
__device__ __forceinline__
|
||||
T operator()(T block_aggregate)
|
||||
{
|
||||
temp_storage.block_aggregate = block_aggregate;
|
||||
|
||||
// Update our status with our tile-aggregate
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
tile_status.SetPartial(tile_idx, block_aggregate);
|
||||
}
|
||||
|
||||
int predecessor_idx = tile_idx - threadIdx.x - 1;
|
||||
StatusWord predecessor_status;
|
||||
T window_aggregate;
|
||||
|
||||
// Wait for the warp-wide window of predecessor tiles to become valid
|
||||
ProcessWindow(predecessor_idx, predecessor_status, window_aggregate);
|
||||
|
||||
// The exclusive tile prefix starts out as the current window aggregate
|
||||
exclusive_prefix = window_aggregate;
|
||||
|
||||
// Keep sliding the window back until we come across a tile whose inclusive prefix is known
|
||||
while (WarpAll(predecessor_status != StatusWord(SCAN_TILE_INCLUSIVE)))
|
||||
{
|
||||
predecessor_idx -= CUB_PTX_WARP_THREADS;
|
||||
|
||||
// Update exclusive tile prefix with the window prefix
|
||||
ProcessWindow(predecessor_idx, predecessor_status, window_aggregate);
|
||||
exclusive_prefix = scan_op(window_aggregate, exclusive_prefix);
|
||||
}
|
||||
|
||||
// Compute the inclusive tile prefix and update the status for this tile
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
inclusive_prefix = scan_op(exclusive_prefix, block_aggregate);
|
||||
tile_status.SetInclusive(tile_idx, inclusive_prefix);
|
||||
|
||||
temp_storage.exclusive_prefix = exclusive_prefix;
|
||||
temp_storage.inclusive_prefix = inclusive_prefix;
|
||||
}
|
||||
|
||||
// Return exclusive_prefix
|
||||
return exclusive_prefix;
|
||||
}
|
||||
|
||||
// Get the exclusive prefix stored in temporary storage
|
||||
__device__ __forceinline__
|
||||
T GetExclusivePrefix()
|
||||
{
|
||||
return temp_storage.exclusive_prefix;
|
||||
}
|
||||
|
||||
// Get the inclusive prefix stored in temporary storage
|
||||
__device__ __forceinline__
|
||||
T GetInclusivePrefix()
|
||||
{
|
||||
return temp_storage.inclusive_prefix;
|
||||
}
|
||||
|
||||
// Get the block aggregate stored in temporary storage
|
||||
__device__ __forceinline__
|
||||
T GetBlockAggregate()
|
||||
{
|
||||
return temp_storage.block_aggregate;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockDiscontinuity class provides [<em>collective</em>](index.html#sec0) methods for flagging discontinuities within an ordered set of items partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockAdjacentDifference
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Constants and type definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
|
||||
/// Shared memory storage layout type (last element from each thread's input)
|
||||
struct _TempStorage
|
||||
{
|
||||
T first_items[BLOCK_THREADS];
|
||||
T last_items[BLOCK_THREADS];
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
/// Specialization for when FlagOp has third index param
|
||||
template <typename FlagOp, bool HAS_PARAM = BinaryOpHasIdxParam<T, FlagOp>::HAS_PARAM>
|
||||
struct ApplyOp
|
||||
{
|
||||
// Apply flag operator
|
||||
static __device__ __forceinline__ T FlagT(FlagOp flag_op, const T &a, const T &b, int idx)
|
||||
{
|
||||
return flag_op(b, a, idx);
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialization for when FlagOp does not have a third index param
|
||||
template <typename FlagOp>
|
||||
struct ApplyOp<FlagOp, false>
|
||||
{
|
||||
// Apply flag operator
|
||||
static __device__ __forceinline__ T FlagT(FlagOp flag_op, const T &a, const T &b, int /*idx*/)
|
||||
{
|
||||
return flag_op(b, a);
|
||||
}
|
||||
};
|
||||
|
||||
/// Templated unrolling of item comparison (inductive case)
|
||||
template <int ITERATION, int MAX_ITERATIONS>
|
||||
struct Iterate
|
||||
{
|
||||
// Head flags
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
static __device__ __forceinline__ void FlagHeads(
|
||||
int linear_tid,
|
||||
FlagT (&flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
T (&preds)[ITEMS_PER_THREAD], ///< [out] Calling thread's predecessor items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
preds[ITERATION] = input[ITERATION - 1];
|
||||
|
||||
flags[ITERATION] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
preds[ITERATION],
|
||||
input[ITERATION],
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITERATION);
|
||||
|
||||
Iterate<ITERATION + 1, MAX_ITERATIONS>::FlagHeads(linear_tid, flags, input, preds, flag_op);
|
||||
}
|
||||
|
||||
// Tail flags
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
static __device__ __forceinline__ void FlagTails(
|
||||
int linear_tid,
|
||||
FlagT (&flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
flags[ITERATION] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITERATION],
|
||||
input[ITERATION + 1],
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITERATION + 1);
|
||||
|
||||
Iterate<ITERATION + 1, MAX_ITERATIONS>::FlagTails(linear_tid, flags, input, flag_op);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Templated unrolling of item comparison (termination case)
|
||||
template <int MAX_ITERATIONS>
|
||||
struct Iterate<MAX_ITERATIONS, MAX_ITERATIONS>
|
||||
{
|
||||
// Head flags
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
static __device__ __forceinline__ void FlagHeads(
|
||||
int /*linear_tid*/,
|
||||
FlagT (&/*flags*/)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&/*input*/)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
T (&/*preds*/)[ITEMS_PER_THREAD], ///< [out] Calling thread's predecessor items
|
||||
FlagOp /*flag_op*/) ///< [in] Binary boolean flag predicate
|
||||
{}
|
||||
|
||||
// Tail flags
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
static __device__ __forceinline__ void FlagTails(
|
||||
int /*linear_tid*/,
|
||||
FlagT (&/*flags*/)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&/*input*/)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp /*flag_op*/) ///< [in] Binary boolean flag predicate
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// \smemstorage{BlockDiscontinuity}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockAdjacentDifference()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockAdjacentDifference(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Head flag operations
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Do not document
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeads(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
T (&preds)[ITEMS_PER_THREAD], ///< [out] Calling thread's predecessor items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share last item
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid == 0)
|
||||
{
|
||||
// Set flag for first thread-item (preds[0] is undefined)
|
||||
head_flags[0] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
preds[0] = temp_storage.last_items[linear_tid - 1];
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(flag_op, preds[0], input[0], linear_tid * ITEMS_PER_THREAD);
|
||||
}
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
}
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeads(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
T (&preds)[ITEMS_PER_THREAD], ///< [out] Calling thread's predecessor items
|
||||
FlagOp flag_op, ///< [in] Binary boolean flag predicate
|
||||
T tile_predecessor_item) ///< [in] <b>[<em>thread</em><sub>0</sub> only]</b> Item with which to compare the first tile item (<tt>input<sub>0</sub></tt> from <em>thread</em><sub>0</sub>).
|
||||
{
|
||||
// Share last item
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Set flag for first thread-item
|
||||
preds[0] = (linear_tid == 0) ?
|
||||
tile_predecessor_item : // First thread
|
||||
temp_storage.last_items[linear_tid - 1];
|
||||
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(flag_op, preds[0], input[0], linear_tid * ITEMS_PER_THREAD);
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
}
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeads(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
FlagHeads(head_flags, input, preds, flag_op);
|
||||
}
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeads(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op, ///< [in] Binary boolean flag predicate
|
||||
T tile_predecessor_item) ///< [in] <b>[<em>thread</em><sub>0</sub> only]</b> Item with which to compare the first tile item (<tt>input<sub>0</sub></tt> from <em>thread</em><sub>0</sub>).
|
||||
{
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
FlagHeads(head_flags, input, preds, flag_op, tile_predecessor_item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagTails(
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share first item
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Set flag for last thread-item
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
1 : // Last thread
|
||||
ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
temp_storage.first_items[linear_tid + 1],
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagTails(
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op, ///< [in] Binary boolean flag predicate
|
||||
T tile_successor_item) ///< [in] <b>[<em>thread</em><sub><tt>BLOCK_THREADS</tt>-1</sub> only]</b> Item with which to compare the last tile item (<tt>input</tt><sub><em>ITEMS_PER_THREAD</em>-1</sub> from <em>thread</em><sub><em>BLOCK_THREADS</em>-1</sub>).
|
||||
{
|
||||
// Share first item
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Set flag for last thread-item
|
||||
T successor_item = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
tile_successor_item : // Last thread
|
||||
temp_storage.first_items[linear_tid + 1];
|
||||
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
successor_item,
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeadsAndTails(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share first and last items
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
|
||||
// Set flag for first thread-item
|
||||
preds[0] = temp_storage.last_items[linear_tid - 1];
|
||||
if (linear_tid == 0)
|
||||
{
|
||||
head_flags[0] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
preds[0],
|
||||
input[0],
|
||||
linear_tid * ITEMS_PER_THREAD);
|
||||
}
|
||||
|
||||
|
||||
// Set flag for last thread-item
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
1 : // Last thread
|
||||
ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
temp_storage.first_items[linear_tid + 1],
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeadsAndTails(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T tile_successor_item, ///< [in] <b>[<em>thread</em><sub><tt>BLOCK_THREADS</tt>-1</sub> only]</b> Item with which to compare the last tile item (<tt>input</tt><sub><em>ITEMS_PER_THREAD</em>-1</sub> from <em>thread</em><sub><em>BLOCK_THREADS</em>-1</sub>).
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share first and last items
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
|
||||
// Set flag for first thread-item
|
||||
if (linear_tid == 0)
|
||||
{
|
||||
head_flags[0] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
preds[0] = temp_storage.last_items[linear_tid - 1];
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
preds[0],
|
||||
input[0],
|
||||
linear_tid * ITEMS_PER_THREAD);
|
||||
}
|
||||
|
||||
// Set flag for last thread-item
|
||||
T successor_item = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
tile_successor_item : // Last thread
|
||||
temp_storage.first_items[linear_tid + 1];
|
||||
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
successor_item,
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeadsAndTails(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T tile_predecessor_item, ///< [in] <b>[<em>thread</em><sub>0</sub> only]</b> Item with which to compare the first tile item (<tt>input<sub>0</sub></tt> from <em>thread</em><sub>0</sub>).
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share first and last items
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
|
||||
// Set flag for first thread-item
|
||||
preds[0] = (linear_tid == 0) ?
|
||||
tile_predecessor_item : // First thread
|
||||
temp_storage.last_items[linear_tid - 1];
|
||||
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
preds[0],
|
||||
input[0],
|
||||
linear_tid * ITEMS_PER_THREAD);
|
||||
|
||||
// Set flag for last thread-item
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
1 : // Last thread
|
||||
ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
temp_storage.first_items[linear_tid + 1],
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename FlagT,
|
||||
typename FlagOp>
|
||||
__device__ __forceinline__ void FlagHeadsAndTails(
|
||||
FlagT (&head_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity head_flags
|
||||
T tile_predecessor_item, ///< [in] <b>[<em>thread</em><sub>0</sub> only]</b> Item with which to compare the first tile item (<tt>input<sub>0</sub></tt> from <em>thread</em><sub>0</sub>).
|
||||
FlagT (&tail_flags)[ITEMS_PER_THREAD], ///< [out] Calling thread's discontinuity tail_flags
|
||||
T tile_successor_item, ///< [in] <b>[<em>thread</em><sub><tt>BLOCK_THREADS</tt>-1</sub> only]</b> Item with which to compare the last tile item (<tt>input</tt><sub><em>ITEMS_PER_THREAD</em>-1</sub> from <em>thread</em><sub><em>BLOCK_THREADS</em>-1</sub>).
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
|
||||
FlagOp flag_op) ///< [in] Binary boolean flag predicate
|
||||
{
|
||||
// Share first and last items
|
||||
temp_storage.first_items[linear_tid] = input[0];
|
||||
temp_storage.last_items[linear_tid] = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
T preds[ITEMS_PER_THREAD];
|
||||
|
||||
// Set flag for first thread-item
|
||||
preds[0] = (linear_tid == 0) ?
|
||||
tile_predecessor_item : // First thread
|
||||
temp_storage.last_items[linear_tid - 1];
|
||||
|
||||
head_flags[0] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
preds[0],
|
||||
input[0],
|
||||
linear_tid * ITEMS_PER_THREAD);
|
||||
|
||||
// Set flag for last thread-item
|
||||
T successor_item = (linear_tid == BLOCK_THREADS - 1) ?
|
||||
tile_successor_item : // Last thread
|
||||
temp_storage.first_items[linear_tid + 1];
|
||||
|
||||
tail_flags[ITEMS_PER_THREAD - 1] = ApplyOp<FlagOp>::FlagT(
|
||||
flag_op,
|
||||
input[ITEMS_PER_THREAD - 1],
|
||||
successor_item,
|
||||
(linear_tid * ITEMS_PER_THREAD) + ITEMS_PER_THREAD);
|
||||
|
||||
// Set head_flags for remaining items
|
||||
Iterate<1, ITEMS_PER_THREAD>::FlagHeads(linear_tid, head_flags, input, preds, flag_op);
|
||||
|
||||
// Set tail_flags for remaining items
|
||||
Iterate<0, ITEMS_PER_THREAD - 1>::FlagTails(linear_tid, tail_flags, input, flag_op);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,415 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockHistogram class provides [<em>collective</em>](index.html#sec0) methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "specializations/block_histogram_sort.cuh"
|
||||
#include "specializations/block_histogram_atomic.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Algorithmic variants
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief BlockHistogramAlgorithm enumerates alternative algorithms for the parallel construction of block-wide histograms.
|
||||
*/
|
||||
enum BlockHistogramAlgorithm
|
||||
{
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* Sorting followed by differentiation. Execution is comprised of two phases:
|
||||
* -# Sort the data using efficient radix sort
|
||||
* -# Look for "runs" of same-valued keys by detecting discontinuities; the run-lengths are histogram bin counts.
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* Delivers consistent throughput regardless of sample bin distribution.
|
||||
*/
|
||||
BLOCK_HISTO_SORT,
|
||||
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* Use atomic addition to update byte counts directly
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* Performance is strongly tied to the hardware implementation of atomic
|
||||
* addition, and may be significantly degraded for non uniformly-random
|
||||
* input distributions where many concurrent updates are likely to be
|
||||
* made to the same bin counter.
|
||||
*/
|
||||
BLOCK_HISTO_ATOMIC,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Block histogram
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* \brief The BlockHistogram class provides [<em>collective</em>](index.html#sec0) methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block. 
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \tparam T The sample type being histogrammed (must be castable to an integer bin identifier)
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam ITEMS_PER_THREAD The number of items per thread
|
||||
* \tparam BINS The number bins within the histogram
|
||||
* \tparam ALGORITHM <b>[optional]</b> cub::BlockHistogramAlgorithm enumerator specifying the underlying algorithm to use (default: cub::BLOCK_HISTO_SORT)
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* - A <a href="http://en.wikipedia.org/wiki/Histogram"><em>histogram</em></a>
|
||||
* counts the number of observations that fall into each of the disjoint categories (known as <em>bins</em>).
|
||||
* - BlockHistogram can be optionally specialized to use different algorithms:
|
||||
* -# <b>cub::BLOCK_HISTO_SORT</b>. Sorting followed by differentiation. [More...](\ref cub::BlockHistogramAlgorithm)
|
||||
* -# <b>cub::BLOCK_HISTO_ATOMIC</b>. Use atomic addition to update byte counts directly. [More...](\ref cub::BlockHistogramAlgorithm)
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - \granularity
|
||||
*
|
||||
* \par A Simple Example
|
||||
* \blockcollective{BlockHistogram}
|
||||
* \par
|
||||
* The code snippet below illustrates a 256-bin histogram of 512 integer samples that
|
||||
* are partitioned across 128 threads where each thread owns 4 samples.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_histogram.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize a 256-bin BlockHistogram type for a 1D block of 128 threads having 4 character samples each
|
||||
* typedef cub::BlockHistogram<unsigned char, 128, 4, 256> BlockHistogram;
|
||||
*
|
||||
* // Allocate shared memory for BlockHistogram
|
||||
* __shared__ typename BlockHistogram::TempStorage temp_storage;
|
||||
*
|
||||
* // Allocate shared memory for block-wide histogram bin counts
|
||||
* __shared__ unsigned int smem_histogram[256];
|
||||
*
|
||||
* // Obtain input samples per thread
|
||||
* unsigned char data[4];
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide histogram
|
||||
* BlockHistogram(temp_storage).Histogram(data, smem_histogram);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \par Performance and Usage Considerations
|
||||
* - The histogram output can be constructed in shared or device-accessible memory
|
||||
* - See cub::BlockHistogramAlgorithm for performance details regarding algorithmic alternatives
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X,
|
||||
int ITEMS_PER_THREAD,
|
||||
int BINS,
|
||||
BlockHistogramAlgorithm ALGORITHM = BLOCK_HISTO_SORT,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockHistogram
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Constants and type definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure the template parameterization meets the requirements of the
|
||||
* targeted device architecture. BLOCK_HISTO_ATOMIC can only be used
|
||||
* on version SM120 or later. Otherwise BLOCK_HISTO_SORT is used
|
||||
* regardless.
|
||||
*/
|
||||
static const BlockHistogramAlgorithm SAFE_ALGORITHM =
|
||||
((ALGORITHM == BLOCK_HISTO_ATOMIC) && (PTX_ARCH < 120)) ?
|
||||
BLOCK_HISTO_SORT :
|
||||
ALGORITHM;
|
||||
|
||||
/// Internal specialization.
|
||||
typedef typename If<(SAFE_ALGORITHM == BLOCK_HISTO_SORT),
|
||||
BlockHistogramSort<T, BLOCK_DIM_X, ITEMS_PER_THREAD, BINS, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH>,
|
||||
BlockHistogramAtomic<BINS> >::Type InternalBlockHistogram;
|
||||
|
||||
/// Shared memory storage layout type for BlockHistogram
|
||||
typedef typename InternalBlockHistogram::TempStorage _TempStorage;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// \smemstorage{BlockHistogram}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockHistogram()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockHistogram(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Histogram operations
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize the shared histogram counters to zero.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a the initialization and update of a
|
||||
* histogram of 512 integer samples that are partitioned across 128 threads
|
||||
* where each thread owns 4 samples.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_histogram.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize a 256-bin BlockHistogram type for a 1D block of 128 threads having 4 character samples each
|
||||
* typedef cub::BlockHistogram<unsigned char, 128, 4, 256> BlockHistogram;
|
||||
*
|
||||
* // Allocate shared memory for BlockHistogram
|
||||
* __shared__ typename BlockHistogram::TempStorage temp_storage;
|
||||
*
|
||||
* // Allocate shared memory for block-wide histogram bin counts
|
||||
* __shared__ unsigned int smem_histogram[256];
|
||||
*
|
||||
* // Obtain input samples per thread
|
||||
* unsigned char thread_samples[4];
|
||||
* ...
|
||||
*
|
||||
* // Initialize the block-wide histogram
|
||||
* BlockHistogram(temp_storage).InitHistogram(smem_histogram);
|
||||
*
|
||||
* // Update the block-wide histogram
|
||||
* BlockHistogram(temp_storage).Composite(thread_samples, smem_histogram);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam CounterT <b>[inferred]</b> Histogram counter type
|
||||
*/
|
||||
template <typename CounterT >
|
||||
__device__ __forceinline__ void InitHistogram(CounterT histogram[BINS])
|
||||
{
|
||||
// Initialize histogram bin counts to zeros
|
||||
int histo_offset = 0;
|
||||
|
||||
#pragma unroll
|
||||
for(; histo_offset + BLOCK_THREADS <= BINS; histo_offset += BLOCK_THREADS)
|
||||
{
|
||||
histogram[histo_offset + linear_tid] = 0;
|
||||
}
|
||||
// Finish up with guarded initialization if necessary
|
||||
if ((BINS % BLOCK_THREADS != 0) && (histo_offset + linear_tid < BINS))
|
||||
{
|
||||
histogram[histo_offset + linear_tid] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Constructs a block-wide histogram in shared/device-accessible memory. Each thread contributes an array of input elements.
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a 256-bin histogram of 512 integer samples that
|
||||
* are partitioned across 128 threads where each thread owns 4 samples.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_histogram.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize a 256-bin BlockHistogram type for a 1D block of 128 threads having 4 character samples each
|
||||
* typedef cub::BlockHistogram<unsigned char, 128, 4, 256> BlockHistogram;
|
||||
*
|
||||
* // Allocate shared memory for BlockHistogram
|
||||
* __shared__ typename BlockHistogram::TempStorage temp_storage;
|
||||
*
|
||||
* // Allocate shared memory for block-wide histogram bin counts
|
||||
* __shared__ unsigned int smem_histogram[256];
|
||||
*
|
||||
* // Obtain input samples per thread
|
||||
* unsigned char thread_samples[4];
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide histogram
|
||||
* BlockHistogram(temp_storage).Histogram(thread_samples, smem_histogram);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam CounterT <b>[inferred]</b> Histogram counter type
|
||||
*/
|
||||
template <
|
||||
typename CounterT >
|
||||
__device__ __forceinline__ void Histogram(
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Calling thread's input values to histogram
|
||||
CounterT histogram[BINS]) ///< [out] Reference to shared/device-accessible memory histogram
|
||||
{
|
||||
// Initialize histogram bin counts to zeros
|
||||
InitHistogram(histogram);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Composite the histogram
|
||||
InternalBlockHistogram(temp_storage).Composite(items, histogram);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Updates an existing block-wide histogram in shared/device-accessible memory. Each thread composites an array of input elements.
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a the initialization and update of a
|
||||
* histogram of 512 integer samples that are partitioned across 128 threads
|
||||
* where each thread owns 4 samples.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_histogram.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize a 256-bin BlockHistogram type for a 1D block of 128 threads having 4 character samples each
|
||||
* typedef cub::BlockHistogram<unsigned char, 128, 4, 256> BlockHistogram;
|
||||
*
|
||||
* // Allocate shared memory for BlockHistogram
|
||||
* __shared__ typename BlockHistogram::TempStorage temp_storage;
|
||||
*
|
||||
* // Allocate shared memory for block-wide histogram bin counts
|
||||
* __shared__ unsigned int smem_histogram[256];
|
||||
*
|
||||
* // Obtain input samples per thread
|
||||
* unsigned char thread_samples[4];
|
||||
* ...
|
||||
*
|
||||
* // Initialize the block-wide histogram
|
||||
* BlockHistogram(temp_storage).InitHistogram(smem_histogram);
|
||||
*
|
||||
* // Update the block-wide histogram
|
||||
* BlockHistogram(temp_storage).Composite(thread_samples, smem_histogram);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam CounterT <b>[inferred]</b> Histogram counter type
|
||||
*/
|
||||
template <
|
||||
typename CounterT >
|
||||
__device__ __forceinline__ void Composite(
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Calling thread's input values to histogram
|
||||
CounterT histogram[BINS]) ///< [out] Reference to shared/device-accessible memory histogram
|
||||
{
|
||||
InternalBlockHistogram(temp_storage).Composite(items, histogram);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,432 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockRadixRank provides operations for ranking unsigned integer types within a CUDA threadblock
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../thread/thread_reduce.cuh"
|
||||
#include "../thread/thread_scan.cuh"
|
||||
#include "../block/block_scan.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief BlockRadixRank provides operations for ranking unsigned integer types within a CUDA threadblock.
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam RADIX_BITS The number of radix bits per digit place
|
||||
* \tparam DESCENDING Whether or not the sorted-order is high-to-low
|
||||
* \tparam MEMOIZE_OUTER_SCAN <b>[optional]</b> Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure (default: true for architectures SM35 and newer, false otherwise). See BlockScanAlgorithm::BLOCK_SCAN_RAKING_MEMOIZE for more details.
|
||||
* \tparam INNER_SCAN_ALGORITHM <b>[optional]</b> The cub::BlockScanAlgorithm algorithm to use (default: cub::BLOCK_SCAN_WARP_SCANS)
|
||||
* \tparam SMEM_CONFIG <b>[optional]</b> Shared memory bank mode (default: \p cudaSharedMemBankSizeFourByte)
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* Blah...
|
||||
* - Keys must be in a form suitable for radix ranking (i.e., unsigned bits).
|
||||
* - \blocked
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - \granularity
|
||||
*
|
||||
* \par Examples
|
||||
* \par
|
||||
* - <b>Example 1:</b> Simple radix rank of 32-bit integer keys
|
||||
* \code
|
||||
* #include <cub/cub.cuh>
|
||||
*
|
||||
* template <int BLOCK_THREADS>
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
*
|
||||
* \endcode
|
||||
*/
|
||||
template <
|
||||
int BLOCK_DIM_X,
|
||||
int RADIX_BITS,
|
||||
bool DESCENDING,
|
||||
bool MEMOIZE_OUTER_SCAN = (CUB_PTX_ARCH >= 350) ? true : false,
|
||||
BlockScanAlgorithm INNER_SCAN_ALGORITHM = BLOCK_SCAN_WARP_SCANS,
|
||||
cudaSharedMemConfig SMEM_CONFIG = cudaSharedMemBankSizeFourByte,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockRadixRank
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Type definitions and constants
|
||||
******************************************************************************/
|
||||
|
||||
// Integer type for digit counters (to be packed into words of type PackedCounters)
|
||||
typedef unsigned short DigitCounter;
|
||||
|
||||
// Integer type for packing DigitCounters into columns of shared memory banks
|
||||
typedef typename If<(SMEM_CONFIG == cudaSharedMemBankSizeEightByte),
|
||||
unsigned long long,
|
||||
unsigned int>::Type PackedCounter;
|
||||
|
||||
enum
|
||||
{
|
||||
// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
RADIX_DIGITS = 1 << RADIX_BITS,
|
||||
|
||||
LOG_WARP_THREADS = CUB_LOG_WARP_THREADS(PTX_ARCH),
|
||||
WARP_THREADS = 1 << LOG_WARP_THREADS,
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
|
||||
BYTES_PER_COUNTER = sizeof(DigitCounter),
|
||||
LOG_BYTES_PER_COUNTER = Log2<BYTES_PER_COUNTER>::VALUE,
|
||||
|
||||
PACKING_RATIO = sizeof(PackedCounter) / sizeof(DigitCounter),
|
||||
LOG_PACKING_RATIO = Log2<PACKING_RATIO>::VALUE,
|
||||
|
||||
LOG_COUNTER_LANES = CUB_MAX((RADIX_BITS - LOG_PACKING_RATIO), 0), // Always at least one lane
|
||||
COUNTER_LANES = 1 << LOG_COUNTER_LANES,
|
||||
|
||||
// The number of packed counters per thread (plus one for padding)
|
||||
PADDED_COUNTER_LANES = COUNTER_LANES + 1,
|
||||
RAKING_SEGMENT = PADDED_COUNTER_LANES,
|
||||
|
||||
LOG_SMEM_BANKS = CUB_LOG_SMEM_BANKS(PTX_ARCH),
|
||||
SMEM_BANKS = 1 << LOG_SMEM_BANKS,
|
||||
};
|
||||
|
||||
|
||||
/// BlockScan type
|
||||
typedef BlockScan<
|
||||
PackedCounter,
|
||||
BLOCK_DIM_X,
|
||||
INNER_SCAN_ALGORITHM,
|
||||
BLOCK_DIM_Y,
|
||||
BLOCK_DIM_Z,
|
||||
PTX_ARCH>
|
||||
BlockScan;
|
||||
|
||||
|
||||
/// Shared memory storage layout type for BlockRadixRank
|
||||
struct __align__(16) _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
DigitCounter digit_counters[PADDED_COUNTER_LANES][BLOCK_THREADS][PACKING_RATIO];
|
||||
PackedCounter raking_grid[BLOCK_THREADS][RAKING_SEGMENT];
|
||||
};
|
||||
|
||||
// Storage for scanning local ranks
|
||||
typename BlockScan::TempStorage block_scan;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
/// Copy of raking segment, promoted to registers
|
||||
PackedCounter cached_segment[RAKING_SEGMENT];
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Internal storage allocator
|
||||
*/
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs upsweep raking reduction, returning the aggregate
|
||||
*/
|
||||
__device__ __forceinline__ PackedCounter Upsweep()
|
||||
{
|
||||
PackedCounter *smem_raking_ptr = temp_storage.raking_grid[linear_tid];
|
||||
PackedCounter *raking_ptr;
|
||||
|
||||
if (MEMOIZE_OUTER_SCAN)
|
||||
{
|
||||
// Copy data into registers
|
||||
#pragma unroll
|
||||
for (int i = 0; i < RAKING_SEGMENT; i++)
|
||||
{
|
||||
cached_segment[i] = smem_raking_ptr[i];
|
||||
}
|
||||
raking_ptr = cached_segment;
|
||||
}
|
||||
else
|
||||
{
|
||||
raking_ptr = smem_raking_ptr;
|
||||
}
|
||||
|
||||
return ThreadReduce<RAKING_SEGMENT>(raking_ptr, Sum());
|
||||
}
|
||||
|
||||
|
||||
/// Performs exclusive downsweep raking scan
|
||||
__device__ __forceinline__ void ExclusiveDownsweep(
|
||||
PackedCounter raking_partial)
|
||||
{
|
||||
PackedCounter *smem_raking_ptr = temp_storage.raking_grid[linear_tid];
|
||||
|
||||
PackedCounter *raking_ptr = (MEMOIZE_OUTER_SCAN) ?
|
||||
cached_segment :
|
||||
smem_raking_ptr;
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ThreadScanExclusive<RAKING_SEGMENT>(raking_ptr, raking_ptr, Sum(), raking_partial);
|
||||
|
||||
if (MEMOIZE_OUTER_SCAN)
|
||||
{
|
||||
// Copy data back to smem
|
||||
#pragma unroll
|
||||
for (int i = 0; i < RAKING_SEGMENT; i++)
|
||||
{
|
||||
smem_raking_ptr[i] = cached_segment[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset shared memory digit counters
|
||||
*/
|
||||
__device__ __forceinline__ void ResetCounters()
|
||||
{
|
||||
// Reset shared memory digit counters
|
||||
#pragma unroll
|
||||
for (int LANE = 0; LANE < PADDED_COUNTER_LANES; LANE++)
|
||||
{
|
||||
*((PackedCounter*) temp_storage.digit_counters[LANE][linear_tid]) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Block-scan prefix callback
|
||||
*/
|
||||
struct PrefixCallBack
|
||||
{
|
||||
__device__ __forceinline__ PackedCounter operator()(PackedCounter block_aggregate)
|
||||
{
|
||||
PackedCounter block_prefix = 0;
|
||||
|
||||
// Propagate totals in packed fields
|
||||
#pragma unroll
|
||||
for (int PACKED = 1; PACKED < PACKING_RATIO; PACKED++)
|
||||
{
|
||||
block_prefix += block_aggregate << (sizeof(DigitCounter) * 8 * PACKED);
|
||||
}
|
||||
|
||||
return block_prefix;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Scan shared memory digit counters.
|
||||
*/
|
||||
__device__ __forceinline__ void ScanCounters()
|
||||
{
|
||||
// Upsweep scan
|
||||
PackedCounter raking_partial = Upsweep();
|
||||
|
||||
// Compute exclusive sum
|
||||
PackedCounter exclusive_partial;
|
||||
PrefixCallBack prefix_call_back;
|
||||
BlockScan(temp_storage.block_scan).ExclusiveSum(raking_partial, exclusive_partial, prefix_call_back);
|
||||
|
||||
// Downsweep scan with exclusive partial
|
||||
ExclusiveDownsweep(exclusive_partial);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// \smemstorage{BlockScan}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockRadixRank()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockRadixRank(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Raking
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Rank keys.
|
||||
*/
|
||||
template <
|
||||
typename UnsignedBits,
|
||||
int KEYS_PER_THREAD>
|
||||
__device__ __forceinline__ void RankKeys(
|
||||
UnsignedBits (&keys)[KEYS_PER_THREAD], ///< [in] Keys for this tile
|
||||
int (&ranks)[KEYS_PER_THREAD], ///< [out] For each key, the local rank within the tile
|
||||
int current_bit, ///< [in] The least-significant bit position of the current digit to extract
|
||||
int num_bits) ///< [in] The number of bits in the current digit
|
||||
{
|
||||
DigitCounter thread_prefixes[KEYS_PER_THREAD]; // For each key, the count of previous keys in this tile having the same digit
|
||||
DigitCounter* digit_counters[KEYS_PER_THREAD]; // For each key, the byte-offset of its corresponding digit counter in smem
|
||||
|
||||
// Reset shared memory digit counters
|
||||
ResetCounters();
|
||||
|
||||
for (int ITEM = 0; ITEM < KEYS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
// Get digit
|
||||
unsigned int digit = BFE(keys[ITEM], current_bit, num_bits);
|
||||
|
||||
// Get sub-counter
|
||||
unsigned int sub_counter = digit >> LOG_COUNTER_LANES;
|
||||
|
||||
// Get counter lane
|
||||
unsigned int counter_lane = digit & (COUNTER_LANES - 1);
|
||||
|
||||
if (DESCENDING)
|
||||
{
|
||||
sub_counter = PACKING_RATIO - 1 - sub_counter;
|
||||
counter_lane = COUNTER_LANES - 1 - counter_lane;
|
||||
}
|
||||
|
||||
// Pointer to smem digit counter
|
||||
digit_counters[ITEM] = &temp_storage.digit_counters[counter_lane][linear_tid][sub_counter];
|
||||
|
||||
// Load thread-exclusive prefix
|
||||
thread_prefixes[ITEM] = *digit_counters[ITEM];
|
||||
|
||||
// Store inclusive prefix
|
||||
*digit_counters[ITEM] = thread_prefixes[ITEM] + 1;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Scan shared memory counters
|
||||
ScanCounters();
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Extract the local ranks of each key
|
||||
for (int ITEM = 0; ITEM < KEYS_PER_THREAD; ++ITEM)
|
||||
{
|
||||
// Add in threadblock exclusive prefix
|
||||
ranks[ITEM] = thread_prefixes[ITEM] + *digit_counters[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Rank keys. For the lower \p RADIX_DIGITS threads, digit counts for each digit are provided for the corresponding thread.
|
||||
*/
|
||||
template <
|
||||
typename UnsignedBits,
|
||||
int KEYS_PER_THREAD>
|
||||
__device__ __forceinline__ void RankKeys(
|
||||
UnsignedBits (&keys)[KEYS_PER_THREAD], ///< [in] Keys for this tile
|
||||
int (&ranks)[KEYS_PER_THREAD], ///< [out] For each key, the local rank within the tile (out parameter)
|
||||
int current_bit, ///< [in] The least-significant bit position of the current digit to extract
|
||||
int num_bits, ///< [in] The number of bits in the current digit
|
||||
int &exclusive_digit_prefix) ///< [out] The exclusive prefix sum for the digit threadIdx.x
|
||||
{
|
||||
// Rank keys
|
||||
RankKeys(keys, ranks, current_bit, num_bits);
|
||||
|
||||
// Get the inclusive and exclusive digit totals corresponding to the calling thread.
|
||||
if ((BLOCK_THREADS == RADIX_DIGITS) || (linear_tid < RADIX_DIGITS))
|
||||
{
|
||||
unsigned int bin_idx = (DESCENDING) ?
|
||||
RADIX_DIGITS - linear_tid - 1 :
|
||||
linear_tid;
|
||||
|
||||
// Obtain ex/inclusive digit counts. (Unfortunately these all reside in the
|
||||
// first counter column, resulting in unavoidable bank conflicts.)
|
||||
unsigned int counter_lane = (bin_idx & (COUNTER_LANES - 1));
|
||||
unsigned int sub_counter = bin_idx >> (LOG_COUNTER_LANES);
|
||||
|
||||
exclusive_digit_prefix = temp_storage.digit_counters[counter_lane][0][sub_counter];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,865 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockRadixSort class provides [<em>collective</em>](index.html#sec0) methods for radix sorting of items partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "block_exchange.cuh"
|
||||
#include "block_radix_rank.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief The BlockRadixSort class provides [<em>collective</em>](index.html#sec0) methods for sorting items partitioned across a CUDA thread block using a radix sorting method. 
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \tparam KeyT KeyT type
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam ITEMS_PER_THREAD The number of items per thread
|
||||
* \tparam ValueT <b>[optional]</b> ValueT type (default: cub::NullType, which indicates a keys-only sort)
|
||||
* \tparam RADIX_BITS <b>[optional]</b> The number of radix bits per digit place (default: 4 bits)
|
||||
* \tparam MEMOIZE_OUTER_SCAN <b>[optional]</b> Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure (default: true for architectures SM35 and newer, false otherwise).
|
||||
* \tparam INNER_SCAN_ALGORITHM <b>[optional]</b> The cub::BlockScanAlgorithm algorithm to use (default: cub::BLOCK_SCAN_WARP_SCANS)
|
||||
* \tparam SMEM_CONFIG <b>[optional]</b> Shared memory bank mode (default: \p cudaSharedMemBankSizeFourByte)
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* - The [<em>radix sorting method</em>](http://en.wikipedia.org/wiki/Radix_sort) arranges
|
||||
* items into ascending order. It relies upon a positional representation for
|
||||
* keys, i.e., each key is comprised of an ordered sequence of symbols (e.g., digits,
|
||||
* characters, etc.) specified from least-significant to most-significant. For a
|
||||
* given input sequence of keys and a set of rules specifying a total ordering
|
||||
* of the symbolic alphabet, the radix sorting method produces a lexicographic
|
||||
* ordering of those keys.
|
||||
* - BlockRadixSort can sort all of the built-in C++ numeric primitive types, e.g.:
|
||||
* <tt>unsigned char</tt>, \p int, \p double, etc. Within each key, the implementation treats fixed-length
|
||||
* bit-sequences of \p RADIX_BITS as radix digit places. Although the direct radix sorting
|
||||
* method can only be applied to unsigned integral types, BlockRadixSort
|
||||
* is able to sort signed and floating-point types via simple bit-wise transformations
|
||||
* that ensure lexicographic key ordering.
|
||||
* - \rowmajor
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - \granularity
|
||||
*
|
||||
* \par A Simple Example
|
||||
* \blockcollective{BlockRadixSort}
|
||||
* \par
|
||||
* The code snippet below illustrates a sort of 512 integer keys that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive items.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer items each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys
|
||||
* BlockRadixSort(temp_storage).Sort(thread_keys);
|
||||
*
|
||||
* ...
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], [8,9,10,11], ..., [508,509,510,511] }</tt>.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
int BLOCK_DIM_X,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename ValueT = NullType,
|
||||
int RADIX_BITS = 4,
|
||||
bool MEMOIZE_OUTER_SCAN = (CUB_PTX_ARCH >= 350) ? true : false,
|
||||
BlockScanAlgorithm INNER_SCAN_ALGORITHM = BLOCK_SCAN_WARP_SCANS,
|
||||
cudaSharedMemConfig SMEM_CONFIG = cudaSharedMemBankSizeFourByte,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockRadixSort
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Constants and type definitions
|
||||
******************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
// Whether or not there are values to be trucked along with keys
|
||||
KEYS_ONLY = Equals<ValueT, NullType>::VALUE,
|
||||
};
|
||||
|
||||
// KeyT traits and unsigned bits type
|
||||
typedef Traits<KeyT> KeyTraits;
|
||||
typedef typename KeyTraits::UnsignedBits UnsignedBits;
|
||||
|
||||
/// Ascending BlockRadixRank utility type
|
||||
typedef BlockRadixRank<
|
||||
BLOCK_DIM_X,
|
||||
RADIX_BITS,
|
||||
false,
|
||||
MEMOIZE_OUTER_SCAN,
|
||||
INNER_SCAN_ALGORITHM,
|
||||
SMEM_CONFIG,
|
||||
BLOCK_DIM_Y,
|
||||
BLOCK_DIM_Z,
|
||||
PTX_ARCH>
|
||||
AscendingBlockRadixRank;
|
||||
|
||||
/// Descending BlockRadixRank utility type
|
||||
typedef BlockRadixRank<
|
||||
BLOCK_DIM_X,
|
||||
RADIX_BITS,
|
||||
true,
|
||||
MEMOIZE_OUTER_SCAN,
|
||||
INNER_SCAN_ALGORITHM,
|
||||
SMEM_CONFIG,
|
||||
BLOCK_DIM_Y,
|
||||
BLOCK_DIM_Z,
|
||||
PTX_ARCH>
|
||||
DescendingBlockRadixRank;
|
||||
|
||||
/// BlockExchange utility type for keys
|
||||
typedef BlockExchange<KeyT, BLOCK_DIM_X, ITEMS_PER_THREAD, false, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> BlockExchangeKeys;
|
||||
|
||||
/// BlockExchange utility type for values
|
||||
typedef BlockExchange<ValueT, BLOCK_DIM_X, ITEMS_PER_THREAD, false, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> BlockExchangeValues;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
typename AscendingBlockRadixRank::TempStorage asending_ranking_storage;
|
||||
typename DescendingBlockRadixRank::TempStorage descending_ranking_storage;
|
||||
typename BlockExchangeKeys::TempStorage exchange_keys;
|
||||
typename BlockExchangeValues::TempStorage exchange_values;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
/// Rank keys (specialized for ascending sort)
|
||||
__device__ __forceinline__ void RankKeys(
|
||||
UnsignedBits (&unsigned_keys)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
int begin_bit,
|
||||
int pass_bits,
|
||||
Int2Type<false> /*is_descending*/)
|
||||
{
|
||||
AscendingBlockRadixRank(temp_storage.asending_ranking_storage).RankKeys(
|
||||
unsigned_keys,
|
||||
ranks,
|
||||
begin_bit,
|
||||
pass_bits);
|
||||
}
|
||||
|
||||
/// Rank keys (specialized for descending sort)
|
||||
__device__ __forceinline__ void RankKeys(
|
||||
UnsignedBits (&unsigned_keys)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
int begin_bit,
|
||||
int pass_bits,
|
||||
Int2Type<true> /*is_descending*/)
|
||||
{
|
||||
DescendingBlockRadixRank(temp_storage.descending_ranking_storage).RankKeys(
|
||||
unsigned_keys,
|
||||
ranks,
|
||||
begin_bit,
|
||||
pass_bits);
|
||||
}
|
||||
|
||||
/// ExchangeValues (specialized for key-value sort, to-blocked arrangement)
|
||||
__device__ __forceinline__ void ExchangeValues(
|
||||
ValueT (&values)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
Int2Type<false> /*is_keys_only*/,
|
||||
Int2Type<true> /*is_blocked*/)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Exchange values through shared memory in blocked arrangement
|
||||
BlockExchangeValues(temp_storage.exchange_values).ScatterToBlocked(values, ranks);
|
||||
}
|
||||
|
||||
/// ExchangeValues (specialized for key-value sort, to-striped arrangement)
|
||||
__device__ __forceinline__ void ExchangeValues(
|
||||
ValueT (&values)[ITEMS_PER_THREAD],
|
||||
int (&ranks)[ITEMS_PER_THREAD],
|
||||
Int2Type<false> /*is_keys_only*/,
|
||||
Int2Type<false> /*is_blocked*/)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Exchange values through shared memory in blocked arrangement
|
||||
BlockExchangeValues(temp_storage.exchange_values).ScatterToStriped(values, ranks);
|
||||
}
|
||||
|
||||
/// ExchangeValues (specialized for keys-only sort)
|
||||
template <int IS_BLOCKED>
|
||||
__device__ __forceinline__ void ExchangeValues(
|
||||
ValueT (&/*values*/)[ITEMS_PER_THREAD],
|
||||
int (&/*ranks*/)[ITEMS_PER_THREAD],
|
||||
Int2Type<true> /*is_keys_only*/,
|
||||
Int2Type<IS_BLOCKED> /*is_blocked*/)
|
||||
{}
|
||||
|
||||
/// Sort blocked arrangement
|
||||
template <int DESCENDING, int KEYS_ONLY>
|
||||
__device__ __forceinline__ void SortBlocked(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< Values to sort
|
||||
int begin_bit, ///< The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit, ///< The past-the-end (most-significant) bit index needed for key comparison
|
||||
Int2Type<DESCENDING> is_descending, ///< Tag whether is a descending-order sort
|
||||
Int2Type<KEYS_ONLY> is_keys_only) ///< Tag whether is keys-only sort
|
||||
{
|
||||
UnsignedBits (&unsigned_keys)[ITEMS_PER_THREAD] =
|
||||
reinterpret_cast<UnsignedBits (&)[ITEMS_PER_THREAD]>(keys);
|
||||
|
||||
// Twiddle bits if necessary
|
||||
#pragma unroll
|
||||
for (int KEY = 0; KEY < ITEMS_PER_THREAD; KEY++)
|
||||
{
|
||||
unsigned_keys[KEY] = KeyTraits::TwiddleIn(unsigned_keys[KEY]);
|
||||
}
|
||||
|
||||
// Radix sorting passes
|
||||
while (true)
|
||||
{
|
||||
int pass_bits = CUB_MIN(RADIX_BITS, end_bit - begin_bit);
|
||||
|
||||
// Rank the blocked keys
|
||||
int ranks[ITEMS_PER_THREAD];
|
||||
RankKeys(unsigned_keys, ranks, begin_bit, pass_bits, is_descending);
|
||||
begin_bit += RADIX_BITS;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Exchange keys through shared memory in blocked arrangement
|
||||
BlockExchangeKeys(temp_storage.exchange_keys).ScatterToBlocked(keys, ranks);
|
||||
|
||||
// Exchange values through shared memory in blocked arrangement
|
||||
ExchangeValues(values, ranks, is_keys_only, Int2Type<true>());
|
||||
|
||||
// Quit if done
|
||||
if (begin_bit >= end_bit) break;
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Untwiddle bits if necessary
|
||||
#pragma unroll
|
||||
for (int KEY = 0; KEY < ITEMS_PER_THREAD; KEY++)
|
||||
{
|
||||
unsigned_keys[KEY] = KeyTraits::TwiddleOut(unsigned_keys[KEY]);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Do not document
|
||||
|
||||
/// Sort blocked -> striped arrangement
|
||||
template <int DESCENDING, int KEYS_ONLY>
|
||||
__device__ __forceinline__ void SortBlockedToStriped(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< Values to sort
|
||||
int begin_bit, ///< The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit, ///< The past-the-end (most-significant) bit index needed for key comparison
|
||||
Int2Type<DESCENDING> is_descending, ///< Tag whether is a descending-order sort
|
||||
Int2Type<KEYS_ONLY> is_keys_only) ///< Tag whether is keys-only sort
|
||||
{
|
||||
UnsignedBits (&unsigned_keys)[ITEMS_PER_THREAD] =
|
||||
reinterpret_cast<UnsignedBits (&)[ITEMS_PER_THREAD]>(keys);
|
||||
|
||||
// Twiddle bits if necessary
|
||||
#pragma unroll
|
||||
for (int KEY = 0; KEY < ITEMS_PER_THREAD; KEY++)
|
||||
{
|
||||
unsigned_keys[KEY] = KeyTraits::TwiddleIn(unsigned_keys[KEY]);
|
||||
}
|
||||
|
||||
// Radix sorting passes
|
||||
while (true)
|
||||
{
|
||||
int pass_bits = CUB_MIN(RADIX_BITS, end_bit - begin_bit);
|
||||
|
||||
// Rank the blocked keys
|
||||
int ranks[ITEMS_PER_THREAD];
|
||||
RankKeys(unsigned_keys, ranks, begin_bit, pass_bits, is_descending);
|
||||
begin_bit += RADIX_BITS;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Check if this is the last pass
|
||||
if (begin_bit >= end_bit)
|
||||
{
|
||||
// Last pass exchanges keys through shared memory in striped arrangement
|
||||
BlockExchangeKeys(temp_storage.exchange_keys).ScatterToStriped(keys, ranks);
|
||||
|
||||
// Last pass exchanges through shared memory in striped arrangement
|
||||
ExchangeValues(values, ranks, is_keys_only, Int2Type<false>());
|
||||
|
||||
// Quit
|
||||
break;
|
||||
}
|
||||
|
||||
// Exchange keys through shared memory in blocked arrangement
|
||||
BlockExchangeKeys(temp_storage.exchange_keys).ScatterToBlocked(keys, ranks);
|
||||
|
||||
// Exchange values through shared memory in blocked arrangement
|
||||
ExchangeValues(values, ranks, is_keys_only, Int2Type<true>());
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// Untwiddle bits if necessary
|
||||
#pragma unroll
|
||||
for (int KEY = 0; KEY < ITEMS_PER_THREAD; KEY++)
|
||||
{
|
||||
unsigned_keys[KEY] = KeyTraits::TwiddleOut(unsigned_keys[KEY]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
/// \smemstorage{BlockRadixSort}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockRadixSort()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockRadixSort(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Sorting (blocked arrangements)
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Performs an ascending block-wide radix sort over a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys.
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys
|
||||
* BlockRadixSort(temp_storage).Sort(thread_keys);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>.
|
||||
* The corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], [8,9,10,11], ..., [508,509,510,511] }</tt>.
|
||||
*/
|
||||
__device__ __forceinline__ void Sort(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
NullType values[ITEMS_PER_THREAD];
|
||||
|
||||
SortBlocked(keys, values, begin_bit, end_bit, Int2Type<false>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs an ascending block-wide radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys and values.
|
||||
*
|
||||
* \par
|
||||
* - BlockRadixSort can only accommodate one associated tile of values. To "truck along"
|
||||
* more than one tile of values, simply perform a key-value sort of the keys paired
|
||||
* with a temporary value array that enumerates the key indices. The reordered indices
|
||||
* can then be used as a gather-vector for exchanging other associated tile data through
|
||||
* shared memory.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys and values that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive pairs.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys and values each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4, int> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* int thread_values[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys and values among block threads
|
||||
* BlockRadixSort(temp_storage).Sort(thread_keys, thread_values);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], [8,9,10,11], ..., [508,509,510,511] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void Sort(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< [in-out] Values to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
SortBlocked(keys, values, begin_bit, end_bit, Int2Type<false>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Performs a descending block-wide radix sort over a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys.
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys
|
||||
* BlockRadixSort(temp_storage).Sort(thread_keys);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>.
|
||||
* The corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [511,510,509,508], [11,10,9,8], [7,6,5,4], ..., [3,2,1,0] }</tt>.
|
||||
*/
|
||||
__device__ __forceinline__ void SortDescending(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
NullType values[ITEMS_PER_THREAD];
|
||||
|
||||
SortBlocked(keys, values, begin_bit, end_bit, Int2Type<true>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs a descending block-wide radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys and values.
|
||||
*
|
||||
* \par
|
||||
* - BlockRadixSort can only accommodate one associated tile of values. To "truck along"
|
||||
* more than one tile of values, simply perform a key-value sort of the keys paired
|
||||
* with a temporary value array that enumerates the key indices. The reordered indices
|
||||
* can then be used as a gather-vector for exchanging other associated tile data through
|
||||
* shared memory.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys and values that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive pairs.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys and values each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4, int> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* int thread_values[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys and values among block threads
|
||||
* BlockRadixSort(temp_storage).Sort(thread_keys, thread_values);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [511,510,509,508], [11,10,9,8], [7,6,5,4], ..., [3,2,1,0] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void SortDescending(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< [in-out] Values to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
SortBlocked(keys, values, begin_bit, end_bit, Int2Type<true>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Sorting (blocked arrangement -> striped arrangement)
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs an ascending radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys, leaving them in a [<em>striped arrangement</em>](index.html#sec5sec3).
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys that
|
||||
* are initially partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive keys. The final partitioning is striped.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys
|
||||
* BlockRadixSort(temp_storage).SortBlockedToStriped(thread_keys);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [0,128,256,384], [1,129,257,385], [2,130,258,386], ..., [127,255,383,511] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void SortBlockedToStriped(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
NullType values[ITEMS_PER_THREAD];
|
||||
|
||||
SortBlockedToStriped(keys, values, begin_bit, end_bit, Int2Type<false>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs an ascending radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys and values, leaving them in a [<em>striped arrangement</em>](index.html#sec5sec3).
|
||||
*
|
||||
* \par
|
||||
* - BlockRadixSort can only accommodate one associated tile of values. To "truck along"
|
||||
* more than one tile of values, simply perform a key-value sort of the keys paired
|
||||
* with a temporary value array that enumerates the key indices. The reordered indices
|
||||
* can then be used as a gather-vector for exchanging other associated tile data through
|
||||
* shared memory.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys and values that
|
||||
* are initially partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive pairs. The final partitioning is striped.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys and values each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4, int> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* int thread_values[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys and values among block threads
|
||||
* BlockRadixSort(temp_storage).SortBlockedToStriped(thread_keys, thread_values);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [0,128,256,384], [1,129,257,385], [2,130,258,386], ..., [127,255,383,511] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void SortBlockedToStriped(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< [in-out] Values to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
SortBlockedToStriped(keys, values, begin_bit, end_bit, Int2Type<false>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs a descending radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys, leaving them in a [<em>striped arrangement</em>](index.html#sec5sec3).
|
||||
*
|
||||
* \par
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys that
|
||||
* are initially partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive keys. The final partitioning is striped.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys
|
||||
* BlockRadixSort(temp_storage).SortBlockedToStriped(thread_keys);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [511,383,255,127], [386,258,130,2], [385,257,128,1], ..., [384,256,128,0] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void SortDescendingBlockedToStriped(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
NullType values[ITEMS_PER_THREAD];
|
||||
|
||||
SortBlockedToStriped(keys, values, begin_bit, end_bit, Int2Type<true>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Performs a descending radix sort across a [<em>blocked arrangement</em>](index.html#sec5sec3) of keys and values, leaving them in a [<em>striped arrangement</em>](index.html#sec5sec3).
|
||||
*
|
||||
* \par
|
||||
* - BlockRadixSort can only accommodate one associated tile of values. To "truck along"
|
||||
* more than one tile of values, simply perform a key-value sort of the keys paired
|
||||
* with a temporary value array that enumerates the key indices. The reordered indices
|
||||
* can then be used as a gather-vector for exchanging other associated tile data through
|
||||
* shared memory.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sort of 512 integer keys and values that
|
||||
* are initially partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive pairs. The final partitioning is striped.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_radix_sort.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockRadixSort for a 1D block of 128 threads owning 4 integer keys and values each
|
||||
* typedef cub::BlockRadixSort<int, 128, 4, int> BlockRadixSort;
|
||||
*
|
||||
* // Allocate shared memory for BlockRadixSort
|
||||
* __shared__ typename BlockRadixSort::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_keys[4];
|
||||
* int thread_values[4];
|
||||
* ...
|
||||
*
|
||||
* // Collectively sort the keys and values among block threads
|
||||
* BlockRadixSort(temp_storage).SortBlockedToStriped(thread_keys, thread_values);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of input \p thread_keys across the block of threads is
|
||||
* <tt>{ [0,511,1,510], [2,509,3,508], [4,507,5,506], ..., [254,257,255,256] }</tt>. The
|
||||
* corresponding output \p thread_keys in those threads will be
|
||||
* <tt>{ [511,383,255,127], [386,258,130,2], [385,257,128,1], ..., [384,256,128,0] }</tt>.
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ void SortDescendingBlockedToStriped(
|
||||
KeyT (&keys)[ITEMS_PER_THREAD], ///< [in-out] Keys to sort
|
||||
ValueT (&values)[ITEMS_PER_THREAD], ///< [in-out] Values to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The beginning (least-significant) bit index needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8) ///< [in] <b>[optional]</b> The past-the-end (most-significant) bit index needed for key comparison
|
||||
{
|
||||
SortBlockedToStriped(keys, values, begin_bit, end_bit, Int2Type<true>(), Int2Type<KEYS_ONLY>());
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_block_radix_sort.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockRakingLayout provides a conflict-free shared memory layout abstraction for warp-raking across thread block data.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_macro.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief BlockRakingLayout provides a conflict-free shared memory layout abstraction for 1D raking across thread block data. 
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \par Overview
|
||||
* This type facilitates a shared memory usage pattern where a block of CUDA
|
||||
* threads places elements into shared memory and then reduces the active
|
||||
* parallelism to one "raking" warp of threads for serially aggregating consecutive
|
||||
* sequences of shared items. Padding is inserted to eliminate bank conflicts
|
||||
* (for most data types).
|
||||
*
|
||||
* \tparam T The data type to be exchanged.
|
||||
* \tparam BLOCK_THREADS The thread block size in threads.
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_THREADS,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
struct BlockRakingLayout
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Constants and type definitions
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
/// The total number of elements that need to be cooperatively reduced
|
||||
SHARED_ELEMENTS = BLOCK_THREADS,
|
||||
|
||||
/// Maximum number of warp-synchronous raking threads
|
||||
MAX_RAKING_THREADS = CUB_MIN(BLOCK_THREADS, CUB_WARP_THREADS(PTX_ARCH)),
|
||||
|
||||
/// Number of raking elements per warp-synchronous raking thread (rounded up)
|
||||
SEGMENT_LENGTH = (SHARED_ELEMENTS + MAX_RAKING_THREADS - 1) / MAX_RAKING_THREADS,
|
||||
|
||||
/// Never use a raking thread that will have no valid data (e.g., when BLOCK_THREADS is 62 and SEGMENT_LENGTH is 2, we should only use 31 raking threads)
|
||||
RAKING_THREADS = (SHARED_ELEMENTS + SEGMENT_LENGTH - 1) / SEGMENT_LENGTH,
|
||||
|
||||
/// Whether we will have bank conflicts (technically we should find out if the GCD is > 1)
|
||||
HAS_CONFLICTS = (CUB_SMEM_BANKS(PTX_ARCH) % SEGMENT_LENGTH == 0),
|
||||
|
||||
/// Degree of bank conflicts (e.g., 4-way)
|
||||
CONFLICT_DEGREE = (HAS_CONFLICTS) ?
|
||||
(MAX_RAKING_THREADS * SEGMENT_LENGTH) / CUB_SMEM_BANKS(PTX_ARCH) :
|
||||
1,
|
||||
|
||||
/// Pad each segment length with one element if degree of bank conflicts is greater than 4-way (heuristic)
|
||||
SEGMENT_PADDING = (CONFLICT_DEGREE > CUB_PREFER_CONFLICT_OVER_PADDING(PTX_ARCH)) ? 1 : 0,
|
||||
// SEGMENT_PADDING = (HAS_CONFLICTS) ? 1 : 0,
|
||||
|
||||
/// Total number of elements in the raking grid
|
||||
GRID_ELEMENTS = RAKING_THREADS * (SEGMENT_LENGTH + SEGMENT_PADDING),
|
||||
|
||||
/// Whether or not we need bounds checking during raking (the number of reduction elements is not a multiple of the number of raking threads)
|
||||
UNGUARDED = (SHARED_ELEMENTS % RAKING_THREADS == 0),
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Shared memory storage type
|
||||
*/
|
||||
struct __align__(16) _TempStorage
|
||||
{
|
||||
T buff[BlockRakingLayout::GRID_ELEMENTS];
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Returns the location for the calling thread to place data into the grid
|
||||
*/
|
||||
static __device__ __forceinline__ T* PlacementPtr(
|
||||
TempStorage &temp_storage,
|
||||
unsigned int linear_tid)
|
||||
{
|
||||
// Offset for partial
|
||||
unsigned int offset = linear_tid;
|
||||
|
||||
// Add in one padding element for every segment
|
||||
if (SEGMENT_PADDING > 0)
|
||||
{
|
||||
offset += offset / SEGMENT_LENGTH;
|
||||
}
|
||||
|
||||
// Incorporating a block of padding partials every shared memory segment
|
||||
return temp_storage.Alias().buff + offset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Returns the location for the calling thread to begin sequential raking
|
||||
*/
|
||||
static __device__ __forceinline__ T* RakingPtr(
|
||||
TempStorage &temp_storage,
|
||||
unsigned int linear_tid)
|
||||
{
|
||||
return temp_storage.Alias().buff + (linear_tid * (SEGMENT_LENGTH + SEGMENT_PADDING));
|
||||
}
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockReduce class provides [<em>collective</em>](index.html#sec0) methods for computing a parallel reduction of items partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "specializations/block_reduce_raking.cuh"
|
||||
#include "specializations/block_reduce_raking_commutative_only.cuh"
|
||||
#include "specializations/block_reduce_warp_reductions.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Algorithmic variants
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* BlockReduceAlgorithm enumerates alternative algorithms for parallel
|
||||
* reduction across a CUDA threadblock.
|
||||
*/
|
||||
enum BlockReduceAlgorithm
|
||||
{
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* An efficient "raking" reduction algorithm that only supports commutative
|
||||
* reduction operators (true for most operations, e.g., addition).
|
||||
*
|
||||
* \par
|
||||
* Execution is comprised of three phases:
|
||||
* -# Upsweep sequential reduction in registers (if threads contribute more
|
||||
* than one input each). Threads in warps other than the first warp place
|
||||
* their partial reductions into shared memory.
|
||||
* -# Upsweep sequential reduction in shared memory. Threads within the first
|
||||
* warp continue to accumulate by raking across segments of shared partial reductions
|
||||
* -# A warp-synchronous Kogge-Stone style reduction within the raking warp.
|
||||
*
|
||||
* \par
|
||||
* \image html block_reduce.png
|
||||
* <div class="centercaption">\p BLOCK_REDUCE_RAKING data flow for a hypothetical 16-thread threadblock and 4-thread raking warp.</div>
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - This variant performs less communication than BLOCK_REDUCE_RAKING_NON_COMMUTATIVE
|
||||
* and is preferable when the reduction operator is commutative. This variant
|
||||
* applies fewer reduction operators than BLOCK_REDUCE_WARP_REDUCTIONS, and can provide higher overall
|
||||
* throughput across the GPU when suitably occupied. However, turn-around latency may be
|
||||
* higher than to BLOCK_REDUCE_WARP_REDUCTIONS and thus less-desirable
|
||||
* when the GPU is under-occupied.
|
||||
*/
|
||||
BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY,
|
||||
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* An efficient "raking" reduction algorithm that supports commutative
|
||||
* (e.g., addition) and non-commutative (e.g., string concatenation) reduction
|
||||
* operators. \blocked.
|
||||
*
|
||||
* \par
|
||||
* Execution is comprised of three phases:
|
||||
* -# Upsweep sequential reduction in registers (if threads contribute more
|
||||
* than one input each). Each thread then places the partial reduction
|
||||
* of its item(s) into shared memory.
|
||||
* -# Upsweep sequential reduction in shared memory. Threads within a
|
||||
* single warp rake across segments of shared partial reductions.
|
||||
* -# A warp-synchronous Kogge-Stone style reduction within the raking warp.
|
||||
*
|
||||
* \par
|
||||
* \image html block_reduce.png
|
||||
* <div class="centercaption">\p BLOCK_REDUCE_RAKING data flow for a hypothetical 16-thread threadblock and 4-thread raking warp.</div>
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - This variant performs more communication than BLOCK_REDUCE_RAKING
|
||||
* and is only preferable when the reduction operator is non-commutative. This variant
|
||||
* applies fewer reduction operators than BLOCK_REDUCE_WARP_REDUCTIONS, and can provide higher overall
|
||||
* throughput across the GPU when suitably occupied. However, turn-around latency may be
|
||||
* higher than to BLOCK_REDUCE_WARP_REDUCTIONS and thus less-desirable
|
||||
* when the GPU is under-occupied.
|
||||
*/
|
||||
BLOCK_REDUCE_RAKING,
|
||||
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* A quick "tiled warp-reductions" reduction algorithm that supports commutative
|
||||
* (e.g., addition) and non-commutative (e.g., string concatenation) reduction
|
||||
* operators.
|
||||
*
|
||||
* \par
|
||||
* Execution is comprised of four phases:
|
||||
* -# Upsweep sequential reduction in registers (if threads contribute more
|
||||
* than one input each). Each thread then places the partial reduction
|
||||
* of its item(s) into shared memory.
|
||||
* -# Compute a shallow, but inefficient warp-synchronous Kogge-Stone style
|
||||
* reduction within each warp.
|
||||
* -# A propagation phase where the warp reduction outputs in each warp are
|
||||
* updated with the aggregate from each preceding warp.
|
||||
*
|
||||
* \par
|
||||
* \image html block_scan_warpscans.png
|
||||
* <div class="centercaption">\p BLOCK_REDUCE_WARP_REDUCTIONS data flow for a hypothetical 16-thread threadblock and 4-thread raking warp.</div>
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - This variant applies more reduction operators than BLOCK_REDUCE_RAKING
|
||||
* or BLOCK_REDUCE_RAKING_NON_COMMUTATIVE, which may result in lower overall
|
||||
* throughput across the GPU. However turn-around latency may be lower and
|
||||
* thus useful when the GPU is under-occupied.
|
||||
*/
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS,
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Block reduce
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \brief The BlockReduce class provides [<em>collective</em>](index.html#sec0) methods for computing a parallel reduction of items partitioned across a CUDA thread block. 
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \tparam T Data type being reduced
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam ALGORITHM <b>[optional]</b> cub::BlockReduceAlgorithm enumerator specifying the underlying algorithm to use (default: cub::BLOCK_REDUCE_WARP_REDUCTIONS)
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* - A <a href="http://en.wikipedia.org/wiki/Reduce_(higher-order_function)"><em>reduction</em></a> (or <em>fold</em>)
|
||||
* uses a binary combining operator to compute a single aggregate from a list of input elements.
|
||||
* - \rowmajor
|
||||
* - BlockReduce can be optionally specialized by algorithm to accommodate different latency/throughput workload profiles:
|
||||
* -# <b>cub::BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY</b>. An efficient "raking" reduction algorithm that only supports commutative reduction operators. [More...](\ref cub::BlockReduceAlgorithm)
|
||||
* -# <b>cub::BLOCK_REDUCE_RAKING</b>. An efficient "raking" reduction algorithm that supports commutative and non-commutative reduction operators. [More...](\ref cub::BlockReduceAlgorithm)
|
||||
* -# <b>cub::BLOCK_REDUCE_WARP_REDUCTIONS</b>. A quick "tiled warp-reductions" reduction algorithm that supports commutative and non-commutative reduction operators. [More...](\ref cub::BlockReduceAlgorithm)
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - \granularity
|
||||
* - Very efficient (only one synchronization barrier).
|
||||
* - Incurs zero bank conflicts for most types
|
||||
* - Computation is slightly more efficient (i.e., having lower instruction overhead) for:
|
||||
* - Summation (<b><em>vs.</em></b> generic reduction)
|
||||
* - \p BLOCK_THREADS is a multiple of the architecture's warp size
|
||||
* - Every thread has a valid input (i.e., full <b><em>vs.</em></b> partial-tiles)
|
||||
* - See cub::BlockReduceAlgorithm for performance details regarding algorithmic alternatives
|
||||
*
|
||||
* \par A Simple Example
|
||||
* \blockcollective{BlockReduce}
|
||||
* \par
|
||||
* The code snippet below illustrates a sum reduction of 512 integer items that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive items.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide sum for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Sum(thread_data);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X,
|
||||
BlockReduceAlgorithm ALGORITHM = BLOCK_REDUCE_WARP_REDUCTIONS,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockReduce
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Constants and type definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
typedef BlockReduceWarpReductions<T, BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> WarpReductions;
|
||||
typedef BlockReduceRakingCommutativeOnly<T, BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> RakingCommutativeOnly;
|
||||
typedef BlockReduceRaking<T, BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> Raking;
|
||||
|
||||
/// Internal specialization type
|
||||
typedef typename If<(ALGORITHM == BLOCK_REDUCE_WARP_REDUCTIONS),
|
||||
WarpReductions,
|
||||
typename If<(ALGORITHM == BLOCK_REDUCE_RAKING_COMMUTATIVE_ONLY),
|
||||
RakingCommutativeOnly,
|
||||
Raking>::Type>::Type InternalBlockReduce; // BlockReduceRaking
|
||||
|
||||
/// Shared memory storage layout type for BlockReduce
|
||||
typedef typename InternalBlockReduce::TempStorage _TempStorage;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// \smemstorage{BlockReduce}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockReduce()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockReduce(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Generic reductions
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using the specified binary reduction functor. Each thread contributes one input element.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \rowmajor
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a max reduction of 128 integer items that
|
||||
* are partitioned across 128 threads.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Each thread obtains an input item
|
||||
* int thread_data;
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide max for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Reduce(thread_data, cub::Max());
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ReductionOp <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T input, ///< [in] Calling thread's input
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction functor
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Reduce<true>(input, BLOCK_THREADS, reduction_op);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using the specified binary reduction functor. Each thread contributes an array of consecutive input elements.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a max reduction of 512 integer items that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive items.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide max for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Reduce(thread_data, cub::Max());
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam ReductionOp <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int ITEMS_PER_THREAD,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T (&inputs)[ITEMS_PER_THREAD], ///< [in] Calling thread's input segment
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction functor
|
||||
{
|
||||
// Reduce partials
|
||||
T partial = ThreadReduce(inputs, reduction_op);
|
||||
return Reduce(partial, reduction_op);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using the specified binary reduction functor. The first \p num_valid threads each contribute one input element.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \rowmajor
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a max reduction of a partially-full tile of integer items that
|
||||
* are partitioned across 128 threads.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(int num_valid, ...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Each thread obtains an input item
|
||||
* int thread_data;
|
||||
* if (threadIdx.x < num_valid) thread_data = ...
|
||||
*
|
||||
* // Compute the block-wide max for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Reduce(thread_data, cub::Max(), num_valid);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ReductionOp <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T input, ///< [in] Calling thread's input
|
||||
ReductionOp reduction_op, ///< [in] Binary reduction functor
|
||||
int num_valid) ///< [in] Number of threads containing valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
// Determine if we scan skip bounds checking
|
||||
if (num_valid >= BLOCK_THREADS)
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Reduce<true>(input, num_valid, reduction_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Reduce<false>(input, num_valid, reduction_op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Summation reductions
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using addition (+) as the reduction operator. Each thread contributes one input element.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \rowmajor
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sum reduction of 128 integer items that
|
||||
* are partitioned across 128 threads.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Each thread obtains an input item
|
||||
* int thread_data;
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide sum for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Sum(thread_data);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ T Sum(
|
||||
T input) ///< [in] Calling thread's input
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Sum<true>(input, BLOCK_THREADS);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using addition (+) as the reduction operator. Each thread contributes an array of consecutive input elements.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sum reduction of 512 integer items that
|
||||
* are partitioned in a [<em>blocked arrangement</em>](index.html#sec5sec3) across 128 threads
|
||||
* where each thread owns 4 consecutive items.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Compute the block-wide sum for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Sum(thread_data);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
*/
|
||||
template <int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ T Sum(
|
||||
T (&inputs)[ITEMS_PER_THREAD]) ///< [in] Calling thread's input segment
|
||||
{
|
||||
// Reduce partials
|
||||
T partial = ThreadReduce(inputs, cub::Sum());
|
||||
return Sum(partial);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a block-wide reduction for thread<sub>0</sub> using addition (+) as the reduction operator. The first \p num_valid threads each contribute one input element.
|
||||
*
|
||||
* \par
|
||||
* - The return value is undefined in threads other than thread<sub>0</sub>.
|
||||
* - \rowmajor
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a sum reduction of a partially-full tile of integer items that
|
||||
* are partitioned across 128 threads.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_reduce.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(int num_valid, ...)
|
||||
* {
|
||||
* // Specialize BlockReduce for a 1D block of 128 threads on type int
|
||||
* typedef cub::BlockReduce<int, 128> BlockReduce;
|
||||
*
|
||||
* // Allocate shared memory for BlockReduce
|
||||
* __shared__ typename BlockReduce::TempStorage temp_storage;
|
||||
*
|
||||
* // Each thread obtains an input item (up to num_items)
|
||||
* int thread_data;
|
||||
* if (threadIdx.x < num_valid)
|
||||
* thread_data = ...
|
||||
*
|
||||
* // Compute the block-wide sum for thread0
|
||||
* int aggregate = BlockReduce(temp_storage).Sum(thread_data, num_valid);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
__device__ __forceinline__ T Sum(
|
||||
T input, ///< [in] Calling thread's input
|
||||
int num_valid) ///< [in] Number of threads containing valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
// Determine if we scan skip bounds checking
|
||||
if (num_valid >= BLOCK_THREADS)
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Sum<true>(input, num_valid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return InternalBlockReduce(temp_storage).template Sum<false>(input, num_valid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_block_reduce.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,305 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockShuffle class provides [<em>collective</em>](index.html#sec0) methods for shuffling data partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_macro.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief The BlockShuffle class provides [<em>collective</em>](index.html#sec0) methods for shuffling data partitioned across a CUDA thread block.
|
||||
* \ingroup BlockModule
|
||||
*
|
||||
* \tparam T The data type to be exchanged.
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* It is commonplace for blocks of threads to rearrange data items between
|
||||
* threads. The BlockShuffle abstraction allows threads to efficiently shift items
|
||||
* either (a) up to their successor or (b) down to their predecessor.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockShuffle
|
||||
{
|
||||
private:
|
||||
|
||||
/******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
LOG_WARP_THREADS = CUB_LOG_WARP_THREADS(PTX_ARCH),
|
||||
WARP_THREADS = 1 << LOG_WARP_THREADS,
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Type definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared memory storage layout type (last element from each thread's input)
|
||||
struct _TempStorage
|
||||
{
|
||||
T prev[BLOCK_THREADS];
|
||||
T next[BLOCK_THREADS];
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// \smemstorage{BlockShuffle}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Shared storage reference
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockShuffle()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockShuffle(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Shuffle movement
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Each <em>thread<sub>i</sub></em> obtains the \p input provided by <em>thread</em><sub><em>i</em>+<tt>distance</tt></sub>. The offset \p distance may be negative.
|
||||
*
|
||||
* \par
|
||||
* - \smemreuse
|
||||
*/
|
||||
__device__ __forceinline__ void Offset(
|
||||
T input, ///< [in] The input item from the calling thread (<em>thread<sub>i</sub></em>)
|
||||
T& output, ///< [out] The \p input item from the successor (or predecessor) thread <em>thread</em><sub><em>i</em>+<tt>distance</tt></sub> (may be aliased to \p input). This value is only updated for for <em>thread<sub>i</sub></em> when 0 <= (<em>i</em> + \p distance) < <tt>BLOCK_THREADS-1</tt>
|
||||
int distance = 1) ///< [in] Offset distance (may be negative)
|
||||
{
|
||||
temp_storage[linear_tid].prev = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if ((linear_tid + distance >= 0) && (linear_tid + distance < BLOCK_THREADS))
|
||||
output = temp_storage[linear_tid + distance].prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Each <em>thread<sub>i</sub></em> obtains the \p input provided by <em>thread</em><sub><em>i</em>+<tt>distance</tt></sub>.
|
||||
*
|
||||
* \par
|
||||
* - \smemreuse
|
||||
*/
|
||||
__device__ __forceinline__ void Rotate(
|
||||
T input, ///< [in] The calling thread's input item
|
||||
T& output, ///< [out] The \p input item from thread <em>thread</em><sub>(<em>i</em>+<tt>distance></tt>)%<tt><BLOCK_THREADS></tt></sub> (may be aliased to \p input). This value is not updated for <em>thread</em><sub>BLOCK_THREADS-1</sub>
|
||||
unsigned int distance = 1) ///< [in] Offset distance (0 < \p distance < <tt>BLOCK_THREADS</tt>)
|
||||
{
|
||||
temp_storage[linear_tid].prev = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
unsigned int offset = threadIdx.x + distance;
|
||||
if (offset >= BLOCK_THREADS)
|
||||
offset -= BLOCK_THREADS;
|
||||
|
||||
output = temp_storage[offset].prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief The thread block rotates its [<em>blocked arrangement</em>](index.html#sec5sec3) of \p input items, shifting it up by one item
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*/
|
||||
template <int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void Up(
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] The calling thread's input items
|
||||
T (&prev)[ITEMS_PER_THREAD]) ///< [out] The corresponding predecessor items (may be aliased to \p input). The item \p prev[0] is not updated for <em>thread</em><sub>0</sub>.
|
||||
{
|
||||
temp_storage[linear_tid].prev = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = ITEMS_PER_THREAD - 1; ITEM > 0; --ITEM)
|
||||
prev[ITEM] = input[ITEM - 1];
|
||||
|
||||
|
||||
if (linear_tid > 0)
|
||||
prev[0] = temp_storage[linear_tid - 1].prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief The thread block rotates its [<em>blocked arrangement</em>](index.html#sec5sec3) of \p input items, shifting it up by one item. All threads receive the \p input provided by <em>thread</em><sub><tt>BLOCK_THREADS-1</tt></sub>.
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*/
|
||||
template <int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void Up(
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] The calling thread's input items
|
||||
T (&prev)[ITEMS_PER_THREAD], ///< [out] The corresponding predecessor items (may be aliased to \p input). The item \p prev[0] is not updated for <em>thread</em><sub>0</sub>.
|
||||
T &block_suffix) ///< [out] The item \p input[ITEMS_PER_THREAD-1] from <em>thread</em><sub><tt>BLOCK_THREADS-1</tt></sub>, provided to all threads
|
||||
{
|
||||
Up(input, prev);
|
||||
block_suffix = temp_storage[BLOCK_THREADS - 1].prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief The thread block rotates its [<em>blocked arrangement</em>](index.html#sec5sec3) of \p input items, shifting it down by one item
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*/
|
||||
template <int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void Down(
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] The calling thread's input items
|
||||
T (&prev)[ITEMS_PER_THREAD]) ///< [out] The corresponding predecessor items (may be aliased to \p input). The value \p prev[0] is not updated for <em>thread</em><sub>BLOCK_THREADS-1</sub>.
|
||||
{
|
||||
temp_storage[linear_tid].prev = input[ITEMS_PER_THREAD - 1];
|
||||
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int ITEM = ITEMS_PER_THREAD - 1; ITEM > 0; --ITEM)
|
||||
prev[ITEM] = input[ITEM - 1];
|
||||
|
||||
if (linear_tid > 0)
|
||||
prev[0] = temp_storage[linear_tid - 1].prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief The thread block rotates its [<em>blocked arrangement</em>](index.html#sec5sec3) of input items, shifting it down by one item. All threads receive \p input[0] provided by <em>thread</em><sub><tt>0</tt></sub>.
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \granularity
|
||||
* - \smemreuse
|
||||
*/
|
||||
template <int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void Down(
|
||||
T (&input)[ITEMS_PER_THREAD], ///< [in] The calling thread's input items
|
||||
T (&prev)[ITEMS_PER_THREAD], ///< [out] The corresponding predecessor items (may be aliased to \p input). The value \p prev[0] is not updated for <em>thread</em><sub>BLOCK_THREADS-1</sub>.
|
||||
T &block_prefix) ///< [out] The item \p input[0] from <em>thread</em><sub><tt>0</tt></sub>, provided to all threads
|
||||
{
|
||||
Up(input, prev);
|
||||
block_prefix = temp_storage[BLOCK_THREADS - 1].prev;
|
||||
}
|
||||
|
||||
//@} end member group
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,994 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Operations for writing linear segments of data from the CUDA thread block
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "block_exchange.cuh"
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_macro.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIo
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Blocked arrangement I/O (direct)
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Store a blocked arrangement of items across a thread block into a linear segment of items.
|
||||
*
|
||||
* \blocked
|
||||
*
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectBlocked(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
OutputIteratorT thread_itr = block_itr + (linear_tid * ITEMS_PER_THREAD);
|
||||
|
||||
// Store directly in thread-blocked order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
thread_itr[ITEM] = items[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a blocked arrangement of items across a thread block into a linear segment of items, guarded by range
|
||||
*
|
||||
* \blocked
|
||||
*
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectBlocked(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
OutputIteratorT thread_itr = block_itr + (linear_tid * ITEMS_PER_THREAD);
|
||||
|
||||
// Store directly in thread-blocked order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
if (ITEM + (linear_tid * ITEMS_PER_THREAD) < valid_items)
|
||||
{
|
||||
thread_itr[ITEM] = items[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a blocked arrangement of items across a thread block into a linear segment of items.
|
||||
*
|
||||
* \blocked
|
||||
*
|
||||
* The output offset (\p block_ptr + \p block_offset) must be quad-item aligned,
|
||||
* which is the default starting offset returned by \p cudaMalloc()
|
||||
*
|
||||
* \par
|
||||
* The following conditions will prevent vectorization and storing will fall back to cub::BLOCK_STORE_DIRECT:
|
||||
* - \p ITEMS_PER_THREAD is odd
|
||||
* - The data type \p T is not a built-in primitive or CUDA vector type (e.g., \p short, \p int2, \p double, \p float2, etc.)
|
||||
*
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void StoreDirectBlockedVectorized(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
T *block_ptr, ///< [in] Input pointer for storing from
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
enum
|
||||
{
|
||||
// Maximum CUDA vector size is 4 elements
|
||||
MAX_VEC_SIZE = CUB_MIN(4, ITEMS_PER_THREAD),
|
||||
|
||||
// Vector size must be a power of two and an even divisor of the items per thread
|
||||
VEC_SIZE = ((((MAX_VEC_SIZE - 1) & MAX_VEC_SIZE) == 0) && ((ITEMS_PER_THREAD % MAX_VEC_SIZE) == 0)) ?
|
||||
MAX_VEC_SIZE :
|
||||
1,
|
||||
|
||||
VECTORS_PER_THREAD = ITEMS_PER_THREAD / VEC_SIZE,
|
||||
};
|
||||
|
||||
// Vector type
|
||||
typedef typename CubVector<T, VEC_SIZE>::Type Vector;
|
||||
|
||||
// Alias global pointer
|
||||
Vector *block_ptr_vectors = reinterpret_cast<Vector*>(const_cast<T*>(block_ptr));
|
||||
|
||||
// Alias pointers (use "raw" array here which should get optimized away to prevent conservative PTXAS lmem spilling)
|
||||
Vector raw_vector[VECTORS_PER_THREAD];
|
||||
T *raw_items = reinterpret_cast<T*>(raw_vector);
|
||||
|
||||
// Copy
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
raw_items[ITEM] = items[ITEM];
|
||||
}
|
||||
|
||||
// Direct-store using vector types
|
||||
StoreDirectBlocked(linear_tid, block_ptr_vectors, raw_vector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Striped arrangement I/O (direct)
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a striped arrangement of data across the thread block into a linear segment of items.
|
||||
*
|
||||
* \striped
|
||||
*
|
||||
* \tparam BLOCK_THREADS The thread block size in threads
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
int BLOCK_THREADS,
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectStriped(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
OutputIteratorT thread_itr = block_itr + linear_tid;
|
||||
|
||||
// Store directly in striped order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
thread_itr[(ITEM * BLOCK_THREADS)] = items[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a striped arrangement of data across the thread block into a linear segment of items, guarded by range
|
||||
*
|
||||
* \striped
|
||||
*
|
||||
* \tparam BLOCK_THREADS The thread block size in threads
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
int BLOCK_THREADS,
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectStriped(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
OutputIteratorT thread_itr = block_itr + linear_tid;
|
||||
|
||||
// Store directly in striped order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
if ((ITEM * BLOCK_THREADS) + linear_tid < valid_items)
|
||||
{
|
||||
thread_itr[(ITEM * BLOCK_THREADS)] = items[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Warp-striped arrangement I/O (direct)
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a warp-striped arrangement of data across the thread block into a linear segment of items.
|
||||
*
|
||||
* \warpstriped
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* The number of threads in the thread block must be a multiple of the architecture's warp size.
|
||||
*
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectWarpStriped(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [out] Data to load
|
||||
{
|
||||
int tid = linear_tid & (CUB_PTX_WARP_THREADS - 1);
|
||||
int wid = linear_tid >> CUB_PTX_LOG_WARP_THREADS;
|
||||
int warp_offset = wid * CUB_PTX_WARP_THREADS * ITEMS_PER_THREAD;
|
||||
|
||||
OutputIteratorT thread_itr = block_itr + warp_offset + tid;
|
||||
|
||||
// Store directly in warp-striped order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
thread_itr[(ITEM * CUB_PTX_WARP_THREADS)] = items[ITEM];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store a warp-striped arrangement of data across the thread block into a linear segment of items, guarded by range
|
||||
*
|
||||
* \warpstriped
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* The number of threads in the thread block must be a multiple of the architecture's warp size.
|
||||
*
|
||||
* \tparam T <b>[inferred]</b> The data type to store.
|
||||
* \tparam ITEMS_PER_THREAD <b>[inferred]</b> The number of consecutive items partitioned onto each thread.
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> The random-access iterator type for output \iterator.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int ITEMS_PER_THREAD,
|
||||
typename OutputIteratorT>
|
||||
__device__ __forceinline__ void StoreDirectWarpStriped(
|
||||
int linear_tid, ///< [in] A suitable 1D thread-identifier for the calling thread (e.g., <tt>(threadIdx.y * blockDim.x) + linear_tid</tt> for 2D thread blocks)
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
int tid = linear_tid & (CUB_PTX_WARP_THREADS - 1);
|
||||
int wid = linear_tid >> CUB_PTX_LOG_WARP_THREADS;
|
||||
int warp_offset = wid * CUB_PTX_WARP_THREADS * ITEMS_PER_THREAD;
|
||||
|
||||
OutputIteratorT thread_itr = block_itr + warp_offset + tid;
|
||||
|
||||
// Store directly in warp-striped order
|
||||
#pragma unroll
|
||||
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ITEM++)
|
||||
{
|
||||
if (warp_offset + tid + (ITEM * CUB_PTX_WARP_THREADS) < valid_items)
|
||||
{
|
||||
thread_itr[(ITEM * CUB_PTX_WARP_THREADS)] = items[ITEM];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
|
||||
/** @} */ // end group UtilIo
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generic BlockStore abstraction
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* \brief cub::BlockStoreAlgorithm enumerates alternative algorithms for cub::BlockStore to write a blocked arrangement of items across a CUDA thread block to a linear segment of memory.
|
||||
*/
|
||||
enum BlockStoreAlgorithm
|
||||
{
|
||||
/**
|
||||
* \par Overview
|
||||
*
|
||||
* A [<em>blocked arrangement</em>](index.html#sec5sec3) of data is written
|
||||
* directly to memory.
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - The utilization of memory transactions (coalescing) decreases as the
|
||||
* access stride between threads increases (i.e., the number items per thread).
|
||||
*/
|
||||
BLOCK_STORE_DIRECT,
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
*
|
||||
* A [<em>blocked arrangement</em>](index.html#sec5sec3) of data is written directly
|
||||
* to memory using CUDA's built-in vectorized stores as a coalescing optimization.
|
||||
* For example, <tt>st.global.v4.s32</tt> instructions will be generated
|
||||
* when \p T = \p int and \p ITEMS_PER_THREAD % 4 == 0.
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - The utilization of memory transactions (coalescing) remains high until the the
|
||||
* access stride between threads (i.e., the number items per thread) exceeds the
|
||||
* maximum vector store width (typically 4 items or 64B, whichever is lower).
|
||||
* - The following conditions will prevent vectorization and writing will fall back to cub::BLOCK_STORE_DIRECT:
|
||||
* - \p ITEMS_PER_THREAD is odd
|
||||
* - The \p OutputIteratorT is not a simple pointer type
|
||||
* - The block output offset is not quadword-aligned
|
||||
* - The data type \p T is not a built-in primitive or CUDA vector type (e.g., \p short, \p int2, \p double, \p float2, etc.)
|
||||
*/
|
||||
BLOCK_STORE_VECTORIZE,
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* A [<em>blocked arrangement</em>](index.html#sec5sec3) is locally
|
||||
* transposed and then efficiently written to memory as a [<em>striped arrangement</em>](index.html#sec5sec3).
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - The utilization of memory transactions (coalescing) remains high regardless
|
||||
* of items written per thread.
|
||||
* - The local reordering incurs slightly longer latencies and throughput than the
|
||||
* direct cub::BLOCK_STORE_DIRECT and cub::BLOCK_STORE_VECTORIZE alternatives.
|
||||
*/
|
||||
BLOCK_STORE_TRANSPOSE,
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* A [<em>blocked arrangement</em>](index.html#sec5sec3) is locally
|
||||
* transposed and then efficiently written to memory as a
|
||||
* [<em>warp-striped arrangement</em>](index.html#sec5sec3)
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* - BLOCK_THREADS must be a multiple of WARP_THREADS
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - The utilization of memory transactions (coalescing) remains high regardless
|
||||
* of items written per thread.
|
||||
* - The local reordering incurs slightly longer latencies and throughput than the
|
||||
* direct cub::BLOCK_STORE_DIRECT and cub::BLOCK_STORE_VECTORIZE alternatives.
|
||||
*/
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
|
||||
/**
|
||||
* \par Overview
|
||||
* A [<em>blocked arrangement</em>](index.html#sec5sec3) is locally
|
||||
* transposed and then efficiently written to memory as a
|
||||
* [<em>warp-striped arrangement</em>](index.html#sec5sec3)
|
||||
* To reduce the shared memory requirement, only one warp's worth of shared
|
||||
* memory is provisioned and is subsequently time-sliced among warps.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* - BLOCK_THREADS must be a multiple of WARP_THREADS
|
||||
*
|
||||
* \par Performance Considerations
|
||||
* - The utilization of memory transactions (coalescing) remains high regardless
|
||||
* of items written per thread.
|
||||
* - Provisions less shared memory temporary storage, but incurs larger
|
||||
* latencies than the BLOCK_STORE_WARP_TRANSPOSE alternative.
|
||||
*/
|
||||
BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED,
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief The BlockStore class provides [<em>collective</em>](index.html#sec0) data movement methods for writing a [<em>blocked arrangement</em>](index.html#sec5sec3) of items partitioned across a CUDA thread block to a linear segment of memory. 
|
||||
* \ingroup BlockModule
|
||||
* \ingroup UtilIo
|
||||
*
|
||||
* \tparam T The type of data to be written.
|
||||
* \tparam BLOCK_DIM_X The thread block length in threads along the X dimension
|
||||
* \tparam ITEMS_PER_THREAD The number of consecutive items partitioned onto each thread.
|
||||
* \tparam ALGORITHM <b>[optional]</b> cub::BlockStoreAlgorithm tuning policy enumeration. default: cub::BLOCK_STORE_DIRECT.
|
||||
* \tparam WARP_TIME_SLICING <b>[optional]</b> Whether or not only one warp's worth of shared memory should be allocated and time-sliced among block-warps during any load-related data transpositions (versus each warp having its own storage). (default: false)
|
||||
* \tparam BLOCK_DIM_Y <b>[optional]</b> The thread block length in threads along the Y dimension (default: 1)
|
||||
* \tparam BLOCK_DIM_Z <b>[optional]</b> The thread block length in threads along the Z dimension (default: 1)
|
||||
* \tparam PTX_ARCH <b>[optional]</b> \ptxversion
|
||||
*
|
||||
* \par Overview
|
||||
* - The BlockStore class provides a single data movement abstraction that can be specialized
|
||||
* to implement different cub::BlockStoreAlgorithm strategies. This facilitates different
|
||||
* performance policies for different architectures, data types, granularity sizes, etc.
|
||||
* - BlockStore can be optionally specialized by different data movement strategies:
|
||||
* -# <b>cub::BLOCK_STORE_DIRECT</b>. A [<em>blocked arrangement</em>](index.html#sec5sec3) of data is written
|
||||
* directly to memory. [More...](\ref cub::BlockStoreAlgorithm)
|
||||
* -# <b>cub::BLOCK_STORE_VECTORIZE</b>. A [<em>blocked arrangement</em>](index.html#sec5sec3)
|
||||
* of data is written directly to memory using CUDA's built-in vectorized stores as a
|
||||
* coalescing optimization. [More...](\ref cub::BlockStoreAlgorithm)
|
||||
* -# <b>cub::BLOCK_STORE_TRANSPOSE</b>. A [<em>blocked arrangement</em>](index.html#sec5sec3)
|
||||
* is locally transposed into a [<em>striped arrangement</em>](index.html#sec5sec3) which is
|
||||
* then written to memory. [More...](\ref cub::BlockStoreAlgorithm)
|
||||
* -# <b>cub::BLOCK_STORE_WARP_TRANSPOSE</b>. A [<em>blocked arrangement</em>](index.html#sec5sec3)
|
||||
* is locally transposed into a [<em>warp-striped arrangement</em>](index.html#sec5sec3) which is
|
||||
* then written to memory. [More...](\ref cub::BlockStoreAlgorithm)
|
||||
* - \rowmajor
|
||||
*
|
||||
* \par A Simple Example
|
||||
* \blockcollective{BlockStore}
|
||||
* \par
|
||||
* The code snippet below illustrates the storing of a "blocked" arrangement
|
||||
* of 512 integers across 128 threads (where each thread owns 4 consecutive items)
|
||||
* into a linear segment of memory. The store is specialized for \p BLOCK_STORE_WARP_TRANSPOSE,
|
||||
* meaning items are locally reordered among threads so that memory references will be
|
||||
* efficiently coalesced using a warp-striped access pattern.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_store.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(int *d_data, ...)
|
||||
* {
|
||||
* // Specialize BlockStore for a 1D block of 128 threads owning 4 integer items each
|
||||
* typedef cub::BlockStore<int, 128, 4, BLOCK_STORE_WARP_TRANSPOSE> BlockStore;
|
||||
*
|
||||
* // Allocate shared memory for BlockStore
|
||||
* __shared__ typename BlockStore::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Store items to linear memory
|
||||
* int thread_data[4];
|
||||
* BlockStore(temp_storage).Store(d_data, thread_data);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of \p thread_data across the block of threads is
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], ..., [508,509,510,511] }</tt>.
|
||||
* The output \p d_data will be <tt>0, 1, 2, 3, 4, 5, ...</tt>.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X,
|
||||
int ITEMS_PER_THREAD,
|
||||
BlockStoreAlgorithm ALGORITHM = BLOCK_STORE_DIRECT,
|
||||
int BLOCK_DIM_Y = 1,
|
||||
int BLOCK_DIM_Z = 1,
|
||||
int PTX_ARCH = CUB_PTX_ARCH>
|
||||
class BlockStore
|
||||
{
|
||||
private:
|
||||
/******************************************************************************
|
||||
* Constants and typed definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Algorithmic variants
|
||||
******************************************************************************/
|
||||
|
||||
/// Store helper
|
||||
template <BlockStoreAlgorithm _POLICY, int DUMMY>
|
||||
struct StoreInternal;
|
||||
|
||||
|
||||
/**
|
||||
* BLOCK_STORE_DIRECT specialization of store helper
|
||||
*/
|
||||
template <int DUMMY>
|
||||
struct StoreInternal<BLOCK_STORE_DIRECT, DUMMY>
|
||||
{
|
||||
/// Shared memory storage layout type
|
||||
typedef NullType TempStorage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ StoreInternal(
|
||||
TempStorage &/*temp_storage*/,
|
||||
int linear_tid)
|
||||
:
|
||||
linear_tid(linear_tid)
|
||||
{}
|
||||
|
||||
/// Store items into a linear segment of memory
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
StoreDirectBlocked(linear_tid, block_itr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, guarded by range
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
StoreDirectBlocked(linear_tid, block_itr, items, valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* BLOCK_STORE_VECTORIZE specialization of store helper
|
||||
*/
|
||||
template <int DUMMY>
|
||||
struct StoreInternal<BLOCK_STORE_VECTORIZE, DUMMY>
|
||||
{
|
||||
/// Shared memory storage layout type
|
||||
typedef NullType TempStorage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ StoreInternal(
|
||||
TempStorage &/*temp_storage*/,
|
||||
int linear_tid)
|
||||
:
|
||||
linear_tid(linear_tid)
|
||||
{}
|
||||
|
||||
/// Store items into a linear segment of memory, specialized for native pointer types (attempts vectorization)
|
||||
__device__ __forceinline__ void Store(
|
||||
T *block_ptr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
StoreDirectBlockedVectorized(linear_tid, block_ptr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, specialized for opaque input iterators (skips vectorization)
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
StoreDirectBlocked(linear_tid, block_itr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, guarded by range
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
StoreDirectBlocked(linear_tid, block_itr, items, valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* BLOCK_STORE_TRANSPOSE specialization of store helper
|
||||
*/
|
||||
template <int DUMMY>
|
||||
struct StoreInternal<BLOCK_STORE_TRANSPOSE, DUMMY>
|
||||
{
|
||||
// BlockExchange utility type for keys
|
||||
typedef BlockExchange<T, BLOCK_DIM_X, ITEMS_PER_THREAD, false, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> BlockExchange;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage : BlockExchange::TempStorage
|
||||
{
|
||||
/// Temporary storage for partially-full block guard
|
||||
volatile int valid_items;
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
/// Thread reference to shared storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ StoreInternal(
|
||||
TempStorage &temp_storage,
|
||||
int linear_tid)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(linear_tid)
|
||||
{}
|
||||
|
||||
/// Store items into a linear segment of memory
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
BlockExchange(temp_storage).BlockedToStriped(items);
|
||||
StoreDirectStriped<BLOCK_THREADS>(linear_tid, block_itr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, guarded by range
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
BlockExchange(temp_storage).BlockedToStriped(items);
|
||||
temp_storage.valid_items = valid_items; // Move through volatile smem as a workaround to prevent RF spilling on subsequent loads
|
||||
StoreDirectStriped<BLOCK_THREADS>(linear_tid, block_itr, items, temp_storage.valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* BLOCK_STORE_WARP_TRANSPOSE specialization of store helper
|
||||
*/
|
||||
template <int DUMMY>
|
||||
struct StoreInternal<BLOCK_STORE_WARP_TRANSPOSE, DUMMY>
|
||||
{
|
||||
enum
|
||||
{
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH)
|
||||
};
|
||||
|
||||
// Assert BLOCK_THREADS must be a multiple of WARP_THREADS
|
||||
CUB_STATIC_ASSERT((BLOCK_THREADS % WARP_THREADS == 0), "BLOCK_THREADS must be a multiple of WARP_THREADS");
|
||||
|
||||
// BlockExchange utility type for keys
|
||||
typedef BlockExchange<T, BLOCK_DIM_X, ITEMS_PER_THREAD, false, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> BlockExchange;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage : BlockExchange::TempStorage
|
||||
{
|
||||
/// Temporary storage for partially-full block guard
|
||||
volatile int valid_items;
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
/// Thread reference to shared storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ StoreInternal(
|
||||
TempStorage &temp_storage,
|
||||
int linear_tid)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(linear_tid)
|
||||
{}
|
||||
|
||||
/// Store items into a linear segment of memory
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
BlockExchange(temp_storage).BlockedToWarpStriped(items);
|
||||
StoreDirectWarpStriped(linear_tid, block_itr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, guarded by range
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
BlockExchange(temp_storage).BlockedToWarpStriped(items);
|
||||
temp_storage.valid_items = valid_items; // Move through volatile smem as a workaround to prevent RF spilling on subsequent loads
|
||||
StoreDirectWarpStriped(linear_tid, block_itr, items, temp_storage.valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED specialization of store helper
|
||||
*/
|
||||
template <int DUMMY>
|
||||
struct StoreInternal<BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED, DUMMY>
|
||||
{
|
||||
enum
|
||||
{
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH)
|
||||
};
|
||||
|
||||
// Assert BLOCK_THREADS must be a multiple of WARP_THREADS
|
||||
CUB_STATIC_ASSERT((BLOCK_THREADS % WARP_THREADS == 0), "BLOCK_THREADS must be a multiple of WARP_THREADS");
|
||||
|
||||
// BlockExchange utility type for keys
|
||||
typedef BlockExchange<T, BLOCK_DIM_X, ITEMS_PER_THREAD, true, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> BlockExchange;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage : BlockExchange::TempStorage
|
||||
{
|
||||
/// Temporary storage for partially-full block guard
|
||||
volatile int valid_items;
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
/// Thread reference to shared storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ StoreInternal(
|
||||
TempStorage &temp_storage,
|
||||
int linear_tid)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(linear_tid)
|
||||
{}
|
||||
|
||||
/// Store items into a linear segment of memory
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
BlockExchange(temp_storage).BlockedToWarpStriped(items);
|
||||
StoreDirectWarpStriped(linear_tid, block_itr, items);
|
||||
}
|
||||
|
||||
/// Store items into a linear segment of memory, guarded by range
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
temp_storage.valid_items = valid_items; // Move through volatile smem as a workaround to prevent RF spilling on subsequent loads
|
||||
BlockExchange(temp_storage).BlockedToWarpStriped(items);
|
||||
StoreDirectWarpStriped(linear_tid, block_itr, items, temp_storage.valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* Type definitions
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal load implementation to use
|
||||
typedef StoreInternal<ALGORITHM, 0> InternalStore;
|
||||
|
||||
|
||||
/// Shared memory storage layout type
|
||||
typedef typename InternalStore::TempStorage _TempStorage;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utility methods
|
||||
******************************************************************************/
|
||||
|
||||
/// Internal storage allocator
|
||||
__device__ __forceinline__ _TempStorage& PrivateStorage()
|
||||
{
|
||||
__shared__ _TempStorage private_storage;
|
||||
return private_storage;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Thread fields
|
||||
******************************************************************************/
|
||||
|
||||
/// Thread reference to shared storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
/// Linear thread-id
|
||||
int linear_tid;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/// \smemstorage{BlockStore}
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Collective constructors
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using a private static allocation of shared memory as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockStore()
|
||||
:
|
||||
temp_storage(PrivateStorage()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Collective constructor using the specified memory allocation as temporary storage.
|
||||
*/
|
||||
__device__ __forceinline__ BlockStore(
|
||||
TempStorage &temp_storage) ///< [in] Reference to memory allocation having layout type TempStorage
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Data movement
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Store items into a linear segment of memory.
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the storing of a "blocked" arrangement
|
||||
* of 512 integers across 128 threads (where each thread owns 4 consecutive items)
|
||||
* into a linear segment of memory. The store is specialized for \p BLOCK_STORE_WARP_TRANSPOSE,
|
||||
* meaning items are locally reordered among threads so that memory references will be
|
||||
* efficiently coalesced using a warp-striped access pattern.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_store.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(int *d_data, ...)
|
||||
* {
|
||||
* // Specialize BlockStore for a 1D block of 128 threads owning 4 integer items each
|
||||
* typedef cub::BlockStore<int, 128, 4, BLOCK_STORE_WARP_TRANSPOSE> BlockStore;
|
||||
*
|
||||
* // Allocate shared memory for BlockStore
|
||||
* __shared__ typename BlockStore::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Store items to linear memory
|
||||
* int thread_data[4];
|
||||
* BlockStore(temp_storage).Store(d_data, thread_data);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of \p thread_data across the block of threads is
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], ..., [508,509,510,511] }</tt>.
|
||||
* The output \p d_data will be <tt>0, 1, 2, 3, 4, 5, ...</tt>.
|
||||
*
|
||||
*/
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD]) ///< [in] Data to store
|
||||
{
|
||||
InternalStore(temp_storage, linear_tid).Store(block_itr, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Store items into a linear segment of memory, guarded by range.
|
||||
*
|
||||
* \par
|
||||
* - \blocked
|
||||
* - \smemreuse
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the guarded storing of a "blocked" arrangement
|
||||
* of 512 integers across 128 threads (where each thread owns 4 consecutive items)
|
||||
* into a linear segment of memory. The store is specialized for \p BLOCK_STORE_WARP_TRANSPOSE,
|
||||
* meaning items are locally reordered among threads so that memory references will be
|
||||
* efficiently coalesced using a warp-striped access pattern.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/block/block_store.cuh>
|
||||
*
|
||||
* __global__ void ExampleKernel(int *d_data, int valid_items, ...)
|
||||
* {
|
||||
* // Specialize BlockStore for a 1D block of 128 threads owning 4 integer items each
|
||||
* typedef cub::BlockStore<int, 128, 4, BLOCK_STORE_WARP_TRANSPOSE> BlockStore;
|
||||
*
|
||||
* // Allocate shared memory for BlockStore
|
||||
* __shared__ typename BlockStore::TempStorage temp_storage;
|
||||
*
|
||||
* // Obtain a segment of consecutive items that are blocked across threads
|
||||
* int thread_data[4];
|
||||
* ...
|
||||
*
|
||||
* // Store items to linear memory
|
||||
* int thread_data[4];
|
||||
* BlockStore(temp_storage).Store(d_data, thread_data, valid_items);
|
||||
*
|
||||
* \endcode
|
||||
* \par
|
||||
* Suppose the set of \p thread_data across the block of threads is
|
||||
* <tt>{ [0,1,2,3], [4,5,6,7], ..., [508,509,510,511] }</tt> and \p valid_items is \p 5.
|
||||
* The output \p d_data will be <tt>0, 1, 2, 3, 4, ?, ?, ?, ...</tt>, with
|
||||
* only the first two threads being unmasked to store portions of valid data.
|
||||
*
|
||||
*/
|
||||
template <typename OutputIteratorT>
|
||||
__device__ __forceinline__ void Store(
|
||||
OutputIteratorT block_itr, ///< [in] The thread block's base output iterator for storing to
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Data to store
|
||||
int valid_items) ///< [in] Number of valid items to write
|
||||
{
|
||||
InternalStore(temp_storage, linear_tid).Store(block_itr, items, valid_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockHistogramAtomic class provides atomic-based methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief The BlockHistogramAtomic class provides atomic-based methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block.
|
||||
*/
|
||||
template <int BINS>
|
||||
struct BlockHistogramAtomic
|
||||
{
|
||||
/// Shared memory storage layout type
|
||||
struct TempStorage {};
|
||||
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockHistogramAtomic(
|
||||
TempStorage &temp_storage)
|
||||
{}
|
||||
|
||||
|
||||
/// Composite data onto an existing histogram
|
||||
template <
|
||||
typename T,
|
||||
typename CounterT,
|
||||
int ITEMS_PER_THREAD>
|
||||
__device__ __forceinline__ void Composite(
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Calling thread's input values to histogram
|
||||
CounterT histogram[BINS]) ///< [out] Reference to shared/device-accessible memory histogram
|
||||
{
|
||||
// Update histogram
|
||||
#pragma unroll
|
||||
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
|
||||
{
|
||||
atomicAdd(histogram + items[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* The cub::BlockHistogramSort class provides sorting-based methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../block/block_radix_sort.cuh"
|
||||
#include "../../block/block_discontinuity.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief The BlockHistogramSort class provides sorting-based methods for constructing block-wide histograms from data samples partitioned across a CUDA thread block.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< Sample type
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int ITEMS_PER_THREAD, ///< The number of samples per thread
|
||||
int BINS, ///< The number of bins into which histogram samples may fall
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockHistogramSort
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
// Parameterize BlockRadixSort type for our thread block
|
||||
typedef BlockRadixSort<
|
||||
T,
|
||||
BLOCK_DIM_X,
|
||||
ITEMS_PER_THREAD,
|
||||
NullType,
|
||||
4,
|
||||
(PTX_ARCH >= 350) ? true : false,
|
||||
BLOCK_SCAN_WARP_SCANS,
|
||||
cudaSharedMemBankSizeFourByte,
|
||||
BLOCK_DIM_Y,
|
||||
BLOCK_DIM_Z,
|
||||
PTX_ARCH>
|
||||
BlockRadixSortT;
|
||||
|
||||
// Parameterize BlockDiscontinuity type for our thread block
|
||||
typedef BlockDiscontinuity<
|
||||
T,
|
||||
BLOCK_DIM_X,
|
||||
BLOCK_DIM_Y,
|
||||
BLOCK_DIM_Z,
|
||||
PTX_ARCH>
|
||||
BlockDiscontinuityT;
|
||||
|
||||
/// Shared memory
|
||||
union _TempStorage
|
||||
{
|
||||
// Storage for sorting bin values
|
||||
typename BlockRadixSortT::TempStorage sort;
|
||||
|
||||
struct
|
||||
{
|
||||
// Storage for detecting discontinuities in the tile of sorted bin values
|
||||
typename BlockDiscontinuityT::TempStorage flag;
|
||||
|
||||
// Storage for noting begin/end offsets of bin runs in the tile of sorted bin values
|
||||
unsigned int run_begin[BINS];
|
||||
unsigned int run_end[BINS];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockHistogramSort(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
// Discontinuity functor
|
||||
struct DiscontinuityOp
|
||||
{
|
||||
// Reference to temp_storage
|
||||
_TempStorage &temp_storage;
|
||||
|
||||
// Constructor
|
||||
__device__ __forceinline__ DiscontinuityOp(_TempStorage &temp_storage) :
|
||||
temp_storage(temp_storage)
|
||||
{}
|
||||
|
||||
// Discontinuity predicate
|
||||
__device__ __forceinline__ bool operator()(const T &a, const T &b, int b_index)
|
||||
{
|
||||
if (a != b)
|
||||
{
|
||||
// Note the begin/end offsets in shared storage
|
||||
temp_storage.run_begin[b] = b_index;
|
||||
temp_storage.run_end[a] = b_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Composite data onto an existing histogram
|
||||
template <
|
||||
typename CounterT >
|
||||
__device__ __forceinline__ void Composite(
|
||||
T (&items)[ITEMS_PER_THREAD], ///< [in] Calling thread's input values to histogram
|
||||
CounterT histogram[BINS]) ///< [out] Reference to shared/device-accessible memory histogram
|
||||
{
|
||||
enum { TILE_SIZE = BLOCK_THREADS * ITEMS_PER_THREAD };
|
||||
|
||||
// Sort bytes in blocked arrangement
|
||||
BlockRadixSortT(temp_storage.sort).Sort(items);
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Initialize the shared memory's run_begin and run_end for each bin
|
||||
int histo_offset = 0;
|
||||
|
||||
#pragma unroll
|
||||
for(; histo_offset + BLOCK_THREADS <= BINS; histo_offset += BLOCK_THREADS)
|
||||
{
|
||||
temp_storage.run_begin[histo_offset + linear_tid] = TILE_SIZE;
|
||||
temp_storage.run_end[histo_offset + linear_tid] = TILE_SIZE;
|
||||
}
|
||||
// Finish up with guarded initialization if necessary
|
||||
if ((BINS % BLOCK_THREADS != 0) && (histo_offset + linear_tid < BINS))
|
||||
{
|
||||
temp_storage.run_begin[histo_offset + linear_tid] = TILE_SIZE;
|
||||
temp_storage.run_end[histo_offset + linear_tid] = TILE_SIZE;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
int flags[ITEMS_PER_THREAD]; // unused
|
||||
|
||||
// Compute head flags to demarcate contiguous runs of the same bin in the sorted tile
|
||||
DiscontinuityOp flag_op(temp_storage);
|
||||
BlockDiscontinuityT(temp_storage.flag).FlagHeads(flags, items, flag_op);
|
||||
|
||||
// Update begin for first item
|
||||
if (linear_tid == 0) temp_storage.run_begin[items[0]] = 0;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Composite into histogram
|
||||
histo_offset = 0;
|
||||
|
||||
#pragma unroll
|
||||
for(; histo_offset + BLOCK_THREADS <= BINS; histo_offset += BLOCK_THREADS)
|
||||
{
|
||||
int thread_offset = histo_offset + linear_tid;
|
||||
CounterT count = temp_storage.run_end[thread_offset] - temp_storage.run_begin[thread_offset];
|
||||
histogram[thread_offset] += count;
|
||||
}
|
||||
|
||||
// Finish up with guarded composition if necessary
|
||||
if ((BINS % BLOCK_THREADS != 0) && (histo_offset + linear_tid < BINS))
|
||||
{
|
||||
int thread_offset = histo_offset + linear_tid;
|
||||
CounterT count = temp_storage.run_end[thread_offset] - temp_storage.run_begin[thread_offset];
|
||||
histogram[thread_offset] += count;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockReduceRaking provides raking-based methods of parallel reduction across a CUDA thread block. Supports non-commutative reduction operators.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../block/block_raking_layout.cuh"
|
||||
#include "../../warp/warp_reduce.cuh"
|
||||
#include "../../thread/thread_reduce.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief BlockReduceRaking provides raking-based methods of parallel reduction across a CUDA thread block. Supports non-commutative reduction operators.
|
||||
*
|
||||
* Supports non-commutative binary reduction operators. Unlike commutative
|
||||
* reduction operators (e.g., addition), the application of a non-commutative
|
||||
* reduction operator (e.g, string concatenation) across a sequence of inputs must
|
||||
* honor the relative ordering of items and partial reductions when applying the
|
||||
* reduction operator.
|
||||
*
|
||||
* Compared to the implementation of BlockReduceRaking (which does not support
|
||||
* non-commutative operators), this implementation requires a few extra
|
||||
* rounds of inter-thread communication.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< Data type being reduced
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockReduceRaking
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
/// Layout type for padded thread block raking grid
|
||||
typedef BlockRakingLayout<T, BLOCK_THREADS, PTX_ARCH> BlockRakingLayout;
|
||||
|
||||
/// WarpReduce utility type
|
||||
typedef typename WarpReduce<T, BlockRakingLayout::RAKING_THREADS, PTX_ARCH>::InternalWarpReduce WarpReduce;
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// Number of raking threads
|
||||
RAKING_THREADS = BlockRakingLayout::RAKING_THREADS,
|
||||
|
||||
/// Number of raking elements per warp synchronous raking thread
|
||||
SEGMENT_LENGTH = BlockRakingLayout::SEGMENT_LENGTH,
|
||||
|
||||
/// Cooperative work can be entirely warp synchronous
|
||||
WARP_SYNCHRONOUS = (RAKING_THREADS == BLOCK_THREADS),
|
||||
|
||||
/// Whether or not warp-synchronous reduction should be unguarded (i.e., the warp-reduction elements is a power of two
|
||||
WARP_SYNCHRONOUS_UNGUARDED = PowerOfTwo<RAKING_THREADS>::VALUE,
|
||||
|
||||
/// Whether or not accesses into smem are unguarded
|
||||
RAKING_UNGUARDED = BlockRakingLayout::UNGUARDED,
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// Shared memory storage layout type
|
||||
union _TempStorage
|
||||
{
|
||||
typename WarpReduce::TempStorage warp_storage; ///< Storage for warp-synchronous reduction
|
||||
typename BlockRakingLayout::TempStorage raking_grid; ///< Padded threadblock raking grid
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockReduceRaking(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
template <bool IS_FULL_TILE, typename ReductionOp, int ITERATION>
|
||||
__device__ __forceinline__ T RakingReduction(
|
||||
ReductionOp reduction_op, ///< [in] Binary scan operator
|
||||
T *raking_segment,
|
||||
T partial, ///< [in] <b>[<em>lane</em><sub>0</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
int num_valid, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
Int2Type<ITERATION> /*iteration*/)
|
||||
{
|
||||
// Update partial if addend is in range
|
||||
if ((IS_FULL_TILE && RAKING_UNGUARDED) || ((linear_tid * SEGMENT_LENGTH) + ITERATION < num_valid))
|
||||
{
|
||||
T addend = raking_segment[ITERATION];
|
||||
partial = reduction_op(partial, addend);
|
||||
}
|
||||
return RakingReduction<IS_FULL_TILE>(reduction_op, raking_segment, partial, num_valid, Int2Type<ITERATION + 1>());
|
||||
}
|
||||
|
||||
template <bool IS_FULL_TILE, typename ReductionOp>
|
||||
__device__ __forceinline__ T RakingReduction(
|
||||
ReductionOp /*reduction_op*/, ///< [in] Binary scan operator
|
||||
T * /*raking_segment*/,
|
||||
T partial, ///< [in] <b>[<em>lane</em><sub>0</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
int /*num_valid*/, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
Int2Type<SEGMENT_LENGTH> /*iteration*/)
|
||||
{
|
||||
return partial;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using the specified reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <
|
||||
bool IS_FULL_TILE,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T partial, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction operator
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp synchronous reduction (unguarded if active threads is a power-of-two)
|
||||
partial = WarpReduce(temp_storage.warp_storage).template Reduce<IS_FULL_TILE, SEGMENT_LENGTH>(
|
||||
partial,
|
||||
num_valid,
|
||||
reduction_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place partial into shared memory grid.
|
||||
*BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid) = partial;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism to one warp
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking reduction in grid
|
||||
T *raking_segment = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
partial = raking_segment[0];
|
||||
|
||||
partial = RakingReduction<IS_FULL_TILE>(reduction_op, raking_segment, partial, num_valid, Int2Type<1>());
|
||||
|
||||
partial = WarpReduce(temp_storage.warp_storage).template Reduce<IS_FULL_TILE && RAKING_UNGUARDED, SEGMENT_LENGTH>(
|
||||
partial,
|
||||
num_valid,
|
||||
reduction_op);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return partial;
|
||||
}
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using addition (+) as the reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <bool IS_FULL_TILE>
|
||||
__device__ __forceinline__ T Sum(
|
||||
T partial, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid) ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
cub::Sum reduction_op;
|
||||
|
||||
return Reduce<IS_FULL_TILE>(partial, num_valid, reduction_op);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockReduceRakingCommutativeOnly provides raking-based methods of parallel reduction across a CUDA thread block. Does not support non-commutative reduction operators.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "block_reduce_raking.cuh"
|
||||
#include "../../warp/warp_reduce.cuh"
|
||||
#include "../../thread/thread_reduce.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief BlockReduceRakingCommutativeOnly provides raking-based methods of parallel reduction across a CUDA thread block. Does not support non-commutative reduction operators. Does not support block sizes that are not a multiple of the warp size.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< Data type being reduced
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockReduceRakingCommutativeOnly
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
// The fall-back implementation to use when BLOCK_THREADS is not a multiple of the warp size or not all threads have valid values
|
||||
typedef BlockReduceRaking<T, BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z, PTX_ARCH> FallBack;
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// Number of warp threads
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
|
||||
/// Whether or not to use fall-back
|
||||
USE_FALLBACK = ((BLOCK_THREADS % WARP_THREADS != 0) || (BLOCK_THREADS <= WARP_THREADS)),
|
||||
|
||||
/// Number of raking threads
|
||||
RAKING_THREADS = WARP_THREADS,
|
||||
|
||||
/// Number of threads actually sharing items with the raking threads
|
||||
SHARING_THREADS = CUB_MAX(1, BLOCK_THREADS - RAKING_THREADS),
|
||||
|
||||
/// Number of raking elements per warp synchronous raking thread
|
||||
SEGMENT_LENGTH = SHARING_THREADS / WARP_THREADS,
|
||||
};
|
||||
|
||||
/// WarpReduce utility type
|
||||
typedef WarpReduce<T, RAKING_THREADS, PTX_ARCH> WarpReduce;
|
||||
|
||||
/// Layout type for padded thread block raking grid
|
||||
typedef BlockRakingLayout<T, SHARING_THREADS, PTX_ARCH> BlockRakingLayout;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
typename WarpReduce::TempStorage warp_storage; ///< Storage for warp-synchronous reduction
|
||||
typename BlockRakingLayout::TempStorage raking_grid; ///< Padded threadblock raking grid
|
||||
};
|
||||
typename FallBack::TempStorage fallback_storage; ///< Fall-back storage for non-commutative block scan
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockReduceRakingCommutativeOnly(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using addition (+) as the reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ T Sum(
|
||||
T partial, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid) ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
if (USE_FALLBACK || !FULL_TILE)
|
||||
{
|
||||
return FallBack(temp_storage.fallback_storage).template Sum<FULL_TILE>(partial, num_valid);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place partial into shared memory grid
|
||||
if (linear_tid >= RAKING_THREADS)
|
||||
*BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid - RAKING_THREADS) = partial;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism to one warp
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking reduction in grid
|
||||
T *raking_segment = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
partial = ThreadReduce<SEGMENT_LENGTH>(raking_segment, cub::Sum(), partial);
|
||||
|
||||
// Warpscan
|
||||
partial = WarpReduce(temp_storage.warp_storage).Sum(partial);
|
||||
}
|
||||
}
|
||||
|
||||
return partial;
|
||||
}
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using the specified reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <
|
||||
bool FULL_TILE,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T partial, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction operator
|
||||
{
|
||||
if (USE_FALLBACK || !FULL_TILE)
|
||||
{
|
||||
return FallBack(temp_storage.fallback_storage).template Reduce<FULL_TILE>(partial, num_valid, reduction_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place partial into shared memory grid
|
||||
if (linear_tid >= RAKING_THREADS)
|
||||
*BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid - RAKING_THREADS) = partial;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism to one warp
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking reduction in grid
|
||||
T *raking_segment = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
partial = ThreadReduce<SEGMENT_LENGTH>(raking_segment, reduction_op, partial);
|
||||
|
||||
// Warpscan
|
||||
partial = WarpReduce(temp_storage.warp_storage).Reduce(partial, reduction_op);
|
||||
}
|
||||
}
|
||||
|
||||
return partial;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockReduceWarpReductions provides variants of warp-reduction-based parallel reduction across a CUDA threadblock. Supports non-commutative reduction operators.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../warp/warp_reduce.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief BlockReduceWarpReductions provides variants of warp-reduction-based parallel reduction across a CUDA threadblock. Supports non-commutative reduction operators.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< Data type being reduced
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockReduceWarpReductions
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
/// Number of warp threads
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
|
||||
/// Number of active warps
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
|
||||
/// The logical warp size for warp reductions
|
||||
LOGICAL_WARP_SIZE = CUB_MIN(BLOCK_THREADS, WARP_THREADS),
|
||||
|
||||
/// Whether or not the logical warp size evenly divides the threadblock size
|
||||
EVEN_WARP_MULTIPLE = (BLOCK_THREADS % LOGICAL_WARP_SIZE == 0)
|
||||
};
|
||||
|
||||
|
||||
/// WarpReduce utility type
|
||||
typedef typename WarpReduce<T, LOGICAL_WARP_SIZE, PTX_ARCH>::InternalWarpReduce WarpReduce;
|
||||
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
typename WarpReduce::TempStorage warp_reduce[WARPS]; ///< Buffer for warp-synchronous scan
|
||||
T warp_aggregates[WARPS]; ///< Shared totals from each warp-synchronous scan
|
||||
T block_prefix; ///< Shared prefix for the entire threadblock
|
||||
};
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
unsigned int warp_id;
|
||||
unsigned int lane_id;
|
||||
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockReduceWarpReductions(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z)),
|
||||
warp_id((WARPS == 1) ? 0 : linear_tid / WARP_THREADS),
|
||||
lane_id(LaneId())
|
||||
{}
|
||||
|
||||
|
||||
template <bool FULL_TILE, typename ReductionOp, int SUCCESSOR_WARP>
|
||||
__device__ __forceinline__ T ApplyWarpAggregates(
|
||||
ReductionOp reduction_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>0</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
int num_valid, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
Int2Type<SUCCESSOR_WARP> /*successor_warp*/)
|
||||
{
|
||||
if (FULL_TILE || (SUCCESSOR_WARP * LOGICAL_WARP_SIZE < num_valid))
|
||||
{
|
||||
T addend = temp_storage.warp_aggregates[SUCCESSOR_WARP];
|
||||
warp_aggregate = reduction_op(warp_aggregate, addend);
|
||||
}
|
||||
return ApplyWarpAggregates<FULL_TILE>(reduction_op, warp_aggregate, num_valid, Int2Type<SUCCESSOR_WARP + 1>());
|
||||
}
|
||||
|
||||
template <bool FULL_TILE, typename ReductionOp>
|
||||
__device__ __forceinline__ T ApplyWarpAggregates(
|
||||
ReductionOp /*reduction_op*/, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>0</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
int /*num_valid*/, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
Int2Type<WARPS> /*successor_warp*/)
|
||||
{
|
||||
return warp_aggregate;
|
||||
}
|
||||
|
||||
|
||||
/// Returns block-wide aggregate in <em>thread</em><sub>0</sub>.
|
||||
template <
|
||||
bool FULL_TILE,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ApplyWarpAggregates(
|
||||
ReductionOp reduction_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>0</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
int num_valid) ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
// Share lane aggregates
|
||||
if (lane_id == 0)
|
||||
{
|
||||
temp_storage.warp_aggregates[warp_id] = warp_aggregate;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Update total aggregate in warp 0, lane 0
|
||||
if (linear_tid == 0)
|
||||
{
|
||||
warp_aggregate = ApplyWarpAggregates<FULL_TILE>(reduction_op, warp_aggregate, num_valid, Int2Type<1>());
|
||||
}
|
||||
|
||||
return warp_aggregate;
|
||||
}
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using addition (+) as the reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <bool FULL_TILE>
|
||||
__device__ __forceinline__ T Sum(
|
||||
T input, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid) ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
{
|
||||
cub::Sum reduction_op;
|
||||
unsigned int warp_offset = warp_id * LOGICAL_WARP_SIZE;
|
||||
unsigned int warp_num_valid = (FULL_TILE && EVEN_WARP_MULTIPLE) ?
|
||||
LOGICAL_WARP_SIZE :
|
||||
(warp_offset < num_valid) ?
|
||||
num_valid - warp_offset :
|
||||
0;
|
||||
|
||||
// Warp reduction in every warp
|
||||
T warp_aggregate = WarpReduce(temp_storage.warp_reduce[warp_id]).template Reduce<(FULL_TILE && EVEN_WARP_MULTIPLE), 1>(
|
||||
input,
|
||||
warp_num_valid,
|
||||
cub::Sum());
|
||||
|
||||
// Update outputs and block_aggregate with warp-wide aggregates from lane-0s
|
||||
return ApplyWarpAggregates<FULL_TILE>(reduction_op, warp_aggregate, num_valid);
|
||||
}
|
||||
|
||||
|
||||
/// Computes a threadblock-wide reduction using the specified reduction operator. The first num_valid threads each contribute one reduction partial. The return value is only valid for thread<sub>0</sub>.
|
||||
template <
|
||||
bool FULL_TILE,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T Reduce(
|
||||
T input, ///< [in] Calling thread's input partial reductions
|
||||
int num_valid, ///< [in] Number of valid elements (may be less than BLOCK_THREADS)
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction operator
|
||||
{
|
||||
unsigned int warp_offset = warp_id * LOGICAL_WARP_SIZE;
|
||||
unsigned int warp_num_valid = (FULL_TILE && EVEN_WARP_MULTIPLE) ?
|
||||
LOGICAL_WARP_SIZE :
|
||||
(warp_offset < static_cast<unsigned int>(num_valid)) ?
|
||||
num_valid - warp_offset :
|
||||
0;
|
||||
|
||||
// Warp reduction in every warp
|
||||
T warp_aggregate = WarpReduce(temp_storage.warp_reduce[warp_id]).template Reduce<(FULL_TILE && EVEN_WARP_MULTIPLE), 1>(
|
||||
input,
|
||||
warp_num_valid,
|
||||
reduction_op);
|
||||
|
||||
// Update outputs and block_aggregate with warp-wide aggregates from lane-0s
|
||||
return ApplyWarpAggregates<FULL_TILE>(reduction_op, warp_aggregate, num_valid);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,665 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockScanRaking provides variants of raking-based parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../block/block_raking_layout.cuh"
|
||||
#include "../../thread/thread_reduce.cuh"
|
||||
#include "../../thread/thread_scan.cuh"
|
||||
#include "../../warp/warp_scan.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief BlockScanRaking provides variants of raking-based parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
template <
|
||||
typename T, ///< Data type being scanned
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
bool MEMOIZE, ///< Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockScanRaking
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
};
|
||||
|
||||
/// Layout type for padded threadblock raking grid
|
||||
typedef BlockRakingLayout<T, BLOCK_THREADS, PTX_ARCH> BlockRakingLayout;
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// Number of raking threads
|
||||
RAKING_THREADS = BlockRakingLayout::RAKING_THREADS,
|
||||
|
||||
/// Number of raking elements per warp synchronous raking thread
|
||||
SEGMENT_LENGTH = BlockRakingLayout::SEGMENT_LENGTH,
|
||||
|
||||
/// Cooperative work can be entirely warp synchronous
|
||||
WARP_SYNCHRONOUS = (BLOCK_THREADS == RAKING_THREADS),
|
||||
};
|
||||
|
||||
/// WarpScan utility type
|
||||
typedef WarpScan<T, RAKING_THREADS, PTX_ARCH> WarpScan;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
typename WarpScan::TempStorage warp_scan; ///< Buffer for warp-synchronous scan
|
||||
typename BlockRakingLayout::TempStorage raking_grid; ///< Padded threadblock raking grid
|
||||
T block_aggregate; ///< Block aggregate
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
T cached_segment[SEGMENT_LENGTH];
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Templated reduction
|
||||
template <int ITERATION, typename ScanOp>
|
||||
__device__ __forceinline__ T GuardedReduce(
|
||||
T* raking_ptr, ///< [in] Input array
|
||||
ScanOp scan_op, ///< [in] Binary reduction operator
|
||||
T raking_partial, ///< [in] Prefix to seed reduction with
|
||||
Int2Type<ITERATION> /*iteration*/)
|
||||
{
|
||||
if ((BlockRakingLayout::UNGUARDED) || (((linear_tid * SEGMENT_LENGTH) + ITERATION) < BLOCK_THREADS))
|
||||
{
|
||||
T addend = raking_ptr[ITERATION];
|
||||
raking_partial = scan_op(raking_partial, addend);
|
||||
}
|
||||
|
||||
return GuardedReduce(raking_ptr, scan_op, raking_partial, Int2Type<ITERATION + 1>());
|
||||
}
|
||||
|
||||
|
||||
/// Templated reduction (base case)
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T GuardedReduce(
|
||||
T* /*raking_ptr*/, ///< [in] Input array
|
||||
ScanOp /*scan_op*/, ///< [in] Binary reduction operator
|
||||
T raking_partial, ///< [in] Prefix to seed reduction with
|
||||
Int2Type<SEGMENT_LENGTH> /*iteration*/)
|
||||
{
|
||||
return raking_partial;
|
||||
}
|
||||
|
||||
|
||||
/// Templated copy
|
||||
template <int ITERATION>
|
||||
__device__ __forceinline__ void CopySegment(
|
||||
T* out, ///< [out] Out array
|
||||
T* in, ///< [in] Input array
|
||||
Int2Type<ITERATION> /*iteration*/)
|
||||
{
|
||||
out[ITERATION] = in[ITERATION];
|
||||
CopySegment(out, in, Int2Type<ITERATION + 1>());
|
||||
}
|
||||
|
||||
|
||||
/// Templated copy (base case)
|
||||
__device__ __forceinline__ void CopySegment(
|
||||
T* /*out*/, ///< [out] Out array
|
||||
T* /*in*/, ///< [in] Input array
|
||||
Int2Type<SEGMENT_LENGTH> /*iteration*/)
|
||||
{}
|
||||
|
||||
|
||||
/// Performs upsweep raking reduction, returning the aggregate
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T Upsweep(
|
||||
ScanOp scan_op)
|
||||
{
|
||||
T *smem_raking_ptr = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
|
||||
// Read data into registers
|
||||
CopySegment(cached_segment, smem_raking_ptr, Int2Type<0>());
|
||||
|
||||
T raking_partial = cached_segment[0];
|
||||
|
||||
return GuardedReduce(cached_segment, scan_op, raking_partial, Int2Type<1>());
|
||||
}
|
||||
|
||||
|
||||
/// Performs exclusive downsweep raking scan
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveDownsweep(
|
||||
ScanOp scan_op,
|
||||
T raking_partial,
|
||||
bool apply_prefix = true)
|
||||
{
|
||||
T *smem_raking_ptr = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
|
||||
// Read data back into registers
|
||||
if (!MEMOIZE)
|
||||
{
|
||||
CopySegment(cached_segment, smem_raking_ptr, Int2Type<0>());
|
||||
}
|
||||
|
||||
ThreadScanExclusive(cached_segment, cached_segment, scan_op, raking_partial, apply_prefix);
|
||||
|
||||
// Write data back to smem
|
||||
CopySegment(smem_raking_ptr, cached_segment, Int2Type<0>());
|
||||
}
|
||||
|
||||
|
||||
/// Performs inclusive downsweep raking scan
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveDownsweep(
|
||||
ScanOp scan_op,
|
||||
T raking_partial,
|
||||
bool apply_prefix = true)
|
||||
{
|
||||
T *smem_raking_ptr = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
|
||||
|
||||
// Read data back into registers
|
||||
if (!MEMOIZE)
|
||||
{
|
||||
CopySegment(cached_segment, smem_raking_ptr, Int2Type<0>());
|
||||
}
|
||||
|
||||
ThreadScanInclusive(cached_segment, cached_segment, scan_op, raking_partial, apply_prefix);
|
||||
|
||||
// Write data back to smem
|
||||
CopySegment(smem_raking_ptr, cached_segment, Int2Type<0>());
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructors
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanRaking(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z))
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Exclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(input, exclusive_output, scan_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(upsweep_partial, exclusive_partial, scan_op);
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ExclusiveDownsweep(scan_op, exclusive_partial, (linear_tid != 0));
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
exclusive_output = *placement_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(input, output, initial_value, scan_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Exclusive Warp-synchronous scan
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(upsweep_partial, exclusive_partial, initial_value, scan_op);
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ExclusiveDownsweep(scan_op, exclusive_partial);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab exclusive partial from shared memory
|
||||
output = *placement_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(input, output, scan_op, block_aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial= Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T inclusive_partial;
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).Scan(upsweep_partial, inclusive_partial, exclusive_partial, scan_op);
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ExclusiveDownsweep(scan_op, exclusive_partial, (linear_tid != 0));
|
||||
|
||||
// Broadcast aggregate to all threads
|
||||
if (linear_tid == RAKING_THREADS - 1)
|
||||
temp_storage.block_aggregate = inclusive_partial;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
output = *placement_ptr;
|
||||
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(input, output, initial_value, scan_op, block_aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(upsweep_partial, exclusive_partial, initial_value, scan_op, block_aggregate);
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ExclusiveDownsweep(scan_op, exclusive_partial);
|
||||
|
||||
// Broadcast aggregate to other threads
|
||||
temp_storage.block_aggregate = block_aggregate;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab exclusive partial from shared memory
|
||||
output = *placement_ptr;
|
||||
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
T block_aggregate;
|
||||
WarpScan warp_scan(temp_storage.warp_scan);
|
||||
warp_scan.ExclusiveScan(input, output, scan_op, block_aggregate);
|
||||
|
||||
// Obtain warp-wide prefix in lane0, then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = warp_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
output = scan_op(block_prefix, output);
|
||||
if (linear_tid == 0)
|
||||
output = block_prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
WarpScan warp_scan(temp_storage.warp_scan);
|
||||
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T exclusive_partial, block_aggregate;
|
||||
warp_scan.ExclusiveScan(upsweep_partial, exclusive_partial, scan_op, block_aggregate);
|
||||
|
||||
// Obtain block-wide prefix in lane0, then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = warp_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
// Update prefix with warpscan exclusive partial
|
||||
T downsweep_prefix = scan_op(block_prefix, exclusive_partial);
|
||||
if (linear_tid == 0)
|
||||
downsweep_prefix = block_prefix;
|
||||
|
||||
// Exclusive raking downsweep scan
|
||||
ExclusiveDownsweep(scan_op, downsweep_prefix);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
output = *placement_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Inclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).InclusiveScan(input, output, scan_op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Exclusive Warp-synchronous scan
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).ExclusiveScan(upsweep_partial, exclusive_partial, scan_op);
|
||||
|
||||
// Inclusive raking downsweep scan
|
||||
InclusiveDownsweep(scan_op, exclusive_partial, (linear_tid != 0));
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
output = *placement_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
WarpScan(temp_storage.warp_scan).InclusiveScan(input, output, scan_op, block_aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T inclusive_partial;
|
||||
T exclusive_partial;
|
||||
WarpScan(temp_storage.warp_scan).Scan(upsweep_partial, inclusive_partial, exclusive_partial, scan_op);
|
||||
|
||||
// Inclusive raking downsweep scan
|
||||
InclusiveDownsweep(scan_op, exclusive_partial, (linear_tid != 0));
|
||||
|
||||
// Broadcast aggregate to all threads
|
||||
if (linear_tid == RAKING_THREADS - 1)
|
||||
temp_storage.block_aggregate = inclusive_partial;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
output = *placement_ptr;
|
||||
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
if (WARP_SYNCHRONOUS)
|
||||
{
|
||||
// Short-circuit directly to warp-synchronous scan
|
||||
T block_aggregate;
|
||||
WarpScan warp_scan(temp_storage.warp_scan);
|
||||
warp_scan.InclusiveScan(input, output, scan_op, block_aggregate);
|
||||
|
||||
// Obtain warp-wide prefix in lane0, then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = warp_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
// Update prefix with exclusive warpscan partial
|
||||
output = scan_op(block_prefix, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Place thread partial into shared memory raking grid
|
||||
T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
|
||||
*placement_ptr = input;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Reduce parallelism down to just raking threads
|
||||
if (linear_tid < RAKING_THREADS)
|
||||
{
|
||||
WarpScan warp_scan(temp_storage.warp_scan);
|
||||
|
||||
// Raking upsweep reduction across shared partials
|
||||
T upsweep_partial = Upsweep(scan_op);
|
||||
|
||||
// Warp-synchronous scan
|
||||
T exclusive_partial, block_aggregate;
|
||||
warp_scan.ExclusiveScan(upsweep_partial, exclusive_partial, scan_op, block_aggregate);
|
||||
|
||||
// Obtain block-wide prefix in lane0, then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = warp_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
// Update prefix with warpscan exclusive partial
|
||||
T downsweep_prefix = scan_op(block_prefix, exclusive_partial);
|
||||
if (linear_tid == 0)
|
||||
downsweep_prefix = block_prefix;
|
||||
|
||||
// Inclusive raking downsweep scan
|
||||
InclusiveDownsweep(scan_op, downsweep_prefix);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Grab thread prefix from shared memory
|
||||
output = *placement_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockScanWarpscans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../warp/warp_scan.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief BlockScanWarpScans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockScanWarpScans
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// Number of warp threads
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
/// Number of active warps
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
};
|
||||
|
||||
/// WarpScan utility type
|
||||
typedef WarpScan<T, WARP_THREADS, PTX_ARCH> WarpScanT;
|
||||
|
||||
/// WarpScan utility type
|
||||
typedef WarpScan<T, WARPS, PTX_ARCH> WarpAggregateScan;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
|
||||
struct __align__(32) _TempStorage
|
||||
{
|
||||
T warp_aggregates[WARPS];
|
||||
typename WarpScanT::TempStorage warp_scan[WARPS]; ///< Buffer for warp-synchronous scans
|
||||
T block_prefix; ///< Shared prefix for the entire threadblock
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
unsigned int warp_id;
|
||||
unsigned int lane_id;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructors
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanWarpScans(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z)),
|
||||
warp_id((WARPS == 1) ? 0 : linear_tid / WARP_THREADS),
|
||||
lane_id(LaneId())
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template <typename ScanOp, int WARP>
|
||||
__device__ __forceinline__ void ApplyWarpAggregates(
|
||||
T &warp_prefix, ///< [out] The calling thread's partial reduction
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
Int2Type<WARP> /*addend_warp*/)
|
||||
{
|
||||
if (warp_id == WARP)
|
||||
warp_prefix = block_aggregate;
|
||||
|
||||
T addend = temp_storage.warp_aggregates[WARP];
|
||||
block_aggregate = scan_op(block_aggregate, addend);
|
||||
|
||||
ApplyWarpAggregates(warp_prefix, scan_op, block_aggregate, Int2Type<WARP + 1>());
|
||||
}
|
||||
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ApplyWarpAggregates(
|
||||
T &/*warp_prefix*/, ///< [out] The calling thread's partial reduction
|
||||
ScanOp /*scan_op*/, ///< [in] Binary scan operator
|
||||
T &/*block_aggregate*/, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
Int2Type<WARPS> /*addend_warp*/)
|
||||
{}
|
||||
|
||||
|
||||
/// Use the warp-wide aggregates to compute the calling warp's prefix. Also returns block-wide aggregate in all threads.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T ComputeWarpPrefix(
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>WARP_THREADS - 1</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Last lane in each warp shares its warp-aggregate
|
||||
if (lane_id == WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = warp_aggregate;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Accumulate block aggregates and save the one that is our warp's prefix
|
||||
T warp_prefix;
|
||||
block_aggregate = temp_storage.warp_aggregates[0];
|
||||
|
||||
// Use template unrolling (since the PTX backend can't handle unrolling it for SM1x)
|
||||
ApplyWarpAggregates(warp_prefix, scan_op, block_aggregate, Int2Type<1>());
|
||||
/*
|
||||
#pragma unroll
|
||||
for (int WARP = 1; WARP < WARPS; ++WARP)
|
||||
{
|
||||
if (warp_id == WARP)
|
||||
warp_prefix = block_aggregate;
|
||||
|
||||
T addend = temp_storage.warp_aggregates[WARP];
|
||||
block_aggregate = scan_op(block_aggregate, addend);
|
||||
}
|
||||
*/
|
||||
|
||||
return warp_prefix;
|
||||
}
|
||||
|
||||
|
||||
/// Use the warp-wide aggregates and initial-value to compute the calling warp's prefix. Also returns block-wide aggregate in all threads.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T ComputeWarpPrefix(
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>WARP_THREADS - 1</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
T &block_aggregate, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
const T &initial_value) ///< [in] Initial value to seed the exclusive scan
|
||||
{
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, warp_aggregate, block_aggregate);
|
||||
|
||||
warp_prefix = scan_op(initial_value, warp_prefix);
|
||||
|
||||
if (warp_id == 0)
|
||||
warp_prefix = initial_value;
|
||||
|
||||
return warp_prefix;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Exclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
// Compute block-wide exclusive scan. The exclusive output from tid0 is invalid.
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, initial_value, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
WarpScanT(temp_storage.warp_scan[warp_id]).Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp. Warp prefix for warp0 is invalid.
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate);
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
if (warp_id != 0)
|
||||
{
|
||||
exclusive_output = scan_op(warp_prefix, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = warp_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
WarpScanT(temp_storage.warp_scan[warp_id]).Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate, initial_value);
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
exclusive_output = scan_op(warp_prefix, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = warp_prefix;
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
// Compute block-wide exclusive scan. The exclusive output from tid0 is invalid.
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
|
||||
// Use the first warp to determine the threadblock prefix, returning the result in lane0
|
||||
if (warp_id == 0)
|
||||
{
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
if (lane_id == 0)
|
||||
{
|
||||
// Share the prefix with all threads
|
||||
temp_storage.block_prefix = block_prefix;
|
||||
exclusive_output = block_prefix; // The block prefix is the exclusive output for tid0
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Incorporate threadblock prefix into outputs
|
||||
T block_prefix = temp_storage.block_prefix;
|
||||
if (linear_tid > 0)
|
||||
{
|
||||
exclusive_output = scan_op(block_prefix, exclusive_output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Inclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
InclusiveScan(input, inclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
WarpScanT(temp_storage.warp_scan[warp_id]).InclusiveScan(input, inclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp. Warp prefix for warp0 is invalid.
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate);
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
if (warp_id != 0)
|
||||
{
|
||||
inclusive_output = scan_op(warp_prefix, inclusive_output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
T block_aggregate;
|
||||
InclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
|
||||
// Use the first warp to determine the threadblock prefix, returning the result in lane0
|
||||
if (warp_id == 0)
|
||||
{
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
if (lane_id == 0)
|
||||
{
|
||||
// Share the prefix with all threads
|
||||
temp_storage.block_prefix = block_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Incorporate threadblock prefix into outputs
|
||||
T block_prefix = temp_storage.block_prefix;
|
||||
exclusive_output = scan_op(block_prefix, exclusive_output);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockScanWarpscans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../warp/warp_scan.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief BlockScanWarpScans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockScanWarpScans
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// Number of warp threads
|
||||
WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
/// Number of active warps
|
||||
WARPS = (BLOCK_THREADS + WARP_THREADS - 1) / WARP_THREADS,
|
||||
};
|
||||
|
||||
/// WarpScan utility type
|
||||
typedef WarpScan<T, WARP_THREADS, PTX_ARCH> WarpScanT;
|
||||
|
||||
/// WarpScan utility type
|
||||
typedef WarpScan<T, WARPS, PTX_ARCH> WarpAggregateScanT;
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
typename WarpAggregateScanT::TempStorage inner_scan[WARPS]; ///< Buffer for warp-synchronous scans
|
||||
typename WarpScanT::TempStorage warp_scan[WARPS]; ///< Buffer for warp-synchronous scans
|
||||
T warp_aggregates[WARPS];
|
||||
T block_prefix; ///< Shared prefix for the entire threadblock
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
unsigned int warp_id;
|
||||
unsigned int lane_id;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructors
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanWarpScans(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z)),
|
||||
warp_id((WARPS == 1) ? 0 : linear_tid / WARP_THREADS),
|
||||
lane_id(LaneId())
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
template <typename ScanOp, int WARP>
|
||||
__device__ __forceinline__ void ApplyWarpAggregates(
|
||||
T &warp_prefix, ///< [out] The calling thread's partial reduction
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
Int2Type<WARP> addend_warp)
|
||||
{
|
||||
if (warp_id == WARP)
|
||||
warp_prefix = block_aggregate;
|
||||
|
||||
T addend = temp_storage.warp_aggregates[WARP];
|
||||
block_aggregate = scan_op(block_aggregate, addend);
|
||||
|
||||
ApplyWarpAggregates(warp_prefix, scan_op, block_aggregate, Int2Type<WARP + 1>());
|
||||
}
|
||||
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ApplyWarpAggregates(
|
||||
T &warp_prefix, ///< [out] The calling thread's partial reduction
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
Int2Type<WARPS> addend_warp)
|
||||
{}
|
||||
|
||||
|
||||
/// Use the warp-wide aggregates to compute the calling warp's prefix. Also returns block-wide aggregate in all threads.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T ComputeWarpPrefix(
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>WARP_THREADS - 1</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Last lane in each warp shares its warp-aggregate
|
||||
if (lane_id == WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = warp_aggregate;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Accumulate block aggregates and save the one that is our warp's prefix
|
||||
T warp_prefix;
|
||||
block_aggregate = temp_storage.warp_aggregates[0];
|
||||
|
||||
// Use template unrolling (since the PTX backend can't handle unrolling it for SM1x)
|
||||
ApplyWarpAggregates(warp_prefix, scan_op, block_aggregate, Int2Type<1>());
|
||||
/*
|
||||
#pragma unroll
|
||||
for (int WARP = 1; WARP < WARPS; ++WARP)
|
||||
{
|
||||
if (warp_id == WARP)
|
||||
warp_prefix = block_aggregate;
|
||||
|
||||
T addend = temp_storage.warp_aggregates[WARP];
|
||||
block_aggregate = scan_op(block_aggregate, addend);
|
||||
}
|
||||
*/
|
||||
|
||||
return warp_prefix;
|
||||
}
|
||||
|
||||
|
||||
/// Use the warp-wide aggregates and initial-value to compute the calling warp's prefix. Also returns block-wide aggregate in all threads.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ T ComputeWarpPrefix(
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T warp_aggregate, ///< [in] <b>[<em>lane</em><sub>WARP_THREADS - 1</sub> only]</b> Warp-wide aggregate reduction of input items
|
||||
T &block_aggregate, ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
const T &initial_value) ///< [in] Initial value to seed the exclusive scan
|
||||
{
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, warp_aggregate, block_aggregate);
|
||||
|
||||
warp_prefix = scan_op(initial_value, warp_prefix);
|
||||
|
||||
if (warp_id == 0)
|
||||
warp_prefix = initial_value;
|
||||
|
||||
return warp_prefix;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Exclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
// Compute block-wide exclusive scan. The exclusive output from tid0 is invalid.
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, initial_value, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
WarpScanT my_warp_scan(temp_storage.warp_scan[warp_id]);
|
||||
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
my_warp_scan.Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp. Warp prefix for warp0 is invalid.
|
||||
// T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate);
|
||||
|
||||
//--------------------------------------------------
|
||||
// Last lane in each warp shares its warp-aggregate
|
||||
if (lane_id == WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Get the warp scan partial
|
||||
T warp_inclusive, warp_prefix;
|
||||
if (lane_id < WARPS)
|
||||
{
|
||||
// Scan the warpscan partials
|
||||
T warp_val = temp_storage.warp_aggregates[lane_id];
|
||||
WarpAggregateScanT(temp_storage.inner_scan[warp_id]).Scan(warp_val, warp_inclusive, warp_prefix, scan_op);
|
||||
}
|
||||
|
||||
warp_prefix = my_warp_scan.Broadcast(warp_prefix, warp_id);
|
||||
block_aggregate = my_warp_scan.Broadcast(warp_inclusive, WARPS - 1);
|
||||
//--------------------------------------------------
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
if (warp_id != 0)
|
||||
{
|
||||
exclusive_output = scan_op(warp_prefix, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = warp_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
WarpScanT my_warp_scan(temp_storage.warp_scan[warp_id]);
|
||||
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
my_warp_scan.Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp
|
||||
// T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate, initial_value);
|
||||
|
||||
//--------------------------------------------------
|
||||
// Last lane in each warp shares its warp-aggregate
|
||||
if (lane_id == WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Get the warp scan partial
|
||||
T warp_inclusive, warp_prefix;
|
||||
if (lane_id < WARPS)
|
||||
{
|
||||
// Scan the warpscan partials
|
||||
T warp_val = temp_storage.warp_aggregates[lane_id];
|
||||
WarpAggregateScanT(temp_storage.inner_scan[warp_id]).Scan(warp_val, warp_inclusive, warp_prefix, initial_value, scan_op);
|
||||
}
|
||||
|
||||
warp_prefix = my_warp_scan.Broadcast(warp_prefix, warp_id);
|
||||
block_aggregate = my_warp_scan.Broadcast(warp_inclusive, WARPS - 1);
|
||||
//--------------------------------------------------
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
exclusive_output = scan_op(warp_prefix, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = warp_prefix;
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
// Compute block-wide exclusive scan. The exclusive output from tid0 is invalid.
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
|
||||
// Use the first warp to determine the threadblock prefix, returning the result in lane0
|
||||
if (warp_id == 0)
|
||||
{
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
if (lane_id == 0)
|
||||
{
|
||||
// Share the prefix with all threads
|
||||
temp_storage.block_prefix = block_prefix;
|
||||
exclusive_output = block_prefix; // The block prefix is the exclusive output for tid0
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Incorporate threadblock prefix into outputs
|
||||
T block_prefix = temp_storage.block_prefix;
|
||||
if (linear_tid > 0)
|
||||
{
|
||||
exclusive_output = scan_op(block_prefix, exclusive_output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Inclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
InclusiveScan(input, inclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
WarpScanT(temp_storage.warp_scan[warp_id]).InclusiveScan(input, inclusive_output, scan_op);
|
||||
|
||||
// Compute the warp-wide prefix and block-wide aggregate for each warp. Warp prefix for warp0 is invalid.
|
||||
T warp_prefix = ComputeWarpPrefix(scan_op, inclusive_output, block_aggregate);
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
if (warp_id != 0)
|
||||
{
|
||||
inclusive_output = scan_op(warp_prefix, inclusive_output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
T block_aggregate;
|
||||
InclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
|
||||
// Use the first warp to determine the threadblock prefix, returning the result in lane0
|
||||
if (warp_id == 0)
|
||||
{
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
if (lane_id == 0)
|
||||
{
|
||||
// Share the prefix with all threads
|
||||
temp_storage.block_prefix = block_prefix;
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Incorporate threadblock prefix into outputs
|
||||
T block_prefix = temp_storage.block_prefix;
|
||||
exclusive_output = scan_op(block_prefix, exclusive_output);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::BlockScanWarpscans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../util_ptx.cuh"
|
||||
#include "../../warp/warp_scan.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \brief BlockScanWarpScans provides warpscan-based variants of parallel prefix scan across a CUDA threadblock.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
|
||||
int BLOCK_DIM_Y, ///< The thread block length in threads along the Y dimension
|
||||
int BLOCK_DIM_Z, ///< The thread block length in threads along the Z dimension
|
||||
int PTX_ARCH> ///< The PTX compute capability for which to to specialize this collective
|
||||
struct BlockScanWarpScans
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
/// The thread block size in threads
|
||||
BLOCK_THREADS = BLOCK_DIM_X * BLOCK_DIM_Y * BLOCK_DIM_Z,
|
||||
|
||||
/// Number of warp threads
|
||||
INNER_WARP_THREADS = CUB_WARP_THREADS(PTX_ARCH),
|
||||
OUTER_WARP_THREADS = BLOCK_THREADS / INNER_WARP_THREADS,
|
||||
|
||||
/// Number of outer scan warps
|
||||
OUTER_WARPS = INNER_WARP_THREADS
|
||||
};
|
||||
|
||||
/// Outer WarpScan utility type
|
||||
typedef WarpScan<T, OUTER_WARP_THREADS, PTX_ARCH> OuterWarpScanT;
|
||||
|
||||
/// Inner WarpScan utility type
|
||||
typedef WarpScan<T, INNER_WARP_THREADS, PTX_ARCH> InnerWarpScanT;
|
||||
|
||||
typedef typename OuterWarpScanT::TempStorage OuterScanArray[OUTER_WARPS];
|
||||
|
||||
|
||||
/// Shared memory storage layout type
|
||||
struct _TempStorage
|
||||
{
|
||||
union
|
||||
{
|
||||
Uninitialized<OuterScanArray> outer_warp_scan; ///< Buffer for warp-synchronous outer scans
|
||||
typename InnerWarpScanT::TempStorage inner_warp_scan; ///< Buffer for warp-synchronous inner scan
|
||||
};
|
||||
T warp_aggregates[OUTER_WARPS];
|
||||
T block_aggregate; ///< Shared prefix for the entire threadblock
|
||||
};
|
||||
|
||||
|
||||
/// Alias wrapper allowing storage to be unioned
|
||||
struct TempStorage : Uninitialized<_TempStorage> {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Per-thread fields
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// Thread fields
|
||||
_TempStorage &temp_storage;
|
||||
unsigned int linear_tid;
|
||||
unsigned int warp_id;
|
||||
unsigned int lane_id;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Constructors
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
__device__ __forceinline__ BlockScanWarpScans(
|
||||
TempStorage &temp_storage)
|
||||
:
|
||||
temp_storage(temp_storage.Alias()),
|
||||
linear_tid(RowMajorTid(BLOCK_DIM_X, BLOCK_DIM_Y, BLOCK_DIM_Z)),
|
||||
warp_id((OUTER_WARPS == 1) ? 0 : linear_tid / OUTER_WARP_THREADS),
|
||||
lane_id((OUTER_WARPS == 1) ? linear_tid : linear_tid % OUTER_WARP_THREADS)
|
||||
{}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Exclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
// Compute block-wide exclusive scan. The exclusive output from tid0 is invalid.
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
ExclusiveScan(input, exclusive_output, initial_value, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs. With no initial value, the output computed for <em>thread</em><sub>0</sub> is undefined.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
OuterWarpScanT(temp_storage.outer_warp_scan.Alias()[warp_id]).Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Share outer warp total
|
||||
if (lane_id == OUTER_WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid < INNER_WARP_THREADS)
|
||||
{
|
||||
T outer_warp_input = temp_storage.warp_aggregates[linear_tid];
|
||||
T outer_warp_exclusive;
|
||||
|
||||
InnerWarpScanT(temp_storage.inner_warp_scan).ExclusiveScan(
|
||||
outer_warp_input, outer_warp_exclusive, scan_op, block_aggregate);
|
||||
|
||||
temp_storage.block_aggregate = block_aggregate;
|
||||
temp_storage.warp_aggregates[linear_tid] = outer_warp_exclusive;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (warp_id != 0)
|
||||
{
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
T outer_warp_exclusive = temp_storage.warp_aggregates[warp_id];
|
||||
exclusive_output = scan_op(outer_warp_exclusive, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = outer_warp_exclusive;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input items
|
||||
T &exclusive_output, ///< [out] Calling thread's output items (may be aliased to \p input)
|
||||
const T &initial_value, ///< [in] Initial value to seed the exclusive scan
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
OuterWarpScanT(temp_storage.outer_warp_scan.Alias()[warp_id]).Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Share outer warp total
|
||||
if (lane_id == OUTER_WARP_THREADS - 1)
|
||||
{
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid < INNER_WARP_THREADS)
|
||||
{
|
||||
T outer_warp_input = temp_storage.warp_aggregates[linear_tid];
|
||||
T outer_warp_exclusive;
|
||||
|
||||
InnerWarpScanT(temp_storage.inner_warp_scan).ExclusiveScan(
|
||||
outer_warp_input, outer_warp_exclusive, initial_value, scan_op, block_aggregate);
|
||||
|
||||
temp_storage.block_aggregate = block_aggregate;
|
||||
temp_storage.warp_aggregates[linear_tid] = outer_warp_exclusive;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
T outer_warp_exclusive = temp_storage.warp_aggregates[warp_id];
|
||||
exclusive_output = scan_op(outer_warp_exclusive, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = outer_warp_exclusive;
|
||||
}
|
||||
|
||||
|
||||
/// Computes an exclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. The call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void ExclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
T inclusive_output;
|
||||
OuterWarpScanT(temp_storage.outer_warp_scan.Alias()[warp_id]).Scan(input, inclusive_output, exclusive_output, scan_op);
|
||||
|
||||
// Share outer warp total
|
||||
if (lane_id == OUTER_WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid < INNER_WARP_THREADS)
|
||||
{
|
||||
InnerWarpScanT inner_scan(temp_storage.inner_warp_scan);
|
||||
|
||||
T upsweep = temp_storage.warp_aggregates[linear_tid];
|
||||
T downsweep_prefix, block_aggregate;
|
||||
|
||||
inner_scan.ExclusiveScan(upsweep, downsweep_prefix, scan_op, block_aggregate);
|
||||
|
||||
// Use callback functor to get block prefix in lane0 and then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = inner_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
downsweep_prefix = scan_op(block_prefix, downsweep_prefix);
|
||||
if (linear_tid == 0)
|
||||
downsweep_prefix = block_prefix;
|
||||
|
||||
temp_storage.warp_aggregates[linear_tid] = downsweep_prefix;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Apply warp prefix to our lane's partial (or assign it if partial is invalid)
|
||||
T outer_warp_exclusive = temp_storage.warp_aggregates[warp_id];
|
||||
exclusive_output = scan_op(outer_warp_exclusive, exclusive_output);
|
||||
if (lane_id == 0)
|
||||
exclusive_output = outer_warp_exclusive;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Inclusive scans
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T block_aggregate;
|
||||
InclusiveScan(input, inclusive_output, scan_op, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. Also provides every thread with the block-wide \p block_aggregate of all inputs.
|
||||
template <typename ScanOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T &block_aggregate) ///< [out] Threadblock-wide aggregate reduction of input items
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
OuterWarpScanT(temp_storage.outer_warp_scan.Alias()[warp_id]).InclusiveScan(
|
||||
input, inclusive_output, scan_op);
|
||||
|
||||
// Share outer warp total
|
||||
if (lane_id == OUTER_WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid < INNER_WARP_THREADS)
|
||||
{
|
||||
T outer_warp_input = temp_storage.warp_aggregates[linear_tid];
|
||||
T outer_warp_exclusive;
|
||||
|
||||
InnerWarpScanT(temp_storage.inner_warp_scan).ExclusiveScan(
|
||||
outer_warp_input, outer_warp_exclusive, scan_op, block_aggregate);
|
||||
|
||||
temp_storage.block_aggregate = block_aggregate;
|
||||
temp_storage.warp_aggregates[linear_tid] = outer_warp_exclusive;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (warp_id != 0)
|
||||
{
|
||||
// Retrieve block aggregate
|
||||
block_aggregate = temp_storage.block_aggregate;
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
T outer_warp_exclusive = temp_storage.warp_aggregates[warp_id];
|
||||
inclusive_output = scan_op(outer_warp_exclusive, inclusive_output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Computes an inclusive threadblock-wide prefix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_prefix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically prefixes the threadblock's scan inputs.
|
||||
template <
|
||||
typename ScanOp,
|
||||
typename BlockPrefixCallbackOp>
|
||||
__device__ __forceinline__ void InclusiveScan(
|
||||
T input, ///< [in] Calling thread's input item
|
||||
T &inclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
BlockPrefixCallbackOp &block_prefix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a threadblock-wide prefix to be applied to all inputs.
|
||||
{
|
||||
// Compute warp scan in each warp. The exclusive output from each lane0 is invalid.
|
||||
OuterWarpScanT(temp_storage.outer_warp_scan.Alias()[warp_id]).InclusiveScan(
|
||||
input, inclusive_output, scan_op);
|
||||
|
||||
// Share outer warp total
|
||||
if (lane_id == OUTER_WARP_THREADS - 1)
|
||||
temp_storage.warp_aggregates[warp_id] = inclusive_output;
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (linear_tid < INNER_WARP_THREADS)
|
||||
{
|
||||
InnerWarpScanT inner_scan(temp_storage.inner_warp_scan);
|
||||
|
||||
T upsweep = temp_storage.warp_aggregates[linear_tid];
|
||||
T downsweep_prefix, block_aggregate;
|
||||
inner_scan.ExclusiveScan(upsweep, downsweep_prefix, scan_op, block_aggregate);
|
||||
|
||||
// Use callback functor to get block prefix in lane0 and then broadcast to other lanes
|
||||
T block_prefix = block_prefix_callback_op(block_aggregate);
|
||||
block_prefix = inner_scan.Broadcast(block_prefix, 0);
|
||||
|
||||
downsweep_prefix = scan_op(block_prefix, downsweep_prefix);
|
||||
if (linear_tid == 0)
|
||||
downsweep_prefix = block_prefix;
|
||||
|
||||
temp_storage.warp_aggregates[linear_tid] = downsweep_prefix;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Apply warp prefix to our lane's partial
|
||||
T outer_warp_exclusive = temp_storage.warp_aggregates[warp_id];
|
||||
inclusive_output = scan_op(outer_warp_exclusive, inclusive_output);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* CUB umbrella include file
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
// Block
|
||||
#include "block/block_histogram.cuh"
|
||||
#include "block/block_discontinuity.cuh"
|
||||
#include "block/block_exchange.cuh"
|
||||
#include "block/block_load.cuh"
|
||||
#include "block/block_radix_rank.cuh"
|
||||
#include "block/block_radix_sort.cuh"
|
||||
#include "block/block_reduce.cuh"
|
||||
#include "block/block_scan.cuh"
|
||||
#include "block/block_store.cuh"
|
||||
//#include "block/block_shift.cuh"
|
||||
|
||||
// Device
|
||||
#include "device/device_histogram.cuh"
|
||||
#include "device/device_partition.cuh"
|
||||
#include "device/device_radix_sort.cuh"
|
||||
#include "device/device_reduce.cuh"
|
||||
#include "device/device_run_length_encode.cuh"
|
||||
#include "device/device_scan.cuh"
|
||||
#include "device/device_segmented_radix_sort.cuh"
|
||||
#include "device/device_segmented_reduce.cuh"
|
||||
#include "device/device_select.cuh"
|
||||
#include "device/device_spmv.cuh"
|
||||
|
||||
// Grid
|
||||
//#include "grid/grid_barrier.cuh"
|
||||
#include "grid/grid_even_share.cuh"
|
||||
#include "grid/grid_mapping.cuh"
|
||||
#include "grid/grid_queue.cuh"
|
||||
|
||||
// Thread
|
||||
#include "thread/thread_load.cuh"
|
||||
#include "thread/thread_operators.cuh"
|
||||
#include "thread/thread_reduce.cuh"
|
||||
#include "thread/thread_scan.cuh"
|
||||
#include "thread/thread_store.cuh"
|
||||
|
||||
// Warp
|
||||
#include "warp/warp_reduce.cuh"
|
||||
#include "warp/warp_scan.cuh"
|
||||
|
||||
// Iterator
|
||||
#include "iterator/arg_index_input_iterator.cuh"
|
||||
#include "iterator/cache_modified_input_iterator.cuh"
|
||||
#include "iterator/cache_modified_output_iterator.cuh"
|
||||
#include "iterator/constant_input_iterator.cuh"
|
||||
#include "iterator/counting_input_iterator.cuh"
|
||||
#include "iterator/tex_obj_input_iterator.cuh"
|
||||
#include "iterator/tex_ref_input_iterator.cuh"
|
||||
#include "iterator/transform_input_iterator.cuh"
|
||||
|
||||
// Util
|
||||
#include "util_allocator.cuh"
|
||||
#include "util_arch.cuh"
|
||||
#include "util_debug.cuh"
|
||||
#include "util_device.cuh"
|
||||
#include "util_macro.cuh"
|
||||
#include "util_ptx.cuh"
|
||||
#include "util_type.cuh"
|
||||
|
||||
|
|
@ -0,0 +1,866 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceHistogram provides device-wide parallel operations for constructing histogram(s) from a sequence of samples data residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include "dispatch/dispatch_histogram.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceHistogram provides device-wide parallel operations for constructing histogram(s) from a sequence of samples data residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* A <a href="http://en.wikipedia.org/wiki/Histogram"><em>histogram</em></a>
|
||||
* counts the number of observations that fall into each of the disjoint categories (known as <em>bins</em>).
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceHistogram}
|
||||
*
|
||||
*/
|
||||
struct DeviceHistogram
|
||||
{
|
||||
/******************************************************************//**
|
||||
* \name Evenly-segmented bin ranges
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Computes an intensity histogram from a sequence of data samples using equal-width bins.
|
||||
*
|
||||
* \par
|
||||
* - The number of histogram bins is (\p num_levels - 1)
|
||||
* - All bins comprise the same width of sample values: (\p upper_level - \p lower_level) / (\p num_levels - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of a six-bin histogram
|
||||
* from a sequence of float samples
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples and
|
||||
* // output histogram
|
||||
* int num_samples; // e.g., 10
|
||||
* float* d_samples; // e.g., [2.2, 6.0, 7.1, 2.9, 3.5, 0.3, 2.9, 2.0, 6.1, 999.5]
|
||||
* int* d_histogram; // e.g., [ -, -, -, -, -, -, -, -]
|
||||
* int num_levels; // e.g., 7 (seven level boundaries for six bins)
|
||||
* float lower_level; // e.g., 0.0 (lower sample value boundary of lowest bin)
|
||||
* float upper_level; // e.g., 12.0 (upper sample value boundary of upper bin)
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::HistogramEven(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level, num_samples);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::HistogramEven(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level, num_samples);
|
||||
*
|
||||
* // d_histogram <-- [1, 0, 5, 0, 3, 0, 0, 0];
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t HistogramEven(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the input sequence of data samples.
|
||||
CounterT* d_histogram, ///< [out] The pointer to the histogram counter output array of length <tt>num_levels</tt> - 1.
|
||||
int num_levels, ///< [in] The number of boundaries (levels) for delineating histogram samples. Implies that the number of bins is <tt>num_levels</tt> - 1.
|
||||
LevelT lower_level, ///< [in] The lower sample value bound (inclusive) for the lowest histogram bin.
|
||||
LevelT upper_level, ///< [in] The upper sample value bound (exclusive) for the highest histogram bin.
|
||||
OffsetT num_samples, ///< [in] The number of input samples (i.e., the length of \p d_samples)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
|
||||
CounterT* d_histogram1[1] = {d_histogram};
|
||||
int num_levels1[1] = {num_levels};
|
||||
LevelT lower_level1[1] = {lower_level};
|
||||
LevelT upper_level1[1] = {upper_level};
|
||||
|
||||
return MultiHistogramEven<1, 1>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram1,
|
||||
num_levels1,
|
||||
lower_level1,
|
||||
upper_level1,
|
||||
num_samples,
|
||||
1,
|
||||
sizeof(SampleT) * num_samples,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes an intensity histogram from a sequence of data samples using equal-width bins.
|
||||
*
|
||||
* \par
|
||||
* - A two-dimensional <em>region of interest</em> within \p d_samples can be specified
|
||||
* using the \p num_row_samples, num_rows, and \p row_stride_bytes parameters.
|
||||
* - The row stride must be a whole multiple of the sample data type
|
||||
* size, i.e., <tt>(row_stride_bytes % sizeof(SampleT)) == 0</tt>.
|
||||
* - The number of histogram bins is (\p num_levels - 1)
|
||||
* - All bins comprise the same width of sample values: (\p upper_level - \p lower_level) / (\p num_levels - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of a six-bin histogram
|
||||
* from a 2x5 region of interest within a flattened 2x7 array of float samples.
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples and
|
||||
* // output histogram
|
||||
* int num_row_samples; // e.g., 5
|
||||
* int num_rows; // e.g., 2;
|
||||
* size_t row_stride_bytes; // e.g., 7 * sizeof(float)
|
||||
* float* d_samples; // e.g., [2.2, 6.0, 7.1, 2.9, 3.5, -, -,
|
||||
* // 0.3, 2.9, 2.0, 6.1, 999.5, -, -]
|
||||
* int* d_histogram; // e.g., [ -, -, -, -, -, -, -, -]
|
||||
* int num_levels; // e.g., 7 (seven level boundaries for six bins)
|
||||
* float lower_level; // e.g., 0.0 (lower sample value boundary of lowest bin)
|
||||
* float upper_level; // e.g., 12.0 (upper sample value boundary of upper bin)
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::HistogramEven(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
* num_row_samples, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::HistogramEven(d_temp_storage, temp_storage_bytes, d_samples, d_histogram,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
* num_row_samples, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // d_histogram <-- [1, 0, 5, 0, 3, 0, 0, 0];
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t HistogramEven(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the input sequence of data samples.
|
||||
CounterT* d_histogram, ///< [out] The pointer to the histogram counter output array of length <tt>num_levels</tt> - 1.
|
||||
int num_levels, ///< [in] The number of boundaries (levels) for delineating histogram samples. Implies that the number of bins is <tt>num_levels</tt> - 1.
|
||||
LevelT lower_level, ///< [in] The lower sample value bound (inclusive) for the lowest histogram bin.
|
||||
LevelT upper_level, ///< [in] The upper sample value bound (exclusive) for the highest histogram bin.
|
||||
OffsetT num_row_samples, ///< [in] The number of data samples per row in the region of interest
|
||||
OffsetT num_rows, ///< [in] The number of rows in the region of interest
|
||||
size_t row_stride_bytes, ///< [in] The number of bytes between starts of consecutive rows in the region of interest
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
CounterT* d_histogram1[1] = {d_histogram};
|
||||
int num_levels1[1] = {num_levels};
|
||||
LevelT lower_level1[1] = {lower_level};
|
||||
LevelT upper_level1[1] = {upper_level};
|
||||
|
||||
return MultiHistogramEven<1, 1>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram1,
|
||||
num_levels1,
|
||||
lower_level1,
|
||||
upper_level1,
|
||||
num_row_samples,
|
||||
num_rows,
|
||||
row_stride_bytes,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Computes per-channel intensity histograms from a sequence of multi-channel "pixel" data samples using equal-width bins.
|
||||
*
|
||||
* \par
|
||||
* - The input is a sequence of <em>pixel</em> structures, where each pixel comprises
|
||||
* a record of \p NUM_CHANNELS consecutive data samples (e.g., an <em>RGBA</em> pixel).
|
||||
* - Of the \p NUM_CHANNELS specified, the function will only compute histograms
|
||||
* for the first \p NUM_ACTIVE_CHANNELS (e.g., only <em>RGB</em> histograms from <em>RGBA</em>
|
||||
* pixel samples).
|
||||
* - The number of histogram bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
* - For channel<sub><em>i</em></sub>, the range of values for all histogram bins
|
||||
* have the same width: (<tt>upper_level[i]</tt> - <tt>lower_level[i]</tt>) / (<tt> num_levels[i]</tt> - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of three 256-bin <em>RGB</em> histograms
|
||||
* from a quad-channel sequence of <em>RGBA</em> pixels (8 bits per channel per pixel)
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples
|
||||
* // and output histograms
|
||||
* int num_pixels; // e.g., 5
|
||||
* unsigned char* d_samples; // e.g., [(2, 6, 7, 5), (3, 0, 2, 1), (7, 0, 6, 2),
|
||||
* // (0, 6, 7, 5), (3, 0, 2, 6)]
|
||||
* int* d_histogram[3]; // e.g., three device pointers to three device buffers,
|
||||
* // each allocated with 256 integer counters
|
||||
* int num_levels[3]; // e.g., {257, 257, 257};
|
||||
* unsigned int lower_level[3]; // e.g., {0, 0, 0};
|
||||
* unsigned int upper_level[3]; // e.g., {256, 256, 256};
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::MultiHistogramEven<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level, num_pixels);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::MultiHistogramEven<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level, num_pixels);
|
||||
*
|
||||
* // d_histogram <-- [ [1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 0, ..., 0],
|
||||
* // [0, 3, 0, 0, 0, 0, 2, 0, 0, 0, 0, ..., 0],
|
||||
* // [0, 0, 2, 0, 0, 0, 1, 2, 0, 0, 0, ..., 0] ]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam NUM_CHANNELS Number of channels interleaved in the input data (may be greater than the number of channels being actively histogrammed)
|
||||
* \tparam NUM_ACTIVE_CHANNELS <b>[inferred]</b> Number of channels actively being histogrammed
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
int NUM_CHANNELS,
|
||||
int NUM_ACTIVE_CHANNELS,
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t MultiHistogramEven(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the multi-channel input sequence of data samples. The samples from different channels are assumed to be interleaved (e.g., an array of 32-bit pixels where each pixel consists of four <em>RGBA</em> 8-bit samples).
|
||||
CounterT* d_histogram[NUM_ACTIVE_CHANNELS], ///< [out] The pointers to the histogram counter output arrays, one for each active channel. For channel<sub><em>i</em></sub>, the allocation length of <tt>d_histogram[i]</tt> should be <tt>num_levels[i]</tt> - 1.
|
||||
int num_levels[NUM_ACTIVE_CHANNELS], ///< [in] The number of boundaries (levels) for delineating histogram samples in each active channel. Implies that the number of bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
LevelT lower_level[NUM_ACTIVE_CHANNELS], ///< [in] The lower sample value bound (inclusive) for the lowest histogram bin in each active channel.
|
||||
LevelT upper_level[NUM_ACTIVE_CHANNELS], ///< [in] The upper sample value bound (exclusive) for the highest histogram bin in each active channel.
|
||||
OffsetT num_pixels, ///< [in] The number of multi-channel pixels (i.e., the length of \p d_samples / NUM_CHANNELS)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
|
||||
return MultiHistogramEven<NUM_CHANNELS, NUM_ACTIVE_CHANNELS>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram,
|
||||
num_levels,
|
||||
lower_level,
|
||||
upper_level,
|
||||
num_pixels,
|
||||
1,
|
||||
sizeof(SampleT) * NUM_CHANNELS * num_pixels,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes per-channel intensity histograms from a sequence of multi-channel "pixel" data samples using equal-width bins.
|
||||
*
|
||||
* \par
|
||||
* - The input is a sequence of <em>pixel</em> structures, where each pixel comprises
|
||||
* a record of \p NUM_CHANNELS consecutive data samples (e.g., an <em>RGBA</em> pixel).
|
||||
* - Of the \p NUM_CHANNELS specified, the function will only compute histograms
|
||||
* for the first \p NUM_ACTIVE_CHANNELS (e.g., only <em>RGB</em> histograms from <em>RGBA</em>
|
||||
* pixel samples).
|
||||
* - A two-dimensional <em>region of interest</em> within \p d_samples can be specified
|
||||
* using the \p num_row_samples, num_rows, and \p row_stride_bytes parameters.
|
||||
* - The row stride must be a whole multiple of the sample data type
|
||||
* size, i.e., <tt>(row_stride_bytes % sizeof(SampleT)) == 0</tt>.
|
||||
* - The number of histogram bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
* - For channel<sub><em>i</em></sub>, the range of values for all histogram bins
|
||||
* have the same width: (<tt>upper_level[i]</tt> - <tt>lower_level[i]</tt>) / (<tt> num_levels[i]</tt> - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of three 256-bin <em>RGB</em> histograms from a 2x3 region of
|
||||
* interest of within a flattened 2x4 array of quad-channel <em>RGBA</em> pixels (8 bits per channel per pixel).
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples
|
||||
* // and output histograms
|
||||
* int num_row_pixels; // e.g., 3
|
||||
* int num_rows; // e.g., 2
|
||||
* size_t row_stride_bytes; // e.g., 4 * sizeof(unsigned char) * NUM_CHANNELS
|
||||
* unsigned char* d_samples; // e.g., [(2, 6, 7, 5), (3, 0, 2, 1), (7, 0, 6, 2), (-, -, -, -),
|
||||
* // (0, 6, 7, 5), (3, 0, 2, 6), (1, 1, 1, 1), (-, -, -, -)]
|
||||
* int* d_histogram[3]; // e.g., three device pointers to three device buffers,
|
||||
* // each allocated with 256 integer counters
|
||||
* int num_levels[3]; // e.g., {257, 257, 257};
|
||||
* unsigned int lower_level[3]; // e.g., {0, 0, 0};
|
||||
* unsigned int upper_level[3]; // e.g., {256, 256, 256};
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::MultiHistogramEven<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
* num_row_pixels, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::MultiHistogramEven<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
* num_row_pixels, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // d_histogram <-- [ [1, 1, 1, 2, 0, 0, 0, 1, 0, 0, 0, ..., 0],
|
||||
* // [0, 4, 0, 0, 0, 0, 2, 0, 0, 0, 0, ..., 0],
|
||||
* // [0, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, ..., 0] ]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam NUM_CHANNELS Number of channels interleaved in the input data (may be greater than the number of channels being actively histogrammed)
|
||||
* \tparam NUM_ACTIVE_CHANNELS <b>[inferred]</b> Number of channels actively being histogrammed
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
int NUM_CHANNELS,
|
||||
int NUM_ACTIVE_CHANNELS,
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t MultiHistogramEven(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the multi-channel input sequence of data samples. The samples from different channels are assumed to be interleaved (e.g., an array of 32-bit pixels where each pixel consists of four <em>RGBA</em> 8-bit samples).
|
||||
CounterT* d_histogram[NUM_ACTIVE_CHANNELS], ///< [out] The pointers to the histogram counter output arrays, one for each active channel. For channel<sub><em>i</em></sub>, the allocation length of <tt>d_histogram[i]</tt> should be <tt>num_levels[i]</tt> - 1.
|
||||
int num_levels[NUM_ACTIVE_CHANNELS], ///< [in] The number of boundaries (levels) for delineating histogram samples in each active channel. Implies that the number of bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
LevelT lower_level[NUM_ACTIVE_CHANNELS], ///< [in] The lower sample value bound (inclusive) for the lowest histogram bin in each active channel.
|
||||
LevelT upper_level[NUM_ACTIVE_CHANNELS], ///< [in] The upper sample value bound (exclusive) for the highest histogram bin in each active channel.
|
||||
OffsetT num_row_pixels, ///< [in] The number of multi-channel pixels per row in the region of interest
|
||||
OffsetT num_rows, ///< [in] The number of rows in the region of interest
|
||||
size_t row_stride_bytes, ///< [in] The number of bytes between starts of consecutive rows in the region of interest
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
Int2Type<sizeof(SampleT) == 1> is_byte_sample;
|
||||
|
||||
if ((sizeof(OffsetT) > sizeof(int)) &&
|
||||
((unsigned long long) (num_rows * row_stride_bytes) < (unsigned long long) std::numeric_limits<int>::max()))
|
||||
{
|
||||
// Down-convert OffsetT data type
|
||||
|
||||
|
||||
return DipatchHistogram<NUM_CHANNELS, NUM_ACTIVE_CHANNELS, SampleIteratorT, CounterT, LevelT, int>::DispatchEven(
|
||||
d_temp_storage, temp_storage_bytes, d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
(int) num_row_pixels, (int) num_rows, (int) (row_stride_bytes / sizeof(SampleT)),
|
||||
stream, debug_synchronous, is_byte_sample);
|
||||
}
|
||||
|
||||
return DipatchHistogram<NUM_CHANNELS, NUM_ACTIVE_CHANNELS, SampleIteratorT, CounterT, LevelT, OffsetT>::DispatchEven(
|
||||
d_temp_storage, temp_storage_bytes, d_samples, d_histogram, num_levels, lower_level, upper_level,
|
||||
num_row_pixels, num_rows, (OffsetT) (row_stride_bytes / sizeof(SampleT)),
|
||||
stream, debug_synchronous, is_byte_sample);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Custom bin ranges
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Computes an intensity histogram from a sequence of data samples using the specified bin boundary levels.
|
||||
*
|
||||
* \par
|
||||
* - The number of histogram bins is (\p num_levels - 1)
|
||||
* - The value range for bin<sub><em>i</em></sub> is [<tt>level[i]</tt>, <tt>level[i+1]</tt>)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of an six-bin histogram
|
||||
* from a sequence of float samples
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples and
|
||||
* // output histogram
|
||||
* int num_samples; // e.g., 10
|
||||
* float* d_samples; // e.g., [2.2, 6.0, 7.1, 2.9, 3.5, 0.3, 2.9, 2.0, 6.1, 999.5]
|
||||
* int* d_histogram; // e.g., [ -, -, -, -, -, -, -, -]
|
||||
* int num_levels // e.g., 7 (seven level boundaries for six bins)
|
||||
* float* d_levels; // e.g., [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 16.0]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::HistogramRange(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_samples);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::HistogramRange(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_samples);
|
||||
*
|
||||
* // d_histogram <-- [1, 0, 5, 0, 3, 0, 0, 0];
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t HistogramRange(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the input sequence of data samples.
|
||||
CounterT* d_histogram, ///< [out] The pointer to the histogram counter output array of length <tt>num_levels</tt> - 1.
|
||||
int num_levels, ///< [in] The number of boundaries (levels) for delineating histogram samples. Implies that the number of bins is <tt>num_levels</tt> - 1.
|
||||
LevelT* d_levels, ///< [in] The pointer to the array of boundaries (levels). Bin ranges are defined by consecutive boundary pairings: lower sample value boundaries are inclusive and upper sample value boundaries are exclusive.
|
||||
OffsetT num_samples, ///< [in] The number of data samples per row in the region of interest
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
|
||||
CounterT* d_histogram1[1] = {d_histogram};
|
||||
int num_levels1[1] = {num_levels};
|
||||
LevelT* d_levels1[1] = {d_levels};
|
||||
|
||||
return MultiHistogramRange<1, 1>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram1,
|
||||
num_levels1,
|
||||
d_levels1,
|
||||
num_samples,
|
||||
1,
|
||||
sizeof(SampleT) * num_samples,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes an intensity histogram from a sequence of data samples using the specified bin boundary levels.
|
||||
*
|
||||
* \par
|
||||
* - A two-dimensional <em>region of interest</em> within \p d_samples can be specified
|
||||
* using the \p num_row_samples, num_rows, and \p row_stride_bytes parameters.
|
||||
* - The row stride must be a whole multiple of the sample data type
|
||||
* size, i.e., <tt>(row_stride_bytes % sizeof(SampleT)) == 0</tt>.
|
||||
* - The number of histogram bins is (\p num_levels - 1)
|
||||
* - The value range for bin<sub><em>i</em></sub> is [<tt>level[i]</tt>, <tt>level[i+1]</tt>)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of a six-bin histogram
|
||||
* from a 2x5 region of interest within a flattened 2x7 array of float samples.
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples and
|
||||
* // output histogram
|
||||
* int num_row_samples; // e.g., 5
|
||||
* int num_rows; // e.g., 2;
|
||||
* int row_stride_bytes; // e.g., 7 * sizeof(float)
|
||||
* float* d_samples; // e.g., [2.2, 6.0, 7.1, 2.9, 3.5, -, -,
|
||||
* // 0.3, 2.9, 2.0, 6.1, 999.5, -, -]
|
||||
* int* d_histogram; // e.g., [ , , , , , , , ]
|
||||
* int num_levels // e.g., 7 (seven level boundaries for six bins)
|
||||
* float *d_levels; // e.g., [0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 16.0]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::HistogramRange(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels,
|
||||
* num_row_samples, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::HistogramRange(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels,
|
||||
* num_row_samples, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // d_histogram <-- [1, 0, 5, 0, 3, 0, 0, 0];
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t HistogramRange(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the input sequence of data samples.
|
||||
CounterT* d_histogram, ///< [out] The pointer to the histogram counter output array of length <tt>num_levels</tt> - 1.
|
||||
int num_levels, ///< [in] The number of boundaries (levels) for delineating histogram samples. Implies that the number of bins is <tt>num_levels</tt> - 1.
|
||||
LevelT* d_levels, ///< [in] The pointer to the array of boundaries (levels). Bin ranges are defined by consecutive boundary pairings: lower sample value boundaries are inclusive and upper sample value boundaries are exclusive.
|
||||
OffsetT num_row_samples, ///< [in] The number of data samples per row in the region of interest
|
||||
OffsetT num_rows, ///< [in] The number of rows in the region of interest
|
||||
size_t row_stride_bytes, ///< [in] The number of bytes between starts of consecutive rows in the region of interest
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
CounterT* d_histogram1[1] = {d_histogram};
|
||||
int num_levels1[1] = {num_levels};
|
||||
LevelT* d_levels1[1] = {d_levels};
|
||||
|
||||
return MultiHistogramRange<1, 1>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram1,
|
||||
num_levels1,
|
||||
d_levels1,
|
||||
num_row_samples,
|
||||
num_rows,
|
||||
row_stride_bytes,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Computes per-channel intensity histograms from a sequence of multi-channel "pixel" data samples using the specified bin boundary levels.
|
||||
*
|
||||
* \par
|
||||
* - The input is a sequence of <em>pixel</em> structures, where each pixel comprises
|
||||
* a record of \p NUM_CHANNELS consecutive data samples (e.g., an <em>RGBA</em> pixel).
|
||||
* - Of the \p NUM_CHANNELS specified, the function will only compute histograms
|
||||
* for the first \p NUM_ACTIVE_CHANNELS (e.g., <em>RGB</em> histograms from <em>RGBA</em>
|
||||
* pixel samples).
|
||||
* - The number of histogram bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
* - For channel<sub><em>i</em></sub>, the range of values for all histogram bins
|
||||
* have the same width: (<tt>upper_level[i]</tt> - <tt>lower_level[i]</tt>) / (<tt> num_levels[i]</tt> - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of three 4-bin <em>RGB</em> histograms
|
||||
* from a quad-channel sequence of <em>RGBA</em> pixels (8 bits per channel per pixel)
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples
|
||||
* // and output histograms
|
||||
* int num_pixels; // e.g., 5
|
||||
* unsigned char *d_samples; // e.g., [(2, 6, 7, 5),(3, 0, 2, 1),(7, 0, 6, 2),
|
||||
* // (0, 6, 7, 5),(3, 0, 2, 6)]
|
||||
* unsigned int *d_histogram[3]; // e.g., [[ -, -, -, -],[ -, -, -, -],[ -, -, -, -]];
|
||||
* int num_levels[3]; // e.g., {5, 5, 5};
|
||||
* unsigned int *d_levels[3]; // e.g., [ [0, 2, 4, 6, 8],
|
||||
* // [0, 2, 4, 6, 8],
|
||||
* // [0, 2, 4, 6, 8] ];
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::MultiHistogramRange<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_pixels);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::MultiHistogramRange<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_pixels);
|
||||
*
|
||||
* // d_histogram <-- [ [1, 3, 0, 1],
|
||||
* // [3, 0, 0, 2],
|
||||
* // [0, 2, 0, 3] ]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam NUM_CHANNELS Number of channels interleaved in the input data (may be greater than the number of channels being actively histogrammed)
|
||||
* \tparam NUM_ACTIVE_CHANNELS <b>[inferred]</b> Number of channels actively being histogrammed
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
int NUM_CHANNELS,
|
||||
int NUM_ACTIVE_CHANNELS,
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t MultiHistogramRange(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the multi-channel input sequence of data samples. The samples from different channels are assumed to be interleaved (e.g., an array of 32-bit pixels where each pixel consists of four <em>RGBA</em> 8-bit samples).
|
||||
CounterT* d_histogram[NUM_ACTIVE_CHANNELS], ///< [out] The pointers to the histogram counter output arrays, one for each active channel. For channel<sub><em>i</em></sub>, the allocation length of <tt>d_histogram[i]</tt> should be <tt>num_levels[i]</tt> - 1.
|
||||
int num_levels[NUM_ACTIVE_CHANNELS], ///< [in] The number of boundaries (levels) for delineating histogram samples in each active channel. Implies that the number of bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
LevelT* d_levels[NUM_ACTIVE_CHANNELS], ///< [in] The pointers to the arrays of boundaries (levels), one for each active channel. Bin ranges are defined by consecutive boundary pairings: lower sample value boundaries are inclusive and upper sample value boundaries are exclusive.
|
||||
OffsetT num_pixels, ///< [in] The number of multi-channel pixels (i.e., the length of \p d_samples / NUM_CHANNELS)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
|
||||
return MultiHistogramRange<NUM_CHANNELS, NUM_ACTIVE_CHANNELS>(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples,
|
||||
d_histogram,
|
||||
num_levels,
|
||||
d_levels,
|
||||
num_pixels,
|
||||
1,
|
||||
sizeof(SampleT) * NUM_CHANNELS * num_pixels,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes per-channel intensity histograms from a sequence of multi-channel "pixel" data samples using the specified bin boundary levels.
|
||||
*
|
||||
* \par
|
||||
* - The input is a sequence of <em>pixel</em> structures, where each pixel comprises
|
||||
* a record of \p NUM_CHANNELS consecutive data samples (e.g., an <em>RGBA</em> pixel).
|
||||
* - Of the \p NUM_CHANNELS specified, the function will only compute histograms
|
||||
* for the first \p NUM_ACTIVE_CHANNELS (e.g., <em>RGB</em> histograms from <em>RGBA</em>
|
||||
* pixel samples).
|
||||
* - A two-dimensional <em>region of interest</em> within \p d_samples can be specified
|
||||
* using the \p num_row_samples, num_rows, and \p row_stride_bytes parameters.
|
||||
* - The row stride must be a whole multiple of the sample data type
|
||||
* size, i.e., <tt>(row_stride_bytes % sizeof(SampleT)) == 0</tt>.
|
||||
* - The number of histogram bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
* - For channel<sub><em>i</em></sub>, the range of values for all histogram bins
|
||||
* have the same width: (<tt>upper_level[i]</tt> - <tt>lower_level[i]</tt>) / (<tt> num_levels[i]</tt> - 1)
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the computation of three 4-bin <em>RGB</em> histograms from a 2x3 region of
|
||||
* interest of within a flattened 2x4 array of quad-channel <em>RGBA</em> pixels (8 bits per channel per pixel).
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_histogram.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input samples
|
||||
* // and output histograms
|
||||
* int num_row_pixels; // e.g., 3
|
||||
* int num_rows; // e.g., 2
|
||||
* size_t row_stride_bytes; // e.g., 4 * sizeof(unsigned char) * NUM_CHANNELS
|
||||
* unsigned char* d_samples; // e.g., [(2, 6, 7, 5),(3, 0, 2, 1),(1, 1, 1, 1),(-, -, -, -),
|
||||
* // (7, 0, 6, 2),(0, 6, 7, 5),(3, 0, 2, 6),(-, -, -, -)]
|
||||
* int* d_histogram[3]; // e.g., [[ -, -, -, -],[ -, -, -, -],[ -, -, -, -]];
|
||||
* int num_levels[3]; // e.g., {5, 5, 5};
|
||||
* unsigned int* d_levels[3]; // e.g., [ [0, 2, 4, 6, 8],
|
||||
* // [0, 2, 4, 6, 8],
|
||||
* // [0, 2, 4, 6, 8] ];
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceHistogram::MultiHistogramRange<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_row_pixels, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Compute histograms
|
||||
* cub::DeviceHistogram::MultiHistogramRange<4, 3>(d_temp_storage, temp_storage_bytes,
|
||||
* d_samples, d_histogram, num_levels, d_levels, num_row_pixels, num_rows, row_stride_bytes);
|
||||
*
|
||||
* // d_histogram <-- [ [2, 3, 0, 1],
|
||||
* // [3, 0, 0, 2],
|
||||
* // [1, 2, 0, 3] ]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam NUM_CHANNELS Number of channels interleaved in the input data (may be greater than the number of channels being actively histogrammed)
|
||||
* \tparam NUM_ACTIVE_CHANNELS <b>[inferred]</b> Number of channels actively being histogrammed
|
||||
* \tparam SampleIteratorT <b>[inferred]</b> Random-access input iterator type for reading input samples. \iterator
|
||||
* \tparam CounterT <b>[inferred]</b> Integer type for histogram bin counters
|
||||
* \tparam LevelT <b>[inferred]</b> Type for specifying boundaries (levels)
|
||||
* \tparam OffsetT <b>[inferred]</b> Signed integer type for sequence offsets, list lengths, pointer differences, etc. \offset_size1
|
||||
*/
|
||||
template <
|
||||
int NUM_CHANNELS,
|
||||
int NUM_ACTIVE_CHANNELS,
|
||||
typename SampleIteratorT,
|
||||
typename CounterT,
|
||||
typename LevelT,
|
||||
typename OffsetT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t MultiHistogramRange(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SampleIteratorT d_samples, ///< [in] The pointer to the multi-channel input sequence of data samples. The samples from different channels are assumed to be interleaved (e.g., an array of 32-bit pixels where each pixel consists of four <em>RGBA</em> 8-bit samples).
|
||||
CounterT* d_histogram[NUM_ACTIVE_CHANNELS], ///< [out] The pointers to the histogram counter output arrays, one for each active channel. For channel<sub><em>i</em></sub>, the allocation length of <tt>d_histogram[i]</tt> should be <tt>num_levels[i]</tt> - 1.
|
||||
int num_levels[NUM_ACTIVE_CHANNELS], ///< [in] The number of boundaries (levels) for delineating histogram samples in each active channel. Implies that the number of bins for channel<sub><em>i</em></sub> is <tt>num_levels[i]</tt> - 1.
|
||||
LevelT* d_levels[NUM_ACTIVE_CHANNELS], ///< [in] The pointers to the arrays of boundaries (levels), one for each active channel. Bin ranges are defined by consecutive boundary pairings: lower sample value boundaries are inclusive and upper sample value boundaries are exclusive.
|
||||
OffsetT num_row_pixels, ///< [in] The number of multi-channel pixels per row in the region of interest
|
||||
OffsetT num_rows, ///< [in] The number of rows in the region of interest
|
||||
size_t row_stride_bytes, ///< [in] The number of bytes between starts of consecutive rows in the region of interest
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
/// The sample value type of the input iterator
|
||||
typedef typename std::iterator_traits<SampleIteratorT>::value_type SampleT;
|
||||
Int2Type<sizeof(SampleT) == 1> is_byte_sample;
|
||||
|
||||
if ((sizeof(OffsetT) > sizeof(int)) &&
|
||||
((unsigned long long) (num_rows * row_stride_bytes) < (unsigned long long) std::numeric_limits<int>::max()))
|
||||
{
|
||||
// Down-convert OffsetT data type
|
||||
return DipatchHistogram<NUM_CHANNELS, NUM_ACTIVE_CHANNELS, SampleIteratorT, CounterT, LevelT, int>::DispatchRange(
|
||||
d_temp_storage, temp_storage_bytes, d_samples, d_histogram, num_levels, d_levels,
|
||||
(int) num_row_pixels, (int) num_rows, (int) (row_stride_bytes / sizeof(SampleT)),
|
||||
stream, debug_synchronous, is_byte_sample);
|
||||
}
|
||||
|
||||
return DipatchHistogram<NUM_CHANNELS, NUM_ACTIVE_CHANNELS, SampleIteratorT, CounterT, LevelT, OffsetT>::DispatchRange(
|
||||
d_temp_storage, temp_storage_bytes, d_samples, d_histogram, num_levels, d_levels,
|
||||
num_row_pixels, num_rows, (OffsetT) (row_stride_bytes / sizeof(SampleT)),
|
||||
stream, debug_synchronous, is_byte_sample);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//@} end member group
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DevicePartition provides device-wide, parallel operations for partitioning sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_select_if.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DevicePartition provides device-wide, parallel operations for partitioning sequences of data items residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* These operations apply a selection criterion to construct a partitioned output sequence from items selected/unselected from
|
||||
* a specified input sequence.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DevicePartition}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{partition}
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DevicePartition::If
|
||||
* performance across different CUDA architectures for \p int32 items,
|
||||
* where 50% of the items are randomly selected for the first partition.
|
||||
* \plots_below
|
||||
*
|
||||
* \image html partition_if_int32_50_percent.png
|
||||
*
|
||||
*/
|
||||
struct DevicePartition
|
||||
{
|
||||
/**
|
||||
* \brief Uses the \p d_flags sequence to split the corresponding items from \p d_in into a partitioned sequence \p d_out. The total number of items copied into the first partition is written to \p d_num_selected_out. 
|
||||
*
|
||||
* \par
|
||||
* - The value type of \p d_flags must be castable to \p bool (e.g., \p bool, \p char, \p int, etc.).
|
||||
* - Copies of the selected items are compacted into \p d_out and maintain their original
|
||||
* relative ordering, however copies of the unselected items are compacted into the
|
||||
* rear of \p d_out in reverse order.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the compaction of items selected from an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_partition.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input, flags, and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
* char *d_flags; // e.g., [1, 0, 0, 1, 0, 1, 1, 0]
|
||||
* int *d_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_selected_out; // e.g., [ ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DevicePartition::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run selection
|
||||
* cub::DevicePartition::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // d_out <-- [1, 4, 6, 7, 8, 5, 3, 2]
|
||||
* // d_num_selected_out <-- [4]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam FlagIterator <b>[inferred]</b> Random-access input iterator type for reading selection flags \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing output items \iterator
|
||||
* \tparam NumSelectedIteratorT <b>[inferred]</b> Output iterator type for recording the number of items selected \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename FlagIterator,
|
||||
typename OutputIteratorT,
|
||||
typename NumSelectedIteratorT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Flagged(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
FlagIterator d_flags, ///< [in] Pointer to the input sequence of selection flags
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of partitioned data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the output total number of items selected (i.e., the offset of the unselected partition)
|
||||
int num_items, ///< [in] Total number of items to select from
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType SelectOp; // Selection op (not used)
|
||||
typedef NullType EqualityOp; // Equality operator (not used)
|
||||
|
||||
return DispatchSelectIf<InputIteratorT, FlagIterator, OutputIteratorT, NumSelectedIteratorT, SelectOp, EqualityOp, OffsetT, true>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_flags,
|
||||
d_out,
|
||||
d_num_selected_out,
|
||||
SelectOp(),
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Uses the \p select_op functor to split the corresponding items from \p d_in into a partitioned sequence \p d_out. The total number of items copied into the first partition is written to \p d_num_selected_out. 
|
||||
*
|
||||
* \par
|
||||
* - Copies of the selected items are compacted into \p d_out and maintain their original
|
||||
* relative ordering, however copies of the unselected items are compacted into the
|
||||
* rear of \p d_out in reverse order.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated partition-if performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively. Items are
|
||||
* selected for the first partition with 50% probability.
|
||||
*
|
||||
* \image html partition_if_int32_50_percent.png
|
||||
* \image html partition_if_int64_50_percent.png
|
||||
*
|
||||
* \par
|
||||
* The following charts are similar, but 5% selection probability for the first partition:
|
||||
*
|
||||
* \image html partition_if_int32_5_percent.png
|
||||
* \image html partition_if_int64_5_percent.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the compaction of items selected from an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_partition.cuh>
|
||||
*
|
||||
* // Functor type for selecting values less than some criteria
|
||||
* struct LessThan
|
||||
* {
|
||||
* int compare;
|
||||
*
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* LessThan(int compare) : compare(compare) {}
|
||||
*
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* bool operator()(const int &a) const {
|
||||
* return (a < compare);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [0, 2, 3, 9, 5, 2, 81, 8]
|
||||
* int *d_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_selected_out; // e.g., [ ]
|
||||
* LessThan select_op(7);
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run selection
|
||||
* cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);
|
||||
*
|
||||
* // d_out <-- [0, 2, 3, 5, 2, 8, 81, 9]
|
||||
* // d_num_selected_out <-- [5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing output items \iterator
|
||||
* \tparam NumSelectedIteratorT <b>[inferred]</b> Output iterator type for recording the number of items selected \iterator
|
||||
* \tparam SelectOp <b>[inferred]</b> Selection functor type having member <tt>bool operator()(const T &a)</tt>
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename NumSelectedIteratorT,
|
||||
typename SelectOp>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t If(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of partitioned data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the output total number of items selected (i.e., the offset of the unselected partition)
|
||||
int num_items, ///< [in] Total number of items to select from
|
||||
SelectOp select_op, ///< [in] Unary selection operator
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType* FlagIterator; // FlagT iterator type (not used)
|
||||
typedef NullType EqualityOp; // Equality operator (not used)
|
||||
|
||||
return DispatchSelectIf<InputIteratorT, FlagIterator, OutputIteratorT, NumSelectedIteratorT, SelectOp, EqualityOp, OffsetT, true>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
NULL,
|
||||
d_out,
|
||||
d_num_selected_out,
|
||||
select_op,
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_device_partition_flagged.cu
|
||||
* \example example_device_partition_if.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,796 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceRadixSort provides device-wide, parallel operations for computing a radix sort across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_radix_sort.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceRadixSort provides device-wide, parallel operations for computing a radix sort across a sequence of data items residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* The [<em>radix sorting method</em>](http://en.wikipedia.org/wiki/Radix_sort) arranges
|
||||
* items into ascending (or descending) order. The algorithm relies upon a positional representation for
|
||||
* keys, i.e., each key is comprised of an ordered sequence of symbols (e.g., digits,
|
||||
* characters, etc.) specified from least-significant to most-significant. For a
|
||||
* given input sequence of keys and a set of rules specifying a total ordering
|
||||
* of the symbolic alphabet, the radix sorting method produces a lexicographic
|
||||
* ordering of those keys.
|
||||
*
|
||||
* \par
|
||||
* DeviceRadixSort can sort all of the built-in C++ numeric primitive types, e.g.:
|
||||
* <tt>unsigned char</tt>, \p int, \p double, etc. Although the direct radix sorting
|
||||
* method can only be applied to unsigned integral types, DeviceRadixSort
|
||||
* is able to sort signed and floating-point types via simple bit-wise transformations
|
||||
* that ensure lexicographic key ordering.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceRadixSort}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{radix sort} The following chart illustrates DeviceRadixSort::SortKeys
|
||||
* performance across different CUDA architectures for uniform-random \p uint32 keys.
|
||||
* \plots_below
|
||||
*
|
||||
* \image html lsb_radix_sort_int32_keys.png
|
||||
*
|
||||
*/
|
||||
struct DeviceRadixSort
|
||||
{
|
||||
|
||||
/******************************************************************//**
|
||||
* \name KeyT-value pairs
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Sorts key-value pairs into ascending order. (~<em>2N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated sorting performance across different
|
||||
* CUDA architectures for uniform-random <tt>uint32,uint32</tt> and
|
||||
* <tt>uint64,uint64</tt> pairs, respectively.
|
||||
*
|
||||
* \image html lsb_radix_sort_int32_pairs.png
|
||||
* \image html lsb_radix_sort_int64_pairs.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [ ... ]
|
||||
* int *d_values_in; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_values_out; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out, num_items);
|
||||
*
|
||||
* // d_keys_out <-- [0, 3, 5, 6, 7, 8, 9]
|
||||
* // d_values_out <-- [5, 4, 3, 1, 2, 0, 6]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
* \tparam ValueT <b>[inferred]</b> ValueT type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairs(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] Pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] Pointer to the sorted output sequence of key data
|
||||
const ValueT *d_values_in, ///< [in] Pointer to the corresponding input sequence of associated value items
|
||||
ValueT *d_values_out, ///< [out] Pointer to the correspondingly-reordered output sequence of associated value items
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<ValueT> d_values(const_cast<ValueT*>(d_values_in), d_values_out);
|
||||
|
||||
return DispatchRadixSort<false, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts key-value pairs into ascending order. (~<em>N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers and a corresponding
|
||||
* pair of associated value buffers. Each pair is managed by a DoubleBuffer
|
||||
* structure that indicates which of the two buffers is "current" (and thus
|
||||
* contains the input data to be sorted).
|
||||
* - The contents of both buffers within each pair may be altered by the sorting
|
||||
* operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within each DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated sorting performance across different
|
||||
* CUDA architectures for uniform-random <tt>uint32,uint32</tt> and
|
||||
* <tt>uint64,uint64</tt> pairs, respectively.
|
||||
*
|
||||
* \image html lsb_radix_sort_int32_pairs.png
|
||||
* \image html lsb_radix_sort_int64_pairs.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [ ... ]
|
||||
* int *d_value_buf; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_value_alt_buf; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Create a set of DoubleBuffers to wrap pairs of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
* cub::DoubleBuffer<int> d_values(d_value_buf, d_value_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, d_keys, d_values, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, d_keys, d_values, num_items);
|
||||
*
|
||||
* // d_keys.Current() <-- [0, 3, 5, 6, 7, 8, 9]
|
||||
* // d_values.Current() <-- [5, 4, 3, 1, 2, 0, 6]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
* \tparam ValueT <b>[inferred]</b> ValueT type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairs(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
DoubleBuffer<ValueT> &d_values, ///< [in,out] Double-buffer of values whose "current" device-accessible buffer contains the unsorted input values and, upon return, is updated to point to the sorted output values
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchRadixSort<false, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts key-value pairs into descending order. (~<em>2N</em> auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* Performance is similar to DeviceRadixSort::SortPairs.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [ ... ]
|
||||
* int *d_values_in; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_values_out; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out, num_items);
|
||||
*
|
||||
* // d_keys_out <-- [9, 8, 7, 6, 5, 3, 0]
|
||||
* // d_values_out <-- [6, 0, 2, 1, 3, 4, 5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
* \tparam ValueT <b>[inferred]</b> ValueT type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairsDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] Pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] Pointer to the sorted output sequence of key data
|
||||
const ValueT *d_values_in, ///< [in] Pointer to the corresponding input sequence of associated value items
|
||||
ValueT *d_values_out, ///< [out] Pointer to the correspondingly-reordered output sequence of associated value items
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<ValueT> d_values(const_cast<ValueT*>(d_values_in), d_values_out);
|
||||
|
||||
return DispatchRadixSort<true, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts key-value pairs into descending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers and a corresponding
|
||||
* pair of associated value buffers. Each pair is managed by a DoubleBuffer
|
||||
* structure that indicates which of the two buffers is "current" (and thus
|
||||
* contains the input data to be sorted).
|
||||
* - The contents of both buffers within each pair may be altered by the sorting
|
||||
* operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within each DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* Performance is similar to DeviceRadixSort::SortPairs.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [ ... ]
|
||||
* int *d_value_buf; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_value_alt_buf; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Create a set of DoubleBuffers to wrap pairs of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
* cub::DoubleBuffer<int> d_values(d_value_buf, d_value_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, d_keys, d_values, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, d_keys, d_values, num_items);
|
||||
*
|
||||
* // d_keys.Current() <-- [9, 8, 7, 6, 5, 3, 0]
|
||||
* // d_values.Current() <-- [6, 0, 2, 1, 3, 4, 5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
* \tparam ValueT <b>[inferred]</b> ValueT type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairsDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
DoubleBuffer<ValueT> &d_values, ///< [in,out] Double-buffer of values whose "current" device-accessible buffer contains the unsorted input values and, upon return, is updated to point to the sorted output values
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchRadixSort<true, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Keys-only
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts keys into ascending order. (~<em>2N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated sorting performance across different
|
||||
* CUDA architectures for uniform-random \p uint32 and \p uint64 keys, respectively.
|
||||
*
|
||||
* \image html lsb_radix_sort_int32_keys.png
|
||||
* \image html lsb_radix_sort_int64_keys.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, num_items);
|
||||
*
|
||||
* // d_keys_out <-- [0, 3, 5, 6, 7, 8, 9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeys(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] Pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] Pointer to the sorted output sequence of key data
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchRadixSort<false, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts keys into ascending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers managed by a
|
||||
* DoubleBuffer structure that indicates which of the two buffers is
|
||||
* "current" (and thus contains the input data to be sorted).
|
||||
* - The contents of both buffers may be altered by the sorting operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within the DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated sorting performance across different
|
||||
* CUDA architectures for uniform-random \p uint32 and \p uint64 keys, respectively.
|
||||
*
|
||||
* \image html lsb_radix_sort_int32_keys.png
|
||||
* \image html lsb_radix_sort_int64_keys.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys, num_items);
|
||||
*
|
||||
* // d_keys.Current() <-- [0, 3, 5, 6, 7, 8, 9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeys(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchRadixSort<false, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sorts keys into descending order. (~<em>2N</em> auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* Performance is similar to DeviceRadixSort::SortKeys.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out, num_items);
|
||||
*
|
||||
* // d_keys_out <-- [9, 8, 7, 6, 5, 3, 0]s
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeysDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] Pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] Pointer to the sorted output sequence of key data
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchRadixSort<true, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts keys into descending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers managed by a
|
||||
* DoubleBuffer structure that indicates which of the two buffers is
|
||||
* "current" (and thus contains the input data to be sorted).
|
||||
* - The contents of both buffers may be altered by the sorting operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within the DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* Performance is similar to DeviceRadixSort::SortKeys.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sorting of a device vector of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [ ... ]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys, num_items);
|
||||
*
|
||||
* // d_keys.Current() <-- [9, 8, 7, 6, 5, 3, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> KeyT type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeysDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
int num_items, ///< [in] Number of items to sort
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchRadixSort<true, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_device_radix_sort.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,701 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceReduce provides device-wide, parallel operations for computing a reduction across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include "../iterator/arg_index_input_iterator.cuh"
|
||||
#include "dispatch/dispatch_reduce.cuh"
|
||||
#include "dispatch/dispatch_reduce_by_key.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceReduce provides device-wide, parallel operations for computing a reduction across a sequence of data items residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* A <a href="http://en.wikipedia.org/wiki/Reduce_(higher-order_function)"><em>reduction</em></a> (or <em>fold</em>)
|
||||
* uses a binary combining operator to compute a single aggregate from a sequence of input elements.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceReduce}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{reduction, reduce-by-key, and run-length encode}
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceReduce::Sum
|
||||
* performance across different CUDA architectures for \p int32 keys.
|
||||
*
|
||||
* \image html reduce_int32.png
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceReduce::ReduceByKey (summation)
|
||||
* performance across different CUDA architectures for \p fp32
|
||||
* values. Segments are identified by \p int32 keys, and have lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html reduce_by_key_fp32_len_500.png
|
||||
*
|
||||
* \par
|
||||
* \plots_below
|
||||
*
|
||||
*/
|
||||
struct DeviceReduce
|
||||
{
|
||||
/**
|
||||
* \brief Computes a device-wide reduction using the specified binary \p reduction_op functor and initial value \p init.
|
||||
*
|
||||
* \par
|
||||
* - Does not support binary reduction operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a user-defined min-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // CustomMin functor
|
||||
* struct CustomMin
|
||||
* {
|
||||
* template <typename T>
|
||||
* __device__ __forceinline__
|
||||
* T operator()(const T &a, const T &b) const {
|
||||
* return (b < a) ? b : a;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-]
|
||||
* CustomMin min_op;
|
||||
* int init; // e.g., INT_MAX
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, min_op, init);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run reduction
|
||||
* cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, min_op, init);
|
||||
*
|
||||
* // d_out <-- [0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
* \tparam ReductionOpT <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
* \tparam T <b>[inferred]</b> Data element type that is convertible to the \p value type of \p InputIteratorT
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename ReductionOpT,
|
||||
typename T>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Reduce(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
T init, ///< [in] Initial value of the reduction
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchReduce<InputIteratorT, OutputIteratorT, OffsetT, ReductionOpT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_items,
|
||||
reduction_op,
|
||||
init,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide sum using the addition (\p +) operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses \p 0 as the initial value of the reduction.
|
||||
* - Does not support \p + operators that are non-commutative..
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated sum-reduction performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively.
|
||||
*
|
||||
* \image html reduce_int32.png
|
||||
* \image html reduce_int64.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sum-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sum-reduction
|
||||
* cub::DeviceReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // d_out <-- [38]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Sum(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
return DispatchReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Sum>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_items,
|
||||
cub::Sum(),
|
||||
OutputT(), // zero-initialize
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide minimum using the less-than ('<') operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses <tt>std::numeric_limits<T>::max()</tt> as the initial value of the reduction.
|
||||
* - Does not support \p < operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the min-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run min-reduction
|
||||
* cub::DeviceReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // d_out <-- [0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Min(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
return DispatchReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Min>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_items,
|
||||
cub::Min(),
|
||||
Traits<InputT>::Max(), // replace with std::numeric_limits<T>::max() when C++11 support is more prevalent
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finds the first device-wide minimum using the less-than ('<') operator, also returning the index of that item.
|
||||
*
|
||||
* \par
|
||||
* - The output value type of \p d_out is cub::KeyValuePair <tt><int, T></tt> (assuming the value type of \p d_in is \p T)
|
||||
* - The minimum is written to <tt>d_out.value</tt> and its offset in the input array is written to <tt>d_out.key</tt>.
|
||||
* - The <tt>{1, std::numeric_limits<T>::max()}</tt> tuple is produced for zero-length inputs
|
||||
* - Does not support \p < operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the argmin-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* KeyValuePair<int, int> *d_out; // e.g., [{-,-}]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_argmin, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run argmin-reduction
|
||||
* cub::DeviceReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_argmin, num_items);
|
||||
*
|
||||
* // d_out <-- [{5, 0}]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items (of some type \p T) \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate (having value type <tt>cub::KeyValuePair<int, T></tt>) \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ArgMin(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputValueT;
|
||||
|
||||
// The output tuple type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
KeyValuePair<OffsetT, InputValueT>, // ... then the key value pair OffsetT + InputValueT
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputTupleT; // ... else the output iterator's value type
|
||||
|
||||
// The output value type
|
||||
typedef typename OutputTupleT::Value OutputValueT;
|
||||
|
||||
// Wrapped input iterator to produce index-value <OffsetT, InputT> tuples
|
||||
typedef ArgIndexInputIterator<InputIteratorT, OffsetT, OutputValueT> ArgIndexInputIteratorT;
|
||||
ArgIndexInputIteratorT d_indexed_in(d_in);
|
||||
|
||||
// Initial value
|
||||
OutputTupleT initial_value(1, Traits<InputValueT>::Max()); // replace with std::numeric_limits<T>::max() when C++11 support is more prevalent
|
||||
|
||||
return DispatchReduce<ArgIndexInputIteratorT, OutputIteratorT, OffsetT, cub::ArgMin>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_indexed_in,
|
||||
d_out,
|
||||
num_items,
|
||||
cub::ArgMin(),
|
||||
initial_value,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide maximum using the greater-than ('>') operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses <tt>std::numeric_limits<T>::lowest()</tt> as the initial value of the reduction.
|
||||
* - Does not support \p > operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the max-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_max, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run max-reduction
|
||||
* cub::DeviceReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_max, num_items);
|
||||
*
|
||||
* // d_out <-- [9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Max(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
return DispatchReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Max>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_items,
|
||||
cub::Max(),
|
||||
Traits<InputT>::Lowest(), // replace with std::numeric_limits<T>::lowest() when C++11 support is more prevalent
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finds the first device-wide maximum using the greater-than ('>') operator, also returning the index of that item
|
||||
*
|
||||
* \par
|
||||
* - The output value type of \p d_out is cub::KeyValuePair <tt><int, T></tt> (assuming the value type of \p d_in is \p T)
|
||||
* - The maximum is written to <tt>d_out.value</tt> and its offset in the input array is written to <tt>d_out.key</tt>.
|
||||
* - The <tt>{1, std::numeric_limits<T>::lowest()}</tt> tuple is produced for zero-length inputs
|
||||
* - Does not support \p > operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the argmax-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_reduce.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* KeyValuePair<int, int> *d_out; // e.g., [{-,-}]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_argmax, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run argmax-reduction
|
||||
* cub::DeviceReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_argmax, num_items);
|
||||
*
|
||||
* // d_out <-- [{6, 9}]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items (of some type \p T) \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate (having value type <tt>cub::KeyValuePair<int, T></tt>) \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ArgMax(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputValueT;
|
||||
|
||||
// The output tuple type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
KeyValuePair<OffsetT, InputValueT>, // ... then the key value pair OffsetT + InputValueT
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputTupleT; // ... else the output iterator's value type
|
||||
|
||||
// The output value type
|
||||
typedef typename OutputTupleT::Value OutputValueT;
|
||||
|
||||
// Wrapped input iterator to produce index-value <OffsetT, InputT> tuples
|
||||
typedef ArgIndexInputIterator<InputIteratorT, OffsetT, OutputValueT> ArgIndexInputIteratorT;
|
||||
ArgIndexInputIteratorT d_indexed_in(d_in);
|
||||
|
||||
// Initial value
|
||||
OutputTupleT initial_value(1, Traits<InputValueT>::Lowest()); // replace with std::numeric_limits<T>::lowest() when C++11 support is more prevalent
|
||||
|
||||
return DispatchReduce<ArgIndexInputIteratorT, OutputIteratorT, OffsetT, cub::ArgMax>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_indexed_in,
|
||||
d_out,
|
||||
num_items,
|
||||
cub::ArgMax(),
|
||||
initial_value,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reduces segments of values, where segments are demarcated by corresponding runs of identical keys.
|
||||
*
|
||||
* \par
|
||||
* This operation computes segmented reductions within \p d_values_in using
|
||||
* the specified binary \p reduction_op functor. The segments are identified by
|
||||
* "runs" of corresponding keys in \p d_keys_in, where runs are maximal ranges of
|
||||
* consecutive, identical keys. For the <em>i</em><sup>th</sup> run encountered,
|
||||
* the first key of the run and the corresponding value aggregate of that run are
|
||||
* written to <tt>d_unique_out[<em>i</em>]</tt> and <tt>d_aggregates_out[<em>i</em>]</tt>,
|
||||
* respectively. The total number of runs encountered is written to \p d_num_runs_out.
|
||||
*
|
||||
* \par
|
||||
* - The <tt>==</tt> equality operator is used to determine whether keys are equivalent
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following chart illustrates reduction-by-key (sum) performance across
|
||||
* different CUDA architectures for \p fp32 and \p fp64 values, respectively. Segments
|
||||
* are identified by \p int32 keys, and have lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html reduce_by_key_fp32_len_500.png
|
||||
* \image html reduce_by_key_fp64_len_500.png
|
||||
*
|
||||
* \par
|
||||
* The following charts are similar, but with segment lengths uniformly sampled from [1,10]:
|
||||
*
|
||||
* \image html reduce_by_key_fp32_len_5.png
|
||||
* \image html reduce_by_key_fp64_len_5.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the segmented reduction of \p int values grouped
|
||||
* by runs of associated \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_reduce.cuh>
|
||||
*
|
||||
* // CustomMin functor
|
||||
* struct CustomMin
|
||||
* {
|
||||
* template <typename T>
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* T operator()(const T &a, const T &b) const {
|
||||
* return (b < a) ? b : a;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_keys_in; // e.g., [0, 2, 2, 9, 5, 5, 5, 8]
|
||||
* int *d_values_in; // e.g., [0, 7, 1, 6, 2, 5, 3, 4]
|
||||
* int *d_unique_out; // e.g., [-, -, -, -, -, -, -, -]
|
||||
* int *d_aggregates_out; // e.g., [-, -, -, -, -, -, -, -]
|
||||
* int *d_num_runs_out; // e.g., [-]
|
||||
* CustomMin reduction_op;
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceReduce::ReduceByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_unique_out, d_values_in, d_aggregates_out, d_num_runs_out, reduction_op, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run reduce-by-key
|
||||
* cub::DeviceReduce::ReduceByKey(d_temp_storage, temp_storage_bytes, d_keys_in, d_unique_out, d_values_in, d_aggregates_out, d_num_runs_out, reduction_op, num_items);
|
||||
*
|
||||
* // d_unique_out <-- [0, 2, 9, 5, 8]
|
||||
* // d_aggregates_out <-- [0, 1, 6, 2, 4]
|
||||
* // d_num_runs_out <-- [5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeysInputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input keys \iterator
|
||||
* \tparam UniqueOutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing unique output keys \iterator
|
||||
* \tparam ValuesInputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input values \iterator
|
||||
* \tparam AggregatesOutputIterator <b>[inferred]</b> Random-access output iterator type for writing output value aggregates \iterator
|
||||
* \tparam NumRunsOutputIteratorT <b>[inferred]</b> Output iterator type for recording the number of runs encountered \iterator
|
||||
* \tparam ReductionOpT <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
typename KeysInputIteratorT,
|
||||
typename UniqueOutputIteratorT,
|
||||
typename ValuesInputIteratorT,
|
||||
typename AggregatesOutputIteratorT,
|
||||
typename NumRunsOutputIteratorT,
|
||||
typename ReductionOpT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t ReduceByKey(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
KeysInputIteratorT d_keys_in, ///< [in] Pointer to the input sequence of keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< [out] Pointer to the output sequence of unique keys (one key per run)
|
||||
ValuesInputIteratorT d_values_in, ///< [in] Pointer to the input sequence of corresponding values
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< [out] Pointer to the output sequence of value aggregates (one aggregate per run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs encountered (i.e., the length of d_unique_out)
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
int num_items, ///< [in] Total number of associated key+value pairs (i.e., the length of \p d_in_keys and \p d_in_values)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// FlagT iterator type (not used)
|
||||
typedef NullType* FlagIterator;
|
||||
|
||||
// Selection op (not used)
|
||||
typedef NullType SelectOp;
|
||||
|
||||
// Default == operator
|
||||
typedef Equality EqualityOp;
|
||||
|
||||
return DispatchReduceByKey<KeysInputIteratorT, UniqueOutputIteratorT, ValuesInputIteratorT, AggregatesOutputIteratorT, NumRunsOutputIteratorT, EqualityOp, ReductionOpT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in,
|
||||
d_unique_out,
|
||||
d_values_in,
|
||||
d_aggregates_out,
|
||||
d_num_runs_out,
|
||||
EqualityOp(),
|
||||
reduction_op,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_device_reduce.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceRunLengthEncode provides device-wide, parallel operations for computing a run-length encoding across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_rle.cuh"
|
||||
#include "dispatch/dispatch_reduce_by_key.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceRunLengthEncode provides device-wide, parallel operations for demarcating "runs" of same-valued items within a sequence residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* A <a href="http://en.wikipedia.org/wiki/Run-length_encoding"><em>run-length encoding</em></a>
|
||||
* computes a simple compressed representation of a sequence of input elements such that each
|
||||
* maximal "run" of consecutive same-valued data items is encoded as a single data value along with a
|
||||
* count of the elements in that run.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceRunLengthEncode}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{run-length encode}
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceRunLengthEncode::RunLengthEncode performance across
|
||||
* different CUDA architectures for \p int32 items.
|
||||
* Segments have lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html rle_int32_len_500.png
|
||||
*
|
||||
* \par
|
||||
* \plots_below
|
||||
*
|
||||
*/
|
||||
struct DeviceRunLengthEncode
|
||||
{
|
||||
|
||||
/**
|
||||
* \brief Computes a run-length encoding of the sequence \p d_in.
|
||||
*
|
||||
* \par
|
||||
* - For the <em>i</em><sup>th</sup> run encountered, the first key of the run and its length are written to
|
||||
* <tt>d_unique_out[<em>i</em>]</tt> and <tt>d_counts_out[<em>i</em>]</tt>,
|
||||
* respectively.
|
||||
* - The total number of runs encountered is written to \p d_num_runs_out.
|
||||
* - The <tt>==</tt> equality operator is used to determine whether values are equivalent
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated encode performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively. Segments have
|
||||
* lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html rle_int32_len_500.png
|
||||
* \image html rle_int64_len_500.png
|
||||
*
|
||||
* \par
|
||||
* The following charts are similar, but with segment lengths uniformly sampled from [1,10]:
|
||||
*
|
||||
* \image html rle_int32_len_5.png
|
||||
* \image html rle_int64_len_5.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the run-length encoding of a sequence of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_run_length_encode.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [0, 2, 2, 9, 5, 5, 5, 8]
|
||||
* int *d_unique_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_counts_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_runs_out; // e.g., [ ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRunLengthEncode::Encode(d_temp_storage, temp_storage_bytes, d_in, d_unique_out, d_counts_out, d_num_runs_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run encoding
|
||||
* cub::DeviceRunLengthEncode::Encode(d_temp_storage, temp_storage_bytes, d_in, d_unique_out, d_counts_out, d_num_runs_out, num_items);
|
||||
*
|
||||
* // d_unique_out <-- [0, 2, 9, 5, 8]
|
||||
* // d_counts_out <-- [1, 2, 1, 3, 1]
|
||||
* // d_num_runs_out <-- [5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam UniqueOutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing unique output items \iterator
|
||||
* \tparam LengthsOutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing output counts \iterator
|
||||
* \tparam NumRunsOutputIteratorT <b>[inferred]</b> Output iterator type for recording the number of runs encountered \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename UniqueOutputIteratorT,
|
||||
typename LengthsOutputIteratorT,
|
||||
typename NumRunsOutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Encode(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< [out] Pointer to the output sequence of unique keys (one key per run)
|
||||
LengthsOutputIteratorT d_counts_out, ///< [out] Pointer to the output sequence of run-lengths (one count per run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs
|
||||
int num_items, ///< [in] Total number of associated key+value pairs (i.e., the length of \p d_in_keys and \p d_in_values)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType* FlagIterator; // FlagT iterator type (not used)
|
||||
typedef NullType SelectOp; // Selection op (not used)
|
||||
typedef Equality EqualityOp; // Default == operator
|
||||
typedef cub::Sum ReductionOp; // Value reduction operator
|
||||
|
||||
// The lengths output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<LengthsOutputIteratorT>::value_type, void>::VALUE), // LengthT = (if output iterator's value type is void) ?
|
||||
OffsetT, // ... then the OffsetT type,
|
||||
typename std::iterator_traits<LengthsOutputIteratorT>::value_type>::Type LengthT; // ... else the output iterator's value type
|
||||
|
||||
// Generator type for providing 1s values for run-length reduction
|
||||
typedef ConstantInputIterator<LengthT, OffsetT> LengthsInputIteratorT;
|
||||
|
||||
return DispatchReduceByKey<InputIteratorT, UniqueOutputIteratorT, LengthsInputIteratorT, LengthsOutputIteratorT, NumRunsOutputIteratorT, EqualityOp, ReductionOp, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_unique_out,
|
||||
LengthsInputIteratorT((LengthT) 1),
|
||||
d_counts_out,
|
||||
d_num_runs_out,
|
||||
EqualityOp(),
|
||||
ReductionOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Enumerates the starting offsets and lengths of all non-trivial runs (of length > 1) of same-valued keys in the sequence \p d_in.
|
||||
*
|
||||
* \par
|
||||
* - For the <em>i</em><sup>th</sup> non-trivial run, the run's starting offset
|
||||
* and its length are written to <tt>d_offsets_out[<em>i</em>]</tt> and
|
||||
* <tt>d_lengths_out[<em>i</em>]</tt>, respectively.
|
||||
* - The total number of runs encountered is written to \p d_num_runs_out.
|
||||
* - The <tt>==</tt> equality operator is used to determine whether values are equivalent
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the identification of non-trivial runs within a sequence of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_run_length_encode.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [0, 2, 2, 9, 5, 5, 5, 8]
|
||||
* int *d_offsets_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_lengths_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_runs_out; // e.g., [ ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceRunLengthEncode::NonTrivialRuns(d_temp_storage, temp_storage_bytes, d_in, d_offsets_out, d_lengths_out, d_num_runs_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run encoding
|
||||
* cub::DeviceRunLengthEncode::NonTrivialRuns(d_temp_storage, temp_storage_bytes, d_in, d_offsets_out, d_lengths_out, d_num_runs_out, num_items);
|
||||
*
|
||||
* // d_offsets_out <-- [1, 4]
|
||||
* // d_lengths_out <-- [2, 3]
|
||||
* // d_num_runs_out <-- [2]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OffsetsOutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing run-offset values \iterator
|
||||
* \tparam LengthsOutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing run-length values \iterator
|
||||
* \tparam NumRunsOutputIteratorT <b>[inferred]</b> Output iterator type for recording the number of runs encountered \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OffsetsOutputIteratorT,
|
||||
typename LengthsOutputIteratorT,
|
||||
typename NumRunsOutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t NonTrivialRuns(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out, ///< [out] Pointer to output sequence of run-offsets (one offset per non-trivial run)
|
||||
LengthsOutputIteratorT d_lengths_out, ///< [out] Pointer to output sequence of run-lengths (one count per non-trivial run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs (i.e., length of \p d_offsets_out)
|
||||
int num_items, ///< [in] Total number of associated key+value pairs (i.e., the length of \p d_in_keys and \p d_in_values)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef Equality EqualityOp; // Default == operator
|
||||
|
||||
return DeviceRleDispatch<InputIteratorT, OffsetsOutputIteratorT, LengthsOutputIteratorT, NumRunsOutputIteratorT, EqualityOp, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_offsets_out,
|
||||
d_lengths_out,
|
||||
d_num_runs_out,
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceScan provides device-wide, parallel operations for computing a prefix scan across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_scan.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceScan provides device-wide, parallel operations for computing a prefix scan across a sequence of data items residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* Given a sequence of input elements and a binary reduction operator, a [<em>prefix scan</em>](http://en.wikipedia.org/wiki/Prefix_sum)
|
||||
* produces an output sequence where each element is computed to be the reduction
|
||||
* of the elements occurring earlier in the input sequence. <em>Prefix sum</em>
|
||||
* connotes a prefix scan with the addition operator. The term \em inclusive indicates
|
||||
* that the <em>i</em><sup>th</sup> output reduction incorporates the <em>i</em><sup>th</sup> input.
|
||||
* The term \em exclusive indicates the <em>i</em><sup>th</sup> input is not incorporated into
|
||||
* the <em>i</em><sup>th</sup> output reduction.
|
||||
*
|
||||
* \par
|
||||
* As of CUB 1.0.1 (2013), CUB's device-wide scan APIs have implemented our <em>"decoupled look-back"</em> algorithm
|
||||
* for performing global prefix scan with only a single pass through the
|
||||
* input data, as described in our 2016 technical report [1]. The central
|
||||
* idea is to leverage a small, constant factor of redundant work in order to overlap the latencies
|
||||
* of global prefix propagation with local computation. As such, our algorithm requires only
|
||||
* ~2<em>n</em> data movement (<em>n</em> inputs are read, <em>n</em> outputs are written), and typically
|
||||
* proceeds at "memcpy" speeds.
|
||||
*
|
||||
* \par
|
||||
* [1] [Duane Merrill and Michael Garland. "Single-pass Parallel Prefix Scan with Decoupled Look-back", <em>NVIDIA Technical Report NVR-2016-002</em>, 2016.](https://research.nvidia.com/publication/single-pass-parallel-prefix-scan-decoupled-look-back)
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceScan}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{prefix scan}
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceScan::ExclusiveSum
|
||||
* performance across different CUDA architectures for \p int32 keys.
|
||||
* \plots_below
|
||||
*
|
||||
* \image html scan_int32.png
|
||||
*
|
||||
*/
|
||||
struct DeviceScan
|
||||
{
|
||||
/******************************************************************//**
|
||||
* \name Exclusive scans
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide exclusive prefix sum. The value of 0 is applied as the initial value, and is assigned to *d_out.
|
||||
*
|
||||
* \par
|
||||
* - Supports non-commutative sum operators.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated exclusive sum performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively.
|
||||
*
|
||||
* \image html scan_int32.png
|
||||
* \image html scan_int64.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the exclusive prefix sum of an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_scan.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [ , , , , , , ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run exclusive prefix sum
|
||||
* cub::DeviceScan::ExclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // d_out s<-- [0, 8, 14, 21, 26, 29, 29]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading scan inputs \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing scan outputs \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ExclusiveSum(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
int num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// Initial value
|
||||
OutputT init_value = 0;
|
||||
|
||||
return DispatchScan<InputIteratorT, OutputIteratorT, Sum, OutputT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
Sum(),
|
||||
init_value,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide exclusive prefix scan using the specified binary \p scan_op functor. The \p init_value value is applied as the initial value, and is assigned to *d_out.
|
||||
*
|
||||
* \par
|
||||
* - Supports non-commutative scan operators.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the exclusive prefix min-scan of an \p int device vector
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_scan.cuh>
|
||||
*
|
||||
* // CustomMin functor
|
||||
* struct CustomMin
|
||||
* {
|
||||
* template <typename T>
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* T operator()(const T &a, const T &b) const {
|
||||
* return (b < a) ? b : a;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [ , , , , , , ]
|
||||
* CustomMin min_op
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements for exclusive prefix scan
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceScan::ExclusiveScan(d_temp_storage, temp_storage_bytes, d_in, d_out, min_op, (int) MAX_INT, num_items);
|
||||
*
|
||||
* // Allocate temporary storage for exclusive prefix scan
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run exclusive prefix min-scan
|
||||
* cub::DeviceScan::ExclusiveScan(d_temp_storage, temp_storage_bytes, d_in, d_out, min_op, (int) MAX_INT, num_items);
|
||||
*
|
||||
* // d_out <-- [2147483647, 8, 6, 6, 5, 3, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading scan inputs \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing scan outputs \iterator
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
* \tparam Identity <b>[inferred]</b> Type of the \p identity value used Binary scan functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename ScanOpT,
|
||||
typename InitValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ExclusiveScan(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
ScanOpT scan_op, ///< [in] Binary scan functor
|
||||
InitValueT init_value, ///< [in] Initial value to seed the exclusive scan (and is assigned to *d_out)
|
||||
int num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchScan<InputIteratorT, OutputIteratorT, ScanOpT, InitValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
scan_op,
|
||||
init_value,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Inclusive scans
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide inclusive prefix sum.
|
||||
*
|
||||
* \par
|
||||
* - Supports non-commutative sum operators.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the inclusive prefix sum of an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_scan.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [ , , , , , , ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements for inclusive prefix sum
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage for inclusive prefix sum
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run inclusive prefix sum
|
||||
* cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, d_in, d_out, num_items);
|
||||
*
|
||||
* // d_out <-- [8, 14, 21, 26, 29, 29, 38]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading scan inputs \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing scan outputs \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t InclusiveSum(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
int num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchScan<InputIteratorT, OutputIteratorT, Sum, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
Sum(),
|
||||
NullType(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide inclusive prefix scan using the specified binary \p scan_op functor.
|
||||
*
|
||||
* \par
|
||||
* - Supports non-commutative scan operators.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the inclusive prefix min-scan of an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_scan.cuh>
|
||||
*
|
||||
* // CustomMin functor
|
||||
* struct CustomMin
|
||||
* {
|
||||
* template <typename T>
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* T operator()(const T &a, const T &b) const {
|
||||
* return (b < a) ? b : a;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 7
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [ , , , , , , ]
|
||||
* CustomMin min_op;
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements for inclusive prefix scan
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceScan::InclusiveScan(d_temp_storage, temp_storage_bytes, d_in, d_out, min_op, num_items);
|
||||
*
|
||||
* // Allocate temporary storage for inclusive prefix scan
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run inclusive prefix min-scan
|
||||
* cub::DeviceScan::InclusiveScan(d_temp_storage, temp_storage_bytes, d_in, d_out, min_op, num_items);
|
||||
*
|
||||
* // d_out <-- [8, 6, 6, 5, 3, 0, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading scan inputs \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing scan outputs \iterator
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename ScanOpT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t InclusiveScan(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
ScanOpT scan_op, ///< [in] Binary scan functor
|
||||
int num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchScan<InputIteratorT, OutputIteratorT, ScanOpT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
scan_op,
|
||||
NullType(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
//@} end member group
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_device_scan.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,855 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSegmentedRadixSort provides device-wide, parallel operations for computing a batched radix sort across multiple, non-overlapping sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_radix_sort.cuh"
|
||||
#include "../util_arch.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceSegmentedRadixSort provides device-wide, parallel operations for computing a batched radix sort across multiple, non-overlapping sequences of data items residing within device-accessible memory. 
|
||||
* \ingroup SegmentedModule
|
||||
*
|
||||
* \par Overview
|
||||
* The [<em>radix sorting method</em>](http://en.wikipedia.org/wiki/Radix_sort) arranges
|
||||
* items into ascending (or descending) order. The algorithm relies upon a positional representation for
|
||||
* keys, i.e., each key is comprised of an ordered sequence of symbols (e.g., digits,
|
||||
* characters, etc.) specified from least-significant to most-significant. For a
|
||||
* given input sequence of keys and a set of rules specifying a total ordering
|
||||
* of the symbolic alphabet, the radix sorting method produces a lexicographic
|
||||
* ordering of those keys.
|
||||
*
|
||||
* \par
|
||||
* DeviceSegmentedRadixSort can sort all of the built-in C++ numeric primitive types, e.g.:
|
||||
* <tt>unsigned char</tt>, \p int, \p double, etc. Although the direct radix sorting
|
||||
* method can only be applied to unsigned integral types, DeviceSegmentedRadixSort
|
||||
* is able to sort signed and floating-point types via simple bit-wise transformations
|
||||
* that ensure lexicographic key ordering.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceSegmentedRadixSort}
|
||||
*
|
||||
*/
|
||||
struct DeviceSegmentedRadixSort
|
||||
{
|
||||
|
||||
/******************************************************************//**
|
||||
* \name Key-value pairs
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of key-value pairs into ascending order. (~<em>2N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* int *d_values_in; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_values_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys_out <-- [6, 7, 8, 0, 3, 5, 9]
|
||||
* // d_values_out <-- [1, 2, 0, 5, 4, 3, 6]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
* \tparam ValueT <b>[inferred]</b> Value type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairs(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] %Device-accessible pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] %Device-accessible pointer to the sorted output sequence of key data
|
||||
const ValueT *d_values_in, ///< [in] %Device-accessible pointer to the corresponding input sequence of associated value items
|
||||
ValueT *d_values_out, ///< [out] %Device-accessible pointer to the correspondingly-reordered output sequence of associated value items
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<ValueT> d_values(const_cast<ValueT*>(d_values_in), d_values_out);
|
||||
|
||||
return DispatchSegmentedRadixSort<false, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of key-value pairs into ascending order. (~<em>N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers and a corresponding
|
||||
* pair of associated value buffers. Each pair is managed by a DoubleBuffer
|
||||
* structure that indicates which of the two buffers is "current" (and thus
|
||||
* contains the input data to be sorted).
|
||||
* - The contents of both buffers within each pair may be altered by the sorting
|
||||
* operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within each DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* int *d_value_buf; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_value_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Create a set of DoubleBuffers to wrap pairs of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
* cub::DoubleBuffer<int> d_values(d_value_buf, d_value_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, d_keys, d_values,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, d_keys, d_values,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys.Current() <-- [6, 7, 8, 0, 3, 5, 9]
|
||||
* // d_values.Current() <-- [5, 4, 3, 1, 2, 0, 6]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
* \tparam ValueT <b>[inferred]</b> Value type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairs(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
DoubleBuffer<ValueT> &d_values, ///< [in,out] Double-buffer of values whose "current" device-accessible buffer contains the unsorted input values and, upon return, is updated to point to the sorted output values
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchSegmentedRadixSort<false, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of key-value pairs into descending order. (~<em>2N</em> auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* int *d_values_in; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_values_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes,
|
||||
* d_keys_in, d_keys_out, d_values_in, d_values_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys_out <-- [8, 7, 6, 9, 5, 3, 0]
|
||||
* // d_values_out <-- [0, 2, 1, 6, 3, 4, 5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
* \tparam ValueT <b>[inferred]</b> Value type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairsDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] %Device-accessible pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] %Device-accessible pointer to the sorted output sequence of key data
|
||||
const ValueT *d_values_in, ///< [in] %Device-accessible pointer to the corresponding input sequence of associated value items
|
||||
ValueT *d_values_out, ///< [out] %Device-accessible pointer to the correspondingly-reordered output sequence of associated value items
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<ValueT> d_values(const_cast<ValueT*>(d_values_in), d_values_out);
|
||||
|
||||
return DispatchSegmentedRadixSort<true, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of key-value pairs into descending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers and a corresponding
|
||||
* pair of associated value buffers. Each pair is managed by a DoubleBuffer
|
||||
* structure that indicates which of the two buffers is "current" (and thus
|
||||
* contains the input data to be sorted).
|
||||
* - The contents of both buffers within each pair may be altered by the sorting
|
||||
* operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within each DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys
|
||||
* with associated vector of \p int values.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* int *d_value_buf; // e.g., [0, 1, 2, 3, 4, 5, 6]
|
||||
* int *d_value_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Create a set of DoubleBuffers to wrap pairs of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
* cub::DoubleBuffer<int> d_values(d_value_buf, d_value_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, d_keys, d_values,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortPairsDescending(d_temp_storage, temp_storage_bytes, d_keys, d_values,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys.Current() <-- [8, 7, 6, 9, 5, 3, 0]
|
||||
* // d_values.Current() <-- [0, 2, 1, 6, 3, 4, 5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
* \tparam ValueT <b>[inferred]</b> Value type
|
||||
*/
|
||||
template <
|
||||
typename KeyT,
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortPairsDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
DoubleBuffer<ValueT> &d_values, ///< [in,out] Double-buffer of values whose "current" device-accessible buffer contains the unsorted input values and, upon return, is updated to point to the sorted output values
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchSegmentedRadixSort<true, KeyT, ValueT, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
/******************************************************************//**
|
||||
* \name Keys-only
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of keys into ascending order. (~<em>2N </em>auxiliary storage required)
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys_out <-- [6, 7, 8, 0, 3, 5, 9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeys(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] %Device-accessible pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] %Device-accessible pointer to the sorted output sequence of key data
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchSegmentedRadixSort<false, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of keys into ascending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers managed by a
|
||||
* DoubleBuffer structure that indicates which of the two buffers is
|
||||
* "current" (and thus contains the input data to be sorted).
|
||||
* - The contents of both buffers may be altered by the sorting operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within the DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortKeys(d_temp_storage, temp_storage_bytes, d_keys,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys.Current() <-- [6, 7, 8, 0, 3, 5, 9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeys(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchSegmentedRadixSort<false, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of keys into descending order. (~<em>2N</em> auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The contents of the input data are not altered by the sorting operation
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageNP For sorting using only <em>O</em>(<tt>P</tt>) temporary storage, see the sorting interface using DoubleBuffer wrappers below.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_keys_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_keys_out; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys_in, d_keys_out,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys_out <-- [8, 7, 6, 9, 5, 3, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeysDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
const KeyT *d_keys_in, ///< [in] %Device-accessible pointer to the input data of key data to sort
|
||||
KeyT *d_keys_out, ///< [out] %Device-accessible pointer to the sorted output sequence of key data
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
DoubleBuffer<KeyT> d_keys(const_cast<KeyT*>(d_keys_in), d_keys_out);
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchSegmentedRadixSort<true, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
false,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Sorts segments of keys into descending order. (~<em>N </em>auxiliary storage required).
|
||||
*
|
||||
* \par
|
||||
* - The sorting operation is given a pair of key buffers managed by a
|
||||
* DoubleBuffer structure that indicates which of the two buffers is
|
||||
* "current" (and thus contains the input data to be sorted).
|
||||
* - The contents of both buffers may be altered by the sorting operation.
|
||||
* - Upon completion, the sorting operation will update the "current" indicator
|
||||
* within the DoubleBuffer wrapper to reference which of the two buffers
|
||||
* now contains the sorted output sequence (a function of the number of key bits
|
||||
* specified and the targeted device architecture).
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - An optional bit subrange <tt>[begin_bit, end_bit)</tt> of differentiating key bits can be specified. This can reduce overall sorting overhead and yield a corresponding performance improvement.
|
||||
* - \devicestorageP
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the batched sorting of three segments (with one zero-length segment) of \p int keys.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_segmentd_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for sorting data
|
||||
* int num_items; // e.g., 7
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_key_buf; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_key_alt_buf; // e.g., [-, -, -, -, -, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Create a DoubleBuffer to wrap the pair of device pointers
|
||||
* cub::DoubleBuffer<int> d_keys(d_key_buf, d_key_alt_buf);
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sorting operation
|
||||
* cub::DeviceSegmentedRadixSort::SortKeysDescending(d_temp_storage, temp_storage_bytes, d_keys,
|
||||
* num_items, num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_keys.Current() <-- [8, 7, 6, 9, 5, 3, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam KeyT <b>[inferred]</b> Key type
|
||||
*/
|
||||
template <typename KeyT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t SortKeysDescending(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
DoubleBuffer<KeyT> &d_keys, ///< [in,out] Reference to the double-buffer of keys whose "current" device-accessible buffer contains the unsorted input keys and, upon return, is updated to point to the sorted output keys
|
||||
int num_items, ///< [in] The total number of items to sort (across all segments)
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
const int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
const int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int begin_bit = 0, ///< [in] <b>[optional]</b> The least-significant bit index (inclusive) needed for key comparison
|
||||
int end_bit = sizeof(KeyT) * 8, ///< [in] <b>[optional]</b> The most-significant bit index (exclusive) needed for key comparison (e.g., sizeof(unsigned int) * 8)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// Null value type
|
||||
DoubleBuffer<NullType> d_values;
|
||||
|
||||
return DispatchSegmentedRadixSort<true, KeyT, NullType, OffsetT>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys,
|
||||
d_values,
|
||||
num_items,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
begin_bit,
|
||||
end_bit,
|
||||
true,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSegmentedReduce provides device-wide, parallel operations for computing a batched reduction across multiple sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "../iterator/arg_index_input_iterator.cuh"
|
||||
#include "dispatch/dispatch_reduce.cuh"
|
||||
#include "dispatch/dispatch_reduce_by_key.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceSegmentedReduce provides device-wide, parallel operations for computing a reduction across multiple sequences of data items residing within device-accessible memory. 
|
||||
* \ingroup SegmentedModule
|
||||
*
|
||||
* \par Overview
|
||||
* A <a href="http://en.wikipedia.org/wiki/Reduce_(higher-order_function)"><em>reduction</em></a> (or <em>fold</em>)
|
||||
* uses a binary combining operator to compute a single aggregate from a sequence of input elements.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceSegmentedReduce}
|
||||
*
|
||||
*/
|
||||
struct DeviceSegmentedReduce
|
||||
{
|
||||
/**
|
||||
* \brief Computes a device-wide segmented reduction using the specified binary \p reduction_op functor.
|
||||
*
|
||||
* \par
|
||||
* - Does not support binary reduction operators that are non-commutative.
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates a custom min-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // CustomMin functor
|
||||
* struct CustomMin
|
||||
* {
|
||||
* template <typename T>
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* T operator()(const T &a, const T &b) const {
|
||||
* return (b < a) ? b : a;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-, -, -]
|
||||
* CustomMin min_op;
|
||||
* int initial_value; // e.g., INT_MAX
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1, min_op, initial_value);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run reduction
|
||||
* cub::DeviceSegmentedReduce::Reduce(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1, min_op, initial_value);
|
||||
*
|
||||
* // d_out <-- [6, INT_MAX, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
* \tparam ReductionOp <b>[inferred]</b> Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
* \tparam T <b>[inferred]</b> Data element type that is convertible to the \p value type of \p InputIteratorT
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename ReductionOp,
|
||||
typename T>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Reduce(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
ReductionOp reduction_op, ///< [in] Binary reduction functor
|
||||
T initial_value, ///< [in] Initial value of the reduction for each segment
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
return DispatchSegmentedReduce<InputIteratorT, OutputIteratorT, OffsetT, ReductionOp>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
reduction_op,
|
||||
initial_value,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide segmented sum using the addition ('+') operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses \p 0 as the initial value of the reduction for each segment.
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - Does not support \p + operators that are non-commutative..
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the sum reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run sum-reduction
|
||||
* cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_out <-- [21, 0, 17]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Sum(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
return DispatchSegmentedReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Sum>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
cub::Sum(),
|
||||
OutputT(), // zero-initialize
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide segmented minimum using the less-than ('<') operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses <tt>std::numeric_limits<T>::max()</tt> as the initial value of the reduction for each segment.
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - Does not support \p < operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the min-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run min-reduction
|
||||
* cub::DeviceSegmentedReduce::Min(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_out <-- [6, INT_MAX, 0]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Min(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
return DispatchSegmentedReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Min>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
cub::Min(),
|
||||
Traits<InputT>::Max(), // replace with std::numeric_limits<T>::max() when C++11 support is more prevalent
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finds the first device-wide minimum in each segment using the less-than ('<') operator, also returning the in-segment index of that item.
|
||||
*
|
||||
* \par
|
||||
* - The output value type of \p d_out is cub::KeyValuePair <tt><int, T></tt> (assuming the value type of \p d_in is \p T)
|
||||
* - The minimum of the <em>i</em><sup>th</sup> segment is written to <tt>d_out[i].value</tt> and its offset in that segment is written to <tt>d_out[i].key</tt>.
|
||||
* - The <tt>{1, std::numeric_limits<T>::max()}</tt> tuple is produced for zero-length inputs
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - Does not support \p < operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the argmin-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* KeyValuePair<int, int> *d_out; // e.g., [{-,-}, {-,-}, {-,-}]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run argmin-reduction
|
||||
* cub::DeviceSegmentedReduce::ArgMin(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_out <-- [{1,6}, {1,INT_MAX}, {2,0}]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items (of some type \p T) \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate (having value type <tt>KeyValuePair<int, T></tt>) \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ArgMin(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputValueT;
|
||||
|
||||
// The output tuple type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
KeyValuePair<OffsetT, InputValueT>, // ... then the key value pair OffsetT + InputValueT
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputTupleT; // ... else the output iterator's value type
|
||||
|
||||
// The output value type
|
||||
typedef typename OutputTupleT::Value OutputValueT;
|
||||
|
||||
// Wrapped input iterator to produce index-value <OffsetT, InputT> tuples
|
||||
typedef ArgIndexInputIterator<InputIteratorT, OffsetT, OutputValueT> ArgIndexInputIteratorT;
|
||||
ArgIndexInputIteratorT d_indexed_in(d_in);
|
||||
|
||||
// Initial value
|
||||
OutputTupleT initial_value(1, Traits<InputValueT>::Max()); // replace with std::numeric_limits<T>::max() when C++11 support is more prevalent
|
||||
|
||||
return DispatchSegmentedReduce<ArgIndexInputIteratorT, OutputIteratorT, OffsetT, cub::ArgMin>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_indexed_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
cub::ArgMin(),
|
||||
initial_value,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Computes a device-wide segmented maximum using the greater-than ('>') operator.
|
||||
*
|
||||
* \par
|
||||
* - Uses <tt>std::numeric_limits<T>::lowest()</tt> as the initial value of the reduction.
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - Does not support \p > operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the max-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_radix_sort.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* int *d_out; // e.g., [-, -, -]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run max-reduction
|
||||
* cub::DeviceSegmentedReduce::Max(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_out <-- [8, INT_MIN, 9]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t Max(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputT;
|
||||
|
||||
return DispatchSegmentedReduce<InputIteratorT, OutputIteratorT, OffsetT, cub::Max>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
cub::Max(),
|
||||
Traits<InputT>::Lowest(), // replace with std::numeric_limits<T>::lowest() when C++11 support is more prevalent
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finds the first device-wide maximum in each segment using the greater-than ('>') operator, also returning the in-segment index of that item
|
||||
*
|
||||
* \par
|
||||
* - The output value type of \p d_out is cub::KeyValuePair <tt><int, T></tt> (assuming the value type of \p d_in is \p T)
|
||||
* - The maximum of the <em>i</em><sup>th</sup> segment is written to <tt>d_out[i].value</tt> and its offset in that segment is written to <tt>d_out[i].key</tt>.
|
||||
* - The <tt>{1, std::numeric_limits<T>::lowest()}</tt> tuple is produced for zero-length inputs
|
||||
* - When input a contiguous sequence of segments, a single sequence
|
||||
* \p segment_offsets (of length <tt>num_segments+1</tt>) can be aliased
|
||||
* for both the \p d_begin_offsets and \p d_end_offsets parameters (where
|
||||
* the latter is specified as <tt>segment_offsets+1</tt>).
|
||||
* - Does not support \p > operators that are non-commutative.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the argmax-reduction of a device vector of \p int data elements.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_reduce.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_segments; // e.g., 3
|
||||
* int *d_offsets; // e.g., [0, 3, 3, 7]
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* KeyValuePair<int, int> *d_out; // e.g., [{-,-}, {-,-}, {-,-}]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSegmentedReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run argmax-reduction
|
||||
* cub::DeviceSegmentedReduce::ArgMax(d_temp_storage, temp_storage_bytes, d_in, d_out,
|
||||
* num_segments, d_offsets, d_offsets + 1);
|
||||
*
|
||||
* // d_out <-- [{0,8}, {1,INT_MIN}, {3,9}]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items (of some type \p T) \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Output iterator type for recording the reduced aggregate (having value type <tt>KeyValuePair<int, T></tt>) \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t ArgMax(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
// Signed integer type for global offsets
|
||||
typedef int OffsetT;
|
||||
|
||||
// The input type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type InputValueT;
|
||||
|
||||
// The output tuple type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
KeyValuePair<OffsetT, InputValueT>, // ... then the key value pair OffsetT + InputValueT
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputTupleT; // ... else the output iterator's value type
|
||||
|
||||
// The output value type
|
||||
typedef typename OutputTupleT::Value OutputValueT;
|
||||
|
||||
// Wrapped input iterator to produce index-value <OffsetT, InputT> tuples
|
||||
typedef ArgIndexInputIterator<InputIteratorT, OffsetT, OutputValueT> ArgIndexInputIteratorT;
|
||||
ArgIndexInputIteratorT d_indexed_in(d_in);
|
||||
|
||||
// Initial value
|
||||
OutputTupleT initial_value(1, Traits<InputValueT>::Lowest()); // replace with std::numeric_limits<T>::lowest() when C++11 support is more prevalent
|
||||
|
||||
return DispatchSegmentedReduce<ArgIndexInputIteratorT, OutputIteratorT, OffsetT, cub::ArgMax>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_indexed_in,
|
||||
d_out,
|
||||
num_segments,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
cub::ArgMax(),
|
||||
initial_value,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSelect provides device-wide, parallel operations for compacting selected items from sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch/dispatch_select_if.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceSelect provides device-wide, parallel operations for compacting selected items from sequences of data items residing within device-accessible memory. 
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* These operations apply a selection criterion to selectively copy
|
||||
* items from a specified input sequence to a compact output sequence.
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceSelect}
|
||||
*
|
||||
* \par Performance
|
||||
* \linear_performance{select-flagged, select-if, and select-unique}
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceSelect::If
|
||||
* performance across different CUDA architectures for \p int32 items,
|
||||
* where 50% of the items are randomly selected.
|
||||
*
|
||||
* \image html select_if_int32_50_percent.png
|
||||
*
|
||||
* \par
|
||||
* The following chart illustrates DeviceSelect::Unique
|
||||
* performance across different CUDA architectures for \p int32 items
|
||||
* where segments have lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html select_unique_int32_len_500.png
|
||||
*
|
||||
* \par
|
||||
* \plots_below
|
||||
*
|
||||
*/
|
||||
struct DeviceSelect
|
||||
{
|
||||
/**
|
||||
* \brief Uses the \p d_flags sequence to selectively copy the corresponding items from \p d_in into \p d_out. The total number of items selected is written to \p d_num_selected_out. 
|
||||
*
|
||||
* \par
|
||||
* - The value type of \p d_flags must be castable to \p bool (e.g., \p bool, \p char, \p int, etc.).
|
||||
* - Copies of the selected items are compacted into \p d_out and maintain their original relative ordering.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the compaction of items selected from an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_select.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input, flags, and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
* char *d_flags; // e.g., [1, 0, 0, 1, 0, 1, 1, 0]
|
||||
* int *d_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_selected_out; // e.g., [ ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSelect::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run selection
|
||||
* cub::DeviceSelect::Flagged(d_temp_storage, temp_storage_bytes, d_in, d_flags, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // d_out <-- [1, 4, 6, 7]
|
||||
* // d_num_selected_out <-- [4]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam FlagIterator <b>[inferred]</b> Random-access input iterator type for reading selection flags \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing selected items \iterator
|
||||
* \tparam NumSelectedIteratorT <b>[inferred]</b> Output iterator type for recording the number of items selected \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename FlagIterator,
|
||||
typename OutputIteratorT,
|
||||
typename NumSelectedIteratorT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Flagged(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
FlagIterator d_flags, ///< [in] Pointer to the input sequence of selection flags
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the output total number of items selected (i.e., length of \p d_out)
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType SelectOp; // Selection op (not used)
|
||||
typedef NullType EqualityOp; // Equality operator (not used)
|
||||
|
||||
return DispatchSelectIf<InputIteratorT, FlagIterator, OutputIteratorT, NumSelectedIteratorT, SelectOp, EqualityOp, OffsetT, false>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_flags,
|
||||
d_out,
|
||||
d_num_selected_out,
|
||||
SelectOp(),
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Uses the \p select_op functor to selectively copy items from \p d_in into \p d_out. The total number of items selected is written to \p d_num_selected_out. 
|
||||
*
|
||||
* \par
|
||||
* - Copies of the selected items are compacted into \p d_out and maintain their original relative ordering.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated select-if performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively. Items are
|
||||
* selected with 50% probability.
|
||||
*
|
||||
* \image html select_if_int32_50_percent.png
|
||||
* \image html select_if_int64_50_percent.png
|
||||
*
|
||||
* \par
|
||||
* The following charts are similar, but 5% selection probability:
|
||||
*
|
||||
* \image html select_if_int32_5_percent.png
|
||||
* \image html select_if_int64_5_percent.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the compaction of items selected from an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_select.cuh>
|
||||
*
|
||||
* // Functor type for selecting values less than some criteria
|
||||
* struct LessThan
|
||||
* {
|
||||
* int compare;
|
||||
*
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* LessThan(int compare) : compare(compare) {}
|
||||
*
|
||||
* CUB_RUNTIME_FUNCTION __forceinline__
|
||||
* bool operator()(const int &a) const {
|
||||
* return (a < compare);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [0, 2, 3, 9, 5, 2, 81, 8]
|
||||
* int *d_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_selected_out; // e.g., [ ]
|
||||
* LessThan select_op(7);
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run selection
|
||||
* cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items, select_op);
|
||||
*
|
||||
* // d_out <-- [0, 2, 3, 5, 2]
|
||||
* // d_num_selected_out <-- [5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing selected items \iterator
|
||||
* \tparam NumSelectedIteratorT <b>[inferred]</b> Output iterator type for recording the number of items selected \iterator
|
||||
* \tparam SelectOp <b>[inferred]</b> Selection operator type having member <tt>bool operator()(const T &a)</tt>
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename NumSelectedIteratorT,
|
||||
typename SelectOp>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t If(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the output total number of items selected (i.e., length of \p d_out)
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
SelectOp select_op, ///< [in] Unary selection operator
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType* FlagIterator; // FlagT iterator type (not used)
|
||||
typedef NullType EqualityOp; // Equality operator (not used)
|
||||
|
||||
return DispatchSelectIf<InputIteratorT, FlagIterator, OutputIteratorT, NumSelectedIteratorT, SelectOp, EqualityOp, OffsetT, false>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
NULL,
|
||||
d_out,
|
||||
d_num_selected_out,
|
||||
select_op,
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Given an input sequence \p d_in having runs of consecutive equal-valued keys, only the first key from each run is selectively copied to \p d_out. The total number of items selected is written to \p d_num_selected_out. 
|
||||
*
|
||||
* \par
|
||||
* - The <tt>==</tt> equality operator is used to determine whether keys are equivalent
|
||||
* - Copies of the selected items are compacted into \p d_out and maintain their original relative ordering.
|
||||
* - \devicestorage
|
||||
*
|
||||
* \par Performance
|
||||
* The following charts illustrate saturated select-unique performance across different
|
||||
* CUDA architectures for \p int32 and \p int64 items, respectively. Segments have
|
||||
* lengths uniformly sampled from [1,1000].
|
||||
*
|
||||
* \image html select_unique_int32_len_500.png
|
||||
* \image html select_unique_int64_len_500.png
|
||||
*
|
||||
* \par
|
||||
* The following charts are similar, but with segment lengths uniformly sampled from [1,10]:
|
||||
*
|
||||
* \image html select_unique_int32_len_5.png
|
||||
* \image html select_unique_int64_len_5.png
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the compaction of items selected from an \p int device vector.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_select.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input and output
|
||||
* int num_items; // e.g., 8
|
||||
* int *d_in; // e.g., [0, 2, 2, 9, 5, 5, 5, 8]
|
||||
* int *d_out; // e.g., [ , , , , , , , ]
|
||||
* int *d_num_selected_out; // e.g., [ ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void *d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSelect::Unique(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run selection
|
||||
* cub::DeviceSelect::Unique(d_temp_storage, temp_storage_bytes, d_in, d_out, d_num_selected_out, num_items);
|
||||
*
|
||||
* // d_out <-- [0, 2, 9, 5, 8]
|
||||
* // d_num_selected_out <-- [5]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Random-access input iterator type for reading input items \iterator
|
||||
* \tparam OutputIteratorT <b>[inferred]</b> Random-access output iterator type for writing selected items \iterator
|
||||
* \tparam NumSelectedIteratorT <b>[inferred]</b> Output iterator type for recording the number of items selected \iterator
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OutputIteratorT,
|
||||
typename NumSelectedIteratorT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Unique(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the output total number of items selected (i.e., length of \p d_out)
|
||||
int num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
typedef int OffsetT; // Signed integer type for global offsets
|
||||
typedef NullType* FlagIterator; // FlagT iterator type (not used)
|
||||
typedef NullType SelectOp; // Selection op (not used)
|
||||
typedef Equality EqualityOp; // Default == operator
|
||||
|
||||
return DispatchSelectIf<InputIteratorT, FlagIterator, OutputIteratorT, NumSelectedIteratorT, SelectOp, EqualityOp, OffsetT, false>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
NULL,
|
||||
d_out,
|
||||
d_num_selected_out,
|
||||
SelectOp(),
|
||||
EqualityOp(),
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \example example_device_select_flagged.cu
|
||||
* \example example_device_select_if.cu
|
||||
* \example example_device_select_unique.cu
|
||||
*/
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSpmv provides device-wide parallel operations for performing sparse-matrix * vector multiplication (SpMV).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include "dispatch/dispatch_spmv_orig.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \brief DeviceSpmv provides device-wide parallel operations for performing sparse-matrix * dense-vector multiplication (SpMV).
|
||||
* \ingroup SingleModule
|
||||
*
|
||||
* \par Overview
|
||||
* The [<em>SpMV computation</em>](http://en.wikipedia.org/wiki/Sparse_matrix-vector_multiplication)
|
||||
* performs the matrix-vector operation
|
||||
* <em>y</em> = <em>alpha</em>*<b>A</b>*<em>x</em> + <em>beta</em>*<em>y</em>,
|
||||
* where:
|
||||
* - <b>A</b> is an <em>m</em>x<em>n</em> sparse matrix whose non-zero structure is specified in
|
||||
* [<em>compressed-storage-row (CSR) format</em>](http://en.wikipedia.org/wiki/Sparse_matrix#Compressed_row_Storage_.28CRS_or_CSR.29)
|
||||
* (i.e., three arrays: <em>values</em>, <em>row_offsets</em>, and <em>column_indices</em>)
|
||||
* - <em>x</em> and <em>y</em> are dense vectors
|
||||
* - <em>alpha</em> and <em>beta</em> are scalar multiplicands
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* \cdp_class{DeviceSpmv}
|
||||
*
|
||||
*/
|
||||
struct DeviceSpmv
|
||||
{
|
||||
/******************************************************************//**
|
||||
* \name CSR matrix operations
|
||||
*********************************************************************/
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief This function performs the matrix-vector operation <em>y</em> = <b>A</b>*<em>x</em>.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates SpMV upon a 9x9 CSR matrix <b>A</b>
|
||||
* representing a 3x3 lattice (24 non-zeros).
|
||||
*
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/device/device_spmv.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize device-accessible pointers for input matrix A, input vector x,
|
||||
* // and output vector y
|
||||
* int num_rows = 9;
|
||||
* int num_cols = 9;
|
||||
* int num_nonzeros = 24;
|
||||
*
|
||||
* float* d_values; // e.g., [1, 1, 1, 1, 1, 1, 1, 1,
|
||||
* // 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
* // 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
*
|
||||
* int* d_column_indices; // e.g., [1, 3, 0, 2, 4, 1, 5, 0,
|
||||
* // 4, 6, 1, 3, 5, 7, 2, 4,
|
||||
* // 8, 3, 7, 4, 6, 8, 5, 7]
|
||||
*
|
||||
* int* d_row_offsets; // e.g., [0, 2, 5, 7, 10, 14, 17, 19, 22, 24]
|
||||
*
|
||||
* float* d_vector_x; // e.g., [1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
* float* d_vector_y; // e.g., [ , , , , , , , , ]
|
||||
* ...
|
||||
*
|
||||
* // Determine temporary device storage requirements
|
||||
* void* d_temp_storage = NULL;
|
||||
* size_t temp_storage_bytes = 0;
|
||||
* cub::DeviceSpmv::CsrMV(d_temp_storage, temp_storage_bytes, d_values,
|
||||
* d_row_offsets, d_column_indices, d_vector_x, d_vector_y,
|
||||
* num_rows, num_cols, num_nonzeros, alpha, beta);
|
||||
*
|
||||
* // Allocate temporary storage
|
||||
* cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
*
|
||||
* // Run SpMV
|
||||
* cub::DeviceSpmv::CsrMV(d_temp_storage, temp_storage_bytes, d_values,
|
||||
* d_row_offsets, d_column_indices, d_vector_x, d_vector_y,
|
||||
* num_rows, num_cols, num_nonzeros, alpha, beta);
|
||||
*
|
||||
* // d_vector_y <-- [2, 3, 2, 3, 4, 3, 2, 3, 2]
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ValueT <b>[inferred]</b> Matrix and vector value type (e.g., /p float, /p double, etc.)
|
||||
*/
|
||||
template <
|
||||
typename ValueT>
|
||||
CUB_RUNTIME_FUNCTION
|
||||
static cudaError_t CsrMV(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
ValueT* d_values, ///< [in] Pointer to the array of \p num_nonzeros values of the corresponding nonzero elements of matrix <b>A</b>.
|
||||
int* d_row_offsets, ///< [in] Pointer to the array of \p m + 1 offsets demarcating the start of every row in \p d_column_indices and \p d_values (with the final entry being equal to \p num_nonzeros)
|
||||
int* d_column_indices, ///< [in] Pointer to the array of \p num_nonzeros column-indices of the corresponding nonzero elements of matrix <b>A</b>. (Indices are zero-valued.)
|
||||
ValueT* d_vector_x, ///< [in] Pointer to the array of \p num_cols values corresponding to the dense input vector <em>x</em>
|
||||
ValueT* d_vector_y, ///< [out] Pointer to the array of \p num_rows values corresponding to the dense output vector <em>y</em>
|
||||
int num_rows, ///< [in] number of rows of matrix <b>A</b>.
|
||||
int num_cols, ///< [in] number of columns of matrix <b>A</b>.
|
||||
int num_nonzeros, ///< [in] number of nonzero elements of matrix <b>A</b>.
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
SpmvParams<ValueT, int> spmv_params;
|
||||
spmv_params.d_values = d_values;
|
||||
spmv_params.d_row_end_offsets = d_row_offsets + 1;
|
||||
spmv_params.d_column_indices = d_column_indices;
|
||||
spmv_params.d_vector_x = d_vector_x;
|
||||
spmv_params.d_vector_y = d_vector_y;
|
||||
spmv_params.num_rows = num_rows;
|
||||
spmv_params.num_cols = num_cols;
|
||||
spmv_params.num_nonzeros = num_nonzeros;
|
||||
spmv_params.alpha = 1.0;
|
||||
spmv_params.beta = 0.0;
|
||||
|
||||
return DispatchSpmv<ValueT, int>::Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
spmv_params,
|
||||
stream,
|
||||
debug_synchronous);
|
||||
}
|
||||
|
||||
//@} end member group
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,928 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceReduce provides device-wide, parallel operations for computing a reduction across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "../../agent/agent_reduce.cuh"
|
||||
#include "../../iterator/arg_index_input_iterator.cuh"
|
||||
#include "../../thread/thread_operators.cuh"
|
||||
#include "../../grid/grid_even_share.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../iterator/arg_index_input_iterator.cuh"
|
||||
#include "../../util_debug.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/******************************************************************************
|
||||
* Kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Reduce region kernel entry point (multi-block). Computes privatized reductions, one per thread block.
|
||||
*/
|
||||
template <
|
||||
typename ChainedPolicyT, ///< Chained tuning policy
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OutputIteratorT, ///< Output iterator type for recording the reduced aggregate \iterator
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT> ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
__launch_bounds__ (int(ChainedPolicyT::ActivePolicy::ReducePolicy::BLOCK_THREADS))
|
||||
__global__ void DeviceReduceKernel(
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
OffsetT num_items, ///< [in] Total number of input data items
|
||||
GridEvenShare<OffsetT> even_share, ///< [in] Even-share descriptor for mapping an equal number of tiles onto each thread block
|
||||
GridQueue<OffsetT> queue, ///< [in] Drain queue descriptor for dynamically mapping tile data onto thread blocks
|
||||
ReductionOpT reduction_op) ///< [in] Binary reduction functor
|
||||
{
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// Thread block type for reducing input tiles
|
||||
typedef AgentReduce<
|
||||
typename ChainedPolicyT::ActivePolicy::ReducePolicy,
|
||||
InputIteratorT,
|
||||
OutputIteratorT,
|
||||
OffsetT,
|
||||
ReductionOpT>
|
||||
AgentReduceT;
|
||||
|
||||
// Shared memory storage
|
||||
__shared__ typename AgentReduceT::TempStorage temp_storage;
|
||||
|
||||
// Consume input tiles
|
||||
OutputT block_aggregate = AgentReduceT(temp_storage, d_in, reduction_op).ConsumeTiles(
|
||||
num_items,
|
||||
even_share,
|
||||
queue,
|
||||
Int2Type<ChainedPolicyT::ActivePolicy::ReducePolicy::GRID_MAPPING>());
|
||||
|
||||
// Output result
|
||||
if (threadIdx.x == 0)
|
||||
d_out[blockIdx.x] = block_aggregate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce a single tile kernel entry point (single-block). Can be used to aggregate privatized threadblock reductions from a previous multi-block reduction pass.
|
||||
*/
|
||||
template <
|
||||
typename ChainedPolicyT, ///< Chained tuning policy
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OutputIteratorT, ///< Output iterator type for recording the reduced aggregate \iterator
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT, ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
typename OuputT> ///< Data element type that is convertible to the \p value type of \p OutputIteratorT
|
||||
__launch_bounds__ (int(ChainedPolicyT::ActivePolicy::SingleTilePolicy::BLOCK_THREADS), 1)
|
||||
__global__ void DeviceReduceSingleTileKernel(
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
OffsetT num_items, ///< [in] Total number of input data items
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
OuputT init) ///< [in] The initial value of the reduction
|
||||
{
|
||||
// Thread block type for reducing input tiles
|
||||
typedef AgentReduce<
|
||||
typename ChainedPolicyT::ActivePolicy::SingleTilePolicy,
|
||||
InputIteratorT,
|
||||
OutputIteratorT,
|
||||
OffsetT,
|
||||
ReductionOpT>
|
||||
AgentReduceT;
|
||||
|
||||
// Shared memory storage
|
||||
__shared__ typename AgentReduceT::TempStorage temp_storage;
|
||||
|
||||
// Check if empty problem
|
||||
if (num_items == 0)
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
*d_out = init;
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume input tiles
|
||||
OuputT block_aggregate = AgentReduceT(temp_storage, d_in, reduction_op).ConsumeRange(
|
||||
OffsetT(0),
|
||||
num_items);
|
||||
|
||||
// Output result
|
||||
if (threadIdx.x == 0)
|
||||
*d_out = reduction_op(init, block_aggregate);
|
||||
}
|
||||
|
||||
|
||||
/// Normalize input iterator to segment offset
|
||||
template <typename T, typename OffsetT, typename IteratorT>
|
||||
__device__ __forceinline__
|
||||
void NormalizeReductionOutput(
|
||||
T &/*val*/,
|
||||
OffsetT /*base_offset*/,
|
||||
IteratorT /*itr*/)
|
||||
{}
|
||||
|
||||
|
||||
/// Normalize input iterator to segment offset (specialized for arg-index)
|
||||
template <typename KeyValuePairT, typename OffsetT, typename WrappedIteratorT, typename OutputValueT>
|
||||
__device__ __forceinline__
|
||||
void NormalizeReductionOutput(
|
||||
KeyValuePairT &val,
|
||||
OffsetT base_offset,
|
||||
ArgIndexInputIterator<WrappedIteratorT, OffsetT, OutputValueT> /*itr*/)
|
||||
{
|
||||
val.key -= base_offset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Segmented reduction (one block per segment)
|
||||
*/
|
||||
template <
|
||||
typename ChainedPolicyT, ///< Chained tuning policy
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OutputIteratorT, ///< Output iterator type for recording the reduced aggregate \iterator
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT, ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
typename OutputT> ///< Data element type that is convertible to the \p value type of \p OutputIteratorT
|
||||
__launch_bounds__ (int(ChainedPolicyT::ActivePolicy::ReducePolicy::BLOCK_THREADS))
|
||||
__global__ void DeviceSegmentedReduceKernel(
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
int /*num_segments*/, ///< [in] The number of segments that comprise the sorting data
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
OutputT init) ///< [in] The initial value of the reduction
|
||||
{
|
||||
// Thread block type for reducing input tiles
|
||||
typedef AgentReduce<
|
||||
typename ChainedPolicyT::ActivePolicy::ReducePolicy,
|
||||
InputIteratorT,
|
||||
OutputIteratorT,
|
||||
OffsetT,
|
||||
ReductionOpT>
|
||||
AgentReduceT;
|
||||
|
||||
// Shared memory storage
|
||||
__shared__ typename AgentReduceT::TempStorage temp_storage;
|
||||
|
||||
OffsetT segment_begin = d_begin_offsets[blockIdx.x];
|
||||
OffsetT segment_end = d_end_offsets[blockIdx.x];
|
||||
|
||||
// Check if empty problem
|
||||
if (segment_begin == segment_end)
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
d_out[blockIdx.x] = init;
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume input tiles
|
||||
OutputT block_aggregate = AgentReduceT(temp_storage, d_in, reduction_op).ConsumeRange(
|
||||
segment_begin,
|
||||
segment_end);
|
||||
|
||||
// Normalize as needed
|
||||
NormalizeReductionOutput(block_aggregate, segment_begin, d_in);
|
||||
|
||||
if (threadIdx.x == 0)
|
||||
d_out[blockIdx.x] = reduction_op(init, block_aggregate);;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Policy
|
||||
******************************************************************************/
|
||||
|
||||
template <
|
||||
typename OuputT, ///< Data type
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT> ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
struct DeviceReducePolicy
|
||||
{
|
||||
//------------------------------------------------------------------------------
|
||||
// Architecture-specific tuning policies
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// SM13
|
||||
struct Policy130 : ChainedPolicy<130, Policy130, Policy130>
|
||||
{
|
||||
// ReducePolicy
|
||||
typedef AgentReducePolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 8, OuputT), ///< Threads per block, items per thread
|
||||
2, ///< Number of items per vectorized load
|
||||
BLOCK_REDUCE_RAKING, ///< Cooperative block-wide reduction algorithm to use
|
||||
LOAD_DEFAULT, ///< Cache load modifier
|
||||
GRID_MAPPING_EVEN_SHARE> ///< How to map tiles of input onto thread blocks
|
||||
ReducePolicy;
|
||||
|
||||
// SingleTilePolicy
|
||||
typedef ReducePolicy SingleTilePolicy;
|
||||
|
||||
// SegmentedReducePolicy
|
||||
typedef ReducePolicy SegmentedReducePolicy;
|
||||
};
|
||||
|
||||
|
||||
/// SM20
|
||||
struct Policy200 : ChainedPolicy<200, Policy200, Policy130>
|
||||
{
|
||||
// ReducePolicy (GTX 580: 178.9 GB/s @ 48M 4B items, 158.1 GB/s @ 192M 1B items)
|
||||
typedef AgentReducePolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 8, OuputT), ///< Threads per block, items per thread
|
||||
4, ///< Number of items per vectorized load
|
||||
BLOCK_REDUCE_RAKING, ///< Cooperative block-wide reduction algorithm to use
|
||||
LOAD_DEFAULT, ///< Cache load modifier
|
||||
GRID_MAPPING_DYNAMIC> ///< How to map tiles of input onto thread blocks
|
||||
ReducePolicy;
|
||||
|
||||
// SingleTilePolicy
|
||||
typedef ReducePolicy SingleTilePolicy;
|
||||
|
||||
// SegmentedReducePolicy
|
||||
typedef ReducePolicy SegmentedReducePolicy;
|
||||
};
|
||||
|
||||
|
||||
/// SM30
|
||||
struct Policy300 : ChainedPolicy<300, Policy300, Policy200>
|
||||
{
|
||||
// ReducePolicy (GTX670: 154.0 @ 48M 4B items)
|
||||
typedef AgentReducePolicy<
|
||||
CUB_NOMINAL_CONFIG(256, 20, OuputT), ///< Threads per block, items per thread
|
||||
2, ///< Number of items per vectorized load
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS, ///< Cooperative block-wide reduction algorithm to use
|
||||
LOAD_DEFAULT, ///< Cache load modifier
|
||||
GRID_MAPPING_EVEN_SHARE> ///< How to map tiles of input onto thread blocks
|
||||
ReducePolicy;
|
||||
|
||||
// SingleTilePolicy
|
||||
typedef ReducePolicy SingleTilePolicy;
|
||||
|
||||
// SegmentedReducePolicy
|
||||
typedef ReducePolicy SegmentedReducePolicy;
|
||||
};
|
||||
|
||||
|
||||
/// SM35
|
||||
struct Policy350 : ChainedPolicy<350, Policy350, Policy300>
|
||||
{
|
||||
// ReducePolicy (GTX Titan: 255.1 GB/s @ 48M 4B items; 228.7 GB/s @ 192M 1B items)
|
||||
typedef AgentReducePolicy<
|
||||
CUB_NOMINAL_CONFIG(256, 20, OuputT), ///< Threads per block, items per thread
|
||||
4, ///< Number of items per vectorized load
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS, ///< Cooperative block-wide reduction algorithm to use
|
||||
LOAD_LDG, ///< Cache load modifier
|
||||
GRID_MAPPING_DYNAMIC> ///< How to map tiles of input onto thread blocks
|
||||
ReducePolicy;
|
||||
|
||||
// SingleTilePolicy
|
||||
typedef ReducePolicy SingleTilePolicy;
|
||||
|
||||
// SegmentedReducePolicy
|
||||
typedef ReducePolicy SegmentedReducePolicy;
|
||||
};
|
||||
|
||||
/// SM60
|
||||
struct Policy600 : ChainedPolicy<600, Policy600, Policy350>
|
||||
{
|
||||
// ReducePolicy (P100: 591 GB/s @ 64M 4B items; 583 GB/s @ 256M 1B items)
|
||||
typedef AgentReducePolicy<
|
||||
CUB_NOMINAL_CONFIG(256, 16, OuputT), ///< Threads per block, items per thread
|
||||
4, ///< Number of items per vectorized load
|
||||
BLOCK_REDUCE_WARP_REDUCTIONS, ///< Cooperative block-wide reduction algorithm to use
|
||||
LOAD_LDG, ///< Cache load modifier
|
||||
GRID_MAPPING_DYNAMIC> ///< How to map tiles of input onto thread blocks
|
||||
ReducePolicy;
|
||||
|
||||
// SingleTilePolicy
|
||||
typedef ReducePolicy SingleTilePolicy;
|
||||
|
||||
// SegmentedReducePolicy
|
||||
typedef ReducePolicy SegmentedReducePolicy;
|
||||
};
|
||||
|
||||
|
||||
/// MaxPolicy
|
||||
typedef Policy600 MaxPolicy;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Single-problem dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for device-wide reduction
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OutputIteratorT, ///< Output iterator type for recording the reduced aggregate \iterator
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT> ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
struct DispatchReduce :
|
||||
DeviceReducePolicy<
|
||||
typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type, // ... else the output iterator's value type
|
||||
OffsetT,
|
||||
ReductionOpT>
|
||||
{
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Data type of output iterator
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Problem state
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void *d_temp_storage; ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes; ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in; ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out; ///< [out] Pointer to the output aggregate
|
||||
OffsetT num_items; ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
ReductionOpT reduction_op; ///< [in] Binary reduction functor
|
||||
OutputT init; ///< [in] The initial value of the reduction
|
||||
cudaStream_t stream; ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous; ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int ptx_version; ///< [in] PTX version
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
DispatchReduce(
|
||||
void* d_temp_storage,
|
||||
size_t &temp_storage_bytes,
|
||||
InputIteratorT d_in,
|
||||
OutputIteratorT d_out,
|
||||
OffsetT num_items,
|
||||
ReductionOpT reduction_op,
|
||||
OutputT init,
|
||||
cudaStream_t stream,
|
||||
bool debug_synchronous,
|
||||
int ptx_version)
|
||||
:
|
||||
d_temp_storage(d_temp_storage),
|
||||
temp_storage_bytes(temp_storage_bytes),
|
||||
d_in(d_in),
|
||||
d_out(d_out),
|
||||
num_items(num_items),
|
||||
reduction_op(reduction_op),
|
||||
init(init),
|
||||
stream(stream),
|
||||
debug_synchronous(debug_synchronous),
|
||||
ptx_version(ptx_version)
|
||||
{}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Small-problem (single tile) invocation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Invoke a single block block to reduce in-core
|
||||
template <
|
||||
typename ActivePolicyT, ///< Umbrella policy active for the target device
|
||||
typename SingleTileKernelT> ///< Function type of cub::DeviceReduceSingleTileKernel
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
cudaError_t InvokeSingleTile(
|
||||
SingleTileKernelT single_tile_kernel) ///< [in] Kernel function pointer to parameterization of cub::DeviceReduceSingleTileKernel
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void)single_tile_kernel;
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
temp_storage_bytes = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Log single_reduce_sweep_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking DeviceReduceSingleTileKernel<<<1, %d, 0, %lld>>>(), %d items per thread\n",
|
||||
ActivePolicyT::SingleTilePolicy::BLOCK_THREADS,
|
||||
(long long) stream,
|
||||
ActivePolicyT::SingleTilePolicy::ITEMS_PER_THREAD);
|
||||
|
||||
// Invoke single_reduce_sweep_kernel
|
||||
single_tile_kernel<<<1, ActivePolicyT::SingleTilePolicy::BLOCK_THREADS, 0, stream>>>(
|
||||
d_in,
|
||||
d_out,
|
||||
num_items,
|
||||
reduction_op,
|
||||
init);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Normal problem size invocation (two-pass)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Invoke two-passes to reduce
|
||||
template <
|
||||
typename ActivePolicyT, ///< Umbrella policy active for the target device
|
||||
typename ReduceKernelT, ///< Function type of cub::DeviceReduceKernel
|
||||
typename SingleTileKernelT, ///< Function type of cub::DeviceReduceSingleTileKernel
|
||||
typename FillAndResetDrainKernelT> ///< Function type of cub::FillAndResetDrainKernel
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
cudaError_t InvokePasses(
|
||||
ReduceKernelT reduce_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceReduceKernel
|
||||
SingleTileKernelT single_tile_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceReduceSingleTileKernel
|
||||
FillAndResetDrainKernelT prepare_drain_kernel) ///< [in] Kernel function pointer to parameterization of cub::FillAndResetDrainKernel
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void) reduce_kernel;
|
||||
(void) single_tile_kernel;
|
||||
(void) prepare_drain_kernel;
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
#else
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Init regular kernel configuration
|
||||
KernelConfig reduce_config;
|
||||
if (CubDebug(error = reduce_config.Init<typename ActivePolicyT::ReducePolicy>(reduce_kernel))) break;
|
||||
int reduce_device_occupancy = reduce_config.sm_occupancy * sm_count;
|
||||
|
||||
// Even-share work distribution
|
||||
int max_blocks = reduce_device_occupancy * CUB_SUBSCRIPTION_FACTOR(ptx_version);
|
||||
GridEvenShare<OffsetT> even_share(num_items, max_blocks, reduce_config.tile_size);
|
||||
|
||||
// Temporary storage allocation requirements
|
||||
void* allocations[2];
|
||||
size_t allocation_sizes[2] =
|
||||
{
|
||||
max_blocks * sizeof(OutputT), // bytes needed for privatized block reductions
|
||||
GridQueue<OffsetT>::AllocationSize() // bytes needed for grid queue descriptor
|
||||
};
|
||||
|
||||
// Alias the temporary allocations from the single storage blob (or compute the necessary size of the blob)
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
// Alias the allocation for the privatized per-block reductions
|
||||
OutputT *d_block_reductions = (OutputT*) allocations[0];
|
||||
|
||||
// Alias the allocation for the grid queue descriptor
|
||||
GridQueue<OffsetT> queue(allocations[1]);
|
||||
|
||||
// Get grid size for device_reduce_sweep_kernel
|
||||
int reduce_grid_size;
|
||||
if (ActivePolicyT::ReducePolicy::GRID_MAPPING == GRID_MAPPING_EVEN_SHARE)
|
||||
{
|
||||
// Work is distributed evenly
|
||||
reduce_grid_size = even_share.grid_size;
|
||||
}
|
||||
else if (ActivePolicyT::ReducePolicy::GRID_MAPPING == GRID_MAPPING_DYNAMIC)
|
||||
{
|
||||
// Work is distributed dynamically
|
||||
int num_tiles = (num_items + reduce_config.tile_size - 1) / reduce_config.tile_size;
|
||||
reduce_grid_size = (num_tiles < reduce_device_occupancy) ?
|
||||
num_tiles : // Not enough to fill the device with threadblocks
|
||||
reduce_device_occupancy; // Fill the device with threadblocks
|
||||
|
||||
// Prepare the dynamic queue descriptor if necessary
|
||||
if (debug_synchronous) _CubLog("Invoking prepare_drain_kernel<<<1, 1, 0, %lld>>>()\n", (long long) stream);
|
||||
|
||||
// Invoke prepare_drain_kernel
|
||||
prepare_drain_kernel<<<1, 1, 0, stream>>>(queue, num_items);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = CubDebug(cudaErrorNotSupported ); break;
|
||||
}
|
||||
|
||||
// Log device_reduce_sweep_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking DeviceReduceKernel<<<%d, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
reduce_grid_size,
|
||||
ActivePolicyT::ReducePolicy::BLOCK_THREADS,
|
||||
(long long) stream,
|
||||
ActivePolicyT::ReducePolicy::ITEMS_PER_THREAD,
|
||||
reduce_config.sm_occupancy);
|
||||
|
||||
// Invoke DeviceReduceKernel
|
||||
reduce_kernel<<<reduce_grid_size, ActivePolicyT::ReducePolicy::BLOCK_THREADS, 0, stream>>>(
|
||||
d_in,
|
||||
d_block_reductions,
|
||||
num_items,
|
||||
even_share,
|
||||
queue,
|
||||
reduction_op);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Log single_reduce_sweep_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking DeviceReduceSingleTileKernel<<<1, %d, 0, %lld>>>(), %d items per thread\n",
|
||||
ActivePolicyT::SingleTilePolicy::BLOCK_THREADS,
|
||||
(long long) stream,
|
||||
ActivePolicyT::SingleTilePolicy::ITEMS_PER_THREAD);
|
||||
|
||||
// Invoke DeviceReduceSingleTileKernel
|
||||
single_tile_kernel<<<1, ActivePolicyT::SingleTilePolicy::BLOCK_THREADS, 0, stream>>>(
|
||||
d_block_reductions,
|
||||
d_out,
|
||||
reduce_grid_size,
|
||||
reduction_op,
|
||||
init);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chained policy invocation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Invocation
|
||||
template <typename ActivePolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
cudaError_t Invoke()
|
||||
{
|
||||
typedef typename ActivePolicyT::SingleTilePolicy SingleTilePolicyT;
|
||||
typedef typename DispatchReduce::MaxPolicy MaxPolicyT;
|
||||
|
||||
// Force kernel code-generation in all compiler passes
|
||||
if (num_items <= (SingleTilePolicyT::BLOCK_THREADS * SingleTilePolicyT::ITEMS_PER_THREAD))
|
||||
{
|
||||
// Small, single tile size
|
||||
return InvokeSingleTile<ActivePolicyT>(
|
||||
DeviceReduceSingleTileKernel<MaxPolicyT, InputIteratorT, OutputIteratorT, OffsetT, ReductionOpT, OutputT>);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular size
|
||||
return InvokePasses<ActivePolicyT>(
|
||||
DeviceReduceKernel<typename DispatchReduce::MaxPolicy, InputIteratorT, OutputT*, OffsetT, ReductionOpT>,
|
||||
DeviceReduceSingleTileKernel<MaxPolicyT, OutputT*, OutputIteratorT, OffsetT, ReductionOpT, OutputT>,
|
||||
FillAndResetDrainKernel<OffsetT>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
OutputT init, ///< [in] The initial value of the reduction
|
||||
cudaStream_t stream, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
typedef typename DispatchReduce::MaxPolicy MaxPolicyT;
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
|
||||
// Create dispatch functor
|
||||
DispatchReduce dispatch(
|
||||
d_temp_storage, temp_storage_bytes,
|
||||
d_in, d_out, num_items, reduction_op, init,
|
||||
stream, debug_synchronous, ptx_version);
|
||||
|
||||
// Dispatch to chained policy
|
||||
if (CubDebug(error = MaxPolicyT::Invoke(ptx_version, dispatch))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Segmented dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for device-wide reduction
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OutputIteratorT, ///< Output iterator type for recording the reduced aggregate \iterator
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ReductionOpT> ///< Binary reduction functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
struct DispatchSegmentedReduce :
|
||||
DeviceReducePolicy<
|
||||
typename std::iterator_traits<InputIteratorT>::value_type,
|
||||
OffsetT,
|
||||
ReductionOpT>
|
||||
{
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Problem state
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void *d_temp_storage; ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes; ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in; ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out; ///< [out] Pointer to the output aggregate
|
||||
OffsetT num_segments; ///< [in] The number of segments that comprise the sorting data
|
||||
OffsetT *d_begin_offsets; ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
OffsetT *d_end_offsets; ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
ReductionOpT reduction_op; ///< [in] Binary reduction functor
|
||||
OutputT init; ///< [in] The initial value of the reduction
|
||||
cudaStream_t stream; ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous; ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int ptx_version; ///< [in] PTX version
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Constructor
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
DispatchSegmentedReduce(
|
||||
void* d_temp_storage,
|
||||
size_t &temp_storage_bytes,
|
||||
InputIteratorT d_in,
|
||||
OutputIteratorT d_out,
|
||||
OffsetT num_segments,
|
||||
OffsetT *d_begin_offsets,
|
||||
OffsetT *d_end_offsets,
|
||||
ReductionOpT reduction_op,
|
||||
OutputT init,
|
||||
cudaStream_t stream,
|
||||
bool debug_synchronous,
|
||||
int ptx_version)
|
||||
:
|
||||
d_temp_storage(d_temp_storage),
|
||||
temp_storage_bytes(temp_storage_bytes),
|
||||
d_in(d_in),
|
||||
d_out(d_out),
|
||||
num_segments(num_segments),
|
||||
d_begin_offsets(d_begin_offsets),
|
||||
d_end_offsets(d_end_offsets),
|
||||
reduction_op(reduction_op),
|
||||
init(init),
|
||||
stream(stream),
|
||||
debug_synchronous(debug_synchronous),
|
||||
ptx_version(ptx_version)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chained policy invocation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Invocation
|
||||
template <
|
||||
typename ActivePolicyT, ///< Umbrella policy active for the target device
|
||||
typename DeviceSegmentedReduceKernelT> ///< Function type of cub::DeviceSegmentedReduceKernel
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
cudaError_t InvokePasses(
|
||||
DeviceSegmentedReduceKernelT segmented_reduce_kernel) ///< [in] Kernel function pointer to parameterization of cub::DeviceSegmentedReduceKernel
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void)segmented_reduce_kernel;
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
temp_storage_bytes = 1;
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
// Init kernel configuration
|
||||
KernelConfig segmented_reduce_config;
|
||||
if (CubDebug(error = segmented_reduce_config.Init<typename ActivePolicyT::SegmentedReducePolicy>(segmented_reduce_kernel))) break;
|
||||
|
||||
// Log device_reduce_sweep_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking SegmentedDeviceReduceKernel<<<%d, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
num_segments,
|
||||
ActivePolicyT::SegmentedReducePolicy::BLOCK_THREADS,
|
||||
(long long) stream,
|
||||
ActivePolicyT::SegmentedReducePolicy::ITEMS_PER_THREAD,
|
||||
segmented_reduce_config.sm_occupancy);
|
||||
|
||||
// Invoke DeviceReduceKernel
|
||||
segmented_reduce_kernel<<<num_segments, ActivePolicyT::SegmentedReducePolicy::BLOCK_THREADS, 0, stream>>>(
|
||||
d_in,
|
||||
d_out,
|
||||
d_begin_offsets,
|
||||
d_end_offsets,
|
||||
num_segments,
|
||||
reduction_op,
|
||||
init);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Invocation
|
||||
template <typename ActivePolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
cudaError_t Invoke()
|
||||
{
|
||||
typedef typename DispatchSegmentedReduce::MaxPolicy MaxPolicyT;
|
||||
|
||||
// Force kernel code-generation in all compiler passes
|
||||
return InvokePasses<ActivePolicyT>(
|
||||
DeviceSegmentedReduceKernel<MaxPolicyT, InputIteratorT, OutputIteratorT, OffsetT, ReductionOpT, OutputT>);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void *d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t &temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output aggregate
|
||||
int num_segments, ///< [in] The number of segments that comprise the sorting data
|
||||
int *d_begin_offsets, ///< [in] %Device-accessible pointer to the sequence of beginning offsets of length \p num_segments, such that <tt>d_begin_offsets[i]</tt> is the first element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>
|
||||
int *d_end_offsets, ///< [in] %Device-accessible pointer to the sequence of ending offsets of length \p num_segments, such that <tt>d_end_offsets[i]-1</tt> is the last element of the <em>i</em><sup>th</sup> data segment in <tt>d_keys_*</tt> and <tt>d_values_*</tt>. If <tt>d_end_offsets[i]-1</tt> <= <tt>d_begin_offsets[i]</tt>, the <em>i</em><sup>th</sup> is considered empty.
|
||||
ReductionOpT reduction_op, ///< [in] Binary reduction functor
|
||||
OutputT init, ///< [in] The initial value of the reduction
|
||||
cudaStream_t stream, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
typedef typename DispatchSegmentedReduce::MaxPolicy MaxPolicyT;
|
||||
|
||||
if (num_segments <= 0)
|
||||
return cudaSuccess;
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
|
||||
// Create dispatch functor
|
||||
DispatchSegmentedReduce dispatch(
|
||||
d_temp_storage, temp_storage_bytes,
|
||||
d_in, d_out,
|
||||
num_segments, d_begin_offsets, d_end_offsets,
|
||||
reduction_op, init,
|
||||
stream, debug_synchronous, ptx_version);
|
||||
|
||||
// Dispatch to chained policy
|
||||
if (CubDebug(error = MaxPolicyT::Invoke(ptx_version, dispatch))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceReduceByKey provides device-wide, parallel operations for reducing segments of values residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch_scan.cuh"
|
||||
#include "../../agent/agent_reduce_by_key.cuh"
|
||||
#include "../../thread/thread_operators.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/******************************************************************************
|
||||
* Kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Multi-block reduce-by-key sweep kernel entry point
|
||||
*/
|
||||
template <
|
||||
typename AgentReduceByKeyPolicyT, ///< Parameterized AgentReduceByKeyPolicyT tuning policy type
|
||||
typename KeysInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename UniqueOutputIteratorT, ///< Random-access output iterator type for keys
|
||||
typename ValuesInputIteratorT, ///< Random-access input iterator type for values
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename NumRunsOutputIteratorT, ///< Output iterator type for recording number of segments encountered
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename EqualityOpT, ///< KeyT equality operator type
|
||||
typename ReductionOpT, ///< ValueT reduction operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
__launch_bounds__ (int(AgentReduceByKeyPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceReduceByKeyKernel(
|
||||
KeysInputIteratorT d_keys_in, ///< Pointer to the input sequence of keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< Pointer to the output sequence of unique keys (one key per run)
|
||||
ValuesInputIteratorT d_values_in, ///< Pointer to the input sequence of corresponding values
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< Pointer to the output sequence of value aggregates (one aggregate per run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< Pointer to total number of runs encountered (i.e., the length of d_unique_out)
|
||||
ScanTileStateT tile_state, ///< Tile status interface
|
||||
int start_tile, ///< The starting tile for the current grid
|
||||
EqualityOpT equality_op, ///< KeyT equality operator
|
||||
ReductionOpT reduction_op, ///< ValueT reduction operator
|
||||
OffsetT num_items) ///< Total number of items to select from
|
||||
{
|
||||
// Thread block type for reducing tiles of value segments
|
||||
typedef AgentReduceByKey<
|
||||
AgentReduceByKeyPolicyT,
|
||||
KeysInputIteratorT,
|
||||
UniqueOutputIteratorT,
|
||||
ValuesInputIteratorT,
|
||||
AggregatesOutputIteratorT,
|
||||
NumRunsOutputIteratorT,
|
||||
EqualityOpT,
|
||||
ReductionOpT,
|
||||
OffsetT>
|
||||
AgentReduceByKeyT;
|
||||
|
||||
// Shared memory for AgentReduceByKey
|
||||
__shared__ typename AgentReduceByKeyT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentReduceByKeyT(temp_storage, d_keys_in, d_unique_out, d_values_in, d_aggregates_out, d_num_runs_out, equality_op, reduction_op).ConsumeRange(
|
||||
num_items,
|
||||
tile_state,
|
||||
start_tile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceReduceByKey
|
||||
*/
|
||||
template <
|
||||
typename KeysInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename UniqueOutputIteratorT, ///< Random-access output iterator type for keys
|
||||
typename ValuesInputIteratorT, ///< Random-access input iterator type for values
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename NumRunsOutputIteratorT, ///< Output iterator type for recording number of segments encountered
|
||||
typename EqualityOpT, ///< KeyT equality operator type
|
||||
typename ReductionOpT, ///< ValueT reduction operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DispatchReduceByKey
|
||||
{
|
||||
//-------------------------------------------------------------------------
|
||||
// Types and constants
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// The input keys type
|
||||
typedef typename std::iterator_traits<KeysInputIteratorT>::value_type KeyInputT;
|
||||
|
||||
// The output keys type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<UniqueOutputIteratorT>::value_type, void>::VALUE), // KeyOutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<KeysInputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<UniqueOutputIteratorT>::value_type>::Type KeyOutputT; // ... else the output iterator's value type
|
||||
|
||||
// The input values type
|
||||
typedef typename std::iterator_traits<ValuesInputIteratorT>::value_type ValueInputT;
|
||||
|
||||
// The output values type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<AggregatesOutputIteratorT>::value_type, void>::VALUE), // ValueOutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<ValuesInputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<AggregatesOutputIteratorT>::value_type>::Type ValueOutputT; // ... else the output iterator's value type
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128,
|
||||
MAX_INPUT_BYTES = CUB_MAX(sizeof(KeyOutputT), sizeof(ValueOutputT)),
|
||||
COMBINED_INPUT_BYTES = sizeof(KeyOutputT) + sizeof(ValueOutputT),
|
||||
};
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<ValueOutputT, OffsetT> ScanTileStateT;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Tuning policies
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 6,
|
||||
ITEMS_PER_THREAD = (MAX_INPUT_BYTES <= 8) ? 6 : CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, ((NOMINAL_4B_ITEMS_PER_THREAD * 8) + COMBINED_INPUT_BYTES - 1) / COMBINED_INPUT_BYTES)),
|
||||
};
|
||||
|
||||
typedef AgentReduceByKeyPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_DIRECT,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ReduceByKeyPolicyT;
|
||||
};
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 6,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, ((NOMINAL_4B_ITEMS_PER_THREAD * 8) + COMBINED_INPUT_BYTES - 1) / COMBINED_INPUT_BYTES)),
|
||||
};
|
||||
|
||||
typedef AgentReduceByKeyPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ReduceByKeyPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 11,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, ((NOMINAL_4B_ITEMS_PER_THREAD * 8) + COMBINED_INPUT_BYTES - 1) / COMBINED_INPUT_BYTES)),
|
||||
};
|
||||
|
||||
typedef AgentReduceByKeyPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ReduceByKeyPolicyT;
|
||||
};
|
||||
|
||||
/// SM13
|
||||
struct Policy130
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 7,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, ((NOMINAL_4B_ITEMS_PER_THREAD * 8) + COMBINED_INPUT_BYTES - 1) / COMBINED_INPUT_BYTES)),
|
||||
};
|
||||
|
||||
typedef AgentReduceByKeyPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ReduceByKeyPolicyT;
|
||||
};
|
||||
|
||||
/// SM11
|
||||
struct Policy110
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 5,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 8) / COMBINED_INPUT_BYTES)),
|
||||
};
|
||||
|
||||
typedef AgentReduceByKeyPolicy<
|
||||
64,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_RAKING>
|
||||
ReduceByKeyPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policies of current PTX compiler pass
|
||||
******************************************************************************/
|
||||
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 130)
|
||||
typedef Policy130 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy110 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxReduceByKeyPolicy : PtxPolicy::ReduceByKeyPolicyT {};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utilities
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &reduce_by_key_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)ptx_version;
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
reduce_by_key_config.template Init<PtxReduceByKeyPolicy>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 350)
|
||||
{
|
||||
reduce_by_key_config.template Init<typename Policy350::ReduceByKeyPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
reduce_by_key_config.template Init<typename Policy300::ReduceByKeyPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
reduce_by_key_config.template Init<typename Policy200::ReduceByKeyPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 130)
|
||||
{
|
||||
reduce_by_key_config.template Init<typename Policy130::ReduceByKeyPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
reduce_by_key_config.template Init<typename Policy110::ReduceByKeyPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduce-by-key using the
|
||||
* specified kernel functions.
|
||||
*/
|
||||
template <
|
||||
typename ScanInitKernelT, ///< Function type of cub::DeviceScanInitKernel
|
||||
typename ReduceByKeyKernelT> ///< Function type of cub::DeviceReduceByKeyKernelT
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
KeysInputIteratorT d_keys_in, ///< [in] Pointer to the input sequence of keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< [out] Pointer to the output sequence of unique keys (one key per run)
|
||||
ValuesInputIteratorT d_values_in, ///< [in] Pointer to the input sequence of corresponding values
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< [out] Pointer to the output sequence of value aggregates (one aggregate per run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs encountered (i.e., the length of d_unique_out)
|
||||
EqualityOpT equality_op, ///< [in] KeyT equality operator
|
||||
ReductionOpT reduction_op, ///< [in] ValueT reduction operator
|
||||
OffsetT num_items, ///< [in] Total number of items to select from
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int /*ptx_version*/, ///< [in] PTX version of dispatch kernels
|
||||
ScanInitKernelT init_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceScanInitKernel
|
||||
ReduceByKeyKernelT reduce_by_key_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceReduceByKeyKernel
|
||||
KernelConfig reduce_by_key_config) ///< [in] Dispatch parameters that match the policy that \p reduce_by_key_kernel was compiled for
|
||||
{
|
||||
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void)d_temp_storage;
|
||||
(void)temp_storage_bytes;
|
||||
(void)d_keys_in;
|
||||
(void)d_unique_out;
|
||||
(void)d_values_in;
|
||||
(void)d_aggregates_out;
|
||||
(void)d_num_runs_out;
|
||||
(void)equality_op;
|
||||
(void)reduction_op;
|
||||
(void)num_items;
|
||||
(void)stream;
|
||||
(void)debug_synchronous;
|
||||
(void)init_kernel;
|
||||
(void)reduce_by_key_kernel;
|
||||
(void)reduce_by_key_config;
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported);
|
||||
|
||||
#else
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Number of input tiles
|
||||
int tile_size = reduce_by_key_config.block_threads * reduce_by_key_config.items_per_thread;
|
||||
int num_tiles = (num_items + tile_size - 1) / tile_size;
|
||||
|
||||
// Specify temporary storage allocation requirements
|
||||
size_t allocation_sizes[1];
|
||||
if (CubDebug(error = ScanTileStateT::AllocationSize(num_tiles, allocation_sizes[0]))) break; // bytes needed for tile status descriptors
|
||||
|
||||
// Compute allocation pointers into the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[1];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the tile status interface
|
||||
ScanTileStateT tile_state;
|
||||
if (CubDebug(error = tile_state.Init(num_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
|
||||
// Log init_kernel configuration
|
||||
int init_grid_size = CUB_MAX(1, (num_tiles + INIT_KERNEL_THREADS - 1) / INIT_KERNEL_THREADS);
|
||||
if (debug_synchronous) _CubLog("Invoking init_kernel<<<%d, %d, 0, %lld>>>()\n", init_grid_size, INIT_KERNEL_THREADS, (long long) stream);
|
||||
|
||||
// Invoke init_kernel to initialize tile descriptors
|
||||
init_kernel<<<init_grid_size, INIT_KERNEL_THREADS, 0, stream>>>(
|
||||
tile_state,
|
||||
num_tiles,
|
||||
d_num_runs_out);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Return if empty problem
|
||||
if (num_items == 0)
|
||||
break;
|
||||
|
||||
// Get SM occupancy for reduce_by_key_kernel
|
||||
int reduce_by_key_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
reduce_by_key_sm_occupancy, // out
|
||||
reduce_by_key_kernel,
|
||||
reduce_by_key_config.block_threads))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Run grids in epochs (in case number of tiles exceeds max x-dimension
|
||||
int scan_grid_size = CUB_MIN(num_tiles, max_dim_x);
|
||||
for (int start_tile = 0; start_tile < num_tiles; start_tile += scan_grid_size)
|
||||
{
|
||||
// Log reduce_by_key_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking %d reduce_by_key_kernel<<<%d, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
start_tile, scan_grid_size, reduce_by_key_config.block_threads, (long long) stream, reduce_by_key_config.items_per_thread, reduce_by_key_sm_occupancy);
|
||||
|
||||
// Invoke reduce_by_key_kernel
|
||||
reduce_by_key_kernel<<<scan_grid_size, reduce_by_key_config.block_threads, 0, stream>>>(
|
||||
d_keys_in,
|
||||
d_unique_out,
|
||||
d_values_in,
|
||||
d_aggregates_out,
|
||||
d_num_runs_out,
|
||||
tile_state,
|
||||
start_tile,
|
||||
equality_op,
|
||||
reduction_op,
|
||||
num_items);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
KeysInputIteratorT d_keys_in, ///< [in] Pointer to the input sequence of keys
|
||||
UniqueOutputIteratorT d_unique_out, ///< [out] Pointer to the output sequence of unique keys (one key per run)
|
||||
ValuesInputIteratorT d_values_in, ///< [in] Pointer to the input sequence of corresponding values
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< [out] Pointer to the output sequence of value aggregates (one aggregate per run)
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs encountered (i.e., the length of d_unique_out)
|
||||
EqualityOpT equality_op, ///< [in] KeyT equality operator
|
||||
ReductionOpT reduction_op, ///< [in] ValueT reduction operator
|
||||
OffsetT num_items, ///< [in] Total number of items to select from
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig reduce_by_key_config;
|
||||
InitConfigs(ptx_version, reduce_by_key_config);
|
||||
|
||||
// Dispatch
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in,
|
||||
d_unique_out,
|
||||
d_values_in,
|
||||
d_aggregates_out,
|
||||
d_num_runs_out,
|
||||
equality_op,
|
||||
reduction_op,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous,
|
||||
ptx_version,
|
||||
DeviceCompactInitKernel<ScanTileStateT, NumRunsOutputIteratorT>,
|
||||
DeviceReduceByKeyKernel<PtxReduceByKeyPolicy, KeysInputIteratorT, UniqueOutputIteratorT, ValuesInputIteratorT, AggregatesOutputIteratorT, NumRunsOutputIteratorT, ScanTileStateT, EqualityOpT, ReductionOpT, OffsetT>,
|
||||
reduce_by_key_config))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,538 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceRle provides device-wide, parallel operations for run-length-encoding sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch_scan.cuh"
|
||||
#include "../../agent/agent_rle.cuh"
|
||||
#include "../../thread/thread_operators.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Select kernel entry point (multi-block)
|
||||
*
|
||||
* Performs functor-based selection if SelectOp functor type != NullType
|
||||
* Otherwise performs flag-based selection if FlagIterator's value type != NullType
|
||||
* Otherwise performs discontinuity selection (keep unique)
|
||||
*/
|
||||
template <
|
||||
typename AgentRlePolicyT, ///< Parameterized AgentRlePolicyT tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OffsetsOutputIteratorT, ///< Random-access output iterator type for writing run-offset values \iterator
|
||||
typename LengthsOutputIteratorT, ///< Random-access output iterator type for writing run-length values \iterator
|
||||
typename NumRunsOutputIteratorT, ///< Output iterator type for recording the number of runs encountered \iterator
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename EqualityOpT, ///< T equality operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
__launch_bounds__ (int(AgentRlePolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceRleSweepKernel(
|
||||
InputIteratorT d_in, ///< [in] Pointer to input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out, ///< [out] Pointer to output sequence of run-offsets
|
||||
LengthsOutputIteratorT d_lengths_out, ///< [out] Pointer to output sequence of run-lengths
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs (i.e., length of \p d_offsets_out)
|
||||
ScanTileStateT tile_status, ///< [in] Tile status interface
|
||||
EqualityOpT equality_op, ///< [in] Equality operator for input items
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
int num_tiles) ///< [in] Total number of tiles for the entire problem
|
||||
{
|
||||
// Thread block type for selecting data from input tiles
|
||||
typedef AgentRle<
|
||||
AgentRlePolicyT,
|
||||
InputIteratorT,
|
||||
OffsetsOutputIteratorT,
|
||||
LengthsOutputIteratorT,
|
||||
EqualityOpT,
|
||||
OffsetT> AgentRleT;
|
||||
|
||||
// Shared memory for AgentRle
|
||||
__shared__ typename AgentRleT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentRleT(temp_storage, d_in, d_offsets_out, d_lengths_out, equality_op, num_items).ConsumeRange(
|
||||
num_tiles,
|
||||
tile_status,
|
||||
d_num_runs_out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceRle
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items \iterator
|
||||
typename OffsetsOutputIteratorT, ///< Random-access output iterator type for writing run-offset values \iterator
|
||||
typename LengthsOutputIteratorT, ///< Random-access output iterator type for writing run-length values \iterator
|
||||
typename NumRunsOutputIteratorT, ///< Output iterator type for recording the number of runs encountered \iterator
|
||||
typename EqualityOpT, ///< T equality operator type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DeviceRleDispatch
|
||||
{
|
||||
/******************************************************************************
|
||||
* Types and constants
|
||||
******************************************************************************/
|
||||
|
||||
// The input value type
|
||||
typedef typename std::iterator_traits<InputIteratorT>::value_type T;
|
||||
|
||||
// The lengths output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<LengthsOutputIteratorT>::value_type, void>::VALUE), // LengthT = (if output iterator's value type is void) ?
|
||||
OffsetT, // ... then the OffsetT type,
|
||||
typename std::iterator_traits<LengthsOutputIteratorT>::value_type>::Type LengthT; // ... else the output iterator's value type
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128,
|
||||
};
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<LengthT, OffsetT> ScanTileStateT;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policies
|
||||
******************************************************************************/
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 15,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(T)))),
|
||||
};
|
||||
|
||||
typedef AgentRlePolicy<
|
||||
96,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_DIRECT,
|
||||
LOAD_LDG,
|
||||
true,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
RleSweepPolicy;
|
||||
};
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 5,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(T)))),
|
||||
};
|
||||
|
||||
typedef AgentRlePolicy<
|
||||
256,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
true,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
RleSweepPolicy;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 15,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(T)))),
|
||||
};
|
||||
|
||||
typedef AgentRlePolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
RleSweepPolicy;
|
||||
};
|
||||
|
||||
/// SM13
|
||||
struct Policy130
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 9,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(T)))),
|
||||
};
|
||||
|
||||
typedef AgentRlePolicy<
|
||||
64,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
true,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
RleSweepPolicy;
|
||||
};
|
||||
|
||||
/// SM10
|
||||
struct Policy100
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 9,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(T)))),
|
||||
};
|
||||
|
||||
typedef AgentRlePolicy<
|
||||
256,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
true,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
RleSweepPolicy;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policies of current PTX compiler pass
|
||||
******************************************************************************/
|
||||
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 130)
|
||||
typedef Policy130 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy100 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxRleSweepPolicy : PtxPolicy::RleSweepPolicy {};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utilities
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig& device_rle_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
device_rle_config.template Init<PtxRleSweepPolicy>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 350)
|
||||
{
|
||||
device_rle_config.template Init<typename Policy350::RleSweepPolicy>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
device_rle_config.template Init<typename Policy300::RleSweepPolicy>();
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
device_rle_config.template Init<typename Policy200::RleSweepPolicy>();
|
||||
}
|
||||
else if (ptx_version >= 130)
|
||||
{
|
||||
device_rle_config.template Init<typename Policy130::RleSweepPolicy>();
|
||||
}
|
||||
else
|
||||
{
|
||||
device_rle_config.template Init<typename Policy100::RleSweepPolicy>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration. Mirrors the constants within AgentRlePolicyT.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
BlockLoadAlgorithm load_policy;
|
||||
bool store_warp_time_slicing;
|
||||
BlockScanAlgorithm scan_algorithm;
|
||||
|
||||
template <typename AgentRlePolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = AgentRlePolicyT::BLOCK_THREADS;
|
||||
items_per_thread = AgentRlePolicyT::ITEMS_PER_THREAD;
|
||||
load_policy = AgentRlePolicyT::LOAD_ALGORITHM;
|
||||
store_warp_time_slicing = AgentRlePolicyT::STORE_WARP_TIME_SLICING;
|
||||
scan_algorithm = AgentRlePolicyT::SCAN_ALGORITHM;
|
||||
}
|
||||
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Print()
|
||||
{
|
||||
printf("%d, %d, %d, %d, %d",
|
||||
block_threads,
|
||||
items_per_thread,
|
||||
load_policy,
|
||||
store_warp_time_slicing,
|
||||
scan_algorithm);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch entrypoints
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide run-length-encode using the
|
||||
* specified kernel functions.
|
||||
*/
|
||||
template <
|
||||
typename DeviceScanInitKernelPtr, ///< Function type of cub::DeviceScanInitKernel
|
||||
typename DeviceRleSweepKernelPtr> ///< Function type of cub::DeviceRleSweepKernelPtr
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out, ///< [out] Pointer to the output sequence of run-offsets
|
||||
LengthsOutputIteratorT d_lengths_out, ///< [out] Pointer to the output sequence of run-lengths
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to the total number of runs encountered (i.e., length of \p d_offsets_out)
|
||||
EqualityOpT equality_op, ///< [in] Equality operator for input items
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int ptx_version, ///< [in] PTX version of dispatch kernels
|
||||
DeviceScanInitKernelPtr device_scan_init_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceScanInitKernel
|
||||
DeviceRleSweepKernelPtr device_rle_sweep_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceRleSweepKernel
|
||||
KernelConfig device_rle_config) ///< [in] Dispatch parameters that match the policy that \p device_rle_sweep_kernel was compiled for
|
||||
{
|
||||
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported);
|
||||
|
||||
#else
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Number of input tiles
|
||||
int tile_size = device_rle_config.block_threads * device_rle_config.items_per_thread;
|
||||
int num_tiles = (num_items + tile_size - 1) / tile_size;
|
||||
|
||||
// Specify temporary storage allocation requirements
|
||||
size_t allocation_sizes[1];
|
||||
if (CubDebug(error = ScanTileStateT::AllocationSize(num_tiles, allocation_sizes[0]))) break; // bytes needed for tile status descriptors
|
||||
|
||||
// Compute allocation pointers into the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[1];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the tile status interface
|
||||
ScanTileStateT tile_status;
|
||||
if (CubDebug(error = tile_status.Init(num_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
|
||||
// Log device_scan_init_kernel configuration
|
||||
int init_grid_size = CUB_MAX(1, (num_tiles + INIT_KERNEL_THREADS - 1) / INIT_KERNEL_THREADS);
|
||||
if (debug_synchronous) _CubLog("Invoking device_scan_init_kernel<<<%d, %d, 0, %lld>>>()\n", init_grid_size, INIT_KERNEL_THREADS, (long long) stream);
|
||||
|
||||
// Invoke device_scan_init_kernel to initialize tile descriptors and queue descriptors
|
||||
device_scan_init_kernel<<<init_grid_size, INIT_KERNEL_THREADS, 0, stream>>>(
|
||||
tile_status,
|
||||
num_tiles,
|
||||
d_num_runs_out);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Return if empty problem
|
||||
if (num_items == 0)
|
||||
break;
|
||||
|
||||
// Get SM occupancy for device_rle_sweep_kernel
|
||||
int device_rle_kernel_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
device_rle_kernel_sm_occupancy, // out
|
||||
device_rle_sweep_kernel,
|
||||
device_rle_config.block_threads))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Get grid size for scanning tiles
|
||||
dim3 scan_grid_size;
|
||||
scan_grid_size.z = 1;
|
||||
scan_grid_size.y = ((unsigned int) num_tiles + max_dim_x - 1) / max_dim_x;
|
||||
scan_grid_size.x = CUB_MIN(num_tiles, max_dim_x);
|
||||
|
||||
// Log device_rle_sweep_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking device_rle_sweep_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
scan_grid_size.x, scan_grid_size.y, scan_grid_size.z, device_rle_config.block_threads, (long long) stream, device_rle_config.items_per_thread, device_rle_kernel_sm_occupancy);
|
||||
|
||||
// Invoke device_rle_sweep_kernel
|
||||
device_rle_sweep_kernel<<<scan_grid_size, device_rle_config.block_threads, 0, stream>>>(
|
||||
d_in,
|
||||
d_offsets_out,
|
||||
d_lengths_out,
|
||||
d_num_runs_out,
|
||||
tile_status,
|
||||
equality_op,
|
||||
num_items,
|
||||
num_tiles);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to input sequence of data items
|
||||
OffsetsOutputIteratorT d_offsets_out, ///< [out] Pointer to output sequence of run-offsets
|
||||
LengthsOutputIteratorT d_lengths_out, ///< [out] Pointer to output sequence of run-lengths
|
||||
NumRunsOutputIteratorT d_num_runs_out, ///< [out] Pointer to total number of runs (i.e., length of \p d_offsets_out)
|
||||
EqualityOpT equality_op, ///< [in] Equality operator for input items
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig device_rle_config;
|
||||
InitConfigs(ptx_version, device_rle_config);
|
||||
|
||||
// Dispatch
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_offsets_out,
|
||||
d_lengths_out,
|
||||
d_num_runs_out,
|
||||
equality_op,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous,
|
||||
ptx_version,
|
||||
DeviceCompactInitKernel<ScanTileStateT, NumRunsOutputIteratorT>,
|
||||
DeviceRleSweepKernel<PtxRleSweepPolicy, InputIteratorT, OffsetsOutputIteratorT, LengthsOutputIteratorT, NumRunsOutputIteratorT, ScanTileStateT, EqualityOpT, OffsetT>,
|
||||
device_rle_config))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,563 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceScan provides device-wide, parallel operations for computing a prefix scan across a sequence of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "../../agent/agent_scan.cuh"
|
||||
#include "../../thread/thread_operators.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_arch.cuh"
|
||||
#include "../../util_debug.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Initialization kernel for tile status initialization (multi-block)
|
||||
*/
|
||||
template <
|
||||
typename ScanTileStateT> ///< Tile status interface type
|
||||
__global__ void DeviceScanInitKernel(
|
||||
ScanTileStateT tile_state, ///< [in] Tile status interface
|
||||
int num_tiles) ///< [in] Number of tiles
|
||||
{
|
||||
// Initialize tile status
|
||||
tile_state.InitializeStatus(num_tiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization kernel for tile status initialization (multi-block)
|
||||
*/
|
||||
template <
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename NumSelectedIteratorT> ///< Output iterator type for recording the number of items selected
|
||||
__global__ void DeviceCompactInitKernel(
|
||||
ScanTileStateT tile_state, ///< [in] Tile status interface
|
||||
int num_tiles, ///< [in] Number of tiles
|
||||
NumSelectedIteratorT d_num_selected_out) ///< [out] Pointer to the total number of items selected (i.e., length of \p d_selected_out)
|
||||
{
|
||||
// Initialize tile status
|
||||
tile_state.InitializeStatus(num_tiles);
|
||||
|
||||
// Initialize d_num_selected_out
|
||||
if ((blockIdx.x == 0) && (threadIdx.x == 0))
|
||||
*d_num_selected_out = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan kernel entry point (multi-block)
|
||||
*/
|
||||
template <
|
||||
typename ScanPolicyT, ///< Parameterized ScanPolicyT tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading scan inputs \iterator
|
||||
typename OutputIteratorT, ///< Random-access output iterator type for writing scan outputs \iterator
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename ScanOpT, ///< Binary scan functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
typename InitValueT, ///< Initial value to seed the exclusive scan (cub::NullType for inclusive scans)
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
__launch_bounds__ (int(ScanPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceScanKernel(
|
||||
InputIteratorT d_in, ///< Input data
|
||||
OutputIteratorT d_out, ///< Output data
|
||||
ScanTileStateT tile_state, ///< Tile status interface
|
||||
int start_tile, ///< The starting tile for the current grid
|
||||
ScanOpT scan_op, ///< Binary scan functor
|
||||
InitValueT init_value, ///< Initial value to seed the exclusive scan
|
||||
OffsetT num_items) ///< Total number of scan items for the entire problem
|
||||
{
|
||||
// Thread block type for scanning input tiles
|
||||
typedef AgentScan<
|
||||
ScanPolicyT,
|
||||
InputIteratorT,
|
||||
OutputIteratorT,
|
||||
ScanOpT,
|
||||
InitValueT,
|
||||
OffsetT> AgentScanT;
|
||||
|
||||
// Shared memory for AgentScan
|
||||
__shared__ typename AgentScanT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentScanT(temp_storage, d_in, d_out, scan_op, init_value).ConsumeRange(
|
||||
num_items,
|
||||
tile_state,
|
||||
start_tile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceScan
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading scan inputs \iterator
|
||||
typename OutputIteratorT, ///< Random-access output iterator type for writing scan outputs \iterator
|
||||
typename ScanOpT, ///< Binary scan functor type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
typename InitValueT, ///< The init_value element type for ScanOpT (cub::NullType for inclusive scans)
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DispatchScan
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Constants and Types
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128
|
||||
};
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<OutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<OutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ScanTileState<OutputT> ScanTileStateT;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// SM600
|
||||
struct Policy600
|
||||
{
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 15, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_STORE_TRANSPOSE,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/// SM520
|
||||
struct Policy520
|
||||
{
|
||||
// Titan X: 32.47B items/s @ 48M 32-bit T
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 12, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_DIRECT,
|
||||
LOAD_LDG,
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
// GTX Titan: 29.5B items/s (232.4 GB/s) @ 48M 32-bit T
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 12, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_DIRECT,
|
||||
LOAD_LDG,
|
||||
BLOCK_STORE_WARP_TRANSPOSE_TIMESLICED,
|
||||
BLOCK_SCAN_RAKING>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(256, 9, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
// GTX 580: 20.3B items/s (162.3 GB/s) @ 48M 32-bit T
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(128, 12, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
/// SM13
|
||||
struct Policy130
|
||||
{
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(96, 21, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
/// SM10
|
||||
struct Policy100
|
||||
{
|
||||
typedef AgentScanPolicy<
|
||||
CUB_NOMINAL_CONFIG(64, 9, OutputT), ///< Threads per block, items per thread
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_STORE_WARP_TRANSPOSE,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
ScanPolicyT;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies of current PTX compiler pass
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#if (CUB_PTX_ARCH >= 600)
|
||||
typedef Policy600 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 520)
|
||||
typedef Policy520 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 130)
|
||||
typedef Policy130 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy100 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxAgentScanPolicy : PtxPolicy::ScanPolicyT {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utilities
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &scan_kernel_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)ptx_version;
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
scan_kernel_config.template Init<PtxAgentScanPolicy>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 600)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy600::ScanPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 520)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy520::ScanPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 350)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy350::ScanPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy300::ScanPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy200::ScanPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 130)
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy130::ScanPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_kernel_config.template Init<typename Policy100::ScanPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide prefix scan using the
|
||||
* specified kernel functions.
|
||||
*/
|
||||
template <
|
||||
typename ScanInitKernelPtrT, ///< Function type of cub::DeviceScanInitKernel
|
||||
typename ScanSweepKernelPtrT> ///< Function type of cub::DeviceScanKernelPtrT
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
ScanOpT scan_op, ///< [in] Binary scan functor
|
||||
InitValueT init_value, ///< [in] Initial value to seed the exclusive scan
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int /*ptx_version*/, ///< [in] PTX version of dispatch kernels
|
||||
ScanInitKernelPtrT init_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceScanInitKernel
|
||||
ScanSweepKernelPtrT scan_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceScanKernel
|
||||
KernelConfig scan_kernel_config) ///< [in] Dispatch parameters that match the policy that \p scan_kernel was compiled for
|
||||
{
|
||||
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void)d_temp_storage;
|
||||
(void)temp_storage_bytes;
|
||||
(void)d_in;
|
||||
(void)d_out;
|
||||
(void)scan_op;
|
||||
(void)init_value;
|
||||
(void)num_items;
|
||||
(void)stream;
|
||||
(void)debug_synchronous;
|
||||
(void)init_kernel;
|
||||
(void)scan_kernel;
|
||||
(void)scan_kernel_config;
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported);
|
||||
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Number of input tiles
|
||||
int tile_size = scan_kernel_config.block_threads * scan_kernel_config.items_per_thread;
|
||||
int num_tiles = (num_items + tile_size - 1) / tile_size;
|
||||
|
||||
// Specify temporary storage allocation requirements
|
||||
size_t allocation_sizes[1];
|
||||
if (CubDebug(error = ScanTileStateT::AllocationSize(num_tiles, allocation_sizes[0]))) break; // bytes needed for tile status descriptors
|
||||
|
||||
// Compute allocation pointers into the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[1];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
break;
|
||||
}
|
||||
|
||||
// Return if empty problem
|
||||
if (num_items == 0)
|
||||
break;
|
||||
|
||||
// Construct the tile status interface
|
||||
ScanTileStateT tile_state;
|
||||
if (CubDebug(error = tile_state.Init(num_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
|
||||
// Log init_kernel configuration
|
||||
int init_grid_size = (num_tiles + INIT_KERNEL_THREADS - 1) / INIT_KERNEL_THREADS;
|
||||
if (debug_synchronous) _CubLog("Invoking init_kernel<<<%d, %d, 0, %lld>>>()\n", init_grid_size, INIT_KERNEL_THREADS, (long long) stream);
|
||||
|
||||
// Invoke init_kernel to initialize tile descriptors
|
||||
init_kernel<<<init_grid_size, INIT_KERNEL_THREADS, 0, stream>>>(
|
||||
tile_state,
|
||||
num_tiles);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Get SM occupancy for scan_kernel
|
||||
int scan_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
scan_sm_occupancy, // out
|
||||
scan_kernel,
|
||||
scan_kernel_config.block_threads))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Run grids in epochs (in case number of tiles exceeds max x-dimension
|
||||
int scan_grid_size = CUB_MIN(num_tiles, max_dim_x);
|
||||
for (int start_tile = 0; start_tile < num_tiles; start_tile += scan_grid_size)
|
||||
{
|
||||
// Log scan_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking %d scan_kernel<<<%d, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
start_tile, scan_grid_size, scan_kernel_config.block_threads, (long long) stream, scan_kernel_config.items_per_thread, scan_sm_occupancy);
|
||||
|
||||
// Invoke scan_kernel
|
||||
scan_kernel<<<scan_grid_size, scan_kernel_config.block_threads, 0, stream>>>(
|
||||
d_in,
|
||||
d_out,
|
||||
tile_state,
|
||||
start_tile,
|
||||
scan_op,
|
||||
init_value,
|
||||
num_items);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
OutputIteratorT d_out, ///< [out] Pointer to the output sequence of data items
|
||||
ScanOpT scan_op, ///< [in] Binary scan functor
|
||||
InitValueT init_value, ///< [in] Initial value to seed the exclusive scan
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., the length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig scan_kernel_config;
|
||||
InitConfigs(ptx_version, scan_kernel_config);
|
||||
|
||||
// Dispatch
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_out,
|
||||
scan_op,
|
||||
init_value,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous,
|
||||
ptx_version,
|
||||
DeviceScanInitKernel<ScanTileStateT>,
|
||||
DeviceScanKernel<PtxAgentScanPolicy, InputIteratorT, OutputIteratorT, ScanTileStateT, ScanOpT, InitValueT, OffsetT>,
|
||||
scan_kernel_config))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,542 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSelect provides device-wide, parallel operations for selecting items from sequences of data items residing within device-accessible memory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch_scan.cuh"
|
||||
#include "../../agent/agent_select_if.cuh"
|
||||
#include "../../thread/thread_operators.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/******************************************************************************
|
||||
* Kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Select kernel entry point (multi-block)
|
||||
*
|
||||
* Performs functor-based selection if SelectOpT functor type != NullType
|
||||
* Otherwise performs flag-based selection if FlagsInputIterator's value type != NullType
|
||||
* Otherwise performs discontinuity selection (keep unique)
|
||||
*/
|
||||
template <
|
||||
typename AgentSelectIfPolicyT, ///< Parameterized AgentSelectIfPolicyT tuning policy type
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items
|
||||
typename FlagsInputIteratorT, ///< Random-access input iterator type for reading selection flags (NullType* if a selection functor or discontinuity flagging is to be used for selection)
|
||||
typename SelectedOutputIteratorT, ///< Random-access output iterator type for writing selected items
|
||||
typename NumSelectedIteratorT, ///< Output iterator type for recording the number of items selected
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename SelectOpT, ///< Selection operator type (NullType if selection flags or discontinuity flagging is to be used for selection)
|
||||
typename EqualityOpT, ///< Equality operator type (NullType if selection functor or selection flags is to be used for selection)
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
bool KEEP_REJECTS> ///< Whether or not we push rejected items to the back of the output
|
||||
__launch_bounds__ (int(AgentSelectIfPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSelectSweepKernel(
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
FlagsInputIteratorT d_flags, ///< [in] Pointer to the input sequence of selection flags (if applicable)
|
||||
SelectedOutputIteratorT d_selected_out, ///< [out] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [out] Pointer to the total number of items selected (i.e., length of \p d_selected_out)
|
||||
ScanTileStateT tile_status, ///< [in] Tile status interface
|
||||
SelectOpT select_op, ///< [in] Selection operator
|
||||
EqualityOpT equality_op, ///< [in] Equality operator
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
int num_tiles) ///< [in] Total number of tiles for the entire problem
|
||||
{
|
||||
// Thread block type for selecting data from input tiles
|
||||
typedef AgentSelectIf<
|
||||
AgentSelectIfPolicyT,
|
||||
InputIteratorT,
|
||||
FlagsInputIteratorT,
|
||||
SelectedOutputIteratorT,
|
||||
SelectOpT,
|
||||
EqualityOpT,
|
||||
OffsetT,
|
||||
KEEP_REJECTS> AgentSelectIfT;
|
||||
|
||||
// Shared memory for AgentSelectIf
|
||||
__shared__ typename AgentSelectIfT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentSelectIfT(temp_storage, d_in, d_flags, d_selected_out, select_op, equality_op, num_items).ConsumeRange(
|
||||
num_tiles,
|
||||
tile_status,
|
||||
d_num_selected_out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceSelect
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT, ///< Random-access input iterator type for reading input items
|
||||
typename FlagsInputIteratorT, ///< Random-access input iterator type for reading selection flags (NullType* if a selection functor or discontinuity flagging is to be used for selection)
|
||||
typename SelectedOutputIteratorT, ///< Random-access output iterator type for writing selected items
|
||||
typename NumSelectedIteratorT, ///< Output iterator type for recording the number of items selected
|
||||
typename SelectOpT, ///< Selection operator type (NullType if selection flags or discontinuity flagging is to be used for selection)
|
||||
typename EqualityOpT, ///< Equality operator type (NullType if selection functor or selection flags is to be used for selection)
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
bool KEEP_REJECTS> ///< Whether or not we push rejected items to the back of the output
|
||||
struct DispatchSelectIf
|
||||
{
|
||||
/******************************************************************************
|
||||
* Types and constants
|
||||
******************************************************************************/
|
||||
|
||||
// The output value type
|
||||
typedef typename If<(Equals<typename std::iterator_traits<SelectedOutputIteratorT>::value_type, void>::VALUE), // OutputT = (if output iterator's value type is void) ?
|
||||
typename std::iterator_traits<InputIteratorT>::value_type, // ... then the input iterator's value type,
|
||||
typename std::iterator_traits<SelectedOutputIteratorT>::value_type>::Type OutputT; // ... else the output iterator's value type
|
||||
|
||||
// The flag value type
|
||||
typedef typename std::iterator_traits<FlagsInputIteratorT>::value_type FlagT;
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128,
|
||||
};
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ScanTileState<OffsetT> ScanTileStateT;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policies
|
||||
******************************************************************************/
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 10,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(OutputT)))),
|
||||
};
|
||||
|
||||
typedef AgentSelectIfPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_DIRECT,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SelectIfPolicyT;
|
||||
};
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 7,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(3, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(OutputT)))),
|
||||
};
|
||||
|
||||
typedef AgentSelectIfPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SelectIfPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = (KEEP_REJECTS) ? 7 : 15,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(OutputT)))),
|
||||
};
|
||||
|
||||
typedef AgentSelectIfPolicy<
|
||||
128,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SelectIfPolicyT;
|
||||
};
|
||||
|
||||
/// SM13
|
||||
struct Policy130
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 9,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(OutputT)))),
|
||||
};
|
||||
|
||||
typedef AgentSelectIfPolicy<
|
||||
64,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SelectIfPolicyT;
|
||||
};
|
||||
|
||||
/// SM10
|
||||
struct Policy100
|
||||
{
|
||||
enum {
|
||||
NOMINAL_4B_ITEMS_PER_THREAD = 9,
|
||||
ITEMS_PER_THREAD = CUB_MIN(NOMINAL_4B_ITEMS_PER_THREAD, CUB_MAX(1, (NOMINAL_4B_ITEMS_PER_THREAD * 4 / sizeof(OutputT)))),
|
||||
};
|
||||
|
||||
typedef AgentSelectIfPolicy<
|
||||
64,
|
||||
ITEMS_PER_THREAD,
|
||||
BLOCK_LOAD_WARP_TRANSPOSE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_RAKING>
|
||||
SelectIfPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Tuning policies of current PTX compiler pass
|
||||
******************************************************************************/
|
||||
|
||||
#if (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 130)
|
||||
typedef Policy130 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy100 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxSelectIfPolicyT : PtxPolicy::SelectIfPolicyT {};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Utilities
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &select_if_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)ptx_version;
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
select_if_config.template Init<PtxSelectIfPolicyT>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 350)
|
||||
{
|
||||
select_if_config.template Init<typename Policy350::SelectIfPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
select_if_config.template Init<typename Policy300::SelectIfPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
select_if_config.template Init<typename Policy200::SelectIfPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 130)
|
||||
{
|
||||
select_if_config.template Init<typename Policy130::SelectIfPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
select_if_config.template Init<typename Policy100::SelectIfPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch entrypoints
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide selection using the
|
||||
* specified kernel functions.
|
||||
*/
|
||||
template <
|
||||
typename ScanInitKernelPtrT, ///< Function type of cub::DeviceScanInitKernel
|
||||
typename SelectIfKernelPtrT> ///< Function type of cub::SelectIfKernelPtrT
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
FlagsInputIteratorT d_flags, ///< [in] Pointer to the input sequence of selection flags (if applicable)
|
||||
SelectedOutputIteratorT d_selected_out, ///< [in] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [in] Pointer to the total number of items selected (i.e., length of \p d_selected_out)
|
||||
SelectOpT select_op, ///< [in] Selection operator
|
||||
EqualityOpT equality_op, ///< [in] Equality operator
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
int /*ptx_version*/, ///< [in] PTX version of dispatch kernels
|
||||
ScanInitKernelPtrT scan_init_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceScanInitKernel
|
||||
SelectIfKernelPtrT select_if_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceSelectSweepKernel
|
||||
KernelConfig select_if_config) ///< [in] Dispatch parameters that match the policy that \p select_if_kernel was compiled for
|
||||
{
|
||||
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
(void)d_temp_storage;
|
||||
(void)temp_storage_bytes;
|
||||
(void)d_in;
|
||||
(void)d_flags;
|
||||
(void)d_selected_out;
|
||||
(void)d_num_selected_out;
|
||||
(void)select_op;
|
||||
(void)equality_op;
|
||||
(void)num_items;
|
||||
(void)stream;
|
||||
(void)debug_synchronous;
|
||||
(void)scan_init_kernel;
|
||||
(void)select_if_kernel;
|
||||
(void)select_if_config;
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported);
|
||||
|
||||
#else
|
||||
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Number of input tiles
|
||||
int tile_size = select_if_config.block_threads * select_if_config.items_per_thread;
|
||||
int num_tiles = (num_items + tile_size - 1) / tile_size;
|
||||
|
||||
// Specify temporary storage allocation requirements
|
||||
size_t allocation_sizes[1];
|
||||
if (CubDebug(error = ScanTileStateT::AllocationSize(num_tiles, allocation_sizes[0]))) break; // bytes needed for tile status descriptors
|
||||
|
||||
// Compute allocation pointers into the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[1];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the tile status interface
|
||||
ScanTileStateT tile_status;
|
||||
if (CubDebug(error = tile_status.Init(num_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
|
||||
// Log scan_init_kernel configuration
|
||||
int init_grid_size = CUB_MAX(1, (num_tiles + INIT_KERNEL_THREADS - 1) / INIT_KERNEL_THREADS);
|
||||
if (debug_synchronous) _CubLog("Invoking scan_init_kernel<<<%d, %d, 0, %lld>>>()\n", init_grid_size, INIT_KERNEL_THREADS, (long long) stream);
|
||||
|
||||
// Invoke scan_init_kernel to initialize tile descriptors
|
||||
scan_init_kernel<<<init_grid_size, INIT_KERNEL_THREADS, 0, stream>>>(
|
||||
tile_status,
|
||||
num_tiles,
|
||||
d_num_selected_out);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Return if empty problem
|
||||
if (num_items == 0)
|
||||
break;
|
||||
|
||||
// Get SM occupancy for select_if_kernel
|
||||
int range_select_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
range_select_sm_occupancy, // out
|
||||
select_if_kernel,
|
||||
select_if_config.block_threads))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Get grid size for scanning tiles
|
||||
dim3 scan_grid_size;
|
||||
scan_grid_size.z = 1;
|
||||
scan_grid_size.y = ((unsigned int) num_tiles + max_dim_x - 1) / max_dim_x;
|
||||
scan_grid_size.x = CUB_MIN(num_tiles, max_dim_x);
|
||||
|
||||
// Log select_if_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking select_if_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
scan_grid_size.x, scan_grid_size.y, scan_grid_size.z, select_if_config.block_threads, (long long) stream, select_if_config.items_per_thread, range_select_sm_occupancy);
|
||||
|
||||
// Invoke select_if_kernel
|
||||
select_if_kernel<<<scan_grid_size, select_if_config.block_threads, 0, stream>>>(
|
||||
d_in,
|
||||
d_flags,
|
||||
d_selected_out,
|
||||
d_num_selected_out,
|
||||
tile_status,
|
||||
select_op,
|
||||
equality_op,
|
||||
num_items,
|
||||
num_tiles);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
InputIteratorT d_in, ///< [in] Pointer to the input sequence of data items
|
||||
FlagsInputIteratorT d_flags, ///< [in] Pointer to the input sequence of selection flags (if applicable)
|
||||
SelectedOutputIteratorT d_selected_out, ///< [in] Pointer to the output sequence of selected data items
|
||||
NumSelectedIteratorT d_num_selected_out, ///< [in] Pointer to the total number of items selected (i.e., length of \p d_selected_out)
|
||||
SelectOpT select_op, ///< [in] Selection operator
|
||||
EqualityOpT equality_op, ///< [in] Equality operator
|
||||
OffsetT num_items, ///< [in] Total number of input items (i.e., length of \p d_in)
|
||||
cudaStream_t stream, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig select_if_config;
|
||||
InitConfigs(ptx_version, select_if_config);
|
||||
|
||||
// Dispatch
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in,
|
||||
d_flags,
|
||||
d_selected_out,
|
||||
d_num_selected_out,
|
||||
select_op,
|
||||
equality_op,
|
||||
num_items,
|
||||
stream,
|
||||
debug_synchronous,
|
||||
ptx_version,
|
||||
DeviceCompactInitKernel<ScanTileStateT, NumSelectedIteratorT>,
|
||||
DeviceSelectSweepKernel<PtxSelectIfPolicyT, InputIteratorT, FlagsInputIteratorT, SelectedOutputIteratorT, NumSelectedIteratorT, ScanTileStateT, SelectOpT, EqualityOpT, OffsetT, KEEP_REJECTS>,
|
||||
select_if_config))) break;
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,477 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSpmv provides device-wide parallel operations for performing sparse-matrix * vector multiplication (SpMV).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "dispatch_scan.cuh"
|
||||
#include "../../agent/agent_spmv_orig.cuh"
|
||||
#include "../../util_type.cuh"
|
||||
#include "../../util_debug.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* SpMV kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Spmv agent entry point
|
||||
*/
|
||||
template <
|
||||
typename SpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
bool HAS_ALPHA, ///< Whether the input parameter Alpha is 1
|
||||
bool HAS_BETA> ///< Whether the input parameter Beta is 0
|
||||
__launch_bounds__ (int(SpmvPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSpmvKernel(
|
||||
SpmvParams<ValueT, OffsetT> spmv_params, ///< [in] SpMV input parameter bundle
|
||||
int merge_items_per_block, ///< [in] Number of merge tiles per block
|
||||
KeyValuePair<OffsetT,ValueT>* d_tile_carry_pairs) ///< [out] Pointer to the temporary array carry-out dot product row-ids, one per block
|
||||
{
|
||||
// Spmv agent type specialization
|
||||
typedef AgentSpmv<
|
||||
SpmvPolicyT,
|
||||
ValueT,
|
||||
OffsetT,
|
||||
HAS_ALPHA,
|
||||
HAS_BETA>
|
||||
AgentSpmvT;
|
||||
|
||||
// Shared memory for AgentSpmv
|
||||
__shared__ typename AgentSpmvT::TempStorage temp_storage;
|
||||
|
||||
AgentSpmvT(temp_storage, spmv_params).ConsumeTile(
|
||||
merge_items_per_block, d_tile_carry_pairs);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceSpmv
|
||||
*/
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DispatchSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Constants and Types
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128
|
||||
};
|
||||
|
||||
// SpmvParams bundle type
|
||||
typedef SpmvParams<ValueT, OffsetT> SpmvParamsT;
|
||||
|
||||
// Tuple type for scanning {row id, accumulated value}
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// SM11
|
||||
struct Policy110
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
128,
|
||||
1,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
18,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_RAKING>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
6,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
/*
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 96 : 128,
|
||||
(sizeof(ValueT) > 4) ? 4 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
(sizeof(ValueT) > 4) ? true : false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
*/
|
||||
typedef AgentSpmvPolicy<
|
||||
128,
|
||||
5,
|
||||
LOAD_CA,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
(sizeof(ValueT) > 4) ? true : false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
/// SM37
|
||||
struct Policy370
|
||||
{
|
||||
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 128 : 128,
|
||||
(sizeof(ValueT) > 4) ? 9 : 14,
|
||||
LOAD_LDG,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
/// SM50
|
||||
struct Policy500
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 64 : 128,
|
||||
(sizeof(ValueT) > 4) ? 6 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_DEFAULT,
|
||||
(sizeof(ValueT) > 4) ? LOAD_LDG : LOAD_DEFAULT,
|
||||
(sizeof(ValueT) > 4) ? LOAD_LDG : LOAD_DEFAULT,
|
||||
LOAD_LDG,
|
||||
(sizeof(ValueT) > 4) ? true : false,
|
||||
(sizeof(ValueT) > 4) ? BLOCK_SCAN_WARP_SCANS : BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SpmvPolicyT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies of current PTX compiler pass
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#if (CUB_PTX_ARCH >= 500)
|
||||
typedef Policy500 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 370)
|
||||
typedef Policy370 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy110 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxSpmvPolicyT : PtxPolicy::SpmvPolicyT {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utilities
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &spmv_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
spmv_config.template Init<PtxSpmvPolicyT>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 500)
|
||||
{
|
||||
spmv_config.template Init<typename Policy500::SpmvPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 370)
|
||||
{
|
||||
spmv_config.template Init<typename Policy370::SpmvPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 350)
|
||||
{
|
||||
spmv_config.template Init<typename Policy350::SpmvPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
spmv_config.template Init<typename Policy300::SpmvPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
spmv_config.template Init<typename Policy200::SpmvPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
spmv_config.template Init<typename Policy110::SpmvPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction using the
|
||||
* specified kernel functions.
|
||||
*
|
||||
* If the input is larger than a single tile, this method uses two-passes of
|
||||
* kernel invocations.
|
||||
*/
|
||||
template <
|
||||
typename SpmvKernelT> ///< Function type of cub::AgentSpmvKernel
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
SpmvKernelT spmv_kernel, ///< [in] Kernel function pointer to parameterization of AgentSpmvKernel
|
||||
KernelConfig spmv_config) ///< [in] Dispatch parameters that match the policy that \p spmv_kernel was compiled for
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Total number of spmv work items
|
||||
int num_merge_items = spmv_params.num_rows + spmv_params.num_nonzeros;
|
||||
|
||||
// Get SM occupancy for kernels
|
||||
int spmv_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
spmv_sm_occupancy,
|
||||
spmv_kernel,
|
||||
spmv_config.block_threads))) break;
|
||||
int spmv_device_occupancy = spmv_sm_occupancy * sm_count;
|
||||
|
||||
// Grid dimensions
|
||||
int spmv_grid_size = CUB_MIN(((num_merge_items + spmv_config.block_threads - 1) / spmv_config.block_threads), spmv_device_occupancy);
|
||||
|
||||
// Merge items per block
|
||||
int merge_items_per_block = (num_merge_items + spmv_grid_size - 1) / spmv_grid_size;
|
||||
|
||||
// Get the temporary storage allocation requirements
|
||||
size_t allocation_sizes[1];
|
||||
allocation_sizes[0] = spmv_grid_size * sizeof(KeyValuePairT); // bytes needed for block carry-out pairs
|
||||
|
||||
// Alias the temporary allocations from the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[1];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
return cudaSuccess;
|
||||
}
|
||||
KeyValuePairT* d_tile_carry_pairs = (KeyValuePairT*) allocations[0]; // Agent carry-out pairs
|
||||
|
||||
// Log spmv_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_kernel<<<%d, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
spmv_grid_size, spmv_config.block_threads, (long long) stream, spmv_config.items_per_thread, spmv_sm_occupancy);
|
||||
|
||||
// Invoke spmv_kernel
|
||||
spmv_kernel<<<spmv_grid_size, spmv_config.block_threads, 0, stream>>>(
|
||||
spmv_params,
|
||||
merge_items_per_block,
|
||||
d_tile_carry_pairs);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig spmv_config;
|
||||
InitConfigs(ptx_version, spmv_config);
|
||||
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
spmv_params,
|
||||
stream,
|
||||
debug_synchronous,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, false, false>,
|
||||
spmv_config))) break;
|
||||
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,850 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSpmv provides device-wide parallel operations for performing sparse-matrix * vector multiplication (SpMV).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "../../agent/single_pass_scan_operators.cuh"
|
||||
#include "../../agent/agent_segment_fixup.cuh"
|
||||
#include "../../agent/agent_spmv_orig.cuh"
|
||||
#include "../../util_type.cuh"
|
||||
#include "../../util_debug.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../thread/thread_search.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* SpMV kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Spmv search kernel. Identifies merge path starting coordinates for each tile.
|
||||
*/
|
||||
template <
|
||||
typename AgentSpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for sequence offsets
|
||||
__global__ void DeviceSpmv1ColKernel(
|
||||
SpmvParams<ValueT, OffsetT> spmv_params) ///< [in] SpMV input parameter bundle
|
||||
{
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VECTOR_VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
VectorValueIteratorT;
|
||||
|
||||
VectorValueIteratorT wrapped_vector_x(spmv_params.d_vector_x);
|
||||
|
||||
int row_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (row_idx < spmv_params.num_rows)
|
||||
{
|
||||
OffsetT end_nonzero_idx = spmv_params.d_row_end_offsets[row_idx];
|
||||
OffsetT nonzero_idx = spmv_params.d_row_end_offsets[row_idx - 1];
|
||||
|
||||
ValueT value = 0.0;
|
||||
if (end_nonzero_idx != nonzero_idx)
|
||||
{
|
||||
value = spmv_params.d_values[nonzero_idx] * wrapped_vector_x[spmv_params.d_column_indices[nonzero_idx]];
|
||||
}
|
||||
|
||||
spmv_params.d_vector_y[row_idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spmv search kernel. Identifies merge path starting coordinates for each tile.
|
||||
*/
|
||||
template <
|
||||
typename SpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
typename CoordinateT, ///< Merge path coordinate type
|
||||
typename SpmvParamsT> ///< SpmvParams type
|
||||
__global__ void DeviceSpmvSearchKernel(
|
||||
int num_merge_tiles, ///< [in] Number of SpMV merge tiles (spmv grid size)
|
||||
CoordinateT* d_tile_coordinates, ///< [out] Pointer to the temporary array of tile starting coordinates
|
||||
SpmvParamsT spmv_params) ///< [in] SpMV input parameter bundle
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = SpmvPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = SpmvPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
SpmvPolicyT::ROW_OFFSETS_SEARCH_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsSearchIteratorT;
|
||||
|
||||
// Find the starting coordinate for all tiles (plus the end coordinate of the last one)
|
||||
int tile_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (tile_idx < num_merge_tiles + 1)
|
||||
{
|
||||
OffsetT diagonal = (tile_idx * TILE_ITEMS);
|
||||
CoordinateT tile_coordinate;
|
||||
CountingInputIterator<OffsetT> nonzero_indices(0);
|
||||
|
||||
// Search the merge path
|
||||
MergePathSearch(
|
||||
diagonal,
|
||||
RowOffsetsSearchIteratorT(spmv_params.d_row_end_offsets),
|
||||
nonzero_indices,
|
||||
spmv_params.num_rows,
|
||||
spmv_params.num_nonzeros,
|
||||
tile_coordinate);
|
||||
|
||||
// Output starting offset
|
||||
d_tile_coordinates[tile_idx] = tile_coordinate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spmv agent entry point
|
||||
*/
|
||||
template <
|
||||
typename SpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
typename CoordinateT, ///< Merge path coordinate type
|
||||
bool HAS_ALPHA, ///< Whether the input parameter Alpha is 1
|
||||
bool HAS_BETA> ///< Whether the input parameter Beta is 0
|
||||
__launch_bounds__ (int(SpmvPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSpmvKernel(
|
||||
SpmvParams<ValueT, OffsetT> spmv_params, ///< [in] SpMV input parameter bundle
|
||||
CoordinateT* d_tile_coordinates, ///< [in] Pointer to the temporary array of tile starting coordinates
|
||||
KeyValuePair<OffsetT,ValueT>* d_tile_carry_pairs, ///< [out] Pointer to the temporary array carry-out dot product row-ids, one per block
|
||||
int num_tiles, ///< [in] Number of merge tiles
|
||||
ScanTileStateT tile_state, ///< [in] Tile status interface for fixup reduce-by-key kernel
|
||||
int num_segment_fixup_tiles) ///< [in] Number of reduce-by-key tiles (fixup grid size)
|
||||
{
|
||||
// Spmv agent type specialization
|
||||
typedef AgentSpmv<
|
||||
SpmvPolicyT,
|
||||
ValueT,
|
||||
OffsetT,
|
||||
HAS_ALPHA,
|
||||
HAS_BETA>
|
||||
AgentSpmvT;
|
||||
|
||||
// Shared memory for AgentSpmv
|
||||
__shared__ typename AgentSpmvT::TempStorage temp_storage;
|
||||
|
||||
AgentSpmvT(temp_storage, spmv_params).ConsumeTile(
|
||||
d_tile_coordinates,
|
||||
d_tile_carry_pairs,
|
||||
num_tiles);
|
||||
|
||||
// Initialize fixup tile status
|
||||
tile_state.InitializeStatus(num_segment_fixup_tiles);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multi-block reduce-by-key sweep kernel entry point
|
||||
*/
|
||||
template <
|
||||
typename AgentSegmentFixupPolicyT, ///< Parameterized AgentSegmentFixupPolicy tuning policy type
|
||||
typename PairsInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ScanTileStateT> ///< Tile status interface type
|
||||
__launch_bounds__ (int(AgentSegmentFixupPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSegmentFixupKernel(
|
||||
PairsInputIteratorT d_pairs_in, ///< [in] Pointer to the array carry-out dot product row-ids, one per spmv block
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< [in,out] Output value aggregates
|
||||
OffsetT num_items, ///< [in] Total number of items to select from
|
||||
int num_tiles, ///< [in] Total number of tiles for the entire problem
|
||||
ScanTileStateT tile_state) ///< [in] Tile status interface
|
||||
{
|
||||
// Thread block type for reducing tiles of value segments
|
||||
typedef AgentSegmentFixup<
|
||||
AgentSegmentFixupPolicyT,
|
||||
PairsInputIteratorT,
|
||||
AggregatesOutputIteratorT,
|
||||
cub::Equality,
|
||||
cub::Sum,
|
||||
OffsetT>
|
||||
AgentSegmentFixupT;
|
||||
|
||||
// Shared memory for AgentSegmentFixup
|
||||
__shared__ typename AgentSegmentFixupT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentSegmentFixupT(temp_storage, d_pairs_in, d_aggregates_out, cub::Equality(), cub::Sum()).ConsumeRange(
|
||||
num_items,
|
||||
num_tiles,
|
||||
tile_state);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceSpmv
|
||||
*/
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DispatchSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Constants and Types
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128
|
||||
};
|
||||
|
||||
// SpmvParams bundle type
|
||||
typedef SpmvParams<ValueT, OffsetT> SpmvParamsT;
|
||||
|
||||
// 2D merge path coordinate type
|
||||
typedef typename CubVector<OffsetT, 2>::Type CoordinateT;
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<ValueT, OffsetT> ScanTileStateT;
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// SM11
|
||||
struct Policy110
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
128,
|
||||
1,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
18,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_RAKING>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
6,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 96 : 128,
|
||||
(sizeof(ValueT) > 4) ? 4 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
(sizeof(ValueT) > 4) ? true : false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/// SM37
|
||||
struct Policy370
|
||||
{
|
||||
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 128 : 128,
|
||||
(sizeof(ValueT) > 4) ? 9 : 14,
|
||||
LOAD_LDG,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
/// SM50
|
||||
struct Policy500
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 64 : 128,
|
||||
(sizeof(ValueT) > 4) ? 6 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_DEFAULT,
|
||||
(sizeof(ValueT) > 4) ? LOAD_LDG : LOAD_DEFAULT,
|
||||
(sizeof(ValueT) > 4) ? LOAD_LDG : LOAD_DEFAULT,
|
||||
LOAD_LDG,
|
||||
(sizeof(ValueT) > 4) ? true : false,
|
||||
(sizeof(ValueT) > 4) ? BLOCK_SCAN_WARP_SCANS : BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SpmvPolicyT;
|
||||
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies of current PTX compiler pass
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#if (CUB_PTX_ARCH >= 500)
|
||||
typedef Policy500 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 370)
|
||||
typedef Policy370 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy110 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxSpmvPolicyT : PtxPolicy::SpmvPolicyT {};
|
||||
struct PtxSegmentFixupPolicy : PtxPolicy::SegmentFixupPolicyT {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utilities
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &spmv_config,
|
||||
KernelConfig &segment_fixup_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
spmv_config.template Init<PtxSpmvPolicyT>();
|
||||
segment_fixup_config.template Init<PtxSegmentFixupPolicy>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 500)
|
||||
{
|
||||
spmv_config.template Init<typename Policy500::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy500::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 370)
|
||||
{
|
||||
spmv_config.template Init<typename Policy370::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy370::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 350)
|
||||
{
|
||||
spmv_config.template Init<typename Policy350::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy350::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
spmv_config.template Init<typename Policy300::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy300::SegmentFixupPolicyT>();
|
||||
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
spmv_config.template Init<typename Policy200::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy200::SegmentFixupPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
spmv_config.template Init<typename Policy110::SpmvPolicyT>();
|
||||
segment_fixup_config.template Init<typename Policy110::SegmentFixupPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction using the
|
||||
* specified kernel functions.
|
||||
*
|
||||
* If the input is larger than a single tile, this method uses two-passes of
|
||||
* kernel invocations.
|
||||
*/
|
||||
template <
|
||||
typename Spmv1ColKernelT, ///< Function type of cub::DeviceSpmv1ColKernel
|
||||
typename SpmvSearchKernelT, ///< Function type of cub::AgentSpmvSearchKernel
|
||||
typename SpmvKernelT, ///< Function type of cub::AgentSpmvKernel
|
||||
typename SegmentFixupKernelT> ///< Function type of cub::DeviceSegmentFixupKernelT
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
Spmv1ColKernelT spmv_1col_kernel, ///< [in] Kernel function pointer to parameterization of DeviceSpmv1ColKernel
|
||||
SpmvSearchKernelT spmv_search_kernel, ///< [in] Kernel function pointer to parameterization of AgentSpmvSearchKernel
|
||||
SpmvKernelT spmv_kernel, ///< [in] Kernel function pointer to parameterization of AgentSpmvKernel
|
||||
SegmentFixupKernelT segment_fixup_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceSegmentFixupKernel
|
||||
KernelConfig spmv_config, ///< [in] Dispatch parameters that match the policy that \p spmv_kernel was compiled for
|
||||
KernelConfig segment_fixup_config) ///< [in] Dispatch parameters that match the policy that \p segment_fixup_kernel was compiled for
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
if (spmv_params.num_cols == 1)
|
||||
{
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
temp_storage_bytes = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get search/init grid dims
|
||||
int degen_col_kernel_block_size = INIT_KERNEL_THREADS;
|
||||
int degen_col_kernel_grid_size = (spmv_params.num_rows + degen_col_kernel_block_size - 1) / degen_col_kernel_block_size;
|
||||
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_1col_kernel<<<%d, %d, 0, %lld>>>()\n",
|
||||
degen_col_kernel_grid_size, degen_col_kernel_block_size, (long long) stream);
|
||||
|
||||
// Invoke spmv_search_kernel
|
||||
spmv_1col_kernel<<<degen_col_kernel_grid_size, degen_col_kernel_block_size, 0, stream>>>(
|
||||
spmv_params);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Total number of spmv work items
|
||||
int num_merge_items = spmv_params.num_rows + spmv_params.num_nonzeros;
|
||||
|
||||
// Tile sizes of kernels
|
||||
int merge_tile_size = spmv_config.block_threads * spmv_config.items_per_thread;
|
||||
int segment_fixup_tile_size = segment_fixup_config.block_threads * segment_fixup_config.items_per_thread;
|
||||
|
||||
// Number of tiles for kernels
|
||||
unsigned int num_merge_tiles = (num_merge_items + merge_tile_size - 1) / merge_tile_size;
|
||||
unsigned int num_segment_fixup_tiles = (num_merge_tiles + segment_fixup_tile_size - 1) / segment_fixup_tile_size;
|
||||
|
||||
// Get SM occupancy for kernels
|
||||
int spmv_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
spmv_sm_occupancy,
|
||||
spmv_kernel,
|
||||
spmv_config.block_threads))) break;
|
||||
|
||||
int segment_fixup_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
segment_fixup_sm_occupancy,
|
||||
segment_fixup_kernel,
|
||||
segment_fixup_config.block_threads))) break;
|
||||
|
||||
// Get grid dimensions
|
||||
dim3 spmv_grid_size(
|
||||
CUB_MIN(num_merge_tiles, max_dim_x),
|
||||
(num_merge_tiles + max_dim_x - 1) / max_dim_x,
|
||||
1);
|
||||
|
||||
dim3 segment_fixup_grid_size(
|
||||
CUB_MIN(num_segment_fixup_tiles, max_dim_x),
|
||||
(num_segment_fixup_tiles + max_dim_x - 1) / max_dim_x,
|
||||
1);
|
||||
|
||||
// Get the temporary storage allocation requirements
|
||||
size_t allocation_sizes[3];
|
||||
if (CubDebug(error = ScanTileStateT::AllocationSize(num_segment_fixup_tiles, allocation_sizes[0]))) break; // bytes needed for reduce-by-key tile status descriptors
|
||||
allocation_sizes[1] = num_merge_tiles * sizeof(KeyValuePairT); // bytes needed for block carry-out pairs
|
||||
allocation_sizes[2] = (num_merge_tiles + 1) * sizeof(CoordinateT); // bytes needed for tile starting coordinates
|
||||
|
||||
// Alias the temporary allocations from the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[3];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the tile status interface
|
||||
ScanTileStateT tile_state;
|
||||
if (CubDebug(error = tile_state.Init(num_segment_fixup_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
|
||||
// Alias the other allocations
|
||||
KeyValuePairT* d_tile_carry_pairs = (KeyValuePairT*) allocations[1]; // Agent carry-out pairs
|
||||
CoordinateT* d_tile_coordinates = (CoordinateT*) allocations[2]; // Agent starting coordinates
|
||||
|
||||
// Get search/init grid dims
|
||||
int search_block_size = INIT_KERNEL_THREADS;
|
||||
int search_grid_size = (num_merge_tiles + 1 + search_block_size - 1) / search_block_size;
|
||||
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Init textures
|
||||
if (CubDebug(error = spmv_params.t_vector_x.BindTexture(spmv_params.d_vector_x))) break;
|
||||
#endif
|
||||
|
||||
if (search_grid_size < sm_count)
|
||||
// if (num_merge_tiles < spmv_sm_occupancy * sm_count)
|
||||
{
|
||||
// Not enough spmv tiles to saturate the device: have spmv blocks search their own staring coords
|
||||
d_tile_coordinates = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use separate search kernel if we have enough spmv tiles to saturate the device
|
||||
|
||||
// Log spmv_search_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_search_kernel<<<%d, %d, 0, %lld>>>()\n",
|
||||
search_grid_size, search_block_size, (long long) stream);
|
||||
|
||||
// Invoke spmv_search_kernel
|
||||
spmv_search_kernel<<<search_grid_size, search_block_size, 0, stream>>>(
|
||||
num_merge_tiles,
|
||||
d_tile_coordinates,
|
||||
spmv_params);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
|
||||
// Log spmv_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
spmv_grid_size.x, spmv_grid_size.y, spmv_grid_size.z, spmv_config.block_threads, (long long) stream, spmv_config.items_per_thread, spmv_sm_occupancy);
|
||||
|
||||
// Invoke spmv_kernel
|
||||
spmv_kernel<<<spmv_grid_size, spmv_config.block_threads, 0, stream>>>(
|
||||
spmv_params,
|
||||
d_tile_coordinates,
|
||||
d_tile_carry_pairs,
|
||||
num_merge_tiles,
|
||||
tile_state,
|
||||
num_segment_fixup_tiles);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
// Run reduce-by-key fixup if necessary
|
||||
if (num_merge_tiles > 1)
|
||||
{
|
||||
// Log segment_fixup_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking segment_fixup_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
segment_fixup_grid_size.x, segment_fixup_grid_size.y, segment_fixup_grid_size.z, segment_fixup_config.block_threads, (long long) stream, segment_fixup_config.items_per_thread, segment_fixup_sm_occupancy);
|
||||
|
||||
// Invoke segment_fixup_kernel
|
||||
segment_fixup_kernel<<<segment_fixup_grid_size, segment_fixup_config.block_threads, 0, stream>>>(
|
||||
d_tile_carry_pairs,
|
||||
spmv_params.d_vector_y,
|
||||
num_merge_tiles,
|
||||
num_segment_fixup_tiles,
|
||||
tile_state);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Free textures
|
||||
if (CubDebug(error = spmv_params.t_vector_x.UnbindTexture())) break;
|
||||
#endif
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig spmv_config, segment_fixup_config;
|
||||
InitConfigs(ptx_version, spmv_config, segment_fixup_config);
|
||||
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmv1ColKernel<PtxSpmvPolicyT, ValueT, OffsetT>,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ScanTileStateT, ValueT, OffsetT, CoordinateT, false, false>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, segment_fixup_config))) break;
|
||||
|
||||
/*
|
||||
// Dispatch
|
||||
if (spmv_params.beta == 0.0)
|
||||
{
|
||||
if (spmv_params.alpha == 1.0)
|
||||
{
|
||||
// Dispatch y = A*x
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmv1ColKernel<PtxSpmvPolicyT, ValueT, OffsetT>,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ScanTileStateT, ValueT, OffsetT, CoordinateT, false, false>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, segment_fixup_config))) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dispatch y = alpha*A*x
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, true, false>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, segment_fixup_config))) break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spmv_params.alpha == 1.0)
|
||||
{
|
||||
// Dispatch y = A*x + beta*y
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, false, true>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, segment_fixup_config))) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dispatch y = alpha*A*x + beta*y
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, true, true>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, segment_fixup_config))) break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,877 @@
|
|||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::DeviceSpmv provides device-wide parallel operations for performing sparse-matrix * vector multiplication (SpMV).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iterator>
|
||||
|
||||
#include "../../agent/single_pass_scan_operators.cuh"
|
||||
#include "../../agent/agent_segment_fixup.cuh"
|
||||
#include "../../agent/agent_spmv_row_based.cuh"
|
||||
#include "../../util_type.cuh"
|
||||
#include "../../util_debug.cuh"
|
||||
#include "../../util_device.cuh"
|
||||
#include "../../thread/thread_search.cuh"
|
||||
#include "../../grid/grid_queue.cuh"
|
||||
#include "../../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* SpMV kernel entry points
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Spmv search kernel. Identifies merge path starting coordinates for each tile.
|
||||
*/
|
||||
template <
|
||||
typename AgentSpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for sequence offsets
|
||||
__global__ void DeviceSpmv1ColKernel(
|
||||
SpmvParams<ValueT, OffsetT> spmv_params) ///< [in] SpMV input parameter bundle
|
||||
{
|
||||
typedef CacheModifiedInputIterator<
|
||||
AgentSpmvPolicyT::VECTOR_VALUES_LOAD_MODIFIER,
|
||||
ValueT,
|
||||
OffsetT>
|
||||
VectorValueIteratorT;
|
||||
|
||||
VectorValueIteratorT wrapped_vector_x(spmv_params.d_vector_x);
|
||||
|
||||
int row_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (row_idx < spmv_params.num_rows)
|
||||
{
|
||||
OffsetT end_nonzero_idx = spmv_params.d_row_end_offsets[row_idx];
|
||||
OffsetT nonzero_idx = spmv_params.d_row_end_offsets[row_idx - 1];
|
||||
|
||||
ValueT value = 0.0;
|
||||
if (end_nonzero_idx != nonzero_idx)
|
||||
{
|
||||
value = spmv_params.d_values[nonzero_idx] * wrapped_vector_x[spmv_params.d_column_indices[nonzero_idx]];
|
||||
}
|
||||
|
||||
spmv_params.d_vector_y[row_idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spmv search kernel. Identifies merge path starting coordinates for each tile.
|
||||
*/
|
||||
template <
|
||||
typename SpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
typename CoordinateT, ///< Merge path coordinate type
|
||||
typename SpmvParamsT> ///< SpmvParams type
|
||||
__global__ void DeviceSpmvSearchKernel(
|
||||
int num_spmv_tiles, ///< [in] Number of SpMV merge tiles (spmv grid size)
|
||||
CoordinateT* d_tile_coordinates, ///< [out] Pointer to the temporary array of tile starting coordinates
|
||||
SpmvParamsT spmv_params) ///< [in] SpMV input parameter bundle
|
||||
{
|
||||
/// Constants
|
||||
enum
|
||||
{
|
||||
BLOCK_THREADS = SpmvPolicyT::BLOCK_THREADS,
|
||||
ITEMS_PER_THREAD = SpmvPolicyT::ITEMS_PER_THREAD,
|
||||
TILE_ITEMS = BLOCK_THREADS * ITEMS_PER_THREAD,
|
||||
};
|
||||
|
||||
typedef CacheModifiedInputIterator<
|
||||
SpmvPolicyT::ROW_OFFSETS_SEARCH_LOAD_MODIFIER,
|
||||
OffsetT,
|
||||
OffsetT>
|
||||
RowOffsetsSearchIteratorT;
|
||||
|
||||
// Find the starting coordinate for all tiles (plus the end coordinate of the last one)
|
||||
int tile_idx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
if (tile_idx < num_spmv_tiles + 1)
|
||||
{
|
||||
OffsetT diagonal = (tile_idx * TILE_ITEMS);
|
||||
CoordinateT tile_coordinate;
|
||||
CountingInputIterator<OffsetT> nonzero_indices(0);
|
||||
|
||||
// Search the merge path
|
||||
MergePathSearch(
|
||||
diagonal,
|
||||
RowOffsetsSearchIteratorT(spmv_params.d_row_end_offsets),
|
||||
nonzero_indices,
|
||||
spmv_params.num_rows,
|
||||
spmv_params.num_nonzeros,
|
||||
tile_coordinate);
|
||||
|
||||
// Output starting offset
|
||||
d_tile_coordinates[tile_idx] = tile_coordinate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spmv agent entry point
|
||||
*/
|
||||
template <
|
||||
typename SpmvPolicyT, ///< Parameterized SpmvPolicy tuning policy type
|
||||
typename ScanTileStateT, ///< Tile status interface type
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT, ///< Signed integer type for sequence offsets
|
||||
typename CoordinateT, ///< Merge path coordinate type
|
||||
bool HAS_ALPHA, ///< Whether the input parameter Alpha is 1
|
||||
bool HAS_BETA> ///< Whether the input parameter Beta is 0
|
||||
__launch_bounds__ (int(SpmvPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSpmvKernel(
|
||||
SpmvParams<ValueT, OffsetT> spmv_params, ///< [in] SpMV input parameter bundle
|
||||
// CoordinateT* d_tile_coordinates, ///< [in] Pointer to the temporary array of tile starting coordinates
|
||||
// KeyValuePair<OffsetT,ValueT>* d_tile_carry_pairs, ///< [out] Pointer to the temporary array carry-out dot product row-ids, one per block
|
||||
// int num_tiles, ///< [in] Number of merge tiles
|
||||
// ScanTileStateT tile_state, ///< [in] Tile status interface for fixup reduce-by-key kernel
|
||||
// int num_fixup_tiles, ///< [in] Number of reduce-by-key tiles (fixup grid size)
|
||||
int rows_per_tile) ///< [in] Number of rows per tile
|
||||
{
|
||||
// Spmv agent type specialization
|
||||
typedef AgentSpmv<
|
||||
SpmvPolicyT,
|
||||
ValueT,
|
||||
OffsetT,
|
||||
HAS_ALPHA,
|
||||
HAS_BETA>
|
||||
AgentSpmvT;
|
||||
|
||||
// Shared memory for AgentSpmv
|
||||
__shared__ typename AgentSpmvT::TempStorage temp_storage;
|
||||
|
||||
AgentSpmvT(temp_storage, spmv_params).ConsumeTile(
|
||||
blockIdx.x,
|
||||
rows_per_tile);
|
||||
|
||||
/*
|
||||
AgentSpmvT(temp_storage, spmv_params).ConsumeTile(
|
||||
d_tile_coordinates,
|
||||
d_tile_carry_pairs,
|
||||
num_tiles);
|
||||
|
||||
// Initialize fixup tile status
|
||||
tile_state.InitializeStatus(num_fixup_tiles);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multi-block reduce-by-key sweep kernel entry point
|
||||
*/
|
||||
template <
|
||||
typename AgentSegmentFixupPolicyT, ///< Parameterized AgentSegmentFixupPolicy tuning policy type
|
||||
typename PairsInputIteratorT, ///< Random-access input iterator type for keys
|
||||
typename AggregatesOutputIteratorT, ///< Random-access output iterator type for values
|
||||
typename OffsetT, ///< Signed integer type for global offsets
|
||||
typename ScanTileStateT> ///< Tile status interface type
|
||||
__launch_bounds__ (int(AgentSegmentFixupPolicyT::BLOCK_THREADS))
|
||||
__global__ void DeviceSegmentFixupKernel(
|
||||
PairsInputIteratorT d_pairs_in, ///< [in] Pointer to the array carry-out dot product row-ids, one per spmv block
|
||||
AggregatesOutputIteratorT d_aggregates_out, ///< [in,out] Output value aggregates
|
||||
OffsetT num_items, ///< [in] Total number of items to select from
|
||||
int num_tiles, ///< [in] Total number of tiles for the entire problem
|
||||
ScanTileStateT tile_state) ///< [in] Tile status interface
|
||||
{
|
||||
// Thread block type for reducing tiles of value segments
|
||||
typedef AgentSegmentFixup<
|
||||
AgentSegmentFixupPolicyT,
|
||||
PairsInputIteratorT,
|
||||
AggregatesOutputIteratorT,
|
||||
cub::Equality,
|
||||
cub::Sum,
|
||||
OffsetT>
|
||||
AgentSegmentFixupT;
|
||||
|
||||
// Shared memory for AgentSegmentFixup
|
||||
__shared__ typename AgentSegmentFixupT::TempStorage temp_storage;
|
||||
|
||||
// Process tiles
|
||||
AgentSegmentFixupT(temp_storage, d_pairs_in, d_aggregates_out, cub::Equality(), cub::Sum()).ConsumeRange(
|
||||
num_items,
|
||||
num_tiles,
|
||||
tile_state);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Dispatch
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Utility class for dispatching the appropriately-tuned kernels for DeviceSpmv
|
||||
*/
|
||||
template <
|
||||
typename ValueT, ///< Matrix and vector value type
|
||||
typename OffsetT> ///< Signed integer type for global offsets
|
||||
struct DispatchSpmv
|
||||
{
|
||||
//---------------------------------------------------------------------
|
||||
// Constants and Types
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
INIT_KERNEL_THREADS = 128
|
||||
};
|
||||
|
||||
// SpmvParams bundle type
|
||||
typedef SpmvParams<ValueT, OffsetT> SpmvParamsT;
|
||||
|
||||
// 2D merge path coordinate type
|
||||
typedef typename CubVector<OffsetT, 2>::Type CoordinateT;
|
||||
|
||||
// Tile status descriptor interface type
|
||||
typedef ReduceByKeyScanTileState<ValueT, OffsetT> ScanTileStateT;
|
||||
|
||||
// Tuple type for scanning (pairs accumulated segment-value with segment-index)
|
||||
typedef KeyValuePair<OffsetT, ValueT> KeyValuePairT;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/// SM11
|
||||
struct Policy110
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
128,
|
||||
1,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
/// SM20
|
||||
struct Policy200
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
18,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_RAKING>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// SM30
|
||||
struct Policy300
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
96,
|
||||
6,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
4,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_DEFAULT,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// SM35
|
||||
struct Policy350
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 64 : 128,
|
||||
(sizeof(ValueT) > 4) ? 7 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
|
||||
/// SM37
|
||||
struct Policy370
|
||||
{
|
||||
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 128 : 128,
|
||||
(sizeof(ValueT) > 4) ? 7 : 7,
|
||||
LOAD_LDG,
|
||||
LOAD_CA,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
LOAD_LDG,
|
||||
false,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_WARP_SCANS>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
/// SM50
|
||||
struct Policy500
|
||||
{
|
||||
typedef AgentSpmvPolicy<
|
||||
(sizeof(ValueT) > 4) ? 64 : 64,
|
||||
7,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_CA,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_DEFAULT,
|
||||
LOAD_LDG,
|
||||
false,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SpmvPolicyT;
|
||||
|
||||
typedef AgentSegmentFixupPolicy<
|
||||
128,
|
||||
3,
|
||||
BLOCK_LOAD_VECTORIZE,
|
||||
LOAD_LDG,
|
||||
BLOCK_SCAN_RAKING_MEMOIZE>
|
||||
SegmentFixupPolicyT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Tuning policies of current PTX compiler pass
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#if (CUB_PTX_ARCH >= 500)
|
||||
typedef Policy500 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 370)
|
||||
typedef Policy370 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 350)
|
||||
typedef Policy350 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 300)
|
||||
typedef Policy300 PtxPolicy;
|
||||
|
||||
#elif (CUB_PTX_ARCH >= 200)
|
||||
typedef Policy200 PtxPolicy;
|
||||
|
||||
#else
|
||||
typedef Policy110 PtxPolicy;
|
||||
|
||||
#endif
|
||||
|
||||
// "Opaque" policies (whose parameterizations aren't reflected in the type signature)
|
||||
struct PtxSpmvPolicyT : PtxPolicy::SpmvPolicyT {};
|
||||
struct PtxSegmentFixupPolicy : PtxPolicy::SegmentFixupPolicyT {};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Utilities
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize kernel dispatch configurations with the policies corresponding to the PTX assembly we will use
|
||||
*/
|
||||
template <typename KernelConfig>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static void InitConfigs(
|
||||
int ptx_version,
|
||||
KernelConfig &spmv_config,
|
||||
KernelConfig &fixup_config)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
|
||||
// We're on the device, so initialize the kernel dispatch configurations with the current PTX policy
|
||||
spmv_config.template Init<PtxSpmvPolicyT>();
|
||||
fixup_config.template Init<PtxSegmentFixupPolicy>();
|
||||
|
||||
#else
|
||||
|
||||
// We're on the host, so lookup and initialize the kernel dispatch configurations with the policies that match the device's PTX version
|
||||
if (ptx_version >= 500)
|
||||
{
|
||||
spmv_config.template Init<typename Policy500::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy500::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 370)
|
||||
{
|
||||
spmv_config.template Init<typename Policy370::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy370::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 350)
|
||||
{
|
||||
spmv_config.template Init<typename Policy350::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy350::SegmentFixupPolicyT>();
|
||||
}
|
||||
else if (ptx_version >= 300)
|
||||
{
|
||||
spmv_config.template Init<typename Policy300::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy300::SegmentFixupPolicyT>();
|
||||
|
||||
}
|
||||
else if (ptx_version >= 200)
|
||||
{
|
||||
spmv_config.template Init<typename Policy200::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy200::SegmentFixupPolicyT>();
|
||||
}
|
||||
else
|
||||
{
|
||||
spmv_config.template Init<typename Policy110::SpmvPolicyT>();
|
||||
fixup_config.template Init<typename Policy110::SegmentFixupPolicyT>();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kernel kernel dispatch configuration.
|
||||
*/
|
||||
struct KernelConfig
|
||||
{
|
||||
int block_threads;
|
||||
int items_per_thread;
|
||||
int tile_items;
|
||||
|
||||
template <typename PolicyT>
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
void Init()
|
||||
{
|
||||
block_threads = PolicyT::BLOCK_THREADS;
|
||||
items_per_thread = PolicyT::ITEMS_PER_THREAD;
|
||||
tile_items = block_threads * items_per_thread;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Dispatch entrypoints
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction using the
|
||||
* specified kernel functions.
|
||||
*
|
||||
* If the input is larger than a single tile, this method uses two-passes of
|
||||
* kernel invocations.
|
||||
*/
|
||||
template <
|
||||
// typename Spmv1ColKernelT, ///< Function type of cub::DeviceSpmv1ColKernel
|
||||
// typename SpmvSearchKernelT, ///< Function type of cub::AgentSpmvSearchKernel
|
||||
typename SpmvKernelT> ///< Function type of cub::AgentSpmvKernel
|
||||
// typename SegmentFixupKernelT> ///< Function type of cub::DeviceSegmentFixupKernelT
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream, ///< [in] CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous, ///< [in] Whether or not to synchronize the stream after every kernel launch to check for errors. Also causes launch configurations to be printed to the console. Default is \p false.
|
||||
// Spmv1ColKernelT spmv_1col_kernel, ///< [in] Kernel function pointer to parameterization of DeviceSpmv1ColKernel
|
||||
// SpmvSearchKernelT spmv_search_kernel, ///< [in] Kernel function pointer to parameterization of AgentSpmvSearchKernel
|
||||
SpmvKernelT spmv_kernel, ///< [in] Kernel function pointer to parameterization of AgentSpmvKernel
|
||||
// SegmentFixupKernelT fixup_kernel, ///< [in] Kernel function pointer to parameterization of cub::DeviceSegmentFixupKernel
|
||||
KernelConfig spmv_config, ///< [in] Dispatch parameters that match the policy that \p spmv_kernel was compiled for
|
||||
KernelConfig fixup_config) ///< [in] Dispatch parameters that match the policy that \p fixup_kernel was compiled for
|
||||
{
|
||||
#ifndef CUB_RUNTIME_ENABLED
|
||||
|
||||
// Kernel launch not supported from this device
|
||||
return CubDebug(cudaErrorNotSupported );
|
||||
|
||||
#else
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
/*
|
||||
if (spmv_params.num_cols == 1)
|
||||
{
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
temp_storage_bytes = 1;
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
// Get search/init grid dims
|
||||
int degen_col_kernel_block_size = INIT_KERNEL_THREADS;
|
||||
int degen_col_kernel_grid_size = (spmv_params.num_rows + degen_col_kernel_block_size - 1) / degen_col_kernel_block_size;
|
||||
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_1col_kernel<<<%d, %d, 0, %lld>>>()\n",
|
||||
degen_col_kernel_grid_size, degen_col_kernel_block_size, (long long) stream);
|
||||
|
||||
// Invoke spmv_search_kernel
|
||||
spmv_1col_kernel<<<degen_col_kernel_grid_size, degen_col_kernel_block_size, 0, stream>>>(
|
||||
spmv_params);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
|
||||
break;
|
||||
}
|
||||
*/
|
||||
// Get device ordinal
|
||||
int device_ordinal;
|
||||
if (CubDebug(error = cudaGetDevice(&device_ordinal))) break;
|
||||
|
||||
// Get SM count
|
||||
int sm_count;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute (&sm_count, cudaDevAttrMultiProcessorCount, device_ordinal))) break;
|
||||
|
||||
// Get max x-dimension of grid
|
||||
int max_dim_x;
|
||||
if (CubDebug(error = cudaDeviceGetAttribute(&max_dim_x, cudaDevAttrMaxGridDimX, device_ordinal))) break;;
|
||||
|
||||
// Get SM occupancy for kernels
|
||||
int spmv_sm_occupancy;
|
||||
if (CubDebug(error = MaxSmOccupancy(
|
||||
spmv_sm_occupancy,
|
||||
spmv_kernel,
|
||||
spmv_config.block_threads))) break;
|
||||
|
||||
// Tile sizes of kernels
|
||||
int spmv_tile_size = spmv_config.block_threads * spmv_config.items_per_thread;
|
||||
int fixup_tile_size = fixup_config.block_threads * fixup_config.items_per_thread;
|
||||
|
||||
unsigned int rows_per_tile = spmv_config.block_threads;
|
||||
|
||||
if (spmv_params.num_rows < rows_per_tile * spmv_sm_occupancy * sm_count * 8)
|
||||
{
|
||||
// Decrease rows per tile if needed to accomodate high expansion factor
|
||||
unsigned int expansion_factor = (spmv_params.num_nonzeros) / spmv_params.num_rows;
|
||||
|
||||
if ((expansion_factor > 0) && (expansion_factor > spmv_config.items_per_thread))
|
||||
rows_per_tile = (spmv_tile_size) / expansion_factor;
|
||||
|
||||
// Decrease rows per tile if needed to accomodate minimum parallelism
|
||||
unsigned int spmv_device_occupancy = sm_count * 2;
|
||||
// unsigned int spmv_device_occupancy = sm_count * ((spmv_sm_occupancy + 1) / 2);
|
||||
if (spmv_params.num_rows < spmv_device_occupancy * rows_per_tile)
|
||||
rows_per_tile = (spmv_params.num_rows) / spmv_device_occupancy;
|
||||
}
|
||||
|
||||
rows_per_tile = CUB_MAX(rows_per_tile, 2);
|
||||
|
||||
if (debug_synchronous) _CubLog("Rows per tile: %d\n", rows_per_tile);
|
||||
|
||||
// Number of tiles for kernels
|
||||
unsigned int num_spmv_tiles = (spmv_params.num_rows + rows_per_tile - 1) / rows_per_tile;
|
||||
// unsigned int num_fixup_tiles = (num_spmv_tiles + fixup_tile_size - 1) / fixup_tile_size;
|
||||
|
||||
// Get grid dimensions
|
||||
dim3 spmv_grid_size(
|
||||
CUB_MIN(num_spmv_tiles, max_dim_x),
|
||||
(num_spmv_tiles + max_dim_x - 1) / max_dim_x,
|
||||
1);
|
||||
|
||||
/*
|
||||
dim3 spmv_grid_size(
|
||||
CUB_MIN(num_spmv_tiles, max_dim_x),
|
||||
(num_spmv_tiles + max_dim_x - 1) / max_dim_x,
|
||||
1);
|
||||
|
||||
dim3 fixup_grid_size(
|
||||
CUB_MIN(num_fixup_tiles, max_dim_x),
|
||||
(num_fixup_tiles + max_dim_x - 1) / max_dim_x,
|
||||
1);
|
||||
*/
|
||||
// Get the temporary storage allocation requirements
|
||||
size_t allocation_sizes[3];
|
||||
// if (CubDebug(error = ScanTileStateT::AllocationSize(num_fixup_tiles, allocation_sizes[0]))) break; // bytes needed for reduce-by-key tile status descriptors
|
||||
allocation_sizes[0] = 0;
|
||||
allocation_sizes[1] = num_spmv_tiles * sizeof(KeyValuePairT); // bytes needed for block carry-out pairs
|
||||
allocation_sizes[2] = (num_spmv_tiles + 1) * sizeof(CoordinateT); // bytes needed for tile starting coordinates
|
||||
|
||||
// Alias the temporary allocations from the single storage blob (or compute the necessary size of the blob)
|
||||
void* allocations[3];
|
||||
if (CubDebug(error = AliasTemporaries(d_temp_storage, temp_storage_bytes, allocations, allocation_sizes))) break;
|
||||
if (d_temp_storage == NULL)
|
||||
{
|
||||
// Return if the caller is simply requesting the size of the storage allocation
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
// Construct the tile status interface
|
||||
/*
|
||||
ScanTileStateT tile_state;
|
||||
if (CubDebug(error = tile_state.Init(num_fixup_tiles, allocations[0], allocation_sizes[0]))) break;
|
||||
*/
|
||||
// Alias the other allocations
|
||||
KeyValuePairT* d_tile_carry_pairs = (KeyValuePairT*) allocations[1]; // Agent carry-out pairs
|
||||
CoordinateT* d_tile_coordinates = (CoordinateT*) allocations[2]; // Agent starting coordinates
|
||||
|
||||
// Get search/init grid dims
|
||||
int search_block_size = INIT_KERNEL_THREADS;
|
||||
int search_grid_size = (num_spmv_tiles + 1 + search_block_size - 1) / search_block_size;
|
||||
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Init textures
|
||||
// if (CubDebug(error = spmv_params.t_vector_x.BindTexture(spmv_params.d_vector_x))) break;
|
||||
#endif
|
||||
|
||||
/*
|
||||
if (search_grid_size < sm_count)
|
||||
{
|
||||
// Not enough spmv tiles to saturate the device: have spmv blocks search their own staring coords
|
||||
d_tile_coordinates = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use separate search kernel if we have enough spmv tiles to saturate the device
|
||||
|
||||
// Log spmv_search_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_search_kernel<<<%d, %d, 0, %lld>>>()\n",
|
||||
search_grid_size, search_block_size, (long long) stream);
|
||||
|
||||
// Invoke spmv_search_kernel
|
||||
spmv_search_kernel<<<search_grid_size, search_block_size, 0, stream>>>(
|
||||
num_spmv_tiles,
|
||||
d_tile_coordinates,
|
||||
spmv_params);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
*/
|
||||
// Log spmv_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking spmv_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
spmv_grid_size.x, spmv_grid_size.y, spmv_grid_size.z, spmv_config.block_threads, (long long) stream, spmv_config.items_per_thread, spmv_sm_occupancy);
|
||||
|
||||
// Invoke spmv_kernel
|
||||
spmv_kernel<<<spmv_grid_size, spmv_config.block_threads, 0, stream>>>(
|
||||
spmv_params,
|
||||
// d_tile_coordinates,
|
||||
// d_tile_carry_pairs,
|
||||
// num_spmv_tiles,
|
||||
// tile_state,
|
||||
// num_fixup_tiles,
|
||||
rows_per_tile);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
/*
|
||||
// Run reduce-by-key fixup if necessary
|
||||
if (num_spmv_tiles > 1)
|
||||
{
|
||||
// Log fixup_kernel configuration
|
||||
if (debug_synchronous) _CubLog("Invoking fixup_kernel<<<{%d,%d,%d}, %d, 0, %lld>>>(), %d items per thread, %d SM occupancy\n",
|
||||
fixup_grid_size.x, fixup_grid_size.y, fixup_grid_size.z, fixup_config.block_threads, (long long) stream, fixup_config.items_per_thread, fixup_sm_occupancy);
|
||||
|
||||
// Invoke fixup_kernel
|
||||
fixup_kernel<<<fixup_grid_size, fixup_config.block_threads, 0, stream>>>(
|
||||
d_tile_carry_pairs,
|
||||
spmv_params.d_vector_y,
|
||||
num_spmv_tiles,
|
||||
num_fixup_tiles,
|
||||
tile_state);
|
||||
|
||||
// Check for failure to launch
|
||||
if (CubDebug(error = cudaPeekAtLastError())) break;
|
||||
|
||||
// Sync the stream if specified to flush runtime errors
|
||||
if (debug_synchronous && (CubDebug(error = SyncStream(stream)))) break;
|
||||
}
|
||||
*/
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Free textures
|
||||
// if (CubDebug(error = spmv_params.t_vector_x.UnbindTexture())) break;
|
||||
#endif
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
|
||||
#endif // CUB_RUNTIME_ENABLED
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal dispatch routine for computing a device-wide reduction
|
||||
*/
|
||||
CUB_RUNTIME_FUNCTION __forceinline__
|
||||
static cudaError_t Dispatch(
|
||||
void* d_temp_storage, ///< [in] %Device-accessible allocation of temporary storage. When NULL, the required allocation size is written to \p temp_storage_bytes and no work is done.
|
||||
size_t& temp_storage_bytes, ///< [in,out] Reference to size in bytes of \p d_temp_storage allocation
|
||||
SpmvParamsT& spmv_params, ///< SpMV input parameter bundle
|
||||
cudaStream_t stream = 0, ///< [in] <b>[optional]</b> CUDA stream to launch kernels within. Default is stream<sub>0</sub>.
|
||||
bool debug_synchronous = false) ///< [in] <b>[optional]</b> Whether or not to synchronize the stream after every kernel launch to check for errors. May cause significant slowdown. Default is \p false.
|
||||
{
|
||||
cudaError error = cudaSuccess;
|
||||
do
|
||||
{
|
||||
// Get PTX version
|
||||
int ptx_version;
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
if (CubDebug(error = PtxVersion(ptx_version))) break;
|
||||
#else
|
||||
ptx_version = CUB_PTX_ARCH;
|
||||
#endif
|
||||
|
||||
// Get kernel kernel dispatch configurations
|
||||
KernelConfig spmv_config, fixup_config;
|
||||
InitConfigs(ptx_version, spmv_config, fixup_config);
|
||||
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
// DeviceSpmv1ColKernel<PtxSpmvPolicyT, ValueT, OffsetT>,
|
||||
// DeviceSpmvSearchKernel<PtxSpmvPolicyT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ScanTileStateT, ValueT, OffsetT, CoordinateT, false, false>,
|
||||
// DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, fixup_config))) break;
|
||||
|
||||
/*
|
||||
// Dispatch
|
||||
if (spmv_params.beta == 0.0)
|
||||
{
|
||||
if (spmv_params.alpha == 1.0)
|
||||
{
|
||||
// Dispatch y = A*x
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmv1ColKernel<PtxSpmvPolicyT, ValueT, OffsetT>,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ScanTileStateT, ValueT, OffsetT, CoordinateT, false, false>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, fixup_config))) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dispatch y = alpha*A*x
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, true, false>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, fixup_config))) break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spmv_params.alpha == 1.0)
|
||||
{
|
||||
// Dispatch y = A*x + beta*y
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, false, true>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, fixup_config))) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dispatch y = alpha*A*x + beta*y
|
||||
if (CubDebug(error = Dispatch(
|
||||
d_temp_storage, temp_storage_bytes, spmv_params, stream, debug_synchronous,
|
||||
DeviceSpmvSearchKernel<PtxSpmvPolicyT, ScanTileStateT, OffsetT, CoordinateT, SpmvParamsT>,
|
||||
DeviceSpmvKernel<PtxSpmvPolicyT, ValueT, OffsetT, CoordinateT, true, true>,
|
||||
DeviceSegmentFixupKernel<PtxSegmentFixupPolicy, KeyValuePairT*, ValueT*, OffsetT, ScanTileStateT>,
|
||||
spmv_config, fixup_config))) break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
while (0);
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::GridBarrier implements a software global barrier among thread blocks within a CUDA grid
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_debug.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
#include "../thread/thread_load.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup GridModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief GridBarrier implements a software global barrier among thread blocks within a CUDA grid
|
||||
*/
|
||||
class GridBarrier
|
||||
{
|
||||
protected :
|
||||
|
||||
typedef unsigned int SyncFlag;
|
||||
|
||||
// Counters in global device memory
|
||||
SyncFlag* d_sync;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
GridBarrier() : d_sync(NULL) {}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronize
|
||||
*/
|
||||
__device__ __forceinline__ void Sync() const
|
||||
{
|
||||
volatile SyncFlag *d_vol_sync = d_sync;
|
||||
|
||||
// Threadfence and syncthreads to make sure global writes are visible before
|
||||
// thread-0 reports in with its sync counter
|
||||
__threadfence();
|
||||
__syncthreads();
|
||||
|
||||
if (blockIdx.x == 0)
|
||||
{
|
||||
// Report in ourselves
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
d_vol_sync[blockIdx.x] = 1;
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Wait for everyone else to report in
|
||||
for (int peer_block = threadIdx.x; peer_block < gridDim.x; peer_block += blockDim.x)
|
||||
{
|
||||
while (ThreadLoad<LOAD_CG>(d_sync + peer_block) == 0)
|
||||
{
|
||||
__threadfence_block();
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
// Let everyone know it's safe to proceed
|
||||
for (int peer_block = threadIdx.x; peer_block < gridDim.x; peer_block += blockDim.x)
|
||||
{
|
||||
d_vol_sync[peer_block] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (threadIdx.x == 0)
|
||||
{
|
||||
// Report in
|
||||
d_vol_sync[blockIdx.x] = 1;
|
||||
|
||||
// Wait for acknowledgment
|
||||
while (ThreadLoad<LOAD_CG>(d_sync + blockIdx.x) == 1)
|
||||
{
|
||||
__threadfence_block();
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief GridBarrierLifetime extends GridBarrier to provide lifetime management of the temporary device storage needed for cooperation.
|
||||
*
|
||||
* Uses RAII for lifetime, i.e., device resources are reclaimed when
|
||||
* the destructor is called.
|
||||
*/
|
||||
class GridBarrierLifetime : public GridBarrier
|
||||
{
|
||||
protected:
|
||||
|
||||
// Number of bytes backed by d_sync
|
||||
size_t sync_bytes;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
GridBarrierLifetime() : GridBarrier(), sync_bytes(0) {}
|
||||
|
||||
|
||||
/**
|
||||
* DeviceFrees and resets the progress counters
|
||||
*/
|
||||
cudaError_t HostReset()
|
||||
{
|
||||
cudaError_t retval = cudaSuccess;
|
||||
if (d_sync)
|
||||
{
|
||||
CubDebug(retval = cudaFree(d_sync));
|
||||
d_sync = NULL;
|
||||
}
|
||||
sync_bytes = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~GridBarrierLifetime()
|
||||
{
|
||||
HostReset();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the progress counters for the next kernel launch (lazily
|
||||
* allocating and initializing them if necessary)
|
||||
*/
|
||||
cudaError_t Setup(int sweep_grid_size)
|
||||
{
|
||||
cudaError_t retval = cudaSuccess;
|
||||
do {
|
||||
size_t new_sync_bytes = sweep_grid_size * sizeof(SyncFlag);
|
||||
if (new_sync_bytes > sync_bytes)
|
||||
{
|
||||
if (d_sync)
|
||||
{
|
||||
if (CubDebug(retval = cudaFree(d_sync))) break;
|
||||
}
|
||||
|
||||
sync_bytes = new_sync_bytes;
|
||||
|
||||
// Allocate and initialize to zero
|
||||
if (CubDebug(retval = cudaMalloc((void**) &d_sync, sync_bytes))) break;
|
||||
if (CubDebug(retval = cudaMemset(d_sync, 0, new_sync_bytes))) break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @} */ // end group GridModule
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::GridEvenShare is a descriptor utility for distributing input among CUDA threadblocks in an "even-share" fashion. Each threadblock gets roughly the same number of fixed-size work units (grains).
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
#include "../util_macro.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup GridModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief GridEvenShare is a descriptor utility for distributing input among CUDA threadblocks in an "even-share" fashion. Each threadblock gets roughly the same number of fixed-size work units (grains).
|
||||
*
|
||||
* \par Overview
|
||||
* GridEvenShare indicates which sections of input are to be mapped onto which threadblocks.
|
||||
* Threadblocks may receive one of three different amounts of work: "big", "normal",
|
||||
* and "last". The "big" workloads are one scheduling grain larger than "normal". The "last" work unit
|
||||
* for the last threadblock may be partially-full if the input is not an even multiple of
|
||||
* the scheduling grain size.
|
||||
*
|
||||
* \par
|
||||
* Before invoking a child grid, a parent thread will typically construct an instance of
|
||||
* GridEvenShare. The instance can be passed to child threadblocks which can
|
||||
* initialize their per-threadblock offsets using \p BlockInit().
|
||||
*
|
||||
* \tparam OffsetT Signed integer type for global offsets
|
||||
*/
|
||||
template <typename OffsetT>
|
||||
struct GridEvenShare
|
||||
{
|
||||
OffsetT total_grains;
|
||||
int big_blocks;
|
||||
OffsetT big_share;
|
||||
OffsetT normal_share;
|
||||
OffsetT normal_base_offset;
|
||||
|
||||
/// Total number of input items
|
||||
OffsetT num_items;
|
||||
|
||||
/// Grid size in threadblocks
|
||||
int grid_size;
|
||||
|
||||
/// OffsetT into input marking the beginning of the owning thread block's segment of input tiles
|
||||
OffsetT block_offset;
|
||||
|
||||
/// OffsetT into input of marking the end (one-past) of the owning thread block's segment of input tiles
|
||||
OffsetT block_end;
|
||||
|
||||
/**
|
||||
* \brief Default constructor. Zero-initializes block-specific fields.
|
||||
*/
|
||||
__host__ __device__ __forceinline__ GridEvenShare() :
|
||||
num_items(0),
|
||||
grid_size(0),
|
||||
block_offset(0),
|
||||
block_end(0) {}
|
||||
|
||||
/**
|
||||
* \brief Constructor. Initializes the grid-specific members \p num_items and \p grid_size. To be called prior prior to kernel launch)
|
||||
*/
|
||||
__host__ __device__ __forceinline__ GridEvenShare(
|
||||
OffsetT num_items, ///< Total number of input items
|
||||
int max_grid_size, ///< Maximum grid size allowable (actual grid size may be less if not warranted by the the number of input items)
|
||||
int schedule_granularity) ///< Granularity by which the input can be parcelled into and distributed among threablocks. Usually the thread block's native tile size (or a multiple thereof.
|
||||
{
|
||||
this->num_items = num_items;
|
||||
this->block_offset = num_items;
|
||||
this->block_end = num_items;
|
||||
this->total_grains = (num_items + schedule_granularity - 1) / schedule_granularity;
|
||||
this->grid_size = CUB_MIN(total_grains, max_grid_size);
|
||||
OffsetT grains_per_block = total_grains / grid_size;
|
||||
this->big_blocks = total_grains - (grains_per_block * grid_size); // leftover grains go to big blocks
|
||||
this->normal_share = grains_per_block * schedule_granularity;
|
||||
this->normal_base_offset = big_blocks * schedule_granularity;
|
||||
this->big_share = normal_share + schedule_granularity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initializes ranges for the specified partition index
|
||||
*/
|
||||
__device__ __forceinline__ void Init(int partition_id)
|
||||
{
|
||||
if (partition_id < big_blocks)
|
||||
{
|
||||
// This threadblock gets a big share of grains (grains_per_block + 1)
|
||||
block_offset = (partition_id * big_share);
|
||||
block_end = block_offset + big_share;
|
||||
}
|
||||
else if (partition_id < total_grains)
|
||||
{
|
||||
// This threadblock gets a normal share of grains (grains_per_block)
|
||||
block_offset = normal_base_offset + (partition_id * normal_share);
|
||||
block_end = CUB_MIN(num_items, block_offset + normal_share);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initializes ranges for the current thread block (e.g., to be called by each threadblock after startup)
|
||||
*/
|
||||
__device__ __forceinline__ void BlockInit()
|
||||
{
|
||||
Init(blockIdx.x);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print to stdout
|
||||
*/
|
||||
__host__ __device__ __forceinline__ void Print()
|
||||
{
|
||||
printf(
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
"\tthreadblock(%d) "
|
||||
"block_offset(%lu) "
|
||||
"block_end(%lu) "
|
||||
#endif
|
||||
"num_items(%lu) "
|
||||
"total_grains(%lu) "
|
||||
"big_blocks(%lu) "
|
||||
"big_share(%lu) "
|
||||
"normal_share(%lu)\n",
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
blockIdx.x,
|
||||
(unsigned long) block_offset,
|
||||
(unsigned long) block_end,
|
||||
#endif
|
||||
(unsigned long) num_items,
|
||||
(unsigned long) total_grains,
|
||||
(unsigned long) big_blocks,
|
||||
(unsigned long) big_share,
|
||||
(unsigned long) normal_share);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group GridModule
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::GridMappingStrategy enumerates alternative strategies for mapping constant-sized tiles of device-wide data onto a grid of CUDA thread blocks.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup GridModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Mapping policies
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* \brief cub::GridMappingStrategy enumerates alternative strategies for mapping constant-sized tiles of device-wide data onto a grid of CUDA thread blocks.
|
||||
*/
|
||||
enum GridMappingStrategy
|
||||
{
|
||||
/**
|
||||
* \brief An "even-share" strategy for assigning input tiles to thread blocks.
|
||||
*
|
||||
* \par Overview
|
||||
* The input is evenly partitioned into \p p segments, where \p p is
|
||||
* constant and corresponds loosely to the number of thread blocks that may
|
||||
* actively reside on the target device. Each segment is comprised of
|
||||
* consecutive tiles, where a tile is a small, constant-sized unit of input
|
||||
* to be processed to completion before the thread block terminates or
|
||||
* obtains more work. The kernel invokes \p p thread blocks, each
|
||||
* of which iteratively consumes a segment of <em>n</em>/<em>p</em> elements
|
||||
* in tile-size increments.
|
||||
*/
|
||||
GRID_MAPPING_EVEN_SHARE,
|
||||
|
||||
/**
|
||||
* \brief A dynamic "queue-based" strategy for assigning input tiles to thread blocks.
|
||||
*
|
||||
* \par Overview
|
||||
* The input is treated as a queue to be dynamically consumed by a grid of
|
||||
* thread blocks. Work is atomically dequeued in tiles, where a tile is a
|
||||
* unit of input to be processed to completion before the thread block
|
||||
* terminates or obtains more work. The grid size \p p is constant,
|
||||
* loosely corresponding to the number of thread blocks that may actively
|
||||
* reside on the target device.
|
||||
*/
|
||||
GRID_MAPPING_DYNAMIC,
|
||||
};
|
||||
|
||||
|
||||
/** @} */ // end group GridModule
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* cub::GridQueue is a descriptor utility for dynamic queue management.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
#include "../util_debug.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup GridModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief GridQueue is a descriptor utility for dynamic queue management.
|
||||
*
|
||||
* \par Overview
|
||||
* GridQueue descriptors provides abstractions for "filling" or
|
||||
* "draining" globally-shared vectors.
|
||||
*
|
||||
* \par
|
||||
* A "filling" GridQueue works by atomically-adding to a zero-initialized counter,
|
||||
* returning a unique offset for the calling thread to write its items.
|
||||
* The GridQueue maintains the total "fill-size". The fill counter must be reset
|
||||
* using GridQueue::ResetFill by the host or kernel instance prior to the kernel instance that
|
||||
* will be filling.
|
||||
*
|
||||
* \par
|
||||
* Similarly, a "draining" GridQueue works by works by atomically-incrementing a
|
||||
* zero-initialized counter, returning a unique offset for the calling thread to
|
||||
* read its items. Threads can safely drain until the array's logical fill-size is
|
||||
* exceeded. The drain counter must be reset using GridQueue::ResetDrain or
|
||||
* GridQueue::FillAndResetDrain by the host or kernel instance prior to the kernel instance that
|
||||
* will be filling. (For dynamic work distribution of existing data, the corresponding fill-size
|
||||
* is simply the number of elements in the array.)
|
||||
*
|
||||
* \par
|
||||
* Iterative work management can be implemented simply with a pair of flip-flopping
|
||||
* work buffers, each with an associated set of fill and drain GridQueue descriptors.
|
||||
*
|
||||
* \tparam OffsetT Signed integer type for global offsets
|
||||
*/
|
||||
template <typename OffsetT>
|
||||
class GridQueue
|
||||
{
|
||||
private:
|
||||
|
||||
/// Counter indices
|
||||
enum
|
||||
{
|
||||
FILL = 0,
|
||||
DRAIN = 1,
|
||||
};
|
||||
|
||||
/// Pair of counters
|
||||
OffsetT *d_counters;
|
||||
|
||||
public:
|
||||
|
||||
/// Returns the device allocation size in bytes needed to construct a GridQueue instance
|
||||
__host__ __device__ __forceinline__
|
||||
static size_t AllocationSize()
|
||||
{
|
||||
return sizeof(OffsetT) * 2;
|
||||
}
|
||||
|
||||
|
||||
/// Constructs an invalid GridQueue descriptor
|
||||
__host__ __device__ __forceinline__ GridQueue()
|
||||
:
|
||||
d_counters(NULL)
|
||||
{}
|
||||
|
||||
|
||||
/// Constructs a GridQueue descriptor around the device storage allocation
|
||||
__host__ __device__ __forceinline__ GridQueue(
|
||||
void *d_storage) ///< Device allocation to back the GridQueue. Must be at least as big as <tt>AllocationSize()</tt>.
|
||||
:
|
||||
d_counters((OffsetT*) d_storage)
|
||||
{}
|
||||
|
||||
|
||||
/// This operation sets the fill-size and resets the drain counter, preparing the GridQueue for draining in the next kernel instance. To be called by the host or by a kernel prior to that which will be draining.
|
||||
__host__ __device__ __forceinline__ cudaError_t FillAndResetDrain(
|
||||
OffsetT fill_size,
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)stream;
|
||||
d_counters[FILL] = fill_size;
|
||||
d_counters[DRAIN] = 0;
|
||||
return cudaSuccess;
|
||||
#else
|
||||
OffsetT counters[2];
|
||||
counters[FILL] = fill_size;
|
||||
counters[DRAIN] = 0;
|
||||
return CubDebug(cudaMemcpyAsync(d_counters, counters, sizeof(OffsetT) * 2, cudaMemcpyHostToDevice, stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// This operation resets the drain so that it may advance to meet the existing fill-size. To be called by the host or by a kernel prior to that which will be draining.
|
||||
__host__ __device__ __forceinline__ cudaError_t ResetDrain(cudaStream_t stream = 0)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)stream;
|
||||
d_counters[DRAIN] = 0;
|
||||
return cudaSuccess;
|
||||
#else
|
||||
return CubDebug(cudaMemsetAsync(d_counters + DRAIN, 0, sizeof(OffsetT), stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// This operation resets the fill counter. To be called by the host or by a kernel prior to that which will be filling.
|
||||
__host__ __device__ __forceinline__ cudaError_t ResetFill(cudaStream_t stream = 0)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)stream;
|
||||
d_counters[FILL] = 0;
|
||||
return cudaSuccess;
|
||||
#else
|
||||
return CubDebug(cudaMemsetAsync(d_counters + FILL, 0, sizeof(OffsetT), stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Returns the fill-size established by the parent or by the previous kernel.
|
||||
__host__ __device__ __forceinline__ cudaError_t FillSize(
|
||||
OffsetT &fill_size,
|
||||
cudaStream_t stream = 0)
|
||||
{
|
||||
#if (CUB_PTX_ARCH > 0)
|
||||
(void)stream;
|
||||
fill_size = d_counters[FILL];
|
||||
return cudaSuccess;
|
||||
#else
|
||||
return CubDebug(cudaMemcpyAsync(&fill_size, d_counters + FILL, sizeof(OffsetT), cudaMemcpyDeviceToHost, stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Drain \p num_items from the queue. Returns offset from which to read items. To be called from CUDA kernel.
|
||||
__device__ __forceinline__ OffsetT Drain(OffsetT num_items)
|
||||
{
|
||||
return atomicAdd(d_counters + DRAIN, num_items);
|
||||
}
|
||||
|
||||
|
||||
/// Fill \p num_items into the queue. Returns offset from which to write items. To be called from CUDA kernel.
|
||||
__device__ __forceinline__ OffsetT Fill(OffsetT num_items)
|
||||
{
|
||||
return atomicAdd(d_counters + FILL, num_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Do not document
|
||||
|
||||
|
||||
/**
|
||||
* Reset grid queue (call with 1 block of 1 thread)
|
||||
*/
|
||||
template <typename OffsetT>
|
||||
__global__ void FillAndResetDrainKernel(
|
||||
GridQueue<OffsetT> grid_queue,
|
||||
OffsetT num_items)
|
||||
{
|
||||
grid_queue.FillAndResetDrain(num_items);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
|
||||
/** @} */ // end group GridModule
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Simple portable mutex
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __cplusplus > 199711L
|
||||
#include <mutex>
|
||||
#else
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <intrin.h>
|
||||
#include <windows.h>
|
||||
#undef small // Windows is terrible for polluting macro namespace
|
||||
|
||||
/**
|
||||
* Compiler read/write barrier
|
||||
*/
|
||||
#pragma intrinsic(_ReadWriteBarrier)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* Simple portable mutex
|
||||
* - Wraps std::mutex when compiled with C++11 or newer (supported on all platforms)
|
||||
* - Uses GNU/Windows spinlock mechanisms for pre C++11 (supported on x86/x64 when compiled with cl.exe or g++)
|
||||
*/
|
||||
struct Mutex
|
||||
{
|
||||
#if __cplusplus > 199711L
|
||||
|
||||
std::mutex mtx;
|
||||
|
||||
void Lock()
|
||||
{
|
||||
mtx.lock();
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
mtx.unlock();
|
||||
}
|
||||
|
||||
void TryLock()
|
||||
{
|
||||
mtx.try_lock();
|
||||
}
|
||||
|
||||
#else //__cplusplus > 199711L
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
// Microsoft VC++
|
||||
typedef long Spinlock;
|
||||
|
||||
#else
|
||||
|
||||
// GNU g++
|
||||
typedef int Spinlock;
|
||||
|
||||
/**
|
||||
* Compiler read/write barrier
|
||||
*/
|
||||
__forceinline__ void _ReadWriteBarrier()
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomic exchange
|
||||
*/
|
||||
__forceinline__ long _InterlockedExchange(volatile int * const Target, const int Value)
|
||||
{
|
||||
// NOTE: __sync_lock_test_and_set would be an acquire barrier, so we force a full barrier
|
||||
_ReadWriteBarrier();
|
||||
return __sync_lock_test_and_set(Target, Value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause instruction to prevent excess processor bus usage
|
||||
*/
|
||||
__forceinline__ void YieldProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
/// Lock member
|
||||
volatile Spinlock lock;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Mutex() : lock(0) {}
|
||||
|
||||
/**
|
||||
* Return when the specified spinlock has been acquired
|
||||
*/
|
||||
__forceinline__ void Lock()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (!_InterlockedExchange(&lock, 1)) return;
|
||||
while (lock) YieldProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Release the specified spinlock
|
||||
*/
|
||||
__forceinline__ void Unlock()
|
||||
{
|
||||
_ReadWriteBarrier();
|
||||
lock = 0;
|
||||
}
|
||||
|
||||
#endif // __cplusplus > 199711L
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#include <thrust/version.h>
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input wrapper for pairing dereferenced values with their corresponding indices (forming \p KeyValuePair tuples).
|
||||
*
|
||||
* \par Overview
|
||||
* - ArgIndexInputIteratorTwraps a random access input iterator \p itr of type \p InputIteratorT.
|
||||
* Dereferencing an ArgIndexInputIteratorTat offset \p i produces a \p KeyValuePair value whose
|
||||
* \p key field is \p i and whose \p value field is <tt>itr[i]</tt>.
|
||||
* - Can be used with any data type.
|
||||
* - Can be constructed, manipulated, and exchanged within and between host and device
|
||||
* functions. Wrapped host memory can only be dereferenced on the host, and wrapped
|
||||
* device memory can only be dereferenced on the device.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p ArgIndexInputIteratorTto
|
||||
* dereference an array of doubles
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/arg_index_input_iterator.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* double *d_in; // e.g., [8.0, 6.0, 7.0, 5.0, 3.0, 0.0, 9.0]
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::ArgIndexInputIterator<double*> itr(d_in);
|
||||
*
|
||||
* // Within device code:
|
||||
* typedef typename cub::ArgIndexInputIterator<double*>::value_type Tuple;
|
||||
* Tuple item_offset_pair.key = *itr;
|
||||
* printf("%f @ %d\n",
|
||||
* item_offset_pair.value,
|
||||
* item_offset_pair.key); // 8.0 @ 0
|
||||
*
|
||||
* itr = itr + 6;
|
||||
* item_offset_pair.key = *itr;
|
||||
* printf("%f @ %d\n",
|
||||
* item_offset_pair.value,
|
||||
* item_offset_pair.key); // 9.0 @ 6
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam InputIteratorT The value type of the wrapped input iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
* \tparam OutputValueT The paired value type of the <offset,value> tuple (Default: value type of input iterator)
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OffsetT = ptrdiff_t,
|
||||
typename OutputValueT = typename std::iterator_traits<InputIteratorT>::value_type>
|
||||
class ArgIndexInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef ArgIndexInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef KeyValuePair<difference_type, OutputValueT> value_type; ///< The type of the element the iterator can point to
|
||||
typedef value_type* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef value_type reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::any_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
InputIteratorT itr;
|
||||
difference_type offset;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ArgIndexInputIterator(
|
||||
InputIteratorT itr, ///< Input iterator to wrap
|
||||
difference_type offset = 0) ///< OffsetT (in items) from \p itr denoting the position of the iterator
|
||||
:
|
||||
itr(itr),
|
||||
offset(offset)
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
offset++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
offset++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
value_type retval;
|
||||
retval.value = itr[offset];
|
||||
retval.key = offset;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(itr, offset + n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
offset += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(itr, offset - n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
offset -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return offset - other.offset;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
self_type offset = (*this) + n;
|
||||
return *offset;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &(*(*this));
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return ((itr == rhs.itr) && (offset == rhs.offset));
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return ((itr != rhs.itr) || (offset != rhs.offset));
|
||||
}
|
||||
|
||||
/// Normalize
|
||||
__host__ __device__ __forceinline__ void normalize()
|
||||
{
|
||||
itr += offset;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& /*itr*/)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input wrapper for dereferencing array values using a PTX cache load modifier.
|
||||
*
|
||||
* \par Overview
|
||||
* - CacheModifiedInputIteratorTis a random-access input iterator that wraps a native
|
||||
* device pointer of type <tt>ValueType*</tt>. \p ValueType references are
|
||||
* made by reading \p ValueType values through loads modified by \p MODIFIER.
|
||||
* - Can be used to load any data type from memory using PTX cache load modifiers (e.g., "LOAD_LDG",
|
||||
* "LOAD_CG", "LOAD_CA", "LOAD_CS", "LOAD_CV", etc.).
|
||||
* - Can be constructed, manipulated, and exchanged within and between host and device
|
||||
* functions, but can only be dereferenced within device functions.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p CacheModifiedInputIteratorTto
|
||||
* dereference a device array of double using the "ldg" PTX load modifier
|
||||
* (i.e., load values through texture cache).
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/cache_modified_input_iterator.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* double *d_in; // e.g., [8.0, 6.0, 7.0, 5.0, 3.0, 0.0, 9.0]
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::CacheModifiedInputIterator<cub::LOAD_LDG, double> itr(d_in);
|
||||
*
|
||||
* // Within device code:
|
||||
* printf("%f\n", itr[0]); // 8.0
|
||||
* printf("%f\n", itr[1]); // 6.0
|
||||
* printf("%f\n", itr[6]); // 9.0
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam CacheLoadModifier The cub::CacheLoadModifier to use when accessing data
|
||||
* \tparam ValueType The value type of this iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
CacheLoadModifier MODIFIER,
|
||||
typename ValueType,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class CacheModifiedInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef CacheModifiedInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef ValueType value_type; ///< The type of the element the iterator can point to
|
||||
typedef ValueType* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef ValueType reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::device_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Wrapped native pointer
|
||||
ValueType* ptr;
|
||||
|
||||
/// Constructor
|
||||
template <typename QualifiedValueType>
|
||||
__host__ __device__ __forceinline__ CacheModifiedInputIterator(
|
||||
QualifiedValueType* ptr) ///< Native pointer to wrap
|
||||
:
|
||||
ptr(const_cast<typename RemoveQualifiers<QualifiedValueType>::Type *>(ptr))
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
ptr++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
ptr++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
return ThreadLoad<MODIFIER>(ptr);
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(ptr + n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
ptr += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(ptr - n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
ptr -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return ptr - other.ptr;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
return ThreadLoad<MODIFIER>(ptr + n);
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &ThreadLoad<MODIFIER>(ptr);
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (ptr == rhs.ptr);
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (ptr != rhs.ptr);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& /*itr*/)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access output wrapper for storing array values using a PTX cache-modifier.
|
||||
*
|
||||
* \par Overview
|
||||
* - CacheModifiedOutputIterator is a random-access output iterator that wraps a native
|
||||
* device pointer of type <tt>ValueType*</tt>. \p ValueType references are
|
||||
* made by writing \p ValueType values through stores modified by \p MODIFIER.
|
||||
* - Can be used to store any data type to memory using PTX cache store modifiers (e.g., "STORE_WB",
|
||||
* "STORE_CG", "STORE_CS", "STORE_WT", etc.).
|
||||
* - Can be constructed, manipulated, and exchanged within and between host and device
|
||||
* functions, but can only be dereferenced within device functions.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p CacheModifiedOutputIterator to
|
||||
* dereference a device array of doubles using the "wt" PTX load modifier
|
||||
* (i.e., write-through to system memory).
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/cache_modified_output_iterator.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* double *d_out; // e.g., [, , , , , , ]
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::CacheModifiedOutputIterator<cub::STORE_WT, double> itr(d_out);
|
||||
*
|
||||
* // Within device code:
|
||||
* itr[0] = 8.0;
|
||||
* itr[1] = 66.0;
|
||||
* itr[55] = 24.0;
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \par Usage Considerations
|
||||
* - Can only be dereferenced within device code
|
||||
*
|
||||
* \tparam CacheStoreModifier The cub::CacheStoreModifier to use when accessing data
|
||||
* \tparam ValueType The value type of this iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
CacheStoreModifier MODIFIER,
|
||||
typename ValueType,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class CacheModifiedOutputIterator
|
||||
{
|
||||
private:
|
||||
|
||||
// Proxy object
|
||||
struct Reference
|
||||
{
|
||||
ValueType* ptr;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ Reference(ValueType* ptr) : ptr(ptr) {}
|
||||
|
||||
/// Assignment
|
||||
__device__ __forceinline__ ValueType operator =(ValueType val)
|
||||
{
|
||||
ThreadStore<MODIFIER>(ptr, val);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef CacheModifiedOutputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef void value_type; ///< The type of the element the iterator can point to
|
||||
typedef void pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef Reference reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::device_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
ValueType* ptr;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
template <typename QualifiedValueType>
|
||||
__host__ __device__ __forceinline__ CacheModifiedOutputIterator(
|
||||
QualifiedValueType* ptr) ///< Native pointer to wrap
|
||||
:
|
||||
ptr(const_cast<typename RemoveQualifiers<QualifiedValueType>::Type *>(ptr))
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
ptr++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
ptr++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
return Reference(ptr);
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(ptr + n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
ptr += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(ptr - n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
ptr -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return ptr - other.ptr;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
return Reference(ptr + n);
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (ptr == rhs.ptr);
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (ptr != rhs.ptr);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input generator for dereferencing a sequence of homogeneous values
|
||||
*
|
||||
* \par Overview
|
||||
* - Read references to a ConstantInputIteratorTiterator always return the supplied constant
|
||||
* of type \p ValueType.
|
||||
* - Can be used with any data type.
|
||||
* - Can be constructed, manipulated, dereferenced, and exchanged within and between host and device
|
||||
* functions.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p ConstantInputIteratorTto
|
||||
* dereference a sequence of homogeneous doubles.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/constant_input_iterator.cuh>
|
||||
*
|
||||
* cub::ConstantInputIterator<double> itr(5.0);
|
||||
*
|
||||
* printf("%f\n", itr[0]); // 5.0
|
||||
* printf("%f\n", itr[1]); // 5.0
|
||||
* printf("%f\n", itr[2]); // 5.0
|
||||
* printf("%f\n", itr[50]); // 5.0
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ValueType The value type of this iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
typename ValueType,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class ConstantInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef ConstantInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef ValueType value_type; ///< The type of the element the iterator can point to
|
||||
typedef ValueType* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef ValueType reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::any_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
ValueType val;
|
||||
OffsetT offset;
|
||||
#ifdef _WIN32
|
||||
OffsetT pad[CUB_MAX(1, (16 / sizeof(OffsetT) - 1))]; // Workaround for win32 parameter-passing bug (ulonglong2 argmin DeviceReduce)
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ConstantInputIterator(
|
||||
ValueType val, ///< Starting value for the iterator instance to report
|
||||
OffsetT offset = 0) ///< Base offset
|
||||
:
|
||||
val(val),
|
||||
offset(offset)
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
offset++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
offset++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(val, offset + n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
offset += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(val, offset - n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
offset -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return offset - other.offset;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance /*n*/) const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &val;
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (offset == rhs.offset) && ((val == rhs.val));
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (offset != rhs.offset) || (val!= rhs.val);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
os << "[" << itr.val << "," << itr.offset << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief A random-access input generator for dereferencing a sequence of incrementing integer values.
|
||||
*
|
||||
* \par Overview
|
||||
* - After initializing a CountingInputIteratorTto a certain integer \p base, read references
|
||||
* at \p offset will return the value \p base + \p offset.
|
||||
* - Can be constructed, manipulated, dereferenced, and exchanged within and between host and device
|
||||
* functions.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p CountingInputIteratorTto
|
||||
* dereference a sequence of incrementing integers.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/counting_input_iterator.cuh>
|
||||
*
|
||||
* cub::CountingInputIterator<int> itr(5);
|
||||
*
|
||||
* printf("%d\n", itr[0]); // 5
|
||||
* printf("%d\n", itr[1]); // 6
|
||||
* printf("%d\n", itr[2]); // 7
|
||||
* printf("%d\n", itr[50]); // 55
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ValueType The value type of this iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
typename ValueType,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class CountingInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef CountingInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef ValueType value_type; ///< The type of the element the iterator can point to
|
||||
typedef ValueType* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef ValueType reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::any_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
ValueType val;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ CountingInputIterator(
|
||||
const ValueType &val) ///< Starting value for the iterator instance to report
|
||||
:
|
||||
val(val)
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
val++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
val++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(val + (ValueType) n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
val += (ValueType) n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(val - (ValueType) n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
val -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return (difference_type) (val - other.val);
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
return val + (ValueType) n;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &val;
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (val == rhs.val);
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (val != rhs.val);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
os << "[" << itr.val << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include <thrust/iterator/discard_iterator.h>
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
#include "../util_macro.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A discard iterator
|
||||
*/
|
||||
template <typename OffsetT = ptrdiff_t>
|
||||
class DiscardOutputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef DiscardOutputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef void value_type; ///< The type of the element the iterator can point to
|
||||
typedef void pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef void reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::any_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
OffsetT offset;
|
||||
|
||||
#if defined(_WIN32) || !defined(_WIN64)
|
||||
// Workaround for win32 parameter-passing bug (ulonglong2 argmin DeviceReduce)
|
||||
OffsetT pad[CUB_MAX(1, (16 / sizeof(OffsetT) - 1))];
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ DiscardOutputIterator(
|
||||
OffsetT offset = 0) ///< Base offset
|
||||
:
|
||||
offset(offset)
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
offset++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
offset++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ self_type& operator*()
|
||||
{
|
||||
// return self reference, which can be assigned to anything
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(offset + n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
offset += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(offset - n);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
offset -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return offset - other.offset;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator[](Distance n)
|
||||
{
|
||||
// return self reference, which can be assigned to anything
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/// Assignment to self (no-op)
|
||||
__host__ __device__ __forceinline__ void operator=(self_type const& other)
|
||||
{
|
||||
offset = other.offset;
|
||||
}
|
||||
|
||||
/// Assignment to anything else (no-op)
|
||||
template<typename T>
|
||||
__host__ __device__ __forceinline__ void operator=(T const&)
|
||||
{}
|
||||
|
||||
/// Cast to void* operator
|
||||
__host__ __device__ __forceinline__ operator void*() const { return NULL; }
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (offset == rhs.offset);
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (offset != rhs.offset);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
os << "[" << itr.offset << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_debug.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input wrapper for dereferencing array values through texture cache. Uses newer Kepler-style texture objects.
|
||||
*
|
||||
* \par Overview
|
||||
* - TexObjInputIteratorTwraps a native device pointer of type <tt>ValueType*</tt>. References
|
||||
* to elements are to be loaded through texture cache.
|
||||
* - Can be used to load any data type from memory through texture cache.
|
||||
* - Can be manipulated and exchanged within and between host and device
|
||||
* functions, can only be constructed within host functions, and can only be
|
||||
* dereferenced within device functions.
|
||||
* - With regard to nested/dynamic parallelism, TexObjInputIteratorTiterators may only be
|
||||
* created by the host thread, but can be used by any descendant kernel.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p TexRefInputIteratorTto
|
||||
* dereference a device array of doubles through texture cache.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/tex_obj_input_iterator.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* int num_items; // e.g., 7
|
||||
* double *d_in; // e.g., [8.0, 6.0, 7.0, 5.0, 3.0, 0.0, 9.0]
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::TexObjInputIterator<double> itr;
|
||||
* itr.BindTexture(d_in, sizeof(double) * num_items);
|
||||
* ...
|
||||
*
|
||||
* // Within device code:
|
||||
* printf("%f\n", itr[0]); // 8.0
|
||||
* printf("%f\n", itr[1]); // 6.0
|
||||
* printf("%f\n", itr[6]); // 9.0
|
||||
*
|
||||
* ...
|
||||
* itr.UnbindTexture();
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam T The value type of this iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class TexObjInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef TexObjInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef T value_type; ///< The type of the element the iterator can point to
|
||||
typedef T* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef T reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::device_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
// Largest texture word we can use in device
|
||||
typedef typename UnitWord<T>::TextureWord TextureWord;
|
||||
|
||||
// Number of texture words per T
|
||||
enum {
|
||||
TEXTURE_MULTIPLE = sizeof(T) / sizeof(TextureWord)
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
difference_type tex_offset;
|
||||
cudaTextureObject_t tex_obj;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ TexObjInputIterator()
|
||||
:
|
||||
ptr(NULL),
|
||||
tex_offset(0),
|
||||
tex_obj(0)
|
||||
{}
|
||||
|
||||
/// Use this iterator to bind \p ptr with a texture reference
|
||||
template <typename QualifiedT>
|
||||
cudaError_t BindTexture(
|
||||
QualifiedT *ptr, ///< Native pointer to wrap that is aligned to cudaDeviceProp::textureAlignment
|
||||
size_t bytes = size_t(-1), ///< Number of bytes in the range
|
||||
size_t tex_offset = 0) ///< OffsetT (in items) from \p ptr denoting the position of the iterator
|
||||
{
|
||||
this->ptr = const_cast<typename RemoveQualifiers<QualifiedT>::Type *>(ptr);
|
||||
this->tex_offset = tex_offset;
|
||||
|
||||
cudaChannelFormatDesc channel_desc = cudaCreateChannelDesc<TextureWord>();
|
||||
cudaResourceDesc res_desc;
|
||||
cudaTextureDesc tex_desc;
|
||||
memset(&res_desc, 0, sizeof(cudaResourceDesc));
|
||||
memset(&tex_desc, 0, sizeof(cudaTextureDesc));
|
||||
res_desc.resType = cudaResourceTypeLinear;
|
||||
res_desc.res.linear.devPtr = this->ptr;
|
||||
res_desc.res.linear.desc = channel_desc;
|
||||
res_desc.res.linear.sizeInBytes = bytes;
|
||||
tex_desc.readMode = cudaReadModeElementType;
|
||||
return cudaCreateTextureObject(&tex_obj, &res_desc, &tex_desc, NULL);
|
||||
}
|
||||
|
||||
/// Unbind this iterator from its texture reference
|
||||
cudaError_t UnbindTexture()
|
||||
{
|
||||
return cudaDestroyTextureObject(tex_obj);
|
||||
}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
tex_offset++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
tex_offset++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Simply dereference the pointer on the host
|
||||
return ptr[tex_offset];
|
||||
#else
|
||||
// Move array of uninitialized words, then alias and assign to return value
|
||||
TextureWord words[TEXTURE_MULTIPLE];
|
||||
|
||||
#pragma unroll
|
||||
for (int i = 0; i < TEXTURE_MULTIPLE; ++i)
|
||||
{
|
||||
words[i] = tex1Dfetch<TextureWord>(
|
||||
tex_obj,
|
||||
(tex_offset * TEXTURE_MULTIPLE) + i);
|
||||
}
|
||||
|
||||
// Load from words
|
||||
return *reinterpret_cast<T*>(words);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval;
|
||||
retval.ptr = ptr;
|
||||
retval.tex_obj = tex_obj;
|
||||
retval.tex_offset = tex_offset + n;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
tex_offset += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval;
|
||||
retval.ptr = ptr;
|
||||
retval.tex_obj = tex_obj;
|
||||
retval.tex_offset = tex_offset - n;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
tex_offset -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return tex_offset - other.tex_offset;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
self_type offset = (*this) + n;
|
||||
return *offset;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &(*(*this));
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return ((ptr == rhs.ptr) && (tex_offset == rhs.tex_offset) && (tex_obj == rhs.tex_obj));
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return ((ptr != rhs.ptr) || (tex_offset != rhs.tex_offset) || (tex_obj != rhs.tex_obj));
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_debug.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (CUDA_VERSION >= 5050) || defined(DOXYGEN_ACTIVE) // This iterator is compatible with CUDA 5.5 and newer
|
||||
|
||||
#if (THRUST_VERSION >= 100700) // This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Static file-scope Tesla/Fermi-style texture references
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Do not document
|
||||
|
||||
// Anonymous namespace
|
||||
namespace {
|
||||
|
||||
/// Global texture reference specialized by type
|
||||
template <typename T>
|
||||
struct IteratorTexRef
|
||||
{
|
||||
/// And by unique ID
|
||||
template <int UNIQUE_ID>
|
||||
struct TexId
|
||||
{
|
||||
// Largest texture word we can use in device
|
||||
typedef typename UnitWord<T>::DeviceWord DeviceWord;
|
||||
typedef typename UnitWord<T>::TextureWord TextureWord;
|
||||
|
||||
// Number of texture words per T
|
||||
enum {
|
||||
DEVICE_MULTIPLE = sizeof(T) / sizeof(DeviceWord),
|
||||
TEXTURE_MULTIPLE = sizeof(T) / sizeof(TextureWord)
|
||||
};
|
||||
|
||||
// Texture reference type
|
||||
typedef texture<TextureWord> TexRef;
|
||||
|
||||
// Texture reference
|
||||
static TexRef ref;
|
||||
|
||||
/// Bind texture
|
||||
static cudaError_t BindTexture(void *d_in, size_t &offset)
|
||||
{
|
||||
if (d_in)
|
||||
{
|
||||
cudaChannelFormatDesc tex_desc = cudaCreateChannelDesc<TextureWord>();
|
||||
ref.channelDesc = tex_desc;
|
||||
return (CubDebug(cudaBindTexture(&offset, ref, d_in)));
|
||||
}
|
||||
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
/// Unbind texture
|
||||
static cudaError_t UnbindTexture()
|
||||
{
|
||||
return CubDebug(cudaUnbindTexture(ref));
|
||||
}
|
||||
|
||||
/// Fetch element
|
||||
template <typename Distance>
|
||||
static __device__ __forceinline__ T Fetch(Distance tex_offset)
|
||||
{
|
||||
DeviceWord temp[DEVICE_MULTIPLE];
|
||||
TextureWord *words = reinterpret_cast<TextureWord*>(temp);
|
||||
|
||||
#pragma unroll
|
||||
for (int i = 0; i < TEXTURE_MULTIPLE; ++i)
|
||||
{
|
||||
words[i] = tex1Dfetch(ref, (tex_offset * TEXTURE_MULTIPLE) + i);
|
||||
}
|
||||
|
||||
return reinterpret_cast<T&>(temp);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Texture reference definitions
|
||||
template <typename T>
|
||||
template <int UNIQUE_ID>
|
||||
typename IteratorTexRef<T>::template TexId<UNIQUE_ID>::TexRef IteratorTexRef<T>::template TexId<UNIQUE_ID>::ref = 0;
|
||||
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input wrapper for dereferencing array values through texture cache. Uses older Tesla/Fermi-style texture references.
|
||||
*
|
||||
* \par Overview
|
||||
* - TexRefInputIteratorTwraps a native device pointer of type <tt>ValueType*</tt>. References
|
||||
* to elements are to be loaded through texture cache.
|
||||
* - Can be used to load any data type from memory through texture cache.
|
||||
* - Can be manipulated and exchanged within and between host and device
|
||||
* functions, can only be constructed within host functions, and can only be
|
||||
* dereferenced within device functions.
|
||||
* - The \p UNIQUE_ID template parameter is used to statically name the underlying texture
|
||||
* reference. Only one TexRefInputIteratorTinstance can be bound at any given time for a
|
||||
* specific combination of (1) data type \p T, (2) \p UNIQUE_ID, (3) host
|
||||
* thread, and (4) compilation .o unit.
|
||||
* - With regard to nested/dynamic parallelism, TexRefInputIteratorTiterators may only be
|
||||
* created by the host thread and used by a top-level kernel (i.e. the one which is launched
|
||||
* from the host).
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
* - Compatible with CUDA toolkit v5.5 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p TexRefInputIteratorTto
|
||||
* dereference a device array of doubles through texture cache.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/tex_ref_input_iterator.cuh>
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* int num_items; // e.g., 7
|
||||
* double *d_in; // e.g., [8.0, 6.0, 7.0, 5.0, 3.0, 0.0, 9.0]
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::TexRefInputIterator<double, __LINE__> itr;
|
||||
* itr.BindTexture(d_in, sizeof(double) * num_items);
|
||||
* ...
|
||||
*
|
||||
* // Within device code:
|
||||
* printf("%f\n", itr[0]); // 8.0
|
||||
* printf("%f\n", itr[1]); // 6.0
|
||||
* printf("%f\n", itr[6]); // 9.0
|
||||
*
|
||||
* ...
|
||||
* itr.UnbindTexture();
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam T The value type of this iterator
|
||||
* \tparam UNIQUE_ID A globally-unique identifier (within the compilation unit) to name the underlying texture reference
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
int UNIQUE_ID,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class TexRefInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef TexRefInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef T value_type; ///< The type of the element the iterator can point to
|
||||
typedef T* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef T reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::device_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
difference_type tex_offset;
|
||||
|
||||
// Texture reference wrapper (old Tesla/Fermi-style textures)
|
||||
typedef typename IteratorTexRef<T>::template TexId<UNIQUE_ID> TexId;
|
||||
|
||||
public:
|
||||
/*
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ TexRefInputIterator()
|
||||
:
|
||||
ptr(NULL),
|
||||
tex_offset(0)
|
||||
{}
|
||||
*/
|
||||
/// Use this iterator to bind \p ptr with a texture reference
|
||||
template <typename QualifiedT>
|
||||
cudaError_t BindTexture(
|
||||
QualifiedT *ptr, ///< Native pointer to wrap that is aligned to cudaDeviceProp::textureAlignment
|
||||
size_t bytes = size_t(-1), ///< Number of bytes in the range
|
||||
size_t tex_offset = 0) ///< OffsetT (in items) from \p ptr denoting the position of the iterator
|
||||
{
|
||||
this->ptr = const_cast<typename RemoveQualifiers<QualifiedT>::Type *>(ptr);
|
||||
size_t offset;
|
||||
cudaError_t retval = TexId::BindTexture(this->ptr + tex_offset, offset);
|
||||
this->tex_offset = (difference_type) (offset / sizeof(QualifiedT));
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Unbind this iterator from its texture reference
|
||||
cudaError_t UnbindTexture()
|
||||
{
|
||||
return TexId::UnbindTexture();
|
||||
}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
tex_offset++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
tex_offset++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
#if (CUB_PTX_ARCH == 0)
|
||||
// Simply dereference the pointer on the host
|
||||
return ptr[tex_offset];
|
||||
#else
|
||||
// Use the texture reference
|
||||
return TexId::Fetch(tex_offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval;
|
||||
retval.ptr = ptr;
|
||||
retval.tex_offset = tex_offset + n;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
tex_offset += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval;
|
||||
retval.ptr = ptr;
|
||||
retval.tex_offset = tex_offset - n;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
tex_offset -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return tex_offset - other.tex_offset;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
self_type offset = (*this) + n;
|
||||
return *offset;
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &(*(*this));
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return ((ptr == rhs.ptr) && (tex_offset == rhs.tex_offset));
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return ((ptr != rhs.ptr) || (tex_offset != rhs.tex_offset));
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
||||
#endif // CUDA_VERSION
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Random-access iterator types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
#include "../thread/thread_load.cuh"
|
||||
#include "../thread/thread_store.cuh"
|
||||
#include "../util_device.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// This iterator is compatible with Thrust API 1.7 and newer
|
||||
#include <thrust/iterator/iterator_facade.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIterator
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief A random-access input wrapper for transforming dereferenced values.
|
||||
*
|
||||
* \par Overview
|
||||
* - TransformInputIteratorTwraps a unary conversion functor of type \p
|
||||
* ConversionOp and a random-access input iterator of type <tt>InputIteratorT</tt>,
|
||||
* using the former to produce references of type \p ValueType from the latter.
|
||||
* - Can be used with any data type.
|
||||
* - Can be constructed, manipulated, and exchanged within and between host and device
|
||||
* functions. Wrapped host memory can only be dereferenced on the host, and wrapped
|
||||
* device memory can only be dereferenced on the device.
|
||||
* - Compatible with Thrust API v1.7 or newer.
|
||||
*
|
||||
* \par Snippet
|
||||
* The code snippet below illustrates the use of \p TransformInputIteratorTto
|
||||
* dereference an array of integers, tripling the values and converting them to doubles.
|
||||
* \par
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/iterator/transform_input_iterator.cuh>
|
||||
*
|
||||
* // Functor for tripling integer values and converting to doubles
|
||||
* struct TripleDoubler
|
||||
* {
|
||||
* __host__ __device__ __forceinline__
|
||||
* double operator()(const int &a) const {
|
||||
* return double(a * 2);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* // Declare, allocate, and initialize a device array
|
||||
* int *d_in; // e.g., [8, 6, 7, 5, 3, 0, 9]
|
||||
* TripleDoubler conversion_op;
|
||||
*
|
||||
* // Create an iterator wrapper
|
||||
* cub::TransformInputIterator<double, TripleDoubler, int*> itr(d_in, conversion_op);
|
||||
*
|
||||
* // Within device code:
|
||||
* printf("%f\n", itr[0]); // 24.0
|
||||
* printf("%f\n", itr[1]); // 18.0
|
||||
* printf("%f\n", itr[6]); // 27.0
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam ValueType The value type of this iterator
|
||||
* \tparam ConversionOp Unary functor type for mapping objects of type \p InputType to type \p ValueType. Must have member <tt>ValueType operator()(const InputType &datum)</tt>.
|
||||
* \tparam InputIteratorT The type of the wrapped input iterator
|
||||
* \tparam OffsetT The difference type of this iterator (Default: \p ptrdiff_t)
|
||||
*
|
||||
*/
|
||||
template <
|
||||
typename ValueType,
|
||||
typename ConversionOp,
|
||||
typename InputIteratorT,
|
||||
typename OffsetT = ptrdiff_t>
|
||||
class TransformInputIterator
|
||||
{
|
||||
public:
|
||||
|
||||
// Required iterator traits
|
||||
typedef TransformInputIterator self_type; ///< My own type
|
||||
typedef OffsetT difference_type; ///< Type to express the result of subtracting one iterator from another
|
||||
typedef ValueType value_type; ///< The type of the element the iterator can point to
|
||||
typedef ValueType* pointer; ///< The type of a pointer to an element the iterator can point to
|
||||
typedef ValueType reference; ///< The type of a reference to an element the iterator can point to
|
||||
|
||||
#if (THRUST_VERSION >= 100700)
|
||||
// Use Thrust's iterator categories so we can use these iterators in Thrust 1.7 (or newer) methods
|
||||
typedef typename thrust::detail::iterator_facade_category<
|
||||
thrust::any_system_tag,
|
||||
thrust::random_access_traversal_tag,
|
||||
value_type,
|
||||
reference
|
||||
>::type iterator_category; ///< The iterator category
|
||||
#else
|
||||
typedef std::random_access_iterator_tag iterator_category; ///< The iterator category
|
||||
#endif // THRUST_VERSION
|
||||
|
||||
private:
|
||||
|
||||
ConversionOp conversion_op;
|
||||
InputIteratorT input_itr;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ TransformInputIterator(
|
||||
InputIteratorT input_itr, ///< Input iterator to wrap
|
||||
ConversionOp conversion_op) ///< Conversion functor to wrap
|
||||
:
|
||||
conversion_op(conversion_op),
|
||||
input_itr(input_itr)
|
||||
{}
|
||||
|
||||
/// Postfix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++(int)
|
||||
{
|
||||
self_type retval = *this;
|
||||
input_itr++;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Prefix increment
|
||||
__host__ __device__ __forceinline__ self_type operator++()
|
||||
{
|
||||
input_itr++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Indirection
|
||||
__host__ __device__ __forceinline__ reference operator*() const
|
||||
{
|
||||
return conversion_op(*input_itr);
|
||||
}
|
||||
|
||||
/// Addition
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator+(Distance n) const
|
||||
{
|
||||
self_type retval(input_itr + n, conversion_op);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Addition assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator+=(Distance n)
|
||||
{
|
||||
input_itr += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtraction
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type operator-(Distance n) const
|
||||
{
|
||||
self_type retval(input_itr - n, conversion_op);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Subtraction assignment
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ self_type& operator-=(Distance n)
|
||||
{
|
||||
input_itr -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Distance
|
||||
__host__ __device__ __forceinline__ difference_type operator-(self_type other) const
|
||||
{
|
||||
return input_itr - other.input_itr;
|
||||
}
|
||||
|
||||
/// Array subscript
|
||||
template <typename Distance>
|
||||
__host__ __device__ __forceinline__ reference operator[](Distance n) const
|
||||
{
|
||||
return conversion_op(input_itr[n]);
|
||||
}
|
||||
|
||||
/// Structure dereference
|
||||
__host__ __device__ __forceinline__ pointer operator->()
|
||||
{
|
||||
return &conversion_op(*input_itr);
|
||||
}
|
||||
|
||||
/// Equal to
|
||||
__host__ __device__ __forceinline__ bool operator==(const self_type& rhs)
|
||||
{
|
||||
return (input_itr == rhs.input_itr);
|
||||
}
|
||||
|
||||
/// Not equal to
|
||||
__host__ __device__ __forceinline__ bool operator!=(const self_type& rhs)
|
||||
{
|
||||
return (input_itr != rhs.input_itr);
|
||||
}
|
||||
|
||||
/// ostream operator
|
||||
friend std::ostream& operator<<(std::ostream& os, const self_type& itr)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilIterator
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,454 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread utilities for reading memory using PTX cache modifiers.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "../util_ptx.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilIo
|
||||
* @{
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tags and constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* \brief Enumeration of cache modifiers for memory load operations.
|
||||
*/
|
||||
enum CacheLoadModifier
|
||||
{
|
||||
LOAD_DEFAULT, ///< Default (no modifier)
|
||||
LOAD_CA, ///< Cache at all levels
|
||||
LOAD_CG, ///< Cache at global level
|
||||
LOAD_CS, ///< Cache streaming (likely to be accessed once)
|
||||
LOAD_CV, ///< Cache as volatile (including cached system lines)
|
||||
LOAD_LDG, ///< Cache as texture
|
||||
LOAD_VOLATILE, ///< Volatile (any memory space)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \name Thread I/O (cache modified)
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Thread utility for reading memory using cub::CacheLoadModifier cache modifiers. Can be used to load any data type.
|
||||
*
|
||||
* \par Example
|
||||
* \code
|
||||
* #include <cub/cub.cuh> // or equivalently <cub/thread/thread_load.cuh>
|
||||
*
|
||||
* // 32-bit load using cache-global modifier:
|
||||
* int *d_in;
|
||||
* int val = cub::ThreadLoad<cub::LOAD_CA>(d_in + threadIdx.x);
|
||||
*
|
||||
* // 16-bit load using default modifier
|
||||
* short *d_in;
|
||||
* short val = cub::ThreadLoad<cub::LOAD_DEFAULT>(d_in + threadIdx.x);
|
||||
*
|
||||
* // 256-bit load using cache-volatile modifier
|
||||
* double4 *d_in;
|
||||
* double4 val = cub::ThreadLoad<cub::LOAD_CV>(d_in + threadIdx.x);
|
||||
*
|
||||
* // 96-bit load using cache-streaming modifier
|
||||
* struct TestFoo { bool a; short b; };
|
||||
* TestFoo *d_struct;
|
||||
* TestFoo val = cub::ThreadLoad<cub::LOAD_CS>(d_in + threadIdx.x);
|
||||
* \endcode
|
||||
*
|
||||
* \tparam MODIFIER <b>[inferred]</b> CacheLoadModifier enumeration
|
||||
* \tparam InputIteratorT <b>[inferred]</b> Input iterator type \iterator
|
||||
*/
|
||||
template <
|
||||
CacheLoadModifier MODIFIER,
|
||||
typename InputIteratorT>
|
||||
__device__ __forceinline__ typename std::iterator_traits<InputIteratorT>::value_type ThreadLoad(InputIteratorT itr);
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Do not document
|
||||
|
||||
|
||||
/// Helper structure for templated load iteration (inductive case)
|
||||
template <int COUNT, int MAX>
|
||||
struct IterateThreadLoad
|
||||
{
|
||||
template <CacheLoadModifier MODIFIER, typename T>
|
||||
static __device__ __forceinline__ void Load(T const *ptr, T *vals)
|
||||
{
|
||||
vals[COUNT] = ThreadLoad<MODIFIER>(ptr + COUNT);
|
||||
IterateThreadLoad<COUNT + 1, MAX>::template Load<MODIFIER>(ptr, vals);
|
||||
}
|
||||
|
||||
template <typename InputIteratorT, typename T>
|
||||
static __device__ __forceinline__ void Dereference(InputIteratorT itr, T *vals)
|
||||
{
|
||||
vals[COUNT] = itr[COUNT];
|
||||
IterateThreadLoad<COUNT + 1, MAX>::Dereference(itr, vals);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Helper structure for templated load iteration (termination case)
|
||||
template <int MAX>
|
||||
struct IterateThreadLoad<MAX, MAX>
|
||||
{
|
||||
template <CacheLoadModifier MODIFIER, typename T>
|
||||
static __device__ __forceinline__ void Load(T const * /*ptr*/, T * /*vals*/) {}
|
||||
|
||||
template <typename InputIteratorT, typename T>
|
||||
static __device__ __forceinline__ void Dereference(InputIteratorT /*itr*/, T * /*vals*/) {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Define a uint4 (16B) ThreadLoad specialization for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_16(cub_modifier, ptx_modifier) \
|
||||
template<> \
|
||||
__device__ __forceinline__ uint4 ThreadLoad<cub_modifier, uint4 const *>(uint4 const *ptr) \
|
||||
{ \
|
||||
uint4 retval; \
|
||||
asm volatile ("ld."#ptx_modifier".v4.u32 {%0, %1, %2, %3}, [%4];" : \
|
||||
"=r"(retval.x), \
|
||||
"=r"(retval.y), \
|
||||
"=r"(retval.z), \
|
||||
"=r"(retval.w) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
} \
|
||||
template<> \
|
||||
__device__ __forceinline__ ulonglong2 ThreadLoad<cub_modifier, ulonglong2 const *>(ulonglong2 const *ptr) \
|
||||
{ \
|
||||
ulonglong2 retval; \
|
||||
asm volatile ("ld."#ptx_modifier".v2.u64 {%0, %1}, [%2];" : \
|
||||
"=l"(retval.x), \
|
||||
"=l"(retval.y) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a uint2 (8B) ThreadLoad specialization for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_8(cub_modifier, ptx_modifier) \
|
||||
template<> \
|
||||
__device__ __forceinline__ ushort4 ThreadLoad<cub_modifier, ushort4 const *>(ushort4 const *ptr) \
|
||||
{ \
|
||||
ushort4 retval; \
|
||||
asm volatile ("ld."#ptx_modifier".v4.u16 {%0, %1, %2, %3}, [%4];" : \
|
||||
"=h"(retval.x), \
|
||||
"=h"(retval.y), \
|
||||
"=h"(retval.z), \
|
||||
"=h"(retval.w) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
} \
|
||||
template<> \
|
||||
__device__ __forceinline__ uint2 ThreadLoad<cub_modifier, uint2 const *>(uint2 const *ptr) \
|
||||
{ \
|
||||
uint2 retval; \
|
||||
asm volatile ("ld."#ptx_modifier".v2.u32 {%0, %1}, [%2];" : \
|
||||
"=r"(retval.x), \
|
||||
"=r"(retval.y) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
} \
|
||||
template<> \
|
||||
__device__ __forceinline__ unsigned long long ThreadLoad<cub_modifier, unsigned long long const *>(unsigned long long const *ptr) \
|
||||
{ \
|
||||
unsigned long long retval; \
|
||||
asm volatile ("ld."#ptx_modifier".u64 %0, [%1];" : \
|
||||
"=l"(retval) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a uint (4B) ThreadLoad specialization for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_4(cub_modifier, ptx_modifier) \
|
||||
template<> \
|
||||
__device__ __forceinline__ unsigned int ThreadLoad<cub_modifier, unsigned int const *>(unsigned int const *ptr) \
|
||||
{ \
|
||||
unsigned int retval; \
|
||||
asm volatile ("ld."#ptx_modifier".u32 %0, [%1];" : \
|
||||
"=r"(retval) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define a unsigned short (2B) ThreadLoad specialization for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_2(cub_modifier, ptx_modifier) \
|
||||
template<> \
|
||||
__device__ __forceinline__ unsigned short ThreadLoad<cub_modifier, unsigned short const *>(unsigned short const *ptr) \
|
||||
{ \
|
||||
unsigned short retval; \
|
||||
asm volatile ("ld."#ptx_modifier".u16 %0, [%1];" : \
|
||||
"=h"(retval) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return retval; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define an unsigned char (1B) ThreadLoad specialization for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_1(cub_modifier, ptx_modifier) \
|
||||
template<> \
|
||||
__device__ __forceinline__ unsigned char ThreadLoad<cub_modifier, unsigned char const *>(unsigned char const *ptr) \
|
||||
{ \
|
||||
unsigned short retval; \
|
||||
asm volatile ( \
|
||||
"{" \
|
||||
" .reg .u8 datum;" \
|
||||
" ld."#ptx_modifier".u8 datum, [%1];" \
|
||||
" cvt.u16.u8 %0, datum;" \
|
||||
"}" : \
|
||||
"=h"(retval) : \
|
||||
_CUB_ASM_PTR_(ptr)); \
|
||||
return (unsigned char) retval; \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define powers-of-two ThreadLoad specializations for the given Cache load modifier
|
||||
*/
|
||||
#define _CUB_LOAD_ALL(cub_modifier, ptx_modifier) \
|
||||
_CUB_LOAD_16(cub_modifier, ptx_modifier) \
|
||||
_CUB_LOAD_8(cub_modifier, ptx_modifier) \
|
||||
_CUB_LOAD_4(cub_modifier, ptx_modifier) \
|
||||
_CUB_LOAD_2(cub_modifier, ptx_modifier) \
|
||||
_CUB_LOAD_1(cub_modifier, ptx_modifier) \
|
||||
|
||||
|
||||
/**
|
||||
* Define powers-of-two ThreadLoad specializations for the various Cache load modifiers
|
||||
*/
|
||||
#if CUB_PTX_ARCH >= 200
|
||||
_CUB_LOAD_ALL(LOAD_CA, ca)
|
||||
_CUB_LOAD_ALL(LOAD_CG, cg)
|
||||
_CUB_LOAD_ALL(LOAD_CS, cs)
|
||||
_CUB_LOAD_ALL(LOAD_CV, cv)
|
||||
#else
|
||||
_CUB_LOAD_ALL(LOAD_CA, global)
|
||||
// Use volatile to ensure coherent reads when this PTX is JIT'd to run on newer architectures with L1
|
||||
_CUB_LOAD_ALL(LOAD_CG, volatile.global)
|
||||
_CUB_LOAD_ALL(LOAD_CS, global)
|
||||
_CUB_LOAD_ALL(LOAD_CV, volatile.global)
|
||||
#endif
|
||||
|
||||
#if CUB_PTX_ARCH >= 350
|
||||
_CUB_LOAD_ALL(LOAD_LDG, global.nc)
|
||||
#else
|
||||
_CUB_LOAD_ALL(LOAD_LDG, global)
|
||||
#endif
|
||||
|
||||
|
||||
// Macro cleanup
|
||||
#undef _CUB_LOAD_ALL
|
||||
#undef _CUB_LOAD_1
|
||||
#undef _CUB_LOAD_2
|
||||
#undef _CUB_LOAD_4
|
||||
#undef _CUB_LOAD_8
|
||||
#undef _CUB_LOAD_16
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for LOAD_DEFAULT modifier on iterator types
|
||||
*/
|
||||
template <typename InputIteratorT>
|
||||
__device__ __forceinline__ typename std::iterator_traits<InputIteratorT>::value_type ThreadLoad(
|
||||
InputIteratorT itr,
|
||||
Int2Type<LOAD_DEFAULT> /*modifier*/,
|
||||
Int2Type<false> /*is_pointer*/)
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for LOAD_DEFAULT modifier on pointer types
|
||||
*/
|
||||
template <typename T>
|
||||
__device__ __forceinline__ T ThreadLoad(
|
||||
T *ptr,
|
||||
Int2Type<LOAD_DEFAULT> /*modifier*/,
|
||||
Int2Type<true> /*is_pointer*/)
|
||||
{
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for LOAD_VOLATILE modifier on primitive pointer types
|
||||
*/
|
||||
template <typename T>
|
||||
__device__ __forceinline__ T ThreadLoadVolatilePointer(
|
||||
T *ptr,
|
||||
Int2Type<true> /*is_primitive*/)
|
||||
{
|
||||
T retval = *reinterpret_cast<volatile T*>(ptr);
|
||||
|
||||
#if (CUB_PTX_ARCH <= 130)
|
||||
if (sizeof(T) == 1) __threadfence_block();
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for LOAD_VOLATILE modifier on non-primitive pointer types
|
||||
*/
|
||||
template <typename T>
|
||||
__device__ __forceinline__ T ThreadLoadVolatilePointer(
|
||||
T *ptr,
|
||||
Int2Type<false> /*is_primitive*/)
|
||||
{
|
||||
|
||||
#if CUB_PTX_ARCH <= 130
|
||||
|
||||
T retval = *ptr;
|
||||
__threadfence_block();
|
||||
return retval;
|
||||
|
||||
#else
|
||||
|
||||
typedef typename UnitWord<T>::VolatileWord VolatileWord; // Word type for memcopying
|
||||
|
||||
const int VOLATILE_MULTIPLE = sizeof(T) / sizeof(VolatileWord);
|
||||
/*
|
||||
VolatileWord words[VOLATILE_MULTIPLE];
|
||||
|
||||
IterateThreadLoad<0, VOLATILE_MULTIPLE>::Dereference(
|
||||
reinterpret_cast<volatile VolatileWord*>(ptr),
|
||||
words);
|
||||
|
||||
return *reinterpret_cast<T*>(words);
|
||||
*/
|
||||
|
||||
T retval;
|
||||
VolatileWord *words = reinterpret_cast<VolatileWord*>(&retval);
|
||||
IterateThreadLoad<0, VOLATILE_MULTIPLE>::Dereference(
|
||||
reinterpret_cast<volatile VolatileWord*>(ptr),
|
||||
words);
|
||||
return retval;
|
||||
|
||||
#endif // CUB_PTX_ARCH <= 130
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for LOAD_VOLATILE modifier on pointer types
|
||||
*/
|
||||
template <typename T>
|
||||
__device__ __forceinline__ T ThreadLoad(
|
||||
T *ptr,
|
||||
Int2Type<LOAD_VOLATILE> /*modifier*/,
|
||||
Int2Type<true> /*is_pointer*/)
|
||||
{
|
||||
// Apply tags for partial-specialization
|
||||
return ThreadLoadVolatilePointer(ptr, Int2Type<Traits<T>::PRIMITIVE>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for generic modifiers on pointer types
|
||||
*/
|
||||
template <typename T, int MODIFIER>
|
||||
__device__ __forceinline__ T ThreadLoad(
|
||||
T const *ptr,
|
||||
Int2Type<MODIFIER> /*modifier*/,
|
||||
Int2Type<true> /*is_pointer*/)
|
||||
{
|
||||
typedef typename UnitWord<T>::DeviceWord DeviceWord;
|
||||
|
||||
const int DEVICE_MULTIPLE = sizeof(T) / sizeof(DeviceWord);
|
||||
|
||||
DeviceWord words[DEVICE_MULTIPLE];
|
||||
|
||||
IterateThreadLoad<0, DEVICE_MULTIPLE>::template Load<CacheLoadModifier(MODIFIER)>(
|
||||
reinterpret_cast<DeviceWord*>(const_cast<T*>(ptr)),
|
||||
words);
|
||||
|
||||
return *reinterpret_cast<T*>(words);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ThreadLoad definition for generic modifiers
|
||||
*/
|
||||
template <
|
||||
CacheLoadModifier MODIFIER,
|
||||
typename InputIteratorT>
|
||||
__device__ __forceinline__ typename std::iterator_traits<InputIteratorT>::value_type ThreadLoad(InputIteratorT itr)
|
||||
{
|
||||
// Apply tags for partial-specialization
|
||||
return ThreadLoad(
|
||||
itr,
|
||||
Int2Type<MODIFIER>(),
|
||||
Int2Type<IsPointer<InputIteratorT>::VALUE>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
|
||||
|
||||
/** @} */ // end group UtilIo
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Simple binary operator functor types
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* Simple functor operators
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_macro.cuh"
|
||||
#include "../util_type.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup UtilModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Default equality functor
|
||||
*/
|
||||
struct Equality
|
||||
{
|
||||
/// Boolean equality operator, returns <tt>(a == b)</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ bool operator()(const T &a, const T &b) const
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default inequality functor
|
||||
*/
|
||||
struct Inequality
|
||||
{
|
||||
/// Boolean inequality operator, returns <tt>(a != b)</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ bool operator()(const T &a, const T &b) const
|
||||
{
|
||||
return a != b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Inequality functor (wraps equality functor)
|
||||
*/
|
||||
template <typename EqualityOp>
|
||||
struct InequalityWrapper
|
||||
{
|
||||
/// Wrapped equality operator
|
||||
EqualityOp op;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
InequalityWrapper(EqualityOp op) : op(op) {}
|
||||
|
||||
/// Boolean inequality operator, returns <tt>(a != b)</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ bool operator()(const T &a, const T &b)
|
||||
{
|
||||
return !op(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default sum functor
|
||||
*/
|
||||
struct Sum
|
||||
{
|
||||
/// Boolean sum operator, returns <tt>a + b</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ T operator()(const T &a, const T &b) const
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default max functor
|
||||
*/
|
||||
struct Max
|
||||
{
|
||||
/// Boolean max operator, returns <tt>(a > b) ? a : b</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ T operator()(const T &a, const T &b) const
|
||||
{
|
||||
return CUB_MAX(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Arg max functor (keeps the value and offset of the first occurrence of the larger item)
|
||||
*/
|
||||
struct ArgMax
|
||||
{
|
||||
/// Boolean max operator, preferring the item having the smaller offset in case of ties
|
||||
template <typename T, typename OffsetT>
|
||||
__host__ __device__ __forceinline__ KeyValuePair<OffsetT, T> operator()(
|
||||
const KeyValuePair<OffsetT, T> &a,
|
||||
const KeyValuePair<OffsetT, T> &b) const
|
||||
{
|
||||
// Mooch BUG (device reduce argmax gk110 3.2 million random fp32)
|
||||
// return ((b.value > a.value) || ((a.value == b.value) && (b.key < a.key))) ? b : a;
|
||||
|
||||
if ((b.value > a.value) || ((a.value == b.value) && (b.key < a.key)))
|
||||
return b;
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default min functor
|
||||
*/
|
||||
struct Min
|
||||
{
|
||||
/// Boolean min operator, returns <tt>(a < b) ? a : b</tt>
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ T operator()(const T &a, const T &b) const
|
||||
{
|
||||
return CUB_MIN(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Arg min functor (keeps the value and offset of the first occurrence of the smallest item)
|
||||
*/
|
||||
struct ArgMin
|
||||
{
|
||||
/// Boolean min operator, preferring the item having the smaller offset in case of ties
|
||||
template <typename T, typename OffsetT>
|
||||
__host__ __device__ __forceinline__ KeyValuePair<OffsetT, T> operator()(
|
||||
const KeyValuePair<OffsetT, T> &a,
|
||||
const KeyValuePair<OffsetT, T> &b) const
|
||||
{
|
||||
// Mooch BUG (device reduce argmax gk110 3.2 million random fp32)
|
||||
// return ((b.value < a.value) || ((a.value == b.value) && (b.key < a.key))) ? b : a;
|
||||
|
||||
if ((b.value < a.value) || ((a.value == b.value) && (b.key < a.key)))
|
||||
return b;
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default cast functor
|
||||
*/
|
||||
template <typename B>
|
||||
struct Cast
|
||||
{
|
||||
/// Cast operator, returns <tt>(B) a</tt>
|
||||
template <typename A>
|
||||
__host__ __device__ __forceinline__ B operator()(const A &a) const
|
||||
{
|
||||
return (B) a;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Binary operator wrapper for switching non-commutative scan arguments
|
||||
*/
|
||||
template <typename ScanOp>
|
||||
class SwizzleScanOp
|
||||
{
|
||||
private:
|
||||
|
||||
/// Wrapped scan operator
|
||||
ScanOp scan_op;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__
|
||||
SwizzleScanOp(ScanOp scan_op) : scan_op(scan_op) {}
|
||||
|
||||
/// Switch the scan arguments
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__
|
||||
T operator()(const T &a, const T &b)
|
||||
{
|
||||
return scan_op(b, a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reduce-by-segment functor.
|
||||
*
|
||||
* Given two cub::KeyValuePair inputs \p a and \p b and a
|
||||
* binary associative combining operator \p <tt>f(const T &x, const T &y)</tt>,
|
||||
* an instance of this functor returns a cub::KeyValuePair whose \p key
|
||||
* field is <tt>a.key</tt> + <tt>a.key</tt>, and whose \p value field
|
||||
* is either b.value if b.key is non-zero, or f(a.value, b.value) otherwise.
|
||||
*
|
||||
* ReduceBySegmentOp is an associative, non-commutative binary combining operator
|
||||
* for input sequences of cub::KeyValuePair pairings. Such
|
||||
* sequences are typically used to represent a segmented set of values to be reduced
|
||||
* and a corresponding set of {0,1}-valued integer "head flags" demarcating the
|
||||
* first value of each segment.
|
||||
*
|
||||
*/
|
||||
template <typename ReductionOpT> ///< Binary reduction operator to apply to values
|
||||
struct ReduceBySegmentOp
|
||||
{
|
||||
/// Wrapped reduction operator
|
||||
ReductionOpT op;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ReduceBySegmentOp() {}
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ReduceBySegmentOp(ReductionOpT op) : op(op) {}
|
||||
|
||||
/// Scan operator
|
||||
template <typename KeyValuePairT> ///< KeyValuePair pairing of T (value) and OffsetT (head flag)
|
||||
__host__ __device__ __forceinline__ KeyValuePairT operator()(
|
||||
const KeyValuePairT &first, ///< First partial reduction
|
||||
const KeyValuePairT &second) ///< Second partial reduction
|
||||
{
|
||||
KeyValuePairT retval;
|
||||
retval.key = first.key + second.key;
|
||||
retval.value = (second.key) ?
|
||||
second.value : // The second partial reduction spans a segment reset, so it's value aggregate becomes the running aggregate
|
||||
op(first.value, second.value); // The second partial reduction does not span a reset, so accumulate both into the running aggregate
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename ReductionOpT> ///< Binary reduction operator to apply to values
|
||||
struct ReduceByKeyOp
|
||||
{
|
||||
/// Wrapped reduction operator
|
||||
ReductionOpT op;
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ReduceByKeyOp() {}
|
||||
|
||||
/// Constructor
|
||||
__host__ __device__ __forceinline__ ReduceByKeyOp(ReductionOpT op) : op(op) {}
|
||||
|
||||
/// Scan operator
|
||||
template <typename KeyValuePairT>
|
||||
__host__ __device__ __forceinline__ KeyValuePairT operator()(
|
||||
const KeyValuePairT &first, ///< First partial reduction
|
||||
const KeyValuePairT &second) ///< Second partial reduction
|
||||
{
|
||||
KeyValuePairT retval = second;
|
||||
|
||||
if (first.key == second.key)
|
||||
retval.value = op(first.value, retval.value);
|
||||
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** @} */ // end group UtilModule
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread utilities for sequential reduction over statically-sized array types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \name Sequential reduction over statically-sized array types
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T* input, ///< [in] Input array
|
||||
ReductionOp reduction_op, ///< [in] Binary reduction operator
|
||||
T prefix, ///< [in] Prefix to seed reduction with
|
||||
Int2Type<LENGTH> /*length*/)
|
||||
{
|
||||
T addend = *input;
|
||||
prefix = reduction_op(prefix, addend);
|
||||
|
||||
return ThreadReduce(input + 1, reduction_op, prefix, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T* /*input*/, ///< [in] Input array
|
||||
ReductionOp /*reduction_op*/, ///< [in] Binary reduction operator
|
||||
T prefix, ///< [in] Prefix to seed reduction with
|
||||
Int2Type<0> /*length*/)
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential reduction over \p LENGTH elements of the \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH LengthT of input array
|
||||
* \tparam T <b>[inferred]</b> The data type to be reduced.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary reduction operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T* input, ///< [in] Input array
|
||||
ReductionOp reduction_op, ///< [in] Binary reduction operator
|
||||
T prefix) ///< [in] Prefix to seed reduction with
|
||||
{
|
||||
return ThreadReduce(input, reduction_op, prefix, Int2Type<LENGTH>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential reduction over \p LENGTH elements of the \p input array. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH LengthT of input array
|
||||
* \tparam T <b>[inferred]</b> The data type to be reduced.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary reduction operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T* input, ///< [in] Input array
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction operator
|
||||
{
|
||||
T prefix = input[0];
|
||||
return ThreadReduce<LENGTH - 1>(input + 1, reduction_op, prefix);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential reduction over the statically-sized \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH <b>[inferred]</b> LengthT of \p input array
|
||||
* \tparam T <b>[inferred]</b> The data type to be reduced.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary reduction operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T (&input)[LENGTH], ///< [in] Input array
|
||||
ReductionOp reduction_op, ///< [in] Binary reduction operator
|
||||
T prefix) ///< [in] Prefix to seed reduction with
|
||||
{
|
||||
return ThreadReduce(input, reduction_op, prefix, Int2Type<LENGTH>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Serial reduction with the specified operator
|
||||
*
|
||||
* \tparam LENGTH <b>[inferred]</b> LengthT of \p input array
|
||||
* \tparam T <b>[inferred]</b> The data type to be reduced.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary reduction operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ReductionOp>
|
||||
__device__ __forceinline__ T ThreadReduce(
|
||||
T (&input)[LENGTH], ///< [in] Input array
|
||||
ReductionOp reduction_op) ///< [in] Binary reduction operator
|
||||
{
|
||||
return ThreadReduce<LENGTH>((T*) input, reduction_op);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
/** @} */ // end group UtilModule
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread utilities for sequential prefix scan over statically-sized array types
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../thread/thread_operators.cuh"
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
/**
|
||||
* \addtogroup UtilModule
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \name Sequential prefix scan over statically-sized array types
|
||||
* @{
|
||||
*/
|
||||
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanExclusive(
|
||||
T inclusive,
|
||||
T exclusive,
|
||||
T *input, ///< [in] Input array
|
||||
T *output, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
Int2Type<LENGTH> /*length*/)
|
||||
{
|
||||
T addend = *input;
|
||||
inclusive = scan_op(exclusive, addend);
|
||||
*output = exclusive;
|
||||
exclusive = inclusive;
|
||||
|
||||
return ThreadScanExclusive(inclusive, exclusive, input + 1, output + 1, scan_op, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanExclusive(
|
||||
T inclusive,
|
||||
T /*exclusive*/,
|
||||
T * /*input*/, ///< [in] Input array
|
||||
T * /*output*/, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp /*scan_op*/, ///< [in] Binary scan operator
|
||||
Int2Type<0> /*length*/)
|
||||
{
|
||||
return inclusive;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential exclusive prefix scan over \p LENGTH elements of the \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanExclusive(
|
||||
T *input, ///< [in] Input array
|
||||
T *output, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T prefix, ///< [in] Prefix to seed scan with
|
||||
bool apply_prefix = true) ///< [in] Whether or not the calling thread should apply its prefix. If not, the first output element is undefined. (Handy for preventing thread-0 from applying a prefix.)
|
||||
{
|
||||
T inclusive = input[0];
|
||||
if (apply_prefix)
|
||||
{
|
||||
inclusive = scan_op(prefix, inclusive);
|
||||
}
|
||||
output[0] = prefix;
|
||||
T exclusive = inclusive;
|
||||
|
||||
return ThreadScanExclusive(inclusive, exclusive, input + 1, output + 1, scan_op, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential exclusive prefix scan over the statically-sized \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH <b>[inferred]</b> LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanExclusive(
|
||||
T (&input)[LENGTH], ///< [in] Input array
|
||||
T (&output)[LENGTH], ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T prefix, ///< [in] Prefix to seed scan with
|
||||
bool apply_prefix = true) ///< [in] Whether or not the calling thread should apply its prefix. (Handy for preventing thread-0 from applying a prefix.)
|
||||
{
|
||||
return ThreadScanExclusive<LENGTH>((T*) input, (T*) output, scan_op, prefix, apply_prefix);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T inclusive,
|
||||
T *input, ///< [in] Input array
|
||||
T *output, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
Int2Type<LENGTH> /*length*/)
|
||||
{
|
||||
T addend = *input;
|
||||
inclusive = scan_op(inclusive, addend);
|
||||
output[0] = inclusive;
|
||||
|
||||
return ThreadScanInclusive(inclusive, input + 1, output + 1, scan_op, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T inclusive,
|
||||
T * /*input*/, ///< [in] Input array
|
||||
T * /*output*/, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp /*scan_op*/, ///< [in] Binary scan operator
|
||||
Int2Type<0> /*length*/)
|
||||
{
|
||||
return inclusive;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential inclusive prefix scan over \p LENGTH elements of the \p input array. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T *input, ///< [in] Input array
|
||||
T *output, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
T inclusive = input[0];
|
||||
output[0] = inclusive;
|
||||
|
||||
// Continue scan
|
||||
return ThreadScanInclusive(inclusive, input + 1, output + 1, scan_op, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential inclusive prefix scan over the statically-sized \p input array. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH <b>[inferred]</b> LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T (&input)[LENGTH], ///< [in] Input array
|
||||
T (&output)[LENGTH], ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op) ///< [in] Binary scan operator
|
||||
{
|
||||
return ThreadScanInclusive<LENGTH>((T*) input, (T*) output, scan_op);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential inclusive prefix scan over \p LENGTH elements of the \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T *input, ///< [in] Input array
|
||||
T *output, ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T prefix, ///< [in] Prefix to seed scan with
|
||||
bool apply_prefix = true) ///< [in] Whether or not the calling thread should apply its prefix. (Handy for preventing thread-0 from applying a prefix.)
|
||||
{
|
||||
T inclusive = input[0];
|
||||
if (apply_prefix)
|
||||
{
|
||||
inclusive = scan_op(prefix, inclusive);
|
||||
}
|
||||
output[0] = inclusive;
|
||||
|
||||
// Continue scan
|
||||
return ThreadScanInclusive(inclusive, input + 1, output + 1, scan_op, Int2Type<LENGTH - 1>());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a sequential inclusive prefix scan over the statically-sized \p input array, seeded with the specified \p prefix. The aggregate is returned.
|
||||
*
|
||||
* \tparam LENGTH <b>[inferred]</b> LengthT of \p input and \p output arrays
|
||||
* \tparam T <b>[inferred]</b> The data type to be scanned.
|
||||
* \tparam ScanOp <b>[inferred]</b> Binary scan operator type having member <tt>T operator()(const T &a, const T &b)</tt>
|
||||
*/
|
||||
template <
|
||||
int LENGTH,
|
||||
typename T,
|
||||
typename ScanOp>
|
||||
__device__ __forceinline__ T ThreadScanInclusive(
|
||||
T (&input)[LENGTH], ///< [in] Input array
|
||||
T (&output)[LENGTH], ///< [out] Output array (may be aliased to \p input)
|
||||
ScanOp scan_op, ///< [in] Binary scan operator
|
||||
T prefix, ///< [in] Prefix to seed scan with
|
||||
bool apply_prefix = true) ///< [in] Whether or not the calling thread should apply its prefix. (Handy for preventing thread-0 from applying a prefix.)
|
||||
{
|
||||
return ThreadScanInclusive<LENGTH>((T*) input, (T*) output, scan_op, prefix, apply_prefix);
|
||||
}
|
||||
|
||||
|
||||
//@} end member group
|
||||
|
||||
/** @} */ // end group UtilModule
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
* Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the NVIDIA CORPORATION nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Thread utilities for sequential search
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../util_namespace.cuh"
|
||||
|
||||
/// Optional outer namespace(s)
|
||||
CUB_NS_PREFIX
|
||||
|
||||
/// CUB namespace
|
||||
namespace cub {
|
||||
|
||||
|
||||
/**
|
||||
* Computes the begin offsets into A and B for the specific diagonal
|
||||
*/
|
||||
template <
|
||||
typename AIteratorT,
|
||||
typename BIteratorT,
|
||||
typename OffsetT,
|
||||
typename CoordinateT>
|
||||
__host__ __device__ __forceinline__ void MergePathSearch(
|
||||
OffsetT diagonal,
|
||||
AIteratorT a,
|
||||
BIteratorT b,
|
||||
OffsetT a_len,
|
||||
OffsetT b_len,
|
||||
CoordinateT& path_coordinate)
|
||||
{
|
||||
/// The value type of the input iterator
|
||||
typedef typename std::iterator_traits<AIteratorT>::value_type T;
|
||||
|
||||
OffsetT split_min = CUB_MAX(diagonal - b_len, 0);
|
||||
OffsetT split_max = CUB_MIN(diagonal, a_len);
|
||||
|
||||
while (split_min < split_max)
|
||||
{
|
||||
OffsetT split_pivot = (split_min + split_max) >> 1;
|
||||
if (a[split_pivot] <= b[diagonal - split_pivot - 1])
|
||||
{
|
||||
// Move candidate split range up A, down B
|
||||
split_min = split_pivot + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move candidate split range up B, down A
|
||||
split_max = split_pivot;
|
||||
}
|
||||
}
|
||||
|
||||
path_coordinate.x = CUB_MIN(split_min, a_len);
|
||||
path_coordinate.y = diagonal - split_min;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Returns the offset of the first value within \p input which does not compare less than \p val
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OffsetT,
|
||||
typename T>
|
||||
__device__ __forceinline__ OffsetT LowerBound(
|
||||
InputIteratorT input, ///< [in] Input sequence
|
||||
OffsetT num_items, ///< [in] Input sequence length
|
||||
T val) ///< [in] Search key
|
||||
{
|
||||
OffsetT retval = 0;
|
||||
while (num_items > 0)
|
||||
{
|
||||
OffsetT half = num_items >> 1;
|
||||
if (input[retval + half] < val)
|
||||
{
|
||||
retval = retval + (half + 1);
|
||||
num_items = num_items - (half + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
num_items = half;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Returns the offset of the first value within \p input which compares greater than \p val
|
||||
*/
|
||||
template <
|
||||
typename InputIteratorT,
|
||||
typename OffsetT,
|
||||
typename T>
|
||||
__device__ __forceinline__ OffsetT UpperBound(
|
||||
InputIteratorT input, ///< [in] Input sequence
|
||||
OffsetT num_items, ///< [in] Input sequence length
|
||||
T val) ///< [in] Search key
|
||||
{
|
||||
OffsetT retval = 0;
|
||||
while (num_items > 0)
|
||||
{
|
||||
OffsetT half = num_items >> 1;
|
||||
if (val < input[retval + half])
|
||||
{
|
||||
num_items = half;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = retval + (half + 1);
|
||||
num_items = num_items - (half + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // CUB namespace
|
||||
CUB_NS_POSTFIX // Optional outer namespace(s)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue