stats.cpp 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  1. /*
  2. * nvbio
  3. * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the NVIDIA CORPORATION nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <nvBowtie/bowtie2/cuda/stats.h>
  28. #include <nvbio/basic/html.h>
  29. #include <nvbio/io/alignments.h>
  30. #include <functional>
  31. #include <algorithm>
  32. #include <numeric>
  33. #ifndef WIN32
  34. #include <string>
  35. #endif
  36. namespace nvbio {
  37. namespace bowtie2 {
  38. namespace cuda {
  39. void generate_kernel_table(const uint32 id, const char* report, const KernelStats& stats);
  40. Stats::Stats(const Params _params) :
  41. n_reads(0),
  42. params( _params )
  43. {
  44. global_time = 0.0f;
  45. hits_total = 0u;
  46. hits_ranges = 0u;
  47. hits_max = 0u;
  48. hits_max_range = 0u;
  49. hits_top_total = 0u;
  50. hits_top_max = 0u;
  51. hits_stats = 0u;
  52. for (uint32 i = 0; i < 28; ++i)
  53. hits_bins[i] = hits_top_bins[i] = 0;
  54. map.name = "map"; map.units = "reads";
  55. select.name = "select"; select.units = "seeds";
  56. sort.name = "sort"; sort.units = "seeds";
  57. locate.name = "locate"; locate.units = "seeds";
  58. score.name = "score"; score.units = "seeds";
  59. opposite_score.name = "score-opposite"; opposite_score.units = "seeds";
  60. backtrack.name = "backtrack"; backtrack.units = "reads";
  61. backtrack_opposite.name = "backtrack-opposite"; backtrack_opposite.units = "reads";
  62. finalize.name = "finalize"; finalize.units = "reads";
  63. alignments_DtoH.name = "alignments-DtoH"; alignments_DtoH.units = "reads";
  64. read_HtoD.name = "reads-HtoD"; read_HtoD.units = "reads";
  65. read_io.name = "reads-IO"; read_io.units = "reads";
  66. io.name = "IO"; io.units = "reads";
  67. opposite_score.user_names[0] = "queue::get utilization"; opposite_score.user_avg[0] = true;
  68. opposite_score.user_names[1] = "queue::run utilization"; opposite_score.user_avg[1] = true;
  69. opposite_score.user_names[2] = "queue::run T_avg"; opposite_score.user_avg[2] = true;
  70. opposite_score.user_names[3] = "queue::run T_sigma"; opposite_score.user_avg[3] = false;
  71. }
  72. void Stats::track_alignment_statistics(
  73. const io::BestAlignments& alignment1,
  74. const io::BestAlignments& alignment2,
  75. const uint8 mapq)
  76. {
  77. n_reads++;
  78. const uint32 log_mapq = mapq ? nvbio::log2(mapq) + 1u : 0u;
  79. if (alignment1.best().is_concordant())
  80. {
  81. // keep track of mapping quality histogram
  82. concordant.mapq_bins[mapq]++;
  83. concordant.mapq_log_bins[log_mapq]++;
  84. // count this read as mapped
  85. concordant.n_mapped++;
  86. if (!alignment1.second_best().is_paired())
  87. {
  88. // we only have one score; count as a unique alignment
  89. concordant.n_unique++;
  90. }
  91. else
  92. {
  93. // we have two best scores, which implies two (or more) alignment positions
  94. // count as multiple alignment
  95. concordant.n_multiple++;
  96. }
  97. // compute final alignment score
  98. const int32 first = alignment1.best().score() + alignment2.best().score();
  99. const int32 second = alignment1.second_best().is_paired() ?
  100. alignment1.second_best().score() + alignment2.second_best().score() :
  101. Field_traits<int32>::min();
  102. // if the two best scores are equal, count as ambiguous
  103. if (first == second)
  104. concordant.n_ambiguous++;
  105. else {
  106. // else, the first score must be higher...
  107. NVBIO_CUDA_ASSERT(first > second);
  108. /// ... which counts as a nonambiguous alignment
  109. concordant.n_unambiguous++;
  110. }
  111. // compute edit distance scores
  112. const uint32 first_ed = alignment1.best().ed() + alignment2.best().ed();
  113. const uint32 second_ed = alignment1.second_best().is_paired() ?
  114. alignment1.second_best().ed() + alignment2.second_best().ed() : 255u;
  115. // update best edit-distance histograms
  116. if (first_ed < concordant.mapped_ed_histogram.size())
  117. {
  118. concordant.mapped_ed_histogram[first_ed]++;
  119. if (alignment1.best().is_rc())
  120. concordant.mapped_ed_histogram_fwd[first_ed]++;
  121. else
  122. concordant.mapped_ed_histogram_rev[first_ed]++;
  123. const uint32 log_first_ed = first_ed ? nvbio::log2(first_ed) + 1 : 0;
  124. concordant.mapped_log_ed_histogram[log_first_ed]++;
  125. }
  126. // track edit-distance correlation
  127. if (first_ed + 1 < 64)
  128. {
  129. if (second_ed + 1 < 64)
  130. concordant.mapped_ed_correlation[first_ed + 1][second_ed + 1]++;
  131. else
  132. concordant.mapped_ed_correlation[first_ed + 1][0]++;
  133. }
  134. }
  135. else if (alignment1.best().is_discordant())
  136. {
  137. // keep track of mapping quality histogram
  138. discordant.mapq_bins[mapq]++;
  139. discordant.mapq_log_bins[log_mapq]++;
  140. // count this read as mapped
  141. discordant.n_mapped++;
  142. if (!alignment1.second_best().is_paired())
  143. {
  144. // we only have one score; count as a unique alignment
  145. discordant.n_unique++;
  146. }
  147. else
  148. {
  149. // we have two best scores, which implies two (or more) alignment positions
  150. // count as multiple alignment
  151. discordant.n_multiple++;
  152. }
  153. // compute final alignment score
  154. const int32 first = alignment1.best().score() + alignment2.best().score();
  155. const int32 second = alignment1.second_best().is_paired() ?
  156. alignment1.second_best().score() + alignment2.second_best().score() :
  157. Field_traits<int32>::min();
  158. // if the two best scores are equal, count as ambiguous
  159. if (first == second)
  160. discordant.n_ambiguous++;
  161. else {
  162. // else, the first score must be higher...
  163. NVBIO_CUDA_ASSERT(first > second);
  164. /// ... which counts as a nonambiguous alignment
  165. discordant.n_unambiguous++;
  166. }
  167. // compute edit distance scores
  168. const uint32 first_ed = alignment1.best().ed() + alignment2.best().ed();
  169. const uint32 second_ed = alignment1.second_best().is_paired() ?
  170. alignment1.second_best().ed() + alignment2.second_best().ed() : 255u;
  171. // update best edit-distance histograms
  172. if (first_ed < discordant.mapped_ed_histogram.size())
  173. {
  174. discordant.mapped_ed_histogram[first_ed]++;
  175. if (alignment1.best().is_rc())
  176. discordant.mapped_ed_histogram_fwd[first_ed]++;
  177. else
  178. discordant.mapped_ed_histogram_rev[first_ed]++;
  179. const uint32 log_first_ed = first_ed ? nvbio::log2(first_ed) + 1 : 0;
  180. discordant.mapped_log_ed_histogram[log_first_ed]++;
  181. }
  182. // track edit-distance correlation
  183. if (first_ed + 1 < 64)
  184. {
  185. if (second_ed + 1 < 64)
  186. discordant.mapped_ed_correlation[first_ed + 1][second_ed + 1]++;
  187. else
  188. discordant.mapped_ed_correlation[first_ed + 1][0]++;
  189. }
  190. }
  191. else
  192. {
  193. //
  194. // track discordand alignments separately for each mate
  195. //
  196. const io::BestAlignments& aln1 = alignment1.best().mate() == 0 ? alignment1 : alignment2;
  197. const io::BestAlignments& aln2 = alignment1.best().mate() == 0 ? alignment2 : alignment1;
  198. track_alignment_statistics( &mate1, aln1, mapq );
  199. track_alignment_statistics( &mate2, aln2, mapq );
  200. }
  201. }
  202. void Stats::track_alignment_statistics(
  203. AlignmentStats* mate,
  204. const io::BestAlignments& alignment,
  205. const uint8 mapq)
  206. {
  207. // check if the mate is aligned
  208. if (!alignment.best().is_aligned())
  209. {
  210. mate->mapped_ed_correlation[0][0]++;
  211. return;
  212. }
  213. // count this read as mapped
  214. mate->n_mapped++;
  215. const uint32 log_mapq = mapq ? nvbio::log2(mapq) + 1u : 0u;
  216. // keep track of mapping quality histogram
  217. mate->mapq_bins[mapq]++;
  218. mate->mapq_log_bins[log_mapq]++;
  219. if (!alignment.second_best().is_aligned())
  220. {
  221. // we only have one score; count as a unique alignment
  222. mate->n_unique++;
  223. }
  224. else
  225. {
  226. // we have two best scores, which implies two (or more) alignment positions
  227. // count as multiple alignment
  228. mate->n_multiple++;
  229. }
  230. // compute final alignment score
  231. const int32 first = alignment.best().score();
  232. const int32 second = alignment.second_best().score();
  233. // if the two best scores are equal, count as ambiguous
  234. if (first == second)
  235. mate->n_ambiguous++;
  236. else {
  237. // else, the first score must be higher...
  238. NVBIO_CUDA_ASSERT(first > second);
  239. /// ... which counts as a nonambiguous alignment
  240. mate->n_unambiguous++;
  241. }
  242. // compute edit distance scores
  243. const uint32 first_ed = alignment.best().ed();
  244. const uint32 second_ed = alignment.second_best().ed();
  245. // update best edit-distance histograms
  246. if (first_ed < mate->mapped_ed_histogram.size())
  247. {
  248. mate->mapped_ed_histogram[first_ed]++;
  249. if (alignment.best().is_rc())
  250. mate->mapped_ed_histogram_fwd[first_ed]++;
  251. else
  252. mate->mapped_ed_histogram_rev[first_ed]++;
  253. const uint32 log_first_ed = first_ed ? nvbio::log2(first_ed) + 1 : 0;
  254. mate->mapped_log_ed_histogram[log_first_ed]++;
  255. }
  256. // track edit-distance correlation
  257. if (first_ed + 1 < 64)
  258. {
  259. if (second_ed + 1 < 64)
  260. mate->mapped_ed_correlation[first_ed + 1][second_ed + 1]++;
  261. else
  262. mate->mapped_ed_correlation[first_ed + 1][0]++;
  263. }
  264. }
  265. namespace { // anonymous
  266. std::string generate_file_name(const char* report, const uint32 id, const char* name)
  267. {
  268. std::string file_name = report;
  269. char id_name[2048]; sprintf( id_name, "%u.%s", id, name );
  270. {
  271. const size_t offset = file_name.find(".html");
  272. file_name.replace( offset+1, file_name.length() - offset - 1, id_name );
  273. file_name.append( ".html" );
  274. }
  275. return file_name;
  276. }
  277. std::string device_file_name(const char* report, const uint32 i)
  278. {
  279. std::string file_name = report;
  280. char name[32]; sprintf( name, "%u", i );
  281. {
  282. const size_t offset = file_name.find(".html");
  283. file_name.replace( offset+1, file_name.length() - offset - 1, name );
  284. file_name.append( ".html" );
  285. }
  286. return file_name;
  287. }
  288. // return the local file name from a path
  289. //
  290. const char* local_file(const std::string& file_name)
  291. {
  292. #if WIN32
  293. const size_t pos = file_name.find_last_of("/\\");
  294. #else
  295. const size_t pos = file_name.rfind('/');
  296. #endif
  297. if (pos == std::string::npos)
  298. return file_name.c_str();
  299. else
  300. return file_name.c_str() + pos + 1u;
  301. }
  302. void add_param(FILE* html_output, const char* name, const uint32 val, bool alt)
  303. {
  304. html::tr_object tr( html_output, "class", alt ? "alt" : "none", NULL );
  305. html::th_object( html_output, html::FORMATTED, NULL, name );
  306. html::td_object( html_output, html::FORMATTED, NULL, "%u", val );
  307. }
  308. void add_param(FILE* html_output, const char* name, const float val, bool alt)
  309. {
  310. html::tr_object tr( html_output, "class", alt ? "alt" : "none", NULL );
  311. html::th_object( html_output, html::FORMATTED, NULL, name );
  312. html::td_object( html_output, html::FORMATTED, NULL, "%f", val );
  313. }
  314. void add_param(FILE* html_output, const char* name, const char* val, bool alt)
  315. {
  316. html::tr_object tr( html_output, "class", alt ? "alt" : "none", NULL );
  317. html::th_object( html_output, html::FORMATTED, NULL, name );
  318. html::td_object( html_output, html::FORMATTED, NULL, "%s", val );
  319. }
  320. void stats_string(char* buffer, const uint32 px, const char* units, const float v, const float p, const float range)
  321. {
  322. sprintf(buffer,"<span><statnum style=\"width:%upx;\">%.1f %s</statnum> <statbar style=\"width:%.1f%%%%\">\'</statbar></span>", px, v, units, 2.0f + range * p);
  323. }
  324. } // anonymous namespace
  325. void generate_report_header(const uint32 n_reads, const Params& params, AlignmentStats& aln_stats, const uint32 n_devices, const Stats* device_stats, const char* report)
  326. {
  327. if (report == NULL)
  328. return;
  329. FILE* html_output = fopen( report, "w" );
  330. if (html_output == NULL)
  331. {
  332. log_warning( stderr, "unable to write HTML report \"%s\"\n", report );
  333. return;
  334. }
  335. {
  336. const uint32 n_mapped = aln_stats.n_mapped;
  337. html::html_object html( html_output );
  338. {
  339. const char* meta_list = "<meta http-equiv=\"refresh\" content=\"2\" />";
  340. html::header_object hd( html_output, "Bowtie2 Report", html::style(), meta_list );
  341. {
  342. html::body_object body( html_output );
  343. //
  344. // params
  345. //
  346. {
  347. html::table_object table( html_output, "params", "params", "parameters" );
  348. {
  349. html::tr_object tr( html_output, NULL );
  350. html::th_object( html_output, html::FORMATTED, NULL, "parameter name" );
  351. html::th_object( html_output, html::FORMATTED, NULL, "value" );
  352. }
  353. add_param( html_output, "randomized", params.randomized ? "yes" : "no", true );
  354. add_param( html_output, "N", params.allow_sub, false );
  355. add_param( html_output, "seed-len", params.seed_len, true );
  356. add_param( html_output, "subseed-len", params.subseed_len, false );
  357. add_param( html_output, "seed-freq", params.seed_freq.type_symbol(), true );
  358. add_param( html_output, "seed-freq", params.seed_freq.k, true );
  359. add_param( html_output, "seed-freq", params.seed_freq.m, true );
  360. add_param( html_output, "max-reseed", params.max_reseed, false );
  361. add_param( html_output, "rep-seeds", params.rep_seeds, true );
  362. add_param( html_output, "max-hits", params.max_hits, false );
  363. add_param( html_output, "max-dist", params.max_dist, true );
  364. add_param( html_output, "max-effort", params.max_effort, false );
  365. add_param( html_output, "min-ext", params.min_ext, true );
  366. add_param( html_output, "max-ext", params.max_ext, false );
  367. add_param( html_output, "mapQ-filter", params.mapq_filter, true );
  368. add_param( html_output, "scoring", params.scoring_file.c_str(), false );
  369. add_param( html_output, "report", params.report.c_str(), true );
  370. }
  371. //
  372. // per-device stats
  373. //
  374. {
  375. char span_string[1024];
  376. html::table_object table( html_output, "device-stats", "stats", "device stats" );
  377. {
  378. html::tr_object tr( html_output, NULL );
  379. html::th_object( html_output, html::FORMATTED, NULL, "" );
  380. html::th_object( html_output, html::FORMATTED, NULL, "time" );
  381. html::th_object( html_output, html::FORMATTED, NULL, "avg speed" );
  382. html::th_object( html_output, html::FORMATTED, NULL, "reads" );
  383. }
  384. float global_time = 0.0f;
  385. for (uint32 i = 0; i < n_devices; ++i)
  386. global_time += device_stats[i].global_time;
  387. for (uint32 i = 0; i < n_devices; ++i)
  388. {
  389. char link_name[1024];
  390. char device_name[64];
  391. sprintf( device_name, "device %u", i );
  392. sprintf( link_name, "<a href=\"%s\">%s</a>", local_file( device_file_name( report, i ) ), device_name );
  393. const char* cls = "none";
  394. html::tr_object tr( html_output, NULL );
  395. html::th_object( html_output, html::FORMATTED, NULL, link_name );
  396. stats_string( span_string, 40, "s", device_stats[i].global_time, device_stats[i].global_time / global_time, 75.0f );
  397. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, span_string );
  398. html::td_object( html_output, html::FORMATTED, NULL, "%.2f K reads/s", 1.0e-3f * float(device_stats[i].n_reads) / device_stats[i].global_time );
  399. html::td_object( html_output, html::FORMATTED, NULL, "%.2f M", float(device_stats[i].n_reads) * 1.0e-6f );
  400. }
  401. }
  402. //
  403. // mapping stats
  404. //
  405. {
  406. html::table_object table( html_output, "mapping-stats", "stats", "mapping stats" );
  407. {
  408. html::tr_object tr( html_output, NULL );
  409. html::th_object( html_output, html::FORMATTED, NULL, "" );
  410. html::th_object( html_output, html::FORMATTED, NULL, "mapped" );
  411. html::th_object( html_output, html::FORMATTED, NULL, "ambiguous" );
  412. html::th_object( html_output, html::FORMATTED, NULL, "multiple" );
  413. }
  414. {
  415. html::tr_object tr( html_output, "class", "alt", NULL );
  416. html::th_object( html_output, html::FORMATTED, NULL, "reads" );
  417. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(n_mapped)/float(n_reads) );
  418. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(aln_stats.n_ambiguous)/float(n_reads) );
  419. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(aln_stats.n_multiple)/float(n_reads) );
  420. }
  421. {
  422. html::tr_object tr( html_output, NULL );
  423. html::th_object( html_output, html::FORMATTED, NULL, "edit distance" );
  424. html::th_object( html_output, html::FORMATTED, NULL, "total" );
  425. html::th_object( html_output, html::FORMATTED, NULL, "forward" );
  426. html::th_object( html_output, html::FORMATTED, NULL, "reverse" );
  427. }
  428. uint32 best_bin[2] = {0};
  429. uint32 best_bin_val[2] = {0};
  430. for (uint32 i = 0; i < aln_stats.mapped_ed_histogram.size(); ++i)
  431. {
  432. const uint32 v = aln_stats.mapped_ed_histogram[i];
  433. if (best_bin_val[0] < v)
  434. {
  435. best_bin_val[1] = best_bin_val[0];
  436. best_bin[1] = best_bin[0];
  437. best_bin_val[0] = v;
  438. best_bin[0] = i;
  439. }
  440. else if (best_bin_val[1] < v)
  441. {
  442. best_bin_val[1] = v;
  443. best_bin[1] = i;
  444. }
  445. }
  446. for (uint32 i = 0; i < aln_stats.mapped_ed_histogram.size(); ++i)
  447. {
  448. const uint32 v = aln_stats.mapped_ed_histogram[i];
  449. const uint32 vf = aln_stats.mapped_ed_histogram_fwd[i];
  450. const uint32 vr = aln_stats.mapped_ed_histogram_rev[i];
  451. if (float(v)/float(n_reads) < 1.0e-3f)
  452. continue;
  453. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  454. html::th_object( html_output, html::FORMATTED, NULL, "%u", i );
  455. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  456. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(v)/float(n_reads) );
  457. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(vf)/float(n_reads) );
  458. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(vr)/float(n_reads) );
  459. }
  460. }
  461. //
  462. // mapping quality stats
  463. //
  464. {
  465. html::table_object table( html_output, "mapping-quality-stats", "stats", "mapping quality stats" );
  466. {
  467. html::tr_object tr( html_output, NULL );
  468. html::th_object( html_output, html::FORMATTED, NULL, "mapQ" );
  469. html::th_object( html_output, html::FORMATTED, NULL, "percentage" );
  470. }
  471. // rebin to a logarithmic scale
  472. uint64 bins[7] = {0};
  473. for (uint32 i = 0; i < 64; ++i)
  474. {
  475. const uint32 log_mapq = i ? nvbio::log2(i) + 1 : 0;
  476. bins[log_mapq] += aln_stats.mapq_bins[i];
  477. }
  478. // compute best bins
  479. uint32 best_bin[2] = {0};
  480. uint64 best_bin_val[2] = {0};
  481. for (uint32 i = 0; i < 7; ++i)
  482. {
  483. if (best_bin_val[0] < bins[i])
  484. {
  485. best_bin_val[1] = best_bin_val[0];
  486. best_bin[1] = best_bin[0];
  487. best_bin_val[0] = bins[i];
  488. best_bin[0] = i;
  489. }
  490. else if (best_bin_val[1] < bins[i])
  491. {
  492. best_bin_val[1] = bins[i];
  493. best_bin[1] = i;
  494. }
  495. }
  496. // output html table
  497. for (uint32 i = 0; i < 7; ++i)
  498. {
  499. const uint32 bin_size = 1u << (i-1);
  500. char buffer[1024];
  501. if (i <= 1)
  502. sprintf( buffer, "%u", i );
  503. else if (bin_size < 1024)
  504. sprintf( buffer, "%u - %u", bin_size, bin_size*2-1 );
  505. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  506. html::th_object( html_output, html::FORMATTED, NULL, buffer );
  507. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  508. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(bins[i])/float(n_reads) );
  509. }
  510. }
  511. //
  512. // best2-mapping stats
  513. //
  514. {
  515. // compute best 2 entries among double alignments
  516. uint2 best_bin2[2] = { make_uint2(0,0) };
  517. uint32 best_bin2_val[2] = { 0 };
  518. for (uint32 i = 1; i <= 16; ++i)
  519. {
  520. for (uint32 j = 1; j <= 16; ++j)
  521. {
  522. if (best_bin2_val[0] < aln_stats.mapped_ed_correlation[i][j])
  523. {
  524. best_bin2_val[1] = best_bin2_val[0];
  525. best_bin2[1] = best_bin2[0];
  526. best_bin2_val[0] = aln_stats.mapped_ed_correlation[i][j];
  527. best_bin2[0] = make_uint2(i,j);
  528. }
  529. else if (best_bin2_val[1] < aln_stats.mapped_ed_correlation[i][j])
  530. {
  531. best_bin2_val[1] = aln_stats.mapped_ed_correlation[i][j];
  532. best_bin2[1] = make_uint2(i,j);
  533. }
  534. }
  535. }
  536. // compute best 2 entries among single alignments
  537. uint2 best_bin1[2] = { make_uint2(0,0) };
  538. uint32 best_bin1_val[2] = { 0 };
  539. for (uint32 i = 0; i <= 16; ++i)
  540. {
  541. if (best_bin1_val[0] < aln_stats.mapped_ed_correlation[i][0])
  542. {
  543. best_bin1_val[1] = best_bin1_val[0];
  544. best_bin1[1] = best_bin1[0];
  545. best_bin1_val[0] = aln_stats.mapped_ed_correlation[i][0];
  546. best_bin1[0] = make_uint2(i,0);
  547. }
  548. else if (best_bin1_val[1] < aln_stats.mapped_ed_correlation[i][0])
  549. {
  550. best_bin1_val[1] = aln_stats.mapped_ed_correlation[i][0];
  551. best_bin1[1] = make_uint2(i,0);
  552. }
  553. }
  554. html::table_object table( html_output, "best2-mapping-stats", "stats", "best2 mapping stats" );
  555. {
  556. html::tr_object tr( html_output, NULL );
  557. html::th_object( html_output, html::FORMATTED, NULL, "" );
  558. for (uint32 i = 0; i <= 16; ++i)
  559. html::th_object( html_output, html::FORMATTED, NULL, (i == 0 ? "-" : "%u"), i-1 );
  560. }
  561. for (uint32 i = 0; i <= 16; ++i)
  562. {
  563. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  564. html::th_object( html_output, html::FORMATTED, NULL, (i == 0 ? "-" : "%u"), i-1 );
  565. for (uint32 j = 0; j <= 16; ++j)
  566. {
  567. const uint32 v = aln_stats.mapped_ed_correlation[i][j];
  568. if (100.0f * float(v)/float(n_reads) >= 0.1f)
  569. {
  570. const char* cls = ((i == best_bin1[0].x && j == best_bin1[0].y) ||
  571. (i == best_bin2[0].x && j == best_bin2[0].y)) ? "yellow" :
  572. ((i == best_bin1[1].x && j == best_bin1[1].y) ||
  573. (i == best_bin2[1].x && j == best_bin2[1].y)) ? "orange" :
  574. (i == j) ? "pink" :
  575. (i+1 == j) ? "azure" : "none";
  576. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(v)/float(n_reads) );
  577. }
  578. else if (100.0f * float(v)/float(n_reads) >= 0.01f)
  579. html::td_object( html_output, html::FORMATTED, "class", "small", NULL, "%.2f %%", 100.0f * float(v)/float(n_reads) );
  580. else
  581. {
  582. const char* cls = (i > params.max_dist+1 || j > params.max_dist+1) ? "gray" : "none";
  583. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "-" );
  584. }
  585. }
  586. }
  587. }
  588. }
  589. }
  590. }
  591. fclose( html_output );
  592. }
  593. void generate_device_report(const uint32 id, Stats& stats, AlignmentStats& aln_stats, const char* report)
  594. {
  595. if (report == NULL)
  596. return;
  597. std::vector<KernelStats*> kernel_stats;
  598. kernel_stats.push_back( &stats.map );
  599. kernel_stats.push_back( &stats.select );
  600. kernel_stats.push_back( &stats.sort );
  601. kernel_stats.push_back( &stats.locate );
  602. kernel_stats.push_back( &stats.score );
  603. kernel_stats.push_back( &stats.opposite_score );
  604. kernel_stats.push_back( &stats.backtrack );
  605. kernel_stats.push_back( &stats.backtrack_opposite );
  606. kernel_stats.push_back( &stats.finalize );
  607. kernel_stats.push_back( &stats.alignments_DtoH );
  608. kernel_stats.push_back( &stats.read_HtoD );
  609. kernel_stats.push_back( &stats.read_io );
  610. //if (stats.params.keep_stats)
  611. {
  612. for (uint32 i = 0; i < kernel_stats.size(); ++i)
  613. generate_kernel_table( id, report, *kernel_stats[i] );
  614. }
  615. const std::string device_report = device_file_name( report, id );
  616. FILE* html_output = fopen( device_report.c_str(), "w" );
  617. if (html_output == NULL)
  618. {
  619. log_warning( stderr, "unable to write HTML report \"%s\"\n", device_report.c_str() );
  620. return;
  621. }
  622. {
  623. const uint32 n_reads = stats.n_reads;
  624. const uint32 n_mapped = aln_stats.n_mapped;
  625. html::html_object html( html_output );
  626. {
  627. const char* meta_list = "<meta http-equiv=\"refresh\" content=\"2\" />";
  628. html::header_object hd( html_output, "Bowtie2 Report", html::style(), meta_list );
  629. {
  630. html::body_object body( html_output );
  631. //
  632. // speed stats
  633. //
  634. {
  635. char span_string[1024];
  636. html::table_object table( html_output, "speed-stats", "stats", "speed stats" );
  637. float worst_time[2] = {0};
  638. uint32 worst[2] = {0};
  639. for (uint32 i = 0; i < kernel_stats.size(); ++i)
  640. {
  641. const float time = kernel_stats[i]->time;
  642. if (worst_time[0] < time)
  643. {
  644. worst_time[1] = worst_time[0];
  645. worst[1] = worst[0];
  646. worst_time[0] = time;
  647. worst[0] = i;
  648. }
  649. else if (worst_time[1] < time)
  650. {
  651. worst_time[1] = time;
  652. worst[1] = i;
  653. }
  654. }
  655. {
  656. html::tr_object tr( html_output, NULL );
  657. html::th_object( html_output, html::FORMATTED, NULL, "" );
  658. html::th_object( html_output, html::FORMATTED, NULL, "time" );
  659. html::th_object( html_output, html::FORMATTED, NULL, "avg speed" );
  660. html::th_object( html_output, html::FORMATTED, NULL, "max speed" );
  661. }
  662. {
  663. html::tr_object tr( html_output, "class", "alt", NULL );
  664. html::th_object( html_output, html::FORMATTED, NULL, "total" );
  665. html::td_object( html_output, html::FORMATTED, "class", "red", NULL, "%.1f s", stats.global_time );
  666. html::td_object( html_output, html::FORMATTED, NULL, "%.1f K reads/s", 1.0e-3f * float(n_reads)/stats.global_time );
  667. html::td_object( html_output, "-", NULL );
  668. }
  669. for (uint32 i = 0; i < kernel_stats.size(); ++i)
  670. {
  671. const KernelStats& kstats = *kernel_stats[i];
  672. const char* name = kstats.name.c_str();
  673. const char* units = kstats.units.c_str();
  674. const std::string file_name = generate_file_name( report, id, name );
  675. char link_name[1024];
  676. sprintf( link_name, "<a href=\"%s\">%s</a>", local_file( file_name ), name );
  677. const char* cls = worst[0] == i ? "yellow" : worst[1] == i ? "orange" : "none";
  678. html::tr_object tr( html_output, NULL );
  679. html::th_object( html_output, html::FORMATTED, NULL, link_name );
  680. stats_string( span_string, 40, "s", kstats.time, kstats.time / stats.global_time, 75.0f );
  681. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, span_string );
  682. html::td_object( html_output, html::FORMATTED, NULL, "%.2f M %s/s", 1.0e-6f * float(kstats.calls)/kstats.time, units );
  683. html::td_object( html_output, html::FORMATTED, NULL, "%.2f M %s/s", 1.0e-6f * kstats.max_speed, units );
  684. }
  685. }
  686. //
  687. // mapping stats
  688. //
  689. {
  690. html::table_object table( html_output, "mapping-stats", "stats", "mapping stats" );
  691. {
  692. html::tr_object tr( html_output, NULL );
  693. html::th_object( html_output, html::FORMATTED, NULL, "" );
  694. html::th_object( html_output, html::FORMATTED, NULL, "mapped" );
  695. html::th_object( html_output, html::FORMATTED, NULL, "ambiguous" );
  696. html::th_object( html_output, html::FORMATTED, NULL, "multiple" );
  697. }
  698. {
  699. html::tr_object tr( html_output, "class", "alt", NULL );
  700. html::th_object( html_output, html::FORMATTED, NULL, "reads" );
  701. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(n_mapped)/float(n_reads) );
  702. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(aln_stats.n_ambiguous)/float(n_reads) );
  703. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(aln_stats.n_multiple)/float(n_reads) );
  704. }
  705. {
  706. html::tr_object tr( html_output, NULL );
  707. html::th_object( html_output, html::FORMATTED, NULL, "edit distance" );
  708. html::th_object( html_output, html::FORMATTED, NULL, "total" );
  709. html::th_object( html_output, html::FORMATTED, NULL, "forward" );
  710. html::th_object( html_output, html::FORMATTED, NULL, "reverse" );
  711. }
  712. uint32 best_bin[2] = {0};
  713. uint32 best_bin_val[2] = {0};
  714. for (uint32 i = 0; i < aln_stats.mapped_ed_histogram.size(); ++i)
  715. {
  716. const uint32 v = aln_stats.mapped_ed_histogram[i];
  717. if (best_bin_val[0] < v)
  718. {
  719. best_bin_val[1] = best_bin_val[0];
  720. best_bin[1] = best_bin[0];
  721. best_bin_val[0] = v;
  722. best_bin[0] = i;
  723. }
  724. else if (best_bin_val[1] < v)
  725. {
  726. best_bin_val[1] = v;
  727. best_bin[1] = i;
  728. }
  729. }
  730. for (uint32 i = 0; i < aln_stats.mapped_ed_histogram.size(); ++i)
  731. {
  732. const uint32 v = aln_stats.mapped_ed_histogram[i];
  733. const uint32 vf = aln_stats.mapped_ed_histogram_fwd[i];
  734. const uint32 vr = aln_stats.mapped_ed_histogram_rev[i];
  735. if (float(v)/float(n_reads) < 1.0e-3f)
  736. continue;
  737. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  738. html::th_object( html_output, html::FORMATTED, NULL, "%u", i );
  739. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  740. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(v)/float(n_reads) );
  741. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(vf)/float(n_reads) );
  742. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * float(vr)/float(n_reads) );
  743. }
  744. }
  745. //
  746. // mapping quality stats
  747. //
  748. {
  749. html::table_object table( html_output, "mapping-quality-stats", "stats", "mapping quality stats" );
  750. {
  751. html::tr_object tr( html_output, NULL );
  752. html::th_object( html_output, html::FORMATTED, NULL, "mapQ" );
  753. html::th_object( html_output, html::FORMATTED, NULL, "percentage" );
  754. }
  755. // rebin to a logarithmic scale
  756. uint64 bins[7] = {0};
  757. for (uint32 i = 0; i < 64; ++i)
  758. {
  759. const uint32 log_mapq = i ? nvbio::log2(i) + 1 : 0;
  760. bins[log_mapq] += aln_stats.mapq_bins[i];
  761. }
  762. // compute best bins
  763. uint32 best_bin[2] = {0};
  764. uint64 best_bin_val[2] = {0};
  765. for (uint32 i = 0; i < 7; ++i)
  766. {
  767. if (best_bin_val[0] < bins[i])
  768. {
  769. best_bin_val[1] = best_bin_val[0];
  770. best_bin[1] = best_bin[0];
  771. best_bin_val[0] = bins[i];
  772. best_bin[0] = i;
  773. }
  774. else if (best_bin_val[1] < bins[i])
  775. {
  776. best_bin_val[1] = bins[i];
  777. best_bin[1] = i;
  778. }
  779. }
  780. // output html table
  781. for (uint32 i = 0; i < 7; ++i)
  782. {
  783. const uint32 bin_size = 1u << (i-1);
  784. char buffer[1024];
  785. if (i <= 1)
  786. sprintf( buffer, "%u", i );
  787. else if (bin_size < 1024)
  788. sprintf( buffer, "%u - %u", bin_size, bin_size*2-1 );
  789. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  790. html::th_object( html_output, html::FORMATTED, NULL, buffer );
  791. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  792. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(bins[i])/float(n_reads) );
  793. }
  794. }
  795. //
  796. // best2-mapping stats
  797. //
  798. {
  799. // compute best 2 entries among double alignments
  800. uint2 best_bin2[2] = { make_uint2(0,0) };
  801. uint32 best_bin2_val[2] = { 0 };
  802. for (uint32 i = 1; i <= 16; ++i)
  803. {
  804. for (uint32 j = 1; j <= 16; ++j)
  805. {
  806. if (best_bin2_val[0] < aln_stats.mapped_ed_correlation[i][j])
  807. {
  808. best_bin2_val[1] = best_bin2_val[0];
  809. best_bin2[1] = best_bin2[0];
  810. best_bin2_val[0] = aln_stats.mapped_ed_correlation[i][j];
  811. best_bin2[0] = make_uint2(i,j);
  812. }
  813. else if (best_bin2_val[1] < aln_stats.mapped_ed_correlation[i][j])
  814. {
  815. best_bin2_val[1] = aln_stats.mapped_ed_correlation[i][j];
  816. best_bin2[1] = make_uint2(i,j);
  817. }
  818. }
  819. }
  820. // compute best 2 entries among single alignments
  821. uint2 best_bin1[2] = { make_uint2(0,0) };
  822. uint32 best_bin1_val[2] = { 0 };
  823. for (uint32 i = 0; i <= 16; ++i)
  824. {
  825. if (best_bin1_val[0] < aln_stats.mapped_ed_correlation[i][0])
  826. {
  827. best_bin1_val[1] = best_bin1_val[0];
  828. best_bin1[1] = best_bin1[0];
  829. best_bin1_val[0] = aln_stats.mapped_ed_correlation[i][0];
  830. best_bin1[0] = make_uint2(i,0);
  831. }
  832. else if (best_bin1_val[1] < aln_stats.mapped_ed_correlation[i][0])
  833. {
  834. best_bin1_val[1] = aln_stats.mapped_ed_correlation[i][0];
  835. best_bin1[1] = make_uint2(i,0);
  836. }
  837. }
  838. html::table_object table( html_output, "best2-mapping-stats", "stats", "best2 mapping stats" );
  839. {
  840. html::tr_object tr( html_output, NULL );
  841. html::th_object( html_output, html::FORMATTED, NULL, "" );
  842. for (uint32 i = 0; i <= 16; ++i)
  843. html::th_object( html_output, html::FORMATTED, NULL, (i == 0 ? "-" : "%u"), i-1 );
  844. }
  845. for (uint32 i = 0; i <= 16; ++i)
  846. {
  847. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  848. html::th_object( html_output, html::FORMATTED, NULL, (i == 0 ? "-" : "%u"), i-1 );
  849. for (uint32 j = 0; j <= 16; ++j)
  850. {
  851. const uint32 v = aln_stats.mapped_ed_correlation[i][j];
  852. if (100.0f * float(v)/float(n_reads) >= 0.1f)
  853. {
  854. const char* cls = ((i == best_bin1[0].x && j == best_bin1[0].y) ||
  855. (i == best_bin2[0].x && j == best_bin2[0].y)) ? "yellow" :
  856. ((i == best_bin1[1].x && j == best_bin1[1].y) ||
  857. (i == best_bin2[1].x && j == best_bin2[1].y)) ? "orange" :
  858. (i == j) ? "pink" :
  859. (i+1 == j) ? "azure" : "none";
  860. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%.1f %%", 100.0f * float(v)/float(n_reads) );
  861. }
  862. else if (100.0f * float(v)/float(n_reads) >= 0.01f)
  863. html::td_object( html_output, html::FORMATTED, "class", "small", NULL, "%.2f %%", 100.0f * float(v)/float(n_reads) );
  864. else
  865. {
  866. const char* cls = (i > stats.params.max_dist+1 || j > stats.params.max_dist+1) ? "gray" : "none";
  867. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "-" );
  868. }
  869. }
  870. }
  871. }
  872. //
  873. // seeding stats
  874. //
  875. if (stats.params.keep_stats)
  876. {
  877. // copy stats locally
  878. uint64 hits_total = stats.hits_total;
  879. uint64 hits_ranges = stats.hits_ranges;
  880. uint32 hits_max = stats.hits_max;
  881. uint32 hits_max_range = stats.hits_max_range;
  882. uint64 hits_top_total = stats.hits_top_total;
  883. uint32 hits_top_max = stats.hits_top_max;
  884. uint64 hits_bins[28];
  885. uint64 hits_bins_sum = 0;
  886. uint64 hits_top_bins[28];
  887. uint64 hits_top_bins_sum = 0;
  888. for (uint32 i = 0; i < 28; ++i)
  889. {
  890. hits_bins_sum += hits_bins[i] = stats.hits_bins[i];
  891. hits_top_bins_sum += hits_top_bins[i] = stats.hits_top_bins[i];
  892. }
  893. html::table_object table( html_output, "seeding-stats", "stats", "seeding stats" );
  894. char buffer[1024];
  895. {
  896. html::tr_object tr( html_output, NULL );
  897. html::th_object( html_output, html::FORMATTED, NULL, "" );
  898. html::th_object( html_output, html::FORMATTED, NULL, "seed hits" );
  899. html::th_object( html_output, html::FORMATTED, NULL, "top-seed hits" );
  900. html::th_object( html_output, html::FORMATTED, NULL, "seed ranges" );
  901. html::th_object( html_output, html::FORMATTED, NULL, "range size" );
  902. }
  903. {
  904. html::tr_object tr( html_output, "class", "alt", NULL );
  905. html::th_object( html_output, html::FORMATTED, NULL, "avg" );
  906. html::td_object( html_output, html::FORMATTED, NULL, "%.1f", float(hits_total)/float(n_reads) );
  907. html::td_object( html_output, html::FORMATTED, NULL, "%.1f", float(hits_top_total)/float(n_reads) );
  908. html::td_object( html_output, html::FORMATTED, NULL, "%.1f", float(hits_ranges)/float(n_reads) );
  909. html::td_object( html_output, html::FORMATTED, NULL, "%.1f", float(hits_total)/float(hits_ranges) );
  910. }
  911. {
  912. html::tr_object tr( html_output, NULL );
  913. html::th_object( html_output, html::FORMATTED, NULL, "max" );
  914. html::td_object( html_output, html::FORMATTED, NULL, "%u", hits_max );
  915. html::td_object( html_output, html::FORMATTED, NULL, "%u", hits_top_max );
  916. html::td_object( html_output, html::FORMATTED, NULL, "" );
  917. html::td_object( html_output, html::FORMATTED, NULL, "%u", hits_max_range );
  918. }
  919. {
  920. html::tr_object tr( html_output, NULL );
  921. html::th_object( html_output, html::FORMATTED, NULL, "# hits" );
  922. html::th_object( html_output, html::FORMATTED, NULL, "%% of seeds" );
  923. html::th_object( html_output, html::FORMATTED, NULL, "%% of top-seeds" );
  924. html::th_object( html_output, html::FORMATTED, NULL, "" );
  925. html::th_object( html_output, html::FORMATTED, NULL, "" );
  926. }
  927. uint32 max_bin = 0;
  928. uint32 best_bin[2] = {0};
  929. uint64 best_bin_val[2] = {0};
  930. uint32 best_top_bin[2] = {0};
  931. uint64 best_top_bin_val[2] = {0};
  932. for (uint32 i = 0; i < 28; ++i)
  933. {
  934. max_bin = float(hits_bins[i]) / float(hits_bins_sum) > 0.001f ? i : max_bin;
  935. max_bin = float(hits_top_bins[i]) / float(hits_top_bins_sum) > 0.001f ? i : max_bin;
  936. if (best_bin_val[0] < hits_bins[i])
  937. {
  938. best_bin_val[1] = best_bin_val[0];
  939. best_bin[1] = best_bin[0];
  940. best_bin_val[0] = hits_bins[i];
  941. best_bin[0] = i;
  942. }
  943. else if (best_bin_val[1] < hits_bins[i])
  944. {
  945. best_bin_val[1] = hits_bins[i];
  946. best_bin[1] = i;
  947. }
  948. if (best_top_bin_val[0] < hits_top_bins[i])
  949. {
  950. best_top_bin_val[1] = best_top_bin_val[0];
  951. best_top_bin[1] = best_top_bin[0];
  952. best_top_bin_val[0] = hits_top_bins[i];
  953. best_top_bin[0] = i;
  954. }
  955. else if (best_top_bin_val[1] < hits_top_bins[i])
  956. {
  957. best_top_bin_val[1] = hits_top_bins[i];
  958. best_top_bin[1] = i;
  959. }
  960. }
  961. for (uint32 i = 0; i < max_bin; ++i)
  962. {
  963. const uint32 bin_size = 1u << (i-1);
  964. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  965. if (i <= 1)
  966. sprintf( buffer, "%u", i );
  967. else if (bin_size < 512)
  968. sprintf( buffer, "%u - %u", bin_size, bin_size*2-1 );
  969. else if (bin_size == 512)
  970. sprintf( buffer, "0.5K - 1K" );
  971. else
  972. sprintf( buffer, "%uK - %uK", bin_size/1024, bin_size*2/1024 );
  973. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  974. const char* cls_top = i == best_top_bin[0] ? "yellow" : i == best_top_bin[1] ? "orange" : "none";
  975. html::th_object( html_output, html::FORMATTED, NULL, buffer );
  976. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, "%4.1f %%", 100.0f * float(hits_bins[i]) / float(hits_bins_sum) );
  977. html::td_object( html_output, html::FORMATTED, "class", cls_top, NULL, "%4.1f %%", 100.0f * float(hits_top_bins[i]) / float(hits_top_bins_sum) );
  978. html::td_object( html_output, html::FORMATTED, NULL, "" );
  979. html::td_object( html_output, html::FORMATTED, NULL, "" );
  980. }
  981. }
  982. }
  983. }
  984. }
  985. fclose( html_output );
  986. }
  987. template <typename T>
  988. void find_gt2(const uint32 n, const T* table, uint32 best_bin[2])
  989. {
  990. best_bin[0] = best_bin[1] = 0;
  991. T best_bin_val[2] = {0};
  992. for (uint32 i = 0; i < n; ++i)
  993. {
  994. if (best_bin_val[0] < table[i])
  995. {
  996. best_bin_val[1] = best_bin_val[0];
  997. best_bin[1] = best_bin[0];
  998. best_bin_val[0] = table[i];
  999. best_bin[0] = i;
  1000. }
  1001. else if (best_bin_val[1] < table[i])
  1002. {
  1003. best_bin_val[1] = table[i];
  1004. best_bin[1] = i;
  1005. }
  1006. }
  1007. }
  1008. void generate_kernel_table(const uint32 id, const char* report, const KernelStats& stats)
  1009. {
  1010. const char* name = stats.name.c_str();
  1011. const char* units = stats.units.c_str();
  1012. const std::string file_name = generate_file_name( report, id, name );
  1013. const std::deque< std::pair<uint32,float> >& table = stats.info;
  1014. FILE* html_output = fopen( file_name.c_str(), "w" );
  1015. if (html_output == NULL)
  1016. {
  1017. log_warning( stderr, "unable to write HTML report \"%s\"\n", file_name.c_str() );
  1018. return;
  1019. }
  1020. {
  1021. html::html_object html( html_output );
  1022. {
  1023. const char* meta_list = "<meta http-equiv=\"refresh\" content=\"2\" />";
  1024. html::header_object hd( html_output, "Bowtie2 Report", html::style(), meta_list );
  1025. {
  1026. html::body_object body( html_output );
  1027. //
  1028. // kernel summary stats
  1029. //
  1030. {
  1031. uint32 bin_calls[32] = {0};
  1032. float bin_sum_time[32] = {0.0f};
  1033. float bin_avg_time[32] = {0.0f};
  1034. float bin_speed[32] = {0.0f};
  1035. float total_time = stats.time;
  1036. float avg_speed = total_time ? float(double(stats.calls) / double(stats.time)) : 0.0f;
  1037. float max_speed = 0.0f;
  1038. for (uint32 bin = 0; bin < 32; ++bin)
  1039. {
  1040. bin_calls[bin] = stats.bin_calls[bin];
  1041. bin_sum_time[bin] = stats.bin_time[bin];
  1042. bin_avg_time[bin] = stats.bin_calls[bin] ? stats.bin_time[bin] / stats.bin_calls[bin] : 0.0f;
  1043. bin_speed[bin] = stats.bin_time[bin] ? float(double(stats.bin_items[bin]) / double(stats.bin_time[bin])) : 0.0f;
  1044. //bin_speed[bin] = stats.bin_calls[bin] ? stats.bin_speed[bin] / stats.bin_calls[bin] : 0.0f;
  1045. max_speed = std::max( max_speed, bin_speed[bin] );
  1046. }
  1047. char buffer1[1024];
  1048. char buffer2[1024];
  1049. sprintf( buffer1, "%s-summary-stats", name );
  1050. sprintf( buffer2, "%s summary stats", name );
  1051. html::table_object tab( html_output, buffer1, "stats", buffer2 );
  1052. {
  1053. html::tr_object tr( html_output, NULL );
  1054. html::th_object( html_output, html::FORMATTED, NULL, "items" );
  1055. html::td_object( html_output, html::FORMATTED, NULL, "%.2f M", float(stats.calls) * 1.0e-6f );
  1056. }
  1057. // write "user" stats
  1058. for (uint32 i = 0; i < 32; ++i)
  1059. {
  1060. if (stats.user_names[i] == NULL)
  1061. break;
  1062. html::tr_object tr( html_output, NULL );
  1063. html::th_object( html_output, html::FORMATTED, NULL, stats.user_names[i] );
  1064. html::td_object( html_output, html::FORMATTED, NULL, "%.3f %s",
  1065. stats.user_avg[i] ? stats.user[i]/float(stats.num) :
  1066. stats.user[i],
  1067. stats.user_units[i] );
  1068. }
  1069. {
  1070. html::tr_object tr( html_output, NULL );
  1071. html::th_object( html_output, html::FORMATTED, NULL, "batch size (%s)", units );
  1072. html::th_object( html_output, html::FORMATTED, NULL, "calls" );
  1073. html::th_object( html_output, html::FORMATTED, NULL, "avg time" );
  1074. html::th_object( html_output, html::FORMATTED, NULL, "sum time" );
  1075. html::th_object( html_output, html::FORMATTED, NULL, "cumul. time" );
  1076. html::th_object( html_output, html::FORMATTED, NULL, "speed" );
  1077. }
  1078. uint32 best_avg_bin[2];
  1079. uint32 best_sum_bin[2];
  1080. find_gt2( 32, bin_avg_time, best_avg_bin );
  1081. find_gt2( 32, bin_sum_time, best_sum_bin );
  1082. float cum_time = 0.0f;
  1083. float max_avg_time = 0.0f;
  1084. float max_sum_time = 0.0f;
  1085. for (uint32 i = 0; i < 32; ++i)
  1086. {
  1087. max_avg_time = nvbio::max( bin_avg_time[i], max_avg_time );
  1088. max_sum_time = nvbio::max( bin_sum_time[i], max_sum_time );
  1089. }
  1090. char span_string[1024];
  1091. for (uint32 i = 0; i < 32; ++i)
  1092. {
  1093. if (bin_calls[i] == 0)
  1094. continue;
  1095. const float speed = bin_speed[i];
  1096. cum_time += bin_sum_time[i];
  1097. const uint32 bin_size = 1u << i;
  1098. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  1099. if (bin_size == 1)
  1100. html::th_object( html_output, html::FORMATTED, NULL, "1" );
  1101. else if (bin_size < 512)
  1102. html::th_object( html_output, html::FORMATTED, NULL, "%u - %u", bin_size, bin_size*2-1 );
  1103. else if (bin_size == 512)
  1104. html::th_object( html_output, html::FORMATTED, NULL, "512 - 1K" );
  1105. else if (bin_size < 512*1024)
  1106. html::th_object( html_output, html::FORMATTED, NULL, "%uK- %uK", bin_size/1024, (bin_size*2)/1024 );
  1107. else if (bin_size == 512*1024)
  1108. html::th_object( html_output, html::FORMATTED, NULL, "512K - 1M" );
  1109. const char* avg_cls = i == best_avg_bin[0] ? "yellow" : i == best_avg_bin[1] ? "orange" : "none";
  1110. const char* sum_cls = i == best_sum_bin[0] ? "yellow" : i == best_sum_bin[1] ? "orange" : "none";
  1111. const char* spd_cls = speed == max_speed ? "yellow" : speed < avg_speed * 0.1f ? "red" : speed < max_speed * 0.1f ? "pink" : "none";
  1112. html::td_object( html_output, html::FORMATTED, NULL, "%u", bin_calls[i] );
  1113. stats_string( span_string, 60, "ms", 1000.0f * bin_avg_time[i], bin_avg_time[i] / max_avg_time, 50.0f );
  1114. html::td_object( html_output, html::FORMATTED, "class", avg_cls, NULL, span_string );
  1115. stats_string( span_string, 60, "ms", 1000.0f * bin_sum_time[i], bin_sum_time[i] / max_sum_time, 50.0f );
  1116. html::td_object( html_output, html::FORMATTED, "class", sum_cls, NULL, span_string );
  1117. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %%", 100.0f * cum_time / total_time );
  1118. html::td_object( html_output, html::FORMATTED, "class", spd_cls, NULL, "%.1f %c %s/s", speed * (speed >= 1.0e6f ? 1.0e-6f : 1.0e-3f), speed >= 1.0e6f ? 'M' : 'K', units );
  1119. }
  1120. }
  1121. //
  1122. // kernel table stats
  1123. //
  1124. {
  1125. char buffer1[1024];
  1126. char buffer2[1024];
  1127. sprintf( buffer1, "%s-stats", name );
  1128. sprintf( buffer2, "%s stats", name );
  1129. html::table_object tab( html_output, buffer1, "stats", buffer2 );
  1130. {
  1131. html::tr_object tr( html_output, NULL );
  1132. html::th_object( html_output, html::FORMATTED, NULL, "launch" );
  1133. html::th_object( html_output, html::FORMATTED, NULL, "batch size (%s)", units );
  1134. html::th_object( html_output, html::FORMATTED, NULL, "time" );
  1135. html::th_object( html_output, html::FORMATTED, NULL, "speed" );
  1136. }
  1137. uint32 best_bin[2] = {0};
  1138. float best_bin_val[2] = {0};
  1139. for (uint32 i = 0; i < table.size(); ++i)
  1140. {
  1141. if (best_bin_val[0] < table[i].second)
  1142. {
  1143. best_bin_val[1] = best_bin_val[0];
  1144. best_bin[1] = best_bin[0];
  1145. best_bin_val[0] = table[i].second;
  1146. best_bin[0] = i;
  1147. }
  1148. else if (best_bin_val[1] < table[i].second)
  1149. {
  1150. best_bin_val[1] = table[i].second;
  1151. best_bin[1] = i;
  1152. }
  1153. }
  1154. float max_time = 0.0f;
  1155. float max_speed = 0.0f;
  1156. for (uint32 i = 0; i < table.size(); ++i)
  1157. {
  1158. const float speed = float(table[i].first) / table[i].second;
  1159. max_time = nvbio::max( float(table[i].second), max_time );
  1160. max_speed = nvbio::max( speed, max_speed );
  1161. }
  1162. char span_string[1024];
  1163. char units_string[1024];
  1164. for (uint32 i = 0; i < table.size(); ++i)
  1165. {
  1166. const float speed = float(table[i].first) / table[i].second;
  1167. html::tr_object tr( html_output, "class", i % 2 ? "none" : "alt", NULL );
  1168. html::th_object( html_output, html::FORMATTED, NULL, "%u", i );
  1169. const char* cls = i == best_bin[0] ? "yellow" : i == best_bin[1] ? "orange" : "none";
  1170. html::td_object( html_output, html::FORMATTED, NULL, "%.1f %c", float(table[i].first) * (table[i].first > 1000000 ? 1.0e-6f : 1.0e-3f), table[i].first > 1000000 ? 'M' : 'K' );
  1171. stats_string( span_string, 50, "ms", 1000.0f * float(table[i].second), float(table[i].second) / max_time, 50.0f );
  1172. html::td_object( html_output, html::FORMATTED, "class", cls, NULL, span_string );
  1173. sprintf(units_string, "%c %s/s", speed > 1000000 ? 'M' : 'K', units );
  1174. stats_string( span_string, 100, units_string, speed * (speed >= 1.0e6f ? 1.0e-6f : 1.0e-3f), speed / max_speed, 50.0f );
  1175. html::td_object( html_output, html::FORMATTED, NULL, span_string );
  1176. }
  1177. }
  1178. }
  1179. }
  1180. }
  1181. fclose( html_output );
  1182. }
  1183. } // namespace cuda
  1184. } // namespace bowtie2
  1185. } // namespace nvbio