timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
print.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25#pragma once
26
29#include "timemory/mpl/math.hpp"
37
38#include <cstdint>
39#include <fstream>
40#include <iomanip>
41#include <iostream>
42#include <memory>
43#include <string>
44#include <vector>
45
46namespace tim
47{
48namespace operation
49{
50namespace finalize
51{
52//
53#if defined(TIMEMORY_OPERATIONS_SOURCE) || !defined(TIMEMORY_USE_OPERATIONS_EXTERN)
54//
55//--------------------------------------------------------------------------------------//
56//
58base::print::print_plot(const std::string& outfname, const std::string suffix) // NOLINT
59{
60 if(node_rank == 0)
61 {
62 auto plot_label = label;
63 if(!suffix.empty())
64 plot_label += std::string(" ") + suffix;
65
66 plotting::plot(label, plot_label, m_settings->get_output_path(),
67 m_settings->get_dart_output(), outfname);
68 }
69}
70//
71//--------------------------------------------------------------------------------------//
72//
74base::print::write(std::ostream& os, stream_type stream) // NOLINT
75{
76 if(stream)
77 os << *stream << std::flush;
78}
79//
80//--------------------------------------------------------------------------------------//
81//
83base::print::print_cout(stream_type stream) // NOLINT
84{
85 printf("\n");
86 write(std::cout, stream); // NOLINT
87 printf("\n");
88}
89//
90//--------------------------------------------------------------------------------------//
91//
93base::print::print_text(const std::string& outfname, stream_type stream) // NOLINT
94{
95 if(outfname.length() > 0 && stream)
96 {
97 std::ofstream fout{};
98 if(filepath::open(fout, outfname))
99 {
100 printf("[%s]|%i> Outputting '%s'...\n", label.c_str(), node_rank,
101 outfname.c_str());
102 write(fout, stream);
103 manager::instance()->add_text_output(label, outfname);
104 }
105 else
106 {
107 fprintf(stderr, "[storage<%s>::%s @ %i]|%i> Error opening '%s'...\n",
108 label.c_str(), __FUNCTION__, __LINE__, node_rank, outfname.c_str());
109 }
110 }
111}
112//
113//--------------------------------------------------------------------------------------//
114//
115#endif // !defined(TIMEMORY_OPERATIONS_SOURCE)
116//
117template <typename Tp>
119: base_type(trait::requires_json<Tp>::value, _settings)
120, data(_data)
121{
122 dmp::barrier();
123 node_init = dmp::is_initialized();
124 node_rank = dmp::rank();
125 node_size = dmp::size();
126 node_results = data->dmp_get();
127 if(tree_output())
128 node_tree = data->dmp_get(node_tree);
129 dmp::barrier();
130
131 settings::indent_width<Tp, 0>(Tp::get_width());
132 settings::indent_width<Tp, 1>(4);
133 settings::indent_width<Tp, 2>(4);
134
135 description = Tp::get_description();
136 for(auto& itr : description)
137 itr = toupper(itr);
138
139 // find the max width
140 for(const auto& mitr : node_results)
141 {
142 for(const auto& itr : mitr)
143 {
144 const auto& itr_obj = itr.data();
145 const auto& itr_prefix = itr.prefix();
146 const auto& itr_depth = itr.depth();
147
148 if(itr_depth < 0 || itr_depth > m_settings->get_max_depth() ||
149 itr_depth > max_call_stack)
150 continue;
151
152 max_depth = std::max<int64_t>(max_depth, itr_depth);
153
154 // find global max
155 settings::indent_width<Tp, 0>(itr_prefix.length());
156 settings::indent_width<Tp, 1>(std::log10(itr_obj.get_laps()) + 1);
157 settings::indent_width<Tp, 2>(std::log10(itr_depth) + 1);
158 }
159 }
160}
161//
162//--------------------------------------------------------------------------------------//
163//
164template <typename Tp>
165void
167{
168 settings::indent_width<Tp, 0>(Tp::get_width());
169 settings::indent_width<Tp, 1>(4);
170 settings::indent_width<Tp, 2>(4);
171
172 description = Tp::get_description();
173 for(auto& itr : description)
174 itr = toupper(itr);
175
176 // find the max width
177 for(const auto& mitr : node_results)
178 {
179 for(const auto& itr : mitr)
180 {
181 const auto& itr_obj = itr.data();
182 const auto& itr_prefix = itr.prefix();
183 const auto& itr_depth = itr.depth();
184
185 if(itr_depth < 0 || itr_depth > m_settings->get_max_depth() ||
186 itr_depth > max_call_stack)
187 continue;
188
189 max_depth = std::max<int64_t>(max_depth, itr_depth);
190
191 // find global max
192 settings::indent_width<Tp, 0>(itr_prefix.length());
193 settings::indent_width<Tp, 1>(std::log10(itr_obj.get_laps()) + 1);
194 settings::indent_width<Tp, 2>(std::log10(itr_depth) + 1);
195 }
196 }
197
198 auto file_exists = [](const std::string& fname) {
199 std::cout << "Checking for existing input at " << fname << "...\n";
200 std::ifstream inpf(fname.c_str());
201 auto success = inpf.is_open();
202 inpf.close();
203 return success;
204 };
205
207 auto extensions = tim::delimit(m_settings->get_input_extensions(), ",; ");
208
209 tree_outfname = settings::compose_output_filename(label, ".tree" + fext);
210 json_outfname = settings::compose_output_filename(label, fext);
211 text_outfname = settings::compose_output_filename(label, ".txt");
212
213 if(m_settings->get_diff_output())
214 {
215 extensions.insert(extensions.begin(), fext);
216 for(const auto& itr : extensions)
217 {
218 auto inpfname = settings::compose_input_filename(label, itr);
219 if(file_exists(inpfname))
220 {
221 json_inpfname = inpfname;
222 break;
223 }
224 }
225 }
226
227 if(!json_inpfname.empty())
228 {
229 auto dext = std::string(".diff") + fext;
230 json_diffname = settings::compose_output_filename(label, dext);
231 text_diffname = settings::compose_output_filename(label, ".diff.txt");
232 if(m_settings->get_debug())
233 {
234 printf("difference filenames: '%s' and '%s'\n", json_diffname.c_str(),
235 text_diffname.c_str());
236 }
237 }
238
239 if(!(file_output() && text_output()) && !cout_output())
240 return;
241
242 write_stream(data_stream, node_results);
243 data_stream->set_banner(description);
244
245 if(!node_delta.empty())
246 {
247 write_stream(diff_stream, node_delta);
248 std::stringstream ss;
249 ss << description << " vs. " << json_inpfname;
250 diff_stream->set_banner(ss.str());
251 }
252}
253//
254//--------------------------------------------------------------------------------------//
255//
256template <typename Tp>
257void
259{
260 auto stream_fmt = Tp::get_format_flags();
261 auto stream_width = Tp::get_width();
262 auto stream_prec = Tp::get_precision();
263
264 stream = std::make_shared<utility::stream>('|', '-', stream_fmt, stream_width,
265 stream_prec);
266
267 using get_return_type = decltype(std::declval<const Tp>().get());
268 using compute_type = math::compute<get_return_type>;
269
270 auto_lock_t slk(type_mutex<decltype(std::cout)>(), std::defer_lock);
271 if(!slk.owns_lock())
272 slk.lock();
273
274 auto result = get_flattened(result_array);
275 for(auto itr = result.begin(); itr != result.end(); ++itr)
276 {
277 auto& itr_obj = (*itr)->data();
278 auto& itr_prefix = (*itr)->prefix();
279 auto& itr_depth = (*itr)->depth();
280 auto itr_laps = itr_obj.get_laps();
281
282 if(itr_depth < 0 || itr_depth > get_max_depth())
283 continue;
284
285 // counts the number of non-exclusive values
286 int64_t nexclusive = 0;
287 // the sum of the exclusive values
288 get_return_type exclusive_values{};
289
290 // if we are not at the bottom of the call stack (i.e. completely
291 // inclusive)
292 if(itr_depth < max_depth)
293 {
294 // get the next iteration
295 auto eitr = itr;
296 std::advance(eitr, 1);
297 // counts the number of non-exclusive values
298 nexclusive = 0;
299 // the sum of the exclusive values
300 exclusive_values = get_return_type{};
301 // continue while not at end of graph until first sibling is
302 // encountered
303 if(eitr != result.end())
304 {
305 auto eitr_depth = (*eitr)->depth();
306 while(eitr_depth != itr_depth)
307 {
308 auto& eitr_obj = (*eitr)->data();
309
310 // if one level down, this is an exclusive value
311 if(eitr_depth == itr_depth + 1)
312 {
313 // if first exclusive value encountered: assign; else: combine
314 if(nexclusive == 0)
315 {
316 exclusive_values = eitr_obj.get();
317 }
318 else
319 {
320 compute_type::plus(exclusive_values, eitr_obj.get());
321 }
322 // increment. beyond 0 vs. 1, this value plays no role
323 ++nexclusive;
324 }
325 // increment iterator for next while check
326 ++eitr;
327 if(eitr == result.end())
328 break;
329 eitr_depth = (*eitr)->depth();
330 }
331 }
332 }
333
334 auto itr_self = compute_type::percent_diff(exclusive_values, itr_obj.get());
335 auto itr_stats = (*itr)->stats();
336
337 bool _first = std::distance(result.begin(), itr) == 0;
338 if(_first)
339 operation::print_header<Tp>(itr_obj, *(stream.get()), itr_stats);
340
341 operation::print<Tp>(itr_obj, *(stream.get()), itr_prefix, itr_laps, itr_depth,
342 itr_self, itr_stats);
343
344 stream->add_row();
345 }
346}
347//
348//--------------------------------------------------------------------------------------//
349//
350template <typename Tp>
351void
353{
354 dmp::barrier();
355 node_init = dmp::is_initialized();
356 node_rank = dmp::rank();
357 node_size = dmp::size();
358 node_results = data->dmp_get();
359 if(tree_output())
360 node_tree = data->dmp_get(node_tree);
361 dmp::barrier();
362
363 if(m_settings->get_debug())
364 {
365 printf("[%s]|%i> dmp results size: %i\n", label.c_str(), node_rank,
366 (int) node_results.size());
367 }
368
369 setup();
370
371 read_json();
372
373 if(!node_input.empty() && node_rank == 0)
374 {
375 node_delta.resize(node_input.size());
376
377 size_t num_ranks = std::min<size_t>(node_input.size(), node_results.size());
378
379 for(size_t i = 0; i < num_ranks; ++i)
380 {
381 for(auto& iitr : node_input.at(i))
382 {
383 for(auto& ritr : node_results.at(i))
384 {
385 if(iitr == ritr)
386 {
387 node_delta.at(i).push_back(ritr);
388 node_delta.at(i).back() -= iitr;
389 break;
390 }
391 }
392 }
393 }
394 write_stream(diff_stream, node_delta);
395 std::stringstream ss;
396 ss << description << " vs. " << json_inpfname;
397 diff_stream->set_banner(ss.str());
398 }
399
400#if defined(DEBUG)
401 if(m_settings->get_debug() && m_settings->get_verbose() > 3)
402 {
403 auto indiv_results = get_flattened(node_results);
404 printf("\n");
405 size_t w = 0;
406 for(const auto& itr : indiv_results)
407 w = std::max<size_t>(w, data->get_prefix(itr->hash()).length());
408 for(const auto& itr : indiv_results)
409 {
410 std::cout << std::setw(w) << std::left << data->get_prefix(itr->hash())
411 << " : " << itr->data();
412 auto _hierarchy = itr->hierarchy();
413 for(size_t i = 0; i < _hierarchy.size(); ++i)
414 {
415 if(i == 0)
416 std::cout << " :: ";
417 std::cout << data->get_prefix(_hierarchy[i]);
418 if(i + 1 < _hierarchy.size())
419 std::cout << '/';
420 }
421 std::cout << std::endl;
422 }
423 printf("\n");
424 }
425#endif
426
427 if(flame_output())
429}
430//
431//--------------------------------------------------------------------------------------//
432//
433template <typename Tp>
434void
436{
437 using policy_type = policy::output_archive_t<Tp>;
438 if(outfname.length() > 0)
439 {
440 std::ofstream ofs{};
441 if(filepath::open(ofs, outfname))
442 {
443 auto fext = outfname.substr(outfname.find_last_of('.') + 1);
444 if(fext.empty())
445 fext = "unknown";
446 manager::instance()->add_file_output(fext, label, outfname);
447 printf("[%s]|%i> Outputting '%s'...\n", label.c_str(), node_rank,
448 outfname.c_str());
449
450 // ensure write final block during destruction before the file is closed
451 auto oa = policy_type::get(ofs);
452
453 oa->setNextName("timemory");
454 oa->startNode();
455 operation::serialization<Tp>{}(*oa, results);
456 oa->finishNode(); // timemory
457 }
458 else
459 {
460 fprintf(stderr, "[storage<%s>::%s @ %i]|%i> Error opening '%s'...\n",
461 label.c_str(), __FUNCTION__, __LINE__, node_rank, outfname.c_str());
462 return;
463 }
464 if(ofs)
465 ofs << std::endl;
466 ofs.close();
467 }
468}
469//
470//--------------------------------------------------------------------------------------//
471//
472template <typename Tp>
473void
475{
476 using policy_type = policy::output_archive_t<Tp>;
477
478 if(outfname.length() > 0)
479 {
480 auto fext = outfname.substr(outfname.find_last_of('.') + 1);
481 if(fext.empty())
482 fext = "unknown";
483 manager::instance()->add_file_output(fext, label, outfname);
484 printf("[%s]|%i> Outputting '%s'...\n", label.c_str(), node_rank,
485 outfname.c_str());
486 std::ofstream ofs{};
487 if(filepath::open(ofs, outfname))
488 {
489 // ensure write final block during destruction before the file is closed
490 auto oa = policy_type::get(ofs);
491
492 oa->setNextName("timemory");
493 oa->startNode();
495 oa->finishNode();
496 }
497 else
498 {
499 fprintf(stderr, "[storage<%s>::%s @ %i]|%i> Error opening '%s'...\n",
500 label.c_str(), __FUNCTION__, __LINE__, node_rank, outfname.c_str());
501 return;
502 }
503 ofs << std::endl;
504 ofs.close();
505 }
506}
507//
508//--------------------------------------------------------------------------------------//
509//
510template <typename Tp>
511void
513{
514 using strvector_t = std::vector<std::string>;
515
516 // if only a specific type should be echoed
517 if(m_settings->get_dart_type().length() > 0)
518 {
519 auto dtype = m_settings->get_dart_type();
522 {
523 return;
524 }
525 }
526
527 uint64_t _nitr = 0;
528
529 auto indiv_results = get_flattened(node_results);
530 for(auto& itr : indiv_results)
531 {
532 auto& itr_depth = itr->depth();
533
534 if(itr_depth < 0 || itr_depth > max_depth)
535 continue;
536
537 // if only a specific number of measurements should be echoed
538 if(m_settings->get_dart_count() > 0 && _nitr >= m_settings->get_dart_count())
539 continue;
540
541 auto& itr_obj = itr->data();
542 auto& itr_hierarchy = itr->hierarchy();
543 strvector_t str_hierarchy{};
544 for(const auto& hitr : itr_hierarchy)
545 str_hierarchy.push_back(data->get_prefix(hitr));
546 operation::echo_measurement<Tp>(itr_obj, str_hierarchy);
547 ++_nitr;
548 }
549}
550//
551//--------------------------------------------------------------------------------------//
552//
553template <typename Tp>
554void
556{
557 using policy_type = policy::input_archive_t<Tp>;
558
559 if(json_inpfname.length() > 0)
560 {
561 std::ifstream ifs(json_inpfname.c_str());
562 if(ifs)
563 {
564 printf("[%s]|%i> Reading '%s'...\n", label.c_str(), node_rank,
565 json_inpfname.c_str());
566
567 // ensure write final block during destruction before the file is closed
568 auto ia = policy_type::get(ifs);
569
570 try
571 {
572 ia->setNextName("timemory");
573 ia->startNode();
574 operation::serialization<Tp>{}(*ia, node_input);
575 ia->finishNode();
576 } catch(tim::cereal::Exception& e)
577 {
578 PRINT_HERE("Exception reading %s :: %s", json_inpfname.c_str(), e.what());
579#if defined(TIMEMORY_INTERNAL_TESTING)
581 throw std::runtime_error("error reading json");
582#endif
583 }
584 }
585 else
586 {
587 fprintf(stderr, "[%s]|%i> Failure opening '%s' for input...\n", label.c_str(),
588 node_rank, json_inpfname.c_str());
589#if defined(TIMEMORY_INTERNAL_TESTING)
590 throw std::runtime_error("error opening file");
591#endif
592 }
593 ifs.close();
594 }
595}
596//
597//--------------------------------------------------------------------------------------//
598//
599} // namespace finalize
600} // namespace operation
601} // namespace tim
static pointer_t instance()
Get a shared pointer to the instance for the current thread.
The declaration for the types for manager without definitions.
bool open(std::ofstream &_ofs, std::string _fpath, Args &&... _args)
Definition: filepath.hpp:207
Tp & plus(Tp &, const Up &)
Definition: plus.hpp:106
Tp percent_diff(const Tp &, const Tp &)
TIMEMORY_OPERATIONS_LINKAGE(void) base
Definition: print.hpp:57
This operation class echoes DartMeasurements for a CDash dashboard.
Definition: types.hpp:985
void plot(const std::string &_label, const std::string &_prefix, const std::string &_dir, bool _echo_dart, const std::string &_json_file)
data::stream stream
Definition: stream.hpp:982
Definition: kokkosp.cpp:39
max_depth
Definition: settings.cpp:1641
MutexT & type_mutex(uint64_t _n=0)
A simple way to get a mutex for a class or common behavior, e.g. type_mutex<decltype(std::cout)>() pr...
Definition: locking.hpp:57
std::unique_lock< mutex_t > auto_lock_t
Unique lock type around mutex_t.
Definition: locking.hpp:42
tree_output
Definition: settings.cpp:1626
text_output
Definition: settings.cpp:1624
char argparse::argument_parser tim::settings * _settings
Definition: config.cpp:255
file_output
Definition: settings.cpp:1623
tim::mpl::apply< std::string > string
Definition: macros.hpp:53
const std::string std::ostream * os
void finalize()
Definition: types.hpp:119
auto get(const auto_bundle< Tag, Types... > &_obj)
cout_output
Definition: settings.cpp:1622
description("A generic option for any setting. Each argument MUST be passed in " "form: 'NAME=VALUE'. E.g. --timemory-args " "\"papi_events=PAPI_TOT_INS,PAPI_TOT_CYC\" text_output=off") .action([&](parser_t &p)
Definition: config.cpp:312
ContainerT delimit(const std::string &line, const std::string &delimiters="\"',;: ", PredicateT &&predicate=[](const std::string &s) -> std::string { return s;})
Definition: delimit.hpp:68
The declaration for the types for operations without definitions.
Include the macros for operations.
Declare the operations types.
int add_row()
indicate that a row of data has been finished
Definition: stream.hpp:693
Struct for performing math operations on complex data structures without using globally overload oper...
Definition: compute.hpp:53
std::shared_ptr< settings > settings_t
Definition: types.hpp:1174
virtual void print_cout(stream_type stream)
std::shared_ptr< utility::stream > stream_type
Definition: types.hpp:1173
virtual void print_plot(const std::string &fname, std::string suffix)
virtual void write(std::ostream &os, stream_type stream)
print(bool _forced_json=false, settings_t _settings=settings::shared_instance())
Definition: types.hpp:1176
virtual void print_text(const std::string &fname, stream_type stream)
impl::storage< Tp, has_data > storage_type
Definition: types.hpp:1350
typename storage_type::dmp_result_t result_type
Definition: types.hpp:1351
std::map< std::string, std::vector< basic_tree_vector_type > > result_tree
Definition: types.hpp:1360
print routines for individual components
Definition: print.hpp:59
Provides a static get() function which returns a shared pointer to an instance of the given archive f...
Definition: policy.hpp:92
Provides a static get() function which return a shared pointer to an instance of the given archive fo...
Definition: policy.hpp:136
static string_t compose_output_filename(string_t _tag, string_t _ext, bool _use_suffix=use_output_suffix(), int32_t _suffix=default_process_suffix(), bool _make_dir=false, std::string _explicit={})
Definition: settings.cpp:322
static string_t compose_input_filename(string_t _tag, string_t _ext, bool _use_suffix=use_output_suffix(), int32_t _suffix=default_process_suffix(), std::string _explicit={})
Definition: settings.cpp:363
Extension for the input or output archive types. It will throw an error if used on new archive types ...
#define TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(CONDITION, DEPTH)
Definition: macros.hpp:202
#define PRINT_HERE(...)
Definition: macros.hpp:152