From c9a8d85b22f205a75cdb341c9e7c0123fa4d4be7 Mon Sep 17 00:00:00 2001 From: Maya Date: Tue, 17 Sep 2024 22:45:14 -0400 Subject: [PATCH 1/2] code :) --- ...e (ms) vs Block Size (lower is better).png | Bin 0 -> 17104 bytes ...e (ms) vs Block Size (lower is better).png | Bin 0 -> 21773 bytes ...ms) vs Element Count (lower is better).png | Bin 0 -> 27112 bytes ...ms) vs Element Count (lower is better).png | Bin 0 -> 30227 bytes src/main.cpp | 17 +- stream_compaction/common.cu | 22 +- stream_compaction/cpu.cu | 77 ++++- stream_compaction/efficient.cu | 308 +++++++++++++++++- stream_compaction/naive.cu | 66 +++- stream_compaction/thrust.cu | 16 +- 10 files changed, 460 insertions(+), 46 deletions(-) create mode 100644 img/Efficient GPU - Time (ms) vs Block Size (lower is better).png create mode 100644 img/Naive GPU - Time (ms) vs Block Size (lower is better).png create mode 100644 img/Stream Compaction - Time (ms) vs Element Count (lower is better).png create mode 100644 img/Various Scans - Time (ms) vs Element Count (lower is better).png diff --git a/img/Efficient GPU - Time (ms) vs Block Size (lower is better).png b/img/Efficient GPU - Time (ms) vs Block Size (lower is better).png new file mode 100644 index 0000000000000000000000000000000000000000..dbdb631beae37d9d6fd5fd375aeeb0b0d489527a GIT binary patch literal 17104 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 VXu`6zcWhP4N1q&8tk}>?clOR;_B{ zjzVy9{HFP%^}ZILq=g>|_ZmG%+s$ik#-2LerGrd0`n0-?%y)-S?l(;1>Q5#cy!+B+ zLfNlfVpn|IHq_$jlkx0>I$NyM-kKhcBGI6F?Q4U5-ltDbl;(V*lNaEo32mG`m01vY zG2UQ!yvA7uEC9+Y)Gd9gw3r!duqM2KfNha=B?%R4ig(ZpO$x$U*S#~gOiu;v#@Y4d zYu&I9q9W6zf_n!G4HSReqhJtLT?_52>h=T3QT zd9QWH(gT!=pq8v!BNRmue$Xg5B|^_^R(dQ>)TnXcw~kZH7gBFf&F48dm!2v2hRpbj zbTr!?ExmpFiCWjJ?9KrDKvizTYE>mMm!*%Qd2e((UF!kqb@SpZI^cC82{X98{my@itdOvpip=rxIDmGG3`=&XiD+pqOFU!oddp&fA7mtHKggi=*B%le`6rr@PNh_ew z=Q)eFI9j*#w#CO!V^!A2$*tOidN=D=ved$bptjQ(54^`D+(-NrD%$E_wRQd9H)wV_KM<$3D4goL1{Y0jYBHAU29>-%Q=Ym+T@LO>fhd?%(o_t z#8biN6XrMTqv#yxeEr3Xk6zm83?9$fw~L2I^Gyn+su-`&*cUyp6PdP(JiM<^8n^0E z#Th20WQ9vK^fW9%)Dl)HRIf+!ib zwqCztTCjJo5A!0u$>uK(8aG#>DUMjI@VgZ?4+^?vni4?@;pa83*R}R#EB!GSYdNN# z(>Vkyi9U$>z+mrp#lN1#93{OXGhU5PS)>@E%PAnV$^RA+ijnS^_YJONctK}V6Yfr! z`MytYH$8Y4a?;$z+n)5ef2;$a`E9O-Mm4ICcTd|SoI7GJQ!#36+RR_+i?H5%ftn~` zk@;vrwr6E0TVHmS8jdm>dBs4_I|r3c?zV7)3hFy@Zme2x6P!EZAm2iQlY>uFMO`}* zzroWz7}(BprS7xt_ZSDKt4-+is z9oT1ee8ft94FXy7@#@HjDd1p(j~t^5?@jsar+9Qh=?{4QaORJ0&c)r}00h;R2fvr{ z-8>WU*cvXst8QD)GiYU}?McnZZ*_WAfU}|jXU&voG8J=QzcF`^dEJf)qQBOKUopuE zkM7DX3sN%fD1}xnusk!$@tI!k`EDNpnG#+Dau@z?GUkV#Ck~|0`<$PSztD#t`p!*I za*R3wqS05rHh{nTVgIh36XQx2k2c_Y(Sp$MnedvurTo!pLeb(Gmasg9hhNP zVx(Q0vM4D^6mP{)x^qGQaQPk+J%^Hm2IyKCda0TD{;Cy%Ex6poh zI0}Pv8DV3gsxfkXr-Q`wFR!e6w0U0_^89&qUs-J;bqqJI(AoZdzCF6|&27W-M%a!d z@=biJdQp~c6*DArzolA*US*D!Sw00?`8l_c6qc9NHpU!udya#*ROW6~QyV#i9kZ${ zDqb$fEkT9&nbE*&ryO_8Q?n~8;aR@Lg8+LtV% z?8cqc{j7B}uDjfGl?9z%B)-bsT@oN>VzPb%OzdE-=Ejr~nDdcOCN=zGOY zW4tIw=5r#@>M;xKe3kb|P4q@%N%Zm6%2y-mrZDjX|5lpVp?mgo5z>2{YWt7Jh1s^c z^W>_swmnk2;dGr+$YY(H{Ng!AAg;}Kolh>w@5k0K+P9H37{cg{lc=#RU424Cl)k%%&^ua?_i;0Y!-D?qA|k4xGps3@~-l9#bwvE zey-tNlDNpWkXdYPgGZa+34U=_05M%%5xgJ4Td^tZji3$bysVPMXZBWMrhgM|EHE>Wrv)r@-O#unA7E<(Tv$f*%i07^HqY<);Dz)DTGRXxlS0g z(=7IIT0cR2+xN3i?W`ws1@!HyQT7>lbi!!|F!-jQd$iv@CXd zcHwj;aZbu5@%355AWA>#Ir~u9CrsdX((6UKoH|7}wnq@DMLe9`&_=_sD)4qoIKgOB zNc&{3gIftUJ)`Kbg4SpW+=tt9EQA-2_kQaduAdr@>4anZ<1TXaHp(^&k!@OhA#m-d zK=W1J4{?v4*?ofx*uj3zolo})d_Gf2x8V^^!3JjVTkmT=4ff8yKIb#NMYSBI zA>7mPd%oSsM)-nw?DrD(RUOeoz5aN*C8Se=x4F{^@x;ziOJzKan!}jauV>b)8E)S% z%`z?+e^l1A%R@V!*km5GA6nJQO@D-UP<)*jHe8kG%QSN_ufEel93c=raj9SXn&*ff zBX8?r<5#R1|4bZ*;+diNER?kxK|K8M4Z#FMQy=@}VF$w(KJNrmTWi-H5CI)sKnR-k z47^}M@4IlF`Nu)8vrJ@!-p6ATA{Ocnw)hKep)LL-m#LWi&d*!ij2-GCn%S(K}n~GP6(qpQ9PJ#hVnAm9=DJL*I|`5XDopT_noAl**=?AsfMi^J;l_ zoz-|X-EN444U&8f#VO#tpyaBah0zjk&_@s4y>{s*ki;w}**B))=Gyu?VmUd$;HkuJ zc@n?uy%%>!9|2W65{7r87qUx^Z!~Qs@969-&u;7Nk5BY?MdaVI=ozS`Mmq9-w1^r~ zB-~CO^mHXtp|b^zw=Qv~Gyo0k%2nLSS+nplT zDrA`uHVowL%0Kq8Ra3nST}d3jU5Sh52^!;J6F3T;uU{L2`YCTObY-lqy)th%tse)f z`<5!7Nrda_U`TX$O5e%m)*>1~*NILM8yW!Rt$v)F#sAQZ-1BUYVs!z^C>xM5CBoq* z${uum8w5!b{GhI`UV!iS_`Ejv;xbd!?H`E(iuFPS*tq^zf|jbP$p?!gua*A1!NzA^ zlrzG%eak-PFJHc&p9QEH>Q0q}M?sKOaVzD;V+_fQ*_IG~r}^)>2p_J}a0{P$=pH)6 zARQH;b4)n}+yQ zd*y|PB2z=@c;9bj&!UqSG4pgpR1tvGG$>71$P##^HUm;Ai(phSaPDwsFwa$m-kIiL zE1)Kyz1_lQdXn8$d{3>a&U@c(bxe%%$ZO!U7VARk90s4+O)8a&PzgW!0DWHxt`|qt zd)A5rS({4a;Pq9DY^PBRV=%OKo|*>n20!1jk%IGHw8gcmaT` zr4Fr2MQjJX#G`=QNTEsmzOK^a2pz#K!%l&myUhRMpWm9 zr3xY4ABgk-4`5gXQXde6?Hs40ErgvKkw|_=swi#_So$JbIu)7TKpNX8m@)}h@ExQ& z<)v07RLhx$xQef8;M(=>r@>4Y(UCZ)FJrjR&A7-&NwaTZ*T*mc2E{L;sLsx}503LR z;~pw4=v*!jTXZ&`F=Z|?{ye9-x9d=bGHD8=>Jx#qx3{x?JNIOkeV;_pcC@M-wV-(O zXcQ#us;xEDx;3eMvkDtFMGsf&&XsjOQj<{QRZxF3SV~ePX4OTIR>Aj^U z3Rw*4xBt=D;4oGhA?CUg(rZ za7BvYm-qcxdVZZsH*1Y$ zVR9ax-hMQbl?`A86vz;N*M}3h}h-cKM&XVw>{o;PzEOkIbMG!nR=ACeB^i+gs zxC;CDqwn%D^;^~!em85a5XdrV(Lkjgpxt%YLslt*v_n(7N(%MH)y**veW zIvkWwM+>njmA*$_f!kL^#2qUJJ<#V`BQT@udyL!acw7yum)(q8L8D&P%p)C~a1#8w za*pBzu<@HoCdvrVM5#utf)Sk%Dj#l{tPcWq9c&5gn)%uPZrAWjHJEplJ1eYDE9PqE zZ>TiZXNE`5n~|^(PPfL60RrhZPsT{}C(DhdklM2ir^I5(E)j#AfmgBQ zpcHC)V@ftvafC*j+2nl$%!3)=Dy4)WV=i9M@vSc@d7kuaFJbEL59%Vn$s4zfhDV&8!R#ce945~@X@6)OAIu7TJVK73rqbyg8j%*{7=FM|P z9X!Tl!x^qmt$0pTe`u(w zO4FOOeZ)s8?#Fd7jR*QWp|5!nN!&$BCNATMn~$Gywbi34c!%1q0pdmBO@1#HoKB`?PkJ6bWg z>QUKry1l5OtINI+%jQ@4vWwn&qPAjpl>78hD`s~@_SfEhd^ej&0h(T+q1dwY12UN$ zQ+aUu>$Hy>#);+54m&q=<0se)(;~ikR66~7F@&0}<@AUm85OGl9IkkmJ2~eV!|Qkb zv{<+F>k0z*<1tzlNP_ZYLTP7r%h4cC9?uf*^_sSo7F#|{QVlZUHm0(iwXr@%)ul(R{NhN(lmr0esX zxK&m57Y&u6NF!RTbnDbl@13Qz@XA~^Hp1y15GBq0use-w9w@B4Uu*dXAWyL#Mds-W zK?O$}ysHG1KSUddQWwY-&xI7iY)=cYeX4ef8xggPyX6tUV@kYLL{wBFRsn6! z`kf4V?N7KH^c~!)7GK^(f{&L~29Y@kUwy-iRzpQ89uoz6k5~F~CjoKf)eq+_Jz9mHQnCG%>yOr`KFw%w^Mbn7PjPT?4BF(lzV+@paO!?TMy-JS zxhaY*p7{KZgfn&cb1@d!(kU~x0x+JxG%m|SV%47dDtp`R)~o|>CH31{4hIF5?w6CD_e03GHowREvbk2vb; zSH4Q9&eYNl$OvW!=$3fWR4^@Mh$Rgy0 z7IX~9A2Zhb$Bv2yoi4{xQgJ;@^eQ3Q*fghFKj7ju_q@ZwA)g#$ewvX_(@_XwuTs^! z-9(%*Bb_x=@43B%UqtB0)_p_s&W=qLyRlb!rHwx|2TEfge0(tj5E=L<{`UW?uW=4! zgH5kVmjg(dJ|)@JhA{aaJQwxa$rZ2#y2>sDY?Vgr1Fc-Z`jFB;ZC_sVOhN4C%ks3Nl(eF*9=U*30J?hOLedb5uoc*5U<#V~L;gZcO! z41_oUv#|yH9?dZu^i>Bv5D?Hh@&jT*?RK}AS>o9&!?lPviX|s|Rj8i83zxrd?Du^Q zy1bkg%v6>=0r0suE|IjHiXVTwWglim5B&OxI2BLRXuW6olB{c&z|ha#At*GRcffVX z$l{opJZNduruc(Z;sXwjkV@OJw?H5H%5M)%@1^53oesf87{rIyEdZ1Vmwjj zo&&_TR(=YFKCRnL68~Cc-npbYTJ#h%bk=Ik)32~jx2K&F_>OLTZ%!YRo&O}z`yH7- zUR9+lgY!Na+(Z)p^Ixx^1~Bk_5i!~No%=)!QW63YMe%_3>A29yIZ*PQd&s>g8c1^4u;>47>HkD2b!OdB+~R1aQ;Z zOGyAYMI%HV7nG@7J?a75asvGngT4Zs}S7 zwPHhZ%%aBn_;So-c3!Myij8LQGgk&lm7VI_RE2hZTT7byrp7}3|FX|B*9M5Qhb3%*7ZCLf?cbU@c z5gW2yb%;!TAE(??VsMr@+aP1`zl9S8;Ox&dsbwHXyoW%`_GK$m%g6NtdGX3v6)!+< zsl$1Uasi0tQx4TS3FnR(!63BLT^k3_^B<4c^)DRzXzrIY9C~Si@JAt$mj4!#{8v_8 zO3;Bx3uUN5F3@{!J!Ml#%@o_Hf1GWFBWIA1hJ*0_>ef>_F|P*dgK0q{A;z8-)B-n` zGHx1>v#Yk(S;`!SrOn>{3op*ldgJtDqd#AV_pn{X_o@4oj}Rl+cRbMz$fz}LU%Pfn zIQFf@eY<6rxS&0QcaD)jGG`5Xf43gcA3`!VY%iF4Erc@YmkK?Jt&sSS0RVViyEkgH zTv#=k4ls(2AtjQpFAgGs)U5jue1km1(<~tyn9HF$7vefx>P5r2-1nM?AJ(scbQ65_ zjM+W;8Ja>wB^tiQTXE%gF#qD81|tQbyjg*$#C*IYo6bzr2@C;o>!|}J5itL#h=-xemys8}TK&fp|TQvNR?xVAC6|6K0N zX*~tP+|KFA)MSlWum3%zvq*FV*tfCh(7$WMI(}SkSY>Gx} zY_V?hGOYZ|n$HvkBQ*gcs|;s9w~eFCrq+-{9?T4HOVceJ&kS z^{MJ&4TeYWg7qnT=LMPj-21}Sw2Nv&;{FZpfU;tfk^tfgnPy;DBQvh%u|6(hGYZ6x zRgE%Y99R~+yR@CWLRD{;Krj|$^U5dqsh$&@u|;b2^3ixuBUda`re5Z=|0r7??8q$ z%K!ELn>v2UpRCX>8JUTcx`p~TH;$sW z++V3#?1%k#BMR5*RA_n$&5*ufH$+dKD5XKjV5?3l>*)$cTGHO{t?Yj#!Wut7O1!f* zeS>~$)oabhoJqk9) z!7%D?s_dMKMqE7qMxQ_u&H&6pu;o!AvGq^hq)BhHbGCL{NMIpBk2ht(6qzOpWwp9W z|2_Gj*6n+2(ywVT8d#9X&Y^*gl}vuPc6zM(;<=7PB*=6?aW=&nWI>wdGh zh?5@9;89dv(n6J|iukDHeS-ueF^wilv4PgFz5l{rAZ5`>J6;UzUmg>Ww5(Y1ih1d> zBt^$`YJj`Pbb@EV4;A0dfx?09{3BI5d#Ln-EP3AXJHtpzffdK_Y!~`xbo2uc8n;V) zE_WF42eYew;`vJl`@a$wyxBzreWmIGbI11#4AX1C~9CC|>W;`~p##6F{L z_wFrsXO5_kvvhi^pGAbel$Cv>VX~#f$!$iz4dn;o&I(2h4?Y&XCL!`5{WF33DvG$7 zKIwe4qg$LVov-G{%$?jA{0Ox6ltW;HTcMKYv|(pP?1pfO4)Cb|zaM2_thtt1#YQ{f z_`Nw=$_KeZpmte1?vBylZ<-aO6hkTDlLqrbr9^sNKN1mbeEK7alT@l?DJj(yOzVRn#WSf~13jRvtWgY(z>%w0ZTgF4Y8j!^PZv(T6uGQj}Q6X)^veUQXRSgg%;!68s$&>?Hdu+ zdprtAdf0k~{UEKX@j11oNu!t%EHp-aJ2m}9{+^G^ax^8o!_G%r+vyAc5R$ZF;8~;a zaALz(7GUsmfGDqKZT*1e#*Lprm+34g>pe}6Y^x@o0Yhs#bU_Mt4Fkjz=ri|oZcI52 zo0dc@U@xgY|CqZu@7S@j&zIHj`NevmV{MBbV?R|;bkUHUj+*G<<=?ddEHnU+24_R9 zvT%omA2+SXt4l@cACTn`*AO)CxH1^P+Md_V)FAgvdW?da`fqfcSl`YPgk=I+f+JW@ zKV#r*7h?q^#gk2miC#YWy>zgYFS(6tRllvneCcO7l!i_JlmQ_NPUcWRJ{{4UPZEyn zO<vD%`P1Qe98By73ibrehjHMx@F0OUi>JHb4~CEYugkodOYQc@canV&r$c%WpHt zWe&-GWy<|A;C4?k_CmjlDlUVuR=G1n!%C)5D=iS#ktFoGcZ{DKa4$XEEgzw`PxbUV zM!(1x@SXF2|N8eTt0&RqjEzdO>dTHqFJ=I#o4kt`0+^?eEBoXWTJ>b%7vz1ODrWEb zJir!w$h)bbF98G!EFbRGPEPfWrx0U0n}};Y=~pW;)$N%C4Wg(xkNY;U`u`zupNy#E4lgXdNbu+hK$@foS&L& z3=(?^O2|pPJSgmZt4w-mxiCoVD6dA&AodjQ_0{7p1>_@R8psQ*+nIAdCcaCV|?-Egz{KZ3Qq=SyX{#A~~ zxsuLIanV%6Exhg>6vD|uIc+Bg!4AB^T*6W4E=`Y-pygSYvwWx%syi%~U`rf1n>=TdkpV8WBtq>=F`2DVY<%xPYS3`|&z= z-U(jz_d*+w!m_T3N^9jm^; zMpgl6qIKe3NWon|c(}iId^Sp+>Y25NQm1BSWb&MkR3Eb%tK{Crj=>*2ew+tP)b|4| z$oOnYt6K6GTz|xY4PltCCXX%Xv!Dgdge$wR?jeq}SiEHCT*pZn_`>7I{d&|-WxkOR znf~dD%Yc__9cf+#H#CSlt`1~Gfb<_U&C-7E@7Khg0?nM($2F!27qfuUrF8aLzB!mA zcb5(@CJ^`$e8GIWFEYwj_E-9*8 z7hUz6MA#HJ2xeQUCKeGd)1J0;clew;Ts)Ws=^uFnQ)Vyp#F?>D=LSxUwMI`D%8DVa z6i&1{0Y|%;Kub-;PVswmPZwxXCm6-bGINc1wf18-Svvy*(^qBxs8KXnSy@B;O&&VO z*E#XJ-gFPQ-fw@fehs{Imm8C9SXB1Nhnot}7sdv`g0NN3uz$$=?u6zMEl`ok1Pabu<;WxK7*XPA?x8A{U ziGIFqLXKQnf``Fdmch;|D`8p*ro;{vfB3q5$;%({l0eX)J}!>qg+i>XtkTcp&jKZI z=f?C1Ana@sMO{DF=H~R1*sKl~nxYQKwqbviIA?mvyLDPL;~(VvnP8=s-r8K=>|}Mg zK-_@~W{+_VDGR3je0C}zh*T&30dFk2T)ez0E>6Wpb%GOKtA%$R@gr#8XTWqzmRiQM zkY!a*z-BQ)es&fs*~Hez<~U5V7XtHhx)tvN)CEoR+cCRVlnQy~-pJ;vj1Rs@Df0-j zF9Srsl+E1~*a8S6PN=cQ^?2r~*Vx`052ne)kFr2lNxjJt({r_JRTW?>q%lXYs&rWt zl!&Nq{+U{xF?8CQ8hMgEfm$f2V?HhTdyL=yL+4Jnj)O(#XW-F0*UgAOTK<_$ozZu_ zBp|d-X?{v77W4!kN@{)QXDU#uI6}{c^=$OrzE3 z@?}ZfQNddT9TWt6IW05#)15y{{0=kLfzoJYqVOvd)}f-6!jXYZpLUf#KW~29GvPi}_=7C=LlLoF!YeUjIis9~ zkSXzb>3s7lGh~63yFw*+bS6pdM#pYmsCB?!>?PoywU{`Af<>ctl2EAFaW}K(RR9*v zT|oDwo8gNZ8Rul+lwjaK~4zL*iH&BR+$?b zbMr6OHe?W(yMq@ICs?YNJ>_2^z^nytCwaLgE0C@ZR8ST6`!zfZxJNo5WB50J{Rj0M z^7+$8%bs;2SB6N**J5Fr)UGFX5~DNyq|B<%d-Yr+tN-ShEtN}Nb|-WA&LuS|KL*uPRZBWu?)`Cr~Eld3C^nL-;Z#6@)up+lep~QBmm@uuKX-S zMi;iMqv9CT+qb)aChtuwKkZ7F6-zGKeptASqhV!R%q^D<`lrwjTX}sYM@B|gEaiyK zR%KU}L%J3`>^nw_J>%GOTXeb3@HhWlFY&R()d`@%kaY-6qtwRaAAxJQRPC?$S2d8Wq-BiGsF?9zWSxXaTnURiNY+cf8bM;y@KuAq}^2 z8r|fWB(qXi26r2|;*aouE_=AF;->(#)qfq6C5E;G%{G;Zi#cimOGm{TZZjng92$9j zUVUyYCJujT%Y7T_dx`H{F3Df1EHHSlLD<7`h=?RSP1}SEd~yO#eWy%Sn-&ws@HEf0 zSbQ{X;rx#6*kpfoo1Qfg7i#_7-$HUO473t`xh?FLDEp1YgF_=hT@bjmQZYqyXu^7x zf#m_@A%@s|mk_URO#_(5+FZoX61v7uUd%b~r=F}m;)oDr;$VSgoqI?bp0Y4f>3SK{ zl_Z(>fmwqsmh2Iu!v)fi_c|V1!sc!S_yBnI#c|>e-eyV-o0@R9$Lpmo&shi`@sz6| zQVKB3(IY0^?@6d^6}sU`1~f1NU%wF{_Z^nMS&TAaybYjF*>Kg*?bg1pPsQa)rrN;uPVr61c~Y|N zHNt|8f?rV^&?-j&lHk*BG*;#Tj6L16Hp?HeIZetMCCXl8mYXxW-yYwjw zh`xh!&GhHrfKEuN)*ZdBenH7wv zV$Pouxd6d+p!HMK87y9LtL;z0wLHG-)ez_Pg{aebfL zPlCQz5FyEQR3s7Ot=k-VYS{G7fQt1TJ}jXJ^(?2aD7ux>dXR=1P0dy3z^(i7w==#8 z&2hge=0(TKP#^Ga>({dE7DsZT-aJW-hH)wsVBSX9u+zJU`vtt8t}yd36MJ;!r3Sha zw}t;~rwg?|(VJNF^g7)>p1m&kz=%)sBFXRlDq}7_4sq8&9#`*9(WOj*eC3+*Y*k}B z|CjchpZ4y(=VcyTPP!-R!z3DUpPQ(-MO~$-;UjNw_|CElo%JMJxMT3@Kw7{?S2m6J z`RfRZT+aJ{w#Gu)?qBeBSNnF0S3gAi@bQOslxb;o^UE>Z(-lGP2!I}FgdL1l8y@;X z{fPllbg-m)k>f-jnV(~{->eqSPC+G`C}TG{xJTo!SB$;>yDx`UR@xdW=D}G8hw@5D z;l~6+tQH?j@xwep0`#gdd`YVb^9QKLlVy>}#>>S1)Iz&Ak{PFbV=BRYO`_qpuTB`a zlPeJ93Q&qGihnoc7DeyR_FQ%5z_wo;JojL`P=~`|$v=LL^4eJlqI*UI1r zIme6XFaZ*OD#IZScFfMmEB(Deq8`tb2!pnj!E_oai)dbOBd`D8jlG7lZ!DU(ft)~B zEnRDysdlHGRMTS*DS$502s-2atamv8L3G3$>X)%y+pd!F&)3si<+#QQCGSC=yS!|2 z>@}8Ydfl)(79T?YA?4M0 z5)BRZ)$P<6^|~cH20+}7_t@vR^|l@I2WBVEc`e(FVVVJ=vquS4&3O1d=r$D((XHau z+-KPTwfN zr{WT356V4wWXdJ=8tt>QR$}P2T_?UGFs9NRhv{pfxR8z*3a1Wo5ocxRp&`asd|>deOAl3XggBI`l~b|TaVRI+7c>A3q@CzUG)OY&>)lvJN-s1duzkjR#aau z)Qwb{O3)z^0ZLxa8A7#Z3_=fDCPJ*gV)!>7F}5xAl4@+{PQg&RY*V=Eb=5RWwK?t5 z+fIBBI)y$M6M)VMp!-_0Ep_XZjP27KnI3R!dGPm4YK}QMbft79JVP}Z0S6uTyrXbFW z7HRR(=kI|cDRl$xfqcz#WD7UQtaSGgI(E`mmEc5cT0e+koi8&~wVbQgy1v{0!XSd~y=;T{$nHaKyIn8GPLJT6Jd-IV zS2u2#R+cpC0)Tz&15#$&hDQ8C62~?66`9q2!t$>T$?Ild3`mmMgrxKQh~BFb+VQqyvIy zeQdx6qL%;LK7O_ldZurOsYmN(-+a2N`#|7y({FLaxagoGccGL0Oiw4ETXVvM74DfoO})#`iT7GjmVZ_I_M--vWbsaLFIMB^9$Zg)PUwx?#Bd~~R! zRBNLg;0T*SmsjqNF29%mdSBOjZ>@%*zMJ)%R-t|@Ir)1#?nUhfb`|f<&*oU(F;In-&twatJ1uOdTQ79*iqrve)qi(c{@ZQ{kb{pcH$YaEiTtzGgaR zS#^6d9c#$cEio}XdeYuqd;>dFXMY2`h?&{+ZyY??NP3vM2T73wE2T(_!FjGXhY#d0 z5q9e=*E<^cpX1Ka^X*^SO>v5<_4R%`d2}e=@Q^Dgk@EW|QJ*peRzrwX^vTWsmzMMm zk7kJ*9rxVZfa^ne-Pr4PLII-g^ZCXh!$(>wWy}A1>dG zXN@$a>S(Xj|5VLhxVWL!BZ-X zUd$QVMiFre)psc(ogiR;O+sRza^OXbIhQ01jQI?)Pl zUVM$j==Natflk30Q83F^A?jn`;=VbDpp6|HMZEY~0)N|w7xkld4L11foTGC4LBa6U zNbm1MMhAQp0QW${IIac!mmHbEsH{2!a@BQXG6lF?LN10vsS#wy z50%O8Xu#j5Fj|J9w`7$|*l0A45q9-Rw3N$96v#ZUQk9m1 zcKgI#DE9Mcv9@Q&qOQl?7Ighs+hd8);od)2ansZTi*+?!`e~joH=MfW7Bfl#$r$>E zQ6I|l+`$K?LkO4ME?OSX%*yI{Y(9MTPR%?w=ji-~A)U!zYuOPy1ZK6*pKDryeM)n_{RQRU9w?l5^o$Y`BVgN)efCo)q}Fs zmzfJcLmDish6W!aod#~|3qQZCOz@)qaO`xCVznXcqI-h8k7(D8_FAQB&)=qsc$K}g zlYYwvAD=6<-b)u)d)52?D!AukxS*XJ8~p$;4XLXfP(B>Ml+*t;p2gz0Nj70^D^^mq zl!x`!9TI$tY|@BN6kZWWAAeR&MMJD8klKVivj5R)5GKG@lw7F*zVcs@!_)F8-_@Hzf-;9_N#8;jjHsswHIb;fIjeTdCr5`j^&VV^Ghcx!C2 zG50J}f-@f%H?q9VK!Z3Ahj1VJb1N-vMSGBU80(TZ*Io_06-0VPY76;A##ML>)N-{o zFD+iwg|A(7{mp#0j_@wnEr35E(HJx-V%tD?j>;d_y$MJKHF*wo)E2KWc(I!)P+_rJ3W~neI2yZoRF?v`jr!% zJD(f~?F`r`nr~6PIv$V&T&jhUG89JyM^4ePesG$Gl1`JTUOjTqpY&szRc1H4*%FZo zt+MPTXWKr2IEX?a4^wX?C@Np{ zLbNQ=AMnvX!p{b5%*KT11QVBJRMKD`-QdVL}$DQter>qa!%2*u^`FkFaiV?aH zg#x!nBuuv{JJ?WzeFK@LnnJ=3j@u_?Lau`QdsCMj(qGyK&BpNyj6`zlmQ~1It(Wj# z9U2M!U+tV*P!m@e#}|>tK|^Z;L|>A=H$%o z*_}P#|M&acIMuz2Y^@cUX75qNKiJP#yVAF+cJG1B*9h5vc9qUsD@MCLp*z^B&1W_x zLDANgaS7-iEyFj7*u8FDm%`3O^oY_=oN)KAiOSC;GtO49q00PM;I<}#A$T3d^EshC z@)llW0KwG?ooQNfanD$>VMN_mfBPdyg-wpHx(Zn||VAKKOUfS}pt2PZMWd1Wc3jH*q-UF}2QzRcUNKfb3NosquNyH4eD zxkD(lwfSN)eq=o3)G4Z1IY?=5O^w_QtoK9a!MdpI59AY5zQgz(#1`ZGN4G`yAF*s} zoyIb)RT`~~N*=9|m5~VF-CCkYm0(vH;hZe0#MEhJY6ex3oBmK6%K@+NnA6nI;=Pq@ zlb@G6JHPC_J0uf`^$xb|PDup({7GilK#>Gb=A;J0!LsN%c9jprA<}+Yy;mObzR1zZyQzdo>!qH#O_k z@GjGpr6V1T2w}hg;bL=Ob>bl(qg=ig+evOjImwS-6|m6yB;Rq#6#`}_)~LqZeC$n@ z0avksBAP6)B|0DTS6?^AbNhR7B33u3_beiF!7lVAHfU5mnpUE{)CI*` zpgX^F^5{-m0c(Yh|F?#IZ_jFDi2ok1M{AEuDt&2B$FYr<`(|KRMy3yG04LAg;lB_o z3wKDsT#6dpTfluE=ENx%BPR^JBj2FxbXcFn1$Z7{lt}<*_y&pU4}T~D4pwsyU{#Na z$9Jo4c4duq*V^wLm3Bsx@8mkBA;5B~mBU&GdK9J9ui|F5Mb=}nq7S?Sg8>TUTwXbL zMLgvr%eLys;HIXgiGTUWL(zD`^-9aI);^)IUI3hDXS-)?d`MFFU6YL8~E^j zv0B)^tD)3S^DI!a!|NPfxU{IS&{tp{z-q~JrNK4~wL{#VzonG^i7P`C6eTb^-LHOV%8a0@U;k&lp?k2={4YNF=GA>~b_sUqsu!Yl9?Ya*v7{kTOW_eK`Mk4|5p8rkVw!B;iLDG$#t z<~GO48|DFc-Nl__ko)Mz1-2HG@tqu(yOD$hx(}`NtW19%8(vm0m$2d z5C5`itZ<1BKiM<(VtuVy)?@W%h3bzeuN=P59l*4zfliR^epns}wad(yYiW~PD2;41 zMu0GBe}CtkWg>j5AYe`8#{zJe%$K*z{eO_&O<@SDPuFb^WPz&34bNptniFyu70i)+Ey|f*)z1$>xmDlB6z}O7>)H`t49CIY0{X?ntr>= Te28F=UO?_{)OBT_f1U6AO&07XPe1tp}r%h5Fu0hO32DKSE7 z#27F~4c>F8_v7vJe0)Fr_leuHLi?J)fFz*+3EZ0 zW(5V(9=ZA^Ff=3W@t3S1R&Di@Wu)gKE*`#yX~d78d!>Hp@E6P3b$r5i77k?B#ko6{ z99B*1-aAqP32w6wK3Vm|IHgFU5>g*MBmQ`|5}F<#{B_72d*=rDJvbF+b)NVO)03e& z1%CSPrxJ)GKAb;;Bo4T7rre8zIN=&HhhJ5qcY&6c-ngua!F$ zL@lR)Op)!)RbxNFF2@45?7rsj@1L;fBg0l?RA1F4_0a<}9>MOvNpk(^iL03LMl5a+ zYCcxy?p!~%tuuYQY_P?^sd2{0?2!w!n7?U9qJM3sQ^aIa!> zW%`D4B+J!O<3{JH_s3vb?^)&jM|VQ1>!?*T|Jg>ROu5)z@EXHwg^uuwUCK#+!^u7G zrzZMj2JHN*Y^@)fp3x)a_ck-}MrF5lq3gZQdCrw@Y%@QN?HhVlzF)=^{l@m+wxF9_Dy_zo>M53sH)#C zwj(m}bwy(S*rK!%R}|+!J3^h!yv&^ARVrQ7d7R9Vo!XXxkQl8F!M6faZEz-*eu}m4 zA7i#TSfo9=>Wc&R_h##?8v+a8Y#-@u^!3C*(?h7)2gH40_%YA%xvP!Yj8R!lRAx9A zKBKxmUqruCoC?QI;fyvM&{s~nH&rlsA+~0j-Rgaq)swHw+7Agf5Ppn|yUpjQrAAIo znXOAb1*)Fv6hLgG_!gH)d###eWoLU^wZRyx^Y?DwQB&)v$22YSZOnBgOplfH45mo9 zWZ>scFkLyy$jC_Iq!yQuut2Cqiv?*oVZ+&EnSc@S!i~Fo@;b8FQtL42inyX8Uc^Z8 z5={JuF49g@GaB!M-bn20(?+cK(G?=|Gk)lf^es27_ee8Hx?ZGWm3rVf2qk@FOy0C4 z$7w5Ow=`5fKB$iHCp1JE$@W+ueYg)>7>VdipUoSR6g#9*UA(5ZxZ^d42BqrQu8`?l`GH#O4$QVnjR4 z#~()B{%PFP+{o4SEea<`*!U`3Z80aQ6BOYyavPDdZ4vNeNgw0B;b1E?A9vkb^-yw9 z>3(!1En#nQW4V1s-L1mS{xBT2J-ojvY%*%4QzB%bvMA`NZ7G|ICW!ZnY z?KACK!=CxtyxnBRC4Vt5!9ecD=|>&;ZA#`xRrtG}h**b!qO zxaj;C5-zdZC7Gztn;!9e*NQg(TAiQWfb;OL!XPhWyx~$$U&0j|yzYsdOAXko-OA7m zD9?9m?z@w{ZD){+aK_=#EN_#Nl7vU@l^QRPF%OdJA@PEgkx#M8l>#Yr65{zDQ}2`|9cw$uHFx`=Bl8uh;v0aBksHOLQHbMo>bX zV;Ul*)4YK)Z)I+v*q{*)>x|_a@3d{)=0T|s?>H`%TAmmv(x3J;XfkT`NYN6tF2LDZ zDvs?1Q_y#{^m@J!a)joVQqM?gl%B(Ft0SKDEydl>wWQ6sirANe)w@Xcu`IJNx8!*; z_qj(5^FUuW2H0}kD%J{4t#d;ADCO7|ea;^it-L#yyT*X1!A-T*IJGanTUjPIlOdns zdsO072Grn`hxFvC*6GD&$ftzfaNGU5uy_H-P|PAiRv4wy_%)4RK>zLgQQes$OYwdm z9Mu!2b5`M%?I!~Ah8Gb|LA~s{7EyfDbNSC-Q!;>qpA3!juoXPu5D_X#QlKQ@xY49W2+C> zHMOWJwKt{9q0&e1DRzYId2yfa#;WC(M)jFjxOOoV$??0F)YREG*k$Bpe5r}t-hV_& z-sOPafK>KKl*+tXA|*6YDg_w6maPg;t+wvX5MJzG&qATp*%JAOw$=STypf6hLg7h# z+--0HobE`K!$<@Cd^@yjaj;apn%tL!c@bA#L_00eYZCUYL<=s0KC6wt-aT-*&d5A@ z`_cn-1Q2i2+}?Ic=`$?7cUp%goB305aq;RdA9w%P_e=M#iEIC8VvbWwma-_(3Lnvg zD@*Yp^Iz&H&NY*N6aT!eo|#-%deq8p3-DXMf9W+~CkY*Z|exLPBHMUhs3 zA!e+8a7XG3^3s*O)ZCK7$n?;>_3Qoa5nAGXHiyt}x37=~jg3&>2)IMnTk6Dl!QAK{ ztAm!@ywV|`+$5(N)%!`vyllNDJYzJzUz_qZcrEi50h%0A$PirhhD7v9bYpUOIP3&b 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 C&L6n| literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index 896ac2b..e97a47a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include "testing_helpers.hpp" -const int SIZE = 1 << 8; // feel free to change the size of array +const int SIZE = 1 << 29; // feel free to change the size of array const int NPOT = SIZE - 3; // Non-Power-Of-Two int *a = new int[SIZE]; int *b = new int[SIZE]; @@ -34,6 +34,7 @@ int main(int argc, char* argv[]) { // initialize b using StreamCompaction::CPU::scan you implement // We use b for further comparison. Make sure your StreamCompaction::CPU::scan is correct. // At first all cases passed because b && c are all zeroes. + zeroArray(SIZE, b); printDesc("cpu scan, power-of-two"); StreamCompaction::CPU::scan(SIZE, b, a); @@ -46,6 +47,7 @@ int main(int argc, char* argv[]) { printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); printArray(NPOT, b, true); printCmpResult(NPOT, b, c); + zeroArray(SIZE, c); printDesc("naive scan, power-of-two"); @@ -67,6 +69,7 @@ int main(int argc, char* argv[]) { //printArray(SIZE, c, true); printCmpResult(NPOT, b, c); + zeroArray(SIZE, c); printDesc("work-efficient scan, power-of-two"); StreamCompaction::Efficient::scan(SIZE, c, a); @@ -94,7 +97,7 @@ int main(int argc, char* argv[]) { printElapsedTime(StreamCompaction::Thrust::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); //printArray(NPOT, c, true); printCmpResult(NPOT, b, c); - + printf("\n"); printf("*****************************\n"); printf("** STREAM COMPACTION TESTS **\n"); @@ -110,12 +113,13 @@ int main(int argc, char* argv[]) { // initialize b using StreamCompaction::CPU::compactWithoutScan you implement // We use b for further comparison. Make sure your StreamCompaction::CPU::compactWithoutScan is correct. + zeroArray(SIZE, b); printDesc("cpu compact without scan, power-of-two"); count = StreamCompaction::CPU::compactWithoutScan(SIZE, b, a); printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); expectedCount = count; - printArray(count, b, true); + //printArray(count, b, true); printCmpLenResult(count, expectedCount, b, b); zeroArray(SIZE, c); @@ -123,20 +127,21 @@ int main(int argc, char* argv[]) { count = StreamCompaction::CPU::compactWithoutScan(NPOT, c, a); printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); expectedNPOT = count; - printArray(count, c, true); + //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); zeroArray(SIZE, c); printDesc("cpu compact with scan"); count = StreamCompaction::CPU::compactWithScan(SIZE, c, a); printElapsedTime(StreamCompaction::CPU::timer().getCpuElapsedTimeForPreviousOperation(), "(std::chrono Measured)"); - printArray(count, c, true); + //printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); zeroArray(SIZE, c); printDesc("work-efficient compact, power-of-two"); count = StreamCompaction::Efficient::compact(SIZE, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); + //printArray(count, b, true); //printArray(count, c, true); printCmpLenResult(count, expectedCount, b, c); @@ -144,8 +149,8 @@ int main(int argc, char* argv[]) { printDesc("work-efficient compact, non-power-of-two"); count = StreamCompaction::Efficient::compact(NPOT, c, a); printElapsedTime(StreamCompaction::Efficient::timer().getGpuElapsedTimeForPreviousOperation(), "(CUDA Measured)"); - //printArray(count, c, true); printCmpLenResult(count, expectedNPOT, b, c); + system("pause"); // stop Win32 console from closing on exit delete[] a; diff --git a/stream_compaction/common.cu b/stream_compaction/common.cu index 2ed6d63..5d83ef1 100644 --- a/stream_compaction/common.cu +++ b/stream_compaction/common.cu @@ -22,18 +22,30 @@ namespace StreamCompaction { * Maps an array to an array of 0s and 1s for stream compaction. Elements * which map to 0 will be removed, and elements which map to 1 will be kept. */ - __global__ void kernMapToBoolean(int n, int *bools, const int *idata) { - // TODO + __global__ void kernMapToBoolean(int n, int* bools, const int* idata) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + + if (idx < n) { + // Map to 1 if idata[idx] is non-zero, else map to 0 + bools[idx] = (idata[idx] != 0) ? 1 : 0; + } } /** * Performs scatter on an array. That is, for each element in idata, * if bools[idx] == 1, it copies idata[idx] to odata[indices[idx]]. */ - __global__ void kernScatter(int n, int *odata, - const int *idata, const int *bools, const int *indices) { - // TODO + __global__ void kernScatter(int n, int* odata, const int* idata, const int* bools, const int* indices) { + int idx = threadIdx.x + blockIdx.x * blockDim.x; + + if (idx < n) { + // Perform scatter only if bools[idx] is 1 + if (bools[idx] == 1) { + odata[indices[idx]] = idata[idx]; + } + } } + } } diff --git a/stream_compaction/cpu.cu b/stream_compaction/cpu.cu index 719fa11..80c11da 100644 --- a/stream_compaction/cpu.cu +++ b/stream_compaction/cpu.cu @@ -19,32 +19,87 @@ namespace StreamCompaction { */ void scan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + + // make sure elements exist + if (n <= 0) { + timer().endCpuTimer(); + return; + } + + // add identity for exclusive scan + odata[0] = 0; + for (int i = 1; i < n; i++) { + // do scan in one big for loop :( + odata[i] = odata[i - 1] + idata[i - 1]; + } + timer().endCpuTimer(); } /** * CPU stream compaction without using the scan function. - * + * @param n number of elements in initial array + * @param idata input array, not modified + * @param odata output array * @returns the number of elements remaining after compaction. */ int compactWithoutScan(int n, int *odata, const int *idata) { timer().startCpuTimer(); - // TODO + + int count = 0; + // loop over entire array as one big loop, + for (int i = 0; i < n; i++) { + // check if they are zero (throw out) or non-zero (keep) + if (idata[i] != 0) { + odata[count] = idata[i]; + count++; + } + } timer().endCpuTimer(); - return -1; + return count; } /** - * CPU stream compaction using scan and scatter, like the parallel version. - * - * @returns the number of elements remaining after compaction. - */ - int compactWithScan(int n, int *odata, const int *idata) { + * CPU stream compaction using scan and scatter, like the parallel version. + * + * @returns the number of elements remaining after compaction. + */ + int compactWithScan(int n, int* odata, const int* idata) { timer().startCpuTimer(); - // TODO + + // create temporary array + int* temp = new int[n]; + + // loop over creating boolean array + for (int i = 0; i < n; ++i) { + temp[i] = (idata[i] != 0) ? 1 : 0; + } + + // create array for scan result + int* scanResult = new int[n]; + scanResult[0] = 0; + // loop, exclusive scan + for (int i = 1; i < n; ++i) { + scanResult[i] = scanResult[i - 1] + temp[i - 1]; + } + + // final loop, use scan result and boolean result to generate new array + int count = 0; + for (int i = 0; i < n; ++i) { + if (temp[i] == 1) { + odata[scanResult[i]] = idata[i]; + count++; + } + } + + // cleanup + delete[] temp; + delete[] scanResult; + timer().endCpuTimer(); - return -1; + + return count; } + } } diff --git a/stream_compaction/efficient.cu b/stream_compaction/efficient.cu index 2db346e..3a6e8d1 100644 --- a/stream_compaction/efficient.cu +++ b/stream_compaction/efficient.cu @@ -2,6 +2,17 @@ #include #include "common.h" #include "efficient.h" +#include "device_launch_parameters.h" +#include + +#ifndef __CUDACC__ +#define __CUDACC__ +#endif + +#define NUM_BANKS 16 +#define LOG_NUM_BANKS 4 +#define CONFLICT_FREE_OFFSET(n) ((n) >> LOG_NUM_BANKS) + namespace StreamCompaction { namespace Efficient { @@ -11,30 +22,293 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } + + // helper function to get next power of two (for host) + __host__ int h_nextPowerOfTwo(int n) { + int power = 1; + while (power < n) + power <<= 1; + return power; + } + + // helper function to get next power of two (for device) + __device__ int d_nextPowerOfTwo(int x) { + if (x == 0) { + return 1; + } + + x--; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return x + 1; + } + + // do scan from GPU Gems + __global__ void scan_ker(int* g_odata, const int* g_idata, int* g_block_sums, int n) { + extern __shared__ int temp[]; // allocated on invocation + int thid = threadIdx.x; + + int blockOffset = blockIdx.x * blockDim.x * 2; + int ai = thid; + int bi = thid + blockDim.x; + + // number of elements to process in this block + int n_block = 2 * blockDim.x; + + // next power of two greater or equal to n_block + int n_shared = n_block; + int bankOffsetA = CONFLICT_FREE_OFFSET(ai); + int bankOffsetB = CONFLICT_FREE_OFFSET(bi); + + // load input into shared memory with padding + if (blockOffset + ai < n) + temp[ai + bankOffsetA] = g_idata[blockOffset + ai]; + else + temp[ai + bankOffsetA] = 0; + if (blockOffset + bi < n) + temp[bi + bankOffsetB] = g_idata[blockOffset + bi]; + else + temp[bi + bankOffsetB] = 0; + + // build sum in place up the tree + int offset = 1; + for (int d = n_shared >> 1; d > 0; d >>= 1) + { + __syncthreads(); + if (thid < d) + { + int ai = offset * (2 * thid + 1) - 1; + int bi = offset * (2 * thid + 2) - 1; + + int bankOffsetA = CONFLICT_FREE_OFFSET(ai); + int bankOffsetB = CONFLICT_FREE_OFFSET(bi); + + temp[bi + bankOffsetB] += temp[ai + bankOffsetA]; + } + offset <<= 1; + } + + // clear the last element + if (thid == 0) { + if (g_block_sums != NULL) + g_block_sums[blockIdx.x] = temp[n_shared - 1 + CONFLICT_FREE_OFFSET(n_shared - 1)]; + temp[n_shared - 1 + CONFLICT_FREE_OFFSET(n_shared - 1)] = 0; + } + + // traverse down tree & build scan + for (int d = 1; d < n_shared; d <<= 1) + { + offset >>= 1; + __syncthreads(); + if (thid < d) + { + int ai = offset * (2 * thid + 1) - 1; + int bi = offset * (2 * thid + 2) - 1; + + int bankOffsetA = CONFLICT_FREE_OFFSET(ai); + int bankOffsetB = CONFLICT_FREE_OFFSET(bi); + + int t = temp[ai + bankOffsetA]; + temp[ai + bankOffsetA] = temp[bi + bankOffsetB]; + temp[bi + bankOffsetB] += t; + } + } + __syncthreads(); + + // write results to global memory + if (blockOffset + ai < n) + g_odata[blockOffset + ai] = temp[ai + bankOffsetA]; + if (blockOffset + bi < n) + g_odata[blockOffset + bi] = temp[bi + bankOffsetB]; + } + + // kernel to add the scanned block sums to each block + __global__ void add_scanned_block_sums(int* g_data, const int* g_block_sums, int n) { + int index = threadIdx.x + blockIdx.x * blockDim.x * 2; + int offset = blockIdx.x; + + if (offset == 0) return; // skip first block + + int addValue = g_block_sums[offset]; + + if (index < n) + g_data[index] += addValue; + if (index + blockDim.x < n) + g_data[index + blockDim.x] += addValue; + } + + // scan function + void scanRecursive(int n, int* d_odata, const int* d_idata) { + // base case + if (n <= 1024) { + int threadsPerBlock = (n + 1) / 2; + int sharedMemSize = h_nextPowerOfTwo(n) * sizeof(int); + + scan_ker<<<1, threadsPerBlock, sharedMemSize>>>(d_odata, d_idata, NULL, n); + cudaDeviceSynchronize(); + checkCUDAError("scan_ker base case kernel execution"); + return; + } + + // determine block and grid sizes + int threadsPerBlock = 512; + int elementsPerBlock = threadsPerBlock * 2; + int numBlocks = (n + elementsPerBlock - 1) / elementsPerBlock; + + // allocate memory for block sums + int* d_block_sums; + cudaMalloc((void**)&d_block_sums, numBlocks * sizeof(int)); + checkCUDAError("cudaMalloc d_block_sums"); + + // shared memory size per block + int sharedMemSize = 2 * threadsPerBlock * sizeof(int); + + // launch the scan kernel + scan_ker <<>>(d_odata, d_idata, d_block_sums, n); + cudaDeviceSynchronize(); + checkCUDAError("scan_ker kernel execution"); + + // if there is more than one block, we need to scan the block sums and add them to the data + if (numBlocks > 1) { + // allocate memory for scanned block sums + int* d_scanned_block_sums; + cudaMalloc((void**)&d_scanned_block_sums, numBlocks * sizeof(int)); + checkCUDAError("cudaMalloc d_scanned_block_sums"); + + // recursively call scanRecursive on the block sums array + scanRecursive(numBlocks, d_scanned_block_sums, d_block_sums); + + // launch kernel to add scanned block sums to data + add_scanned_block_sums<<>>(d_odata, d_scanned_block_sums, n); + cudaDeviceSynchronize(); + checkCUDAError("add_scanned_block_sums kernel execution"); + + cudaFree(d_scanned_block_sums); + } + + cudaFree(d_block_sums); + } + + // scan (has timer) + void scan(int n, int* odata, const int* idata) { + // allocate device memory + int* d_idata, * d_odata; + cudaMalloc((void**)&d_idata, n * sizeof(int)); + checkCUDAError("cudaMalloc d_idata"); + cudaMalloc((void**)&d_odata, n * sizeof(int)); + checkCUDAError("cudaMalloc d_odata"); + + // copy input data to device + cudaMemcpy(d_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy to d_idata"); - /** - * Performs prefix-sum (aka scan) on idata, storing the result into odata. - */ - void scan(int n, int *odata, const int *idata) { timer().startGpuTimer(); - // TODO + + // call the recursive scan function + scanRecursive(n, d_odata, d_idata); + timer().endGpuTimer(); + + // copy result back to host + cudaMemcpy(odata, d_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy to odata"); + + // free device memory + cudaFree(d_idata); + cudaFree(d_odata); + } + + // same as above, but does not call the GPU timer, to be used within a larger call in stream compact (efficient) + void scan_no_timer(int n, int* odata, const int* idata) { + // allocate device memory + int* d_idata, * d_odata; + cudaMalloc((void**)&d_idata, n * sizeof(int)); + checkCUDAError("cudaMalloc d_idata"); + cudaMalloc((void**)&d_odata, n * sizeof(int)); + checkCUDAError("cudaMalloc d_odata"); + + // copy input data to device + cudaMemcpy(d_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy to d_idata"); + + + // call the recursive scan function + scanRecursive(n, d_odata, d_idata); + + + // copy result back to host + cudaMemcpy(odata, d_odata, n * sizeof(int), cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy to odata"); + + // free device memory + cudaFree(d_idata); + cudaFree(d_odata); } - /** - * Performs stream compaction on idata, storing the result into odata. - * All zeroes are discarded. - * - * @param n The number of elements in idata. - * @param odata The array into which to store elements. - * @param idata The array of elements to compact. - * @returns The number of elements remaining after compaction. - */ - int compact(int n, int *odata, const int *idata) { + // compact + int compact(int n, int* odata, const int* idata) { + // allocate memory on the device + int* d_idata, * d_bools, * d_indices, * d_odata; + cudaMalloc((void**)&d_idata, n * sizeof(int)); + cudaMalloc((void**)&d_bools, n * sizeof(int)); + cudaMalloc((void**)&d_indices, n * sizeof(int)); + cudaMalloc((void**)&d_odata, n * sizeof(int)); + + // copy input data to device + cudaMemcpy(d_idata, idata, n * sizeof(int), cudaMemcpyHostToDevice); + + int blockSize = 1024; + int gridSize = (n + blockSize - 1) / blockSize; // Ensure all elements are covered + + // start timing after memory allocations and copies timer().startGpuTimer(); - // TODO + + // map input data to boolean (1 for non-zero, 0 for zero) + StreamCompaction::Common::kernMapToBoolean<<>>(n, d_bools, d_idata); + cudaDeviceSynchronize(); + + // perform an exclusive prefix sum (scan) on the boolean array + scan_no_timer(n, d_indices, d_bools); + cudaDeviceSynchronize(); + + // scatter non-zero elements from idata to odata based on the scan results + StreamCompaction::Common::kernScatter<<>>(n, d_odata, d_idata, d_bools, d_indices); + cudaDeviceSynchronize(); + + // end timing before any device-to-host memory transfers timer().endGpuTimer(); - return -1; + + // retrieve the number of valid (non-zero) elements + int numValidElements; + cudaMemcpy(&numValidElements, &d_indices[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + + // check if the last element is valid (if bools[n - 1] is 1, add 1 to numValidElements) + int lastBool; + cudaMemcpy(&lastBool, &d_bools[n - 1], sizeof(int), cudaMemcpyDeviceToHost); + + if (lastBool == 1) { + numValidElements += 1; + } + + // copy the compacted data to the output array on the host + cudaMemcpy(odata, d_odata, numValidElements * sizeof(int), cudaMemcpyDeviceToHost); + + // free device memory + cudaFree(d_idata); + cudaFree(d_bools); + cudaFree(d_indices); + cudaFree(d_odata); + + // return the number of elements remaining after compaction + return numValidElements; } + + + } } diff --git a/stream_compaction/naive.cu b/stream_compaction/naive.cu index 4308876..f712113 100644 --- a/stream_compaction/naive.cu +++ b/stream_compaction/naive.cu @@ -11,15 +11,75 @@ namespace StreamCompaction { static PerformanceTimer timer; return timer; } - // TODO: __global__ + + // naive scan (kernel) + __global__ void scanKernel(int* g_odata, const int* g_idata, int n, int offset) { + // get index + int index = threadIdx.x + blockIdx.x * blockDim.x; + + // return early if bad val + if (index >= n) return; + + + if (index >= offset) { + g_odata[index] = g_idata[index] + g_idata[index - offset]; + + } else { + g_odata[index] = g_idata[index]; + } + } + /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ - void scan(int n, int *odata, const int *idata) { + void scan(int n, int* odata, const int* idata) { + // allocate memory on device + int* d_ping, * d_pong; + cudaMalloc((void**)&d_ping, n * sizeof(int)); + checkCUDAError("cudaMalloc d_ping"); + cudaMalloc((void**)&d_pong, n * sizeof(int)); + checkCUDAError("cudaMalloc d_pong"); + + // copy data over + cudaMemcpy(d_ping + 1, idata, (n - 1) * sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy to d_ping"); + + int zero = 0; + cudaMemcpy(d_ping, &zero, sizeof(int), cudaMemcpyHostToDevice); + checkCUDAError("cudaMemcpy identity to d_ping"); + + // setup info + int logn = ilog2ceil(n); + int blockSize = 384; + int numBlocks = (n + blockSize - 1) / blockSize; + timer().startGpuTimer(); - // TODO + + // do scan on subset of array + for (int d = 0; d < logn; d++) { + int offset = 1 << d; + + // launch kernel + scanKernel<<>>(d_pong, d_ping, n, offset); + cudaDeviceSynchronize(); + checkCUDAError("scanKernel execution"); + + // swap pointers + int* temp = d_ping; + d_ping = d_pong; + d_pong = temp; + } + timer().endGpuTimer(); + + // copy result back to host memory + cudaMemcpy(odata, d_ping, n * sizeof(int), cudaMemcpyDeviceToHost); + checkCUDAError("cudaMemcpy to odata"); + + // free device memory + cudaFree(d_ping); + cudaFree(d_pong); } } } diff --git a/stream_compaction/thrust.cu b/stream_compaction/thrust.cu index 1def45e..3a14394 100644 --- a/stream_compaction/thrust.cu +++ b/stream_compaction/thrust.cu @@ -17,12 +17,20 @@ namespace StreamCompaction { /** * Performs prefix-sum (aka scan) on idata, storing the result into odata. */ - void scan(int n, int *odata, const int *idata) { + void scan(int n, int* odata, const int* idata) { + + // create thrust device vectors + thrust::device_vector d_idata(idata, idata + n); + thrust::device_vector d_odata(n); timer().startGpuTimer(); - // TODO use `thrust::exclusive_scan` - // example: for device_vectors dv_in and dv_out: - // thrust::exclusive_scan(dv_in.begin(), dv_in.end(), dv_out.begin()); + + // use thrust exclusive scan + thrust::exclusive_scan(d_idata.begin(), d_idata.end(), d_odata.begin()); + timer().endGpuTimer(); + + // copy result back + thrust::copy(d_odata.begin(), d_odata.end(), odata); } } } From ec70e907e5ab1771ed9310e6da1b04d71b074936 Mon Sep 17 00:00:00 2001 From: Maya <56408670+Aorus1@users.noreply.github.com> Date: Tue, 17 Sep 2024 23:16:50 -0400 Subject: [PATCH 2/2] Update README.md --- README.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0e38ddb..d611fb0 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,77 @@ CUDA Stream Compaction **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2** -* (TODO) YOUR NAME HERE - * (TODO) [LinkedIn](), [personal website](), [twitter](), etc. -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Maya Diaz Huizar +* Tested on: Windows 10, R7-5800X @ 3.8GHz 32GB, RTX 3080 10GB -### (TODO: Your README) +### Questions +* Roughly optimize the block sizes of each of your implementations for minimal + run time on your GPU. + * Graphs: + * The optimal block size for the CPU implementation is N/A. + * ![image]() + * ![image]() -Include analysis, etc. (Remember, this is public, so don't put -anything here that you don't want to share with the world.) +* Compare all of these GPU Scan implementations (Naive, Work-Efficient, and Thrust) to the serial CPU version of Scan. Plot a graph of the comparison (with array size on the independent axis). + * ![image]() + * ![image]() + * Write a brief explanation of the phenomena you see here. + * This generally makes sense, the efficient GPU scan and compact is much more efficient and more parallelizable, when compared to the naive approach. The CPU method is fast for small arrays and scales linearly, and thus is much worse at very large arrays when compared to the GPU implementation. Thrust almost certainly takes different approaches based on the size of the array, ensuring that it yields the best of both worlds, with fast small and large arrays. I also am wholly and entirely confident that the developers of the thrust library are more than capable of writing a faster library when compared to an undergrad CMPE major. +* Paste the output of the test program into a triple-backtick block in your README. +``` +The below tests results are from scanning and steam compacting 2^29 element arrays. +**************** +** SCAN TESTS ** +**************** + [ 42 34 32 22 8 16 34 39 37 30 7 2 14 ... 1 0 ] +==== cpu scan, power-of-two ==== + elapsed time: 246.098ms (std::chrono Measured) + [ 0 42 76 108 130 138 154 188 227 264 294 301 303 ... 264144619 264144620 ] +==== cpu scan, non-power-of-two ==== + elapsed time: 245.254ms (std::chrono Measured) + [ 0 42 76 108 130 138 154 188 227 264 294 301 303 ... 264144559 264144572 ] + passed +==== naive scan, power-of-two ==== + elapsed time: 378.275ms (CUDA Measured) + passed +==== naive scan, non-power-of-two ==== + elapsed time: 377.967ms (CUDA Measured) + passed +==== work-efficient scan, power-of-two ==== + elapsed time: 17.0086ms (CUDA Measured) + passed +==== work-efficient scan, non-power-of-two ==== + elapsed time: 16.9234ms (CUDA Measured) + passed +==== thrust scan, power-of-two ==== + elapsed time: 7.19872ms (CUDA Measured) + passed +==== thrust scan, non-power-of-two ==== + elapsed time: 7.27962ms (CUDA Measured) + passed + +***************************** +** STREAM COMPACTION TESTS ** +***************************** + [ 3 0 2 1 3 2 0 0 3 3 0 3 2 ... 0 0 ] +==== cpu compact without scan, power-of-two ==== + elapsed time: 743.234ms (std::chrono Measured) + passed +==== cpu compact without scan, non-power-of-two ==== + elapsed time: 743.523ms (std::chrono Measured) + passed +==== cpu compact with scan ==== + elapsed time: 2002.52ms (std::chrono Measured) + passed +==== work-efficient compact, power-of-two ==== + elapsed time: 1130.99ms (CUDA Measured) + passed +==== work-efficient compact, non-power-of-two ==== + elapsed time: 875.497ms (CUDA Measured) + passed +``` + +* Extra Credit + * My efficient GPU scan was efficient from the onset, but I also wasn't following the slides very closely. (5pt GPU approach) + * I also implemented improvements for memory access to better align and thus prevent bank conflicts, based upon the overview provided by GPU Gems 3 Ch 39.2.3.