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