├── .pre-commit-config.yaml ├── CMakeLists.txt ├── README.md ├── auto_log ├── __init__.py ├── autolog.h ├── autolog.py ├── device.py ├── env.py ├── lite_autolog.h └── utils.py ├── requirements.txt └── setup.py /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | - repo: https://github.com/PaddlePaddle/mirrors-yapf.git 2 | sha: 0d79c0c469bab64f7229c9aca2b1186ef47f0e37 3 | hooks: 4 | - id: yapf 5 | files: \.py$ 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | sha: a11d9314b22d8f8c7556443875b731ef05965464 8 | hooks: 9 | - id: check-merge-conflict 10 | - id: check-symlinks 11 | - id: detect-private-key 12 | files: (?!.*paddle)^.*$ 13 | - id: end-of-file-fixer 14 | files: \.md$ 15 | - id: trailing-whitespace 16 | files: \.md$ 17 | - repo: https://github.com/Lucas-C/pre-commit-hooks 18 | sha: v1.0.1 19 | hooks: 20 | - id: forbid-crlf 21 | files: \.md$ 22 | - id: remove-crlf 23 | files: \.md$ 24 | - id: forbid-tabs 25 | files: \.md$ 26 | - id: remove-tabs 27 | files: \.md$ 28 | - repo: local 29 | hooks: 30 | - id: clang-format 31 | name: clang-format 32 | description: Format files with ClangFormat 33 | entry: bash .clang_format.hook -i 34 | language: system 35 | files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|cuh|proto)$ 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(cpp_auto_log CXX C) 3 | 4 | message("default cmake for auto_log, no need to compile") 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # AutoLog 3 | 4 | 包含自动计时,统计CPU内存、GPU显存等信息,自动生成日志等功能。 5 | ``` 6 | ├── auto_log 7 | │ ├── autolog.py # 包含AutoLogger类,规范日志 8 | │ ├── device.py # 包含统计CPU内存,GPU显存的函数 9 | │ ├── ens.py # 包含获取环境信息的函数 10 | │ ├── util.py # 包含自动计时的类 11 | 12 | ``` 13 | 14 | 依赖环境: 15 | ``` 16 | python3 17 | GPUtil 18 | psutil 19 | pynvml 20 | distro 21 | ``` 22 | 23 | autolog编译安装: 24 | ``` 25 | git clone https://github.com/LDOUBLEV/AutoLog 26 | pip3 install -r requirements.txt 27 | python3 setup.py bdist_wheel 28 | pip3 install ./dist/auto_log-1.0.0-py3-none-any.whl 29 | ``` 30 | 31 | 32 | 使用方式可以参考[PR](https://github.com/PaddlePaddle/PaddleOCR/pull/3182/files): 33 | 34 | AutoLogger类参数说明: 35 | ``` 36 | model_name="det", # string 模型名字,可自定义 37 | model_precision=args.precision, # string 精度,'fp32', 'fp16', 'int8' 38 | batch_size=1, # int, batchsize大小 39 | data_shape="dynamic", # string, list, tuple, 模型输入的shape 40 | save_path="./output/auto_log.lpg", # string, 日志保存的路径 41 | inference_config=self.config, # paddle.infer.Config类,用于获取enable_mkldnn, enable_trt等信息 42 | pids=pid, # int, 当前进程的pid,可用os.getpid()获取 43 | process_name=None, # string 当前进程的的名字,比如'Python',用于获取当前进程的pid号,默认为None 44 | gpu_ids=0, # int, 当前进程的GPU卡号,默认为None, 45 | time_keys=None, # list, 统计时间的键值,默认为['preprocess_time', 'inference_time', 'postprocess_time'] 46 | warmup=10 # int, warmup times,默认为0,warmup次数内,不会统计时间 47 | ``` 48 | 49 | # Updates 50 | - 2021.8.5: 增加获取GPU信息的类GpuInfoV2,从nvidia-smi中获取GPU显存占用,可以不需要引入pynuml和GPUtil 这两个依赖 51 | 52 | -------------------------------------------------------------------------------- /auto_log/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # from .build_log import * 16 | from .utils import * 17 | from .device import * 18 | from .env import * 19 | from .autolog import * -------------------------------------------------------------------------------- /auto_log/autolog.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | class AutoLogger { 27 | public: 28 | AutoLogger(std::string model_name, 29 | bool use_gpu, 30 | bool enable_tensorrt, 31 | bool enable_mkldnn, 32 | int cpu_threads, 33 | int batch_size, 34 | std::string input_shape, 35 | std::string model_precision, 36 | std::vector time_info, 37 | int img_num) { 38 | this->model_name_ = model_name; 39 | this->use_gpu_ = use_gpu; 40 | this->enable_tensorrt_ = enable_tensorrt; 41 | this->enable_mkldnn_ = enable_mkldnn; 42 | this->cpu_threads_ = cpu_threads; 43 | this->batch_size_ = batch_size; 44 | this->input_shape_ = input_shape; 45 | this->model_precision_ = model_precision; 46 | this->time_info_ = time_info; 47 | this->img_num_ = img_num; 48 | } 49 | void report() { 50 | LOG(INFO) << "----------------------- Config info -----------------------"; 51 | LOG(INFO) << "runtime_device: " << (this->use_gpu_ ? "gpu" : "cpu"); 52 | LOG(INFO) << "ir_optim: " << "True"; 53 | LOG(INFO) << "enable_memory_optim: " << "True"; 54 | LOG(INFO) << "enable_tensorrt: " << this->enable_tensorrt_; 55 | LOG(INFO) << "enable_mkldnn: " << (this->enable_mkldnn_ ? "True" : "False"); 56 | LOG(INFO) << "cpu_math_library_num_threads: " << this->cpu_threads_; 57 | LOG(INFO) << "----------------------- Data info -----------------------"; 58 | LOG(INFO) << "batch_size: " << this->batch_size_; 59 | LOG(INFO) << "input_shape: " << this->input_shape_; 60 | LOG(INFO) << "data_num: " << this->img_num_; 61 | LOG(INFO) << "----------------------- Model info -----------------------"; 62 | LOG(INFO) << "model_name: " << this->model_name_; 63 | LOG(INFO) << "precision: " << this->model_precision_; 64 | LOG(INFO) << "----------------------- Perf info ------------------------"; 65 | LOG(INFO) << "Total time spent(ms): " 66 | << std::accumulate(this->time_info_.begin(), this->time_info_.end(), 0); 67 | LOG(INFO) << "preprocess_time(ms): " << this->time_info_[0] / this->img_num_ 68 | << ", inference_time(ms): " << this->time_info_[1] / this->img_num_ 69 | << ", postprocess_time(ms): " << this->time_info_[2] / this->img_num_; 70 | } 71 | 72 | private: 73 | std::string model_name_; 74 | bool use_gpu_ = false; 75 | bool enable_tensorrt_ = false; 76 | bool enable_mkldnn_ = true; 77 | int cpu_threads_ = 10; 78 | int batch_size_ = 1; 79 | std::string input_shape_ = "dynamic"; 80 | std::string model_precision_ = "fp32"; 81 | std::vector time_info_; 82 | int img_num_; 83 | }; 84 | -------------------------------------------------------------------------------- /auto_log/autolog.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import numpy as np 16 | import time 17 | import pynvml 18 | import psutil 19 | import GPUtil 20 | import os 21 | import paddle 22 | from pathlib import Path 23 | import logging 24 | import sys 25 | from .env import get_env_info 26 | from .utils import Times 27 | from .device import MemInfo, SubprocessGetMem 28 | 29 | 30 | def get_infer_gpuid(): 31 | cmd = "env | grep CUDA_VISIBLE_DEVICES" 32 | env_cuda = os.popen(cmd).readlines() 33 | if len(env_cuda) == 0: 34 | return 0 35 | else: 36 | gpu_id = env_cuda[0].strip().split("=")[1] 37 | return int(gpu_id[0]) 38 | 39 | 40 | class RunConfig: 41 | def __init(self, 42 | run_devices="cpu", 43 | ir_optim=False, 44 | enable_tensorrt=False, 45 | enable_mkldnn=False, 46 | cpu_threads=0, 47 | enable_mem_optim=True): 48 | 49 | self.run_devices = run_devices 50 | self.ir_optim = ir_optim 51 | self.enable_mkldnn = enable_mkldnn 52 | self.enable_tensorrt = enable_tensorrt 53 | self.cpu_math_library_num_threads = cpu_threads 54 | self.enable_mem_optim = enable_mem_optim 55 | 56 | 57 | class AutoLogger(RunConfig): 58 | def __init__(self, 59 | model_name, 60 | model_precision="fp32", 61 | batch_size=1, 62 | data_shape="dynamic", 63 | save_path=None, 64 | inference_config=None, 65 | pids=None, 66 | process_name=None, 67 | gpu_ids=None, 68 | time_keys=[ 69 | 'preprocess_time', 'inference_time', 'postprocess_time' 70 | ], 71 | warmup=0, 72 | logger=None, 73 | **kwargs): 74 | super(AutoLogger, self).__init__(**kwargs) 75 | self.autolog_version = 1.0 76 | self.save_path = save_path 77 | self.model_name = model_name 78 | self.precision = model_precision 79 | self.batch_size = batch_size 80 | self.data_shape = data_shape 81 | self.paddle_infer_config = inference_config 82 | 83 | self.config_status = self.parse_config(self.paddle_infer_config) 84 | 85 | self.time_keys = time_keys 86 | self.times = Times(keys=time_keys, warmup=warmup) 87 | 88 | self.get_paddle_info() 89 | 90 | self.logger = self.init_logger() if logger is None else logger 91 | 92 | if pids is None: 93 | pids = os.getpid() 94 | self.pids = pids 95 | if gpu_ids == "auto": 96 | gpu_ids = get_infer_gpuid() 97 | self.gpu_ids = gpu_ids 98 | 99 | self.get_mem = SubprocessGetMem(pid=pids, gpu_id=gpu_ids) 100 | self.start_subprocess_get_mem() 101 | 102 | def start_subprocess_get_mem(self): 103 | self.get_mem.get_mem_subprocess_run(0.2) 104 | 105 | def end_subprocess_get_mem(self): 106 | self.get_mem.get_mem_subprocess_end() 107 | cpu_infos = self.get_mem.cpu_infos 108 | gpu_infos = self.get_mem.gpu_infos 109 | self.cpu_infos = cpu_infos[str(self.pids)] 110 | if self.gpu_ids is None: 111 | self.gpu_infos = {} 112 | else: 113 | self.gpu_infos = gpu_infos[str(self.gpu_ids)] 114 | return self.cpu_infos, self.gpu_infos 115 | 116 | def init_logger(self, name='root', log_level=logging.DEBUG): 117 | log_file = self.save_path 118 | 119 | logger = logging.getLogger(name) 120 | 121 | formatter = logging.Formatter( 122 | '[%(asctime)s] %(name)s %(levelname)s: %(message)s', 123 | datefmt="%Y/%m/%d %H:%M:%S") 124 | 125 | stream_handler = logging.StreamHandler(stream=sys.stdout) 126 | stream_handler.setFormatter(formatter) 127 | logger.addHandler(stream_handler) 128 | if log_file is not None: 129 | dir_name = os.path.dirname(log_file) 130 | if len(dir_name) > 0 and not os.path.exists(dir_name): 131 | os.makedirs(dir_name) 132 | file_handler = logging.FileHandler(log_file, 'w') 133 | file_handler.setFormatter(formatter) 134 | logger.addHandler(file_handler) 135 | logger.setLevel(log_level) 136 | return logger 137 | 138 | def parse_config(self, config) -> dict: 139 | """ 140 | parse paddle predictor config 141 | args: 142 | config(paddle.inference.Config): paddle inference config 143 | return: 144 | config_status(dict): dict style config info 145 | """ 146 | config_status = {} 147 | if config is not None and type(config) is not dict: 148 | config_status['runtime_device'] = "gpu" if config.use_gpu( 149 | ) else "cpu" 150 | config_status['ir_optim'] = config.ir_optim() 151 | config_status['enable_tensorrt'] = config.tensorrt_engine_enabled() 152 | config_status['precision'] = self.precision 153 | config_status['enable_mkldnn'] = config.mkldnn_enabled() 154 | config_status[ 155 | 'cpu_math_library_num_threads'] = config.cpu_math_library_num_threads( 156 | ) 157 | elif type(config) is dict: 158 | config_status['runtime_device'] = config[ 159 | 'runtime_device'] if 'runtime_device' in config else None 160 | config_status['ir_optim'] = config[ 161 | 'ir_optim'] if 'ir_optim' in config else None 162 | config_status['enable_tensorrt'] = config[ 163 | 'enable_tensorrt'] if 'enable_tensorrt' in config else None 164 | config_status['precision'] = config[ 165 | 'precision'] if 'precision' in config else None 166 | config_status['enable_mkldnn'] = config[ 167 | 'enable_mkldnn'] if 'enable_mkldnn' in config else None 168 | config_status['cpu_math_library_num_threads'] = config[ 169 | 'cpu_math_library_num_threads'] if 'cpu_math_library_num_threads' in config else None 170 | else: 171 | config_status['runtime_device'] = "None" 172 | config_status['ir_optim'] = "None" 173 | config_status['enable_tensorrt'] = "None" 174 | config_status['precision'] = self.precision 175 | config_status['enable_mkldnn'] = "None" 176 | config_status['cpu_math_library_num_threads'] = "None" 177 | return config_status 178 | 179 | def get_paddle_info(self): 180 | self.paddle_version = paddle.__version__ 181 | self.paddle_commit = paddle.__git_commit__ 182 | 183 | def report(self, identifier=None): 184 | #TODO: support multi-model report 185 | """ 186 | print log report 187 | args: 188 | identifier(string): identify log 189 | """ 190 | if identifier: 191 | identifier = f"[{identifier}]" 192 | else: 193 | identifier = "" 194 | 195 | # report time 196 | _times_value = self.times.value(key=self.time_keys, mode='mean') 197 | preprocess_time_ms = round(_times_value['preprocess_time'] * 1000, 4) 198 | inference_time_ms = round(_times_value['inference_time'] * 1000, 4) 199 | postprocess_time_ms = round(_times_value['postprocess_time'] * 1000, 4) 200 | data_num = self.times._num_counts() 201 | total_time_s = round(self.times._report_total_time(mode='sum'), 4) 202 | 203 | # report memory 204 | cpu_infos, gpu_infos = self.end_subprocess_get_mem() 205 | 206 | cpu_rss_mb = self.cpu_infos['cpu_rss'] 207 | gpu_rss_mb = self.gpu_infos[ 208 | 'memory.used'] if self.gpu_ids is not None else None 209 | gpu_util = self.gpu_infos[ 210 | 'utilization.gpu'] if self.gpu_ids is not None else None 211 | 212 | # report env 213 | envs = get_env_info() 214 | 215 | self.logger.info("\n") 216 | self.logger.info( 217 | "---------------------- Env info ----------------------") 218 | # envs['nvidia_driver_version'] envs['cudnn_version']envs['cuda_version'] envs['os_info'] 219 | self.logger.info(f"{identifier} OS_version: {envs['os_info']}") 220 | self.logger.info(f"{identifier} CUDA_version: {envs['cuda_version']}") 221 | self.logger.info( 222 | f"{identifier} CUDNN_version: {envs['cudnn_version']}") 223 | self.logger.info( 224 | f"{identifier} drivier_version: {envs['nvidia_driver_version']}") 225 | self.logger.info( 226 | "---------------------- Paddle info ----------------------") 227 | self.logger.info(f"{identifier} paddle_version: {self.paddle_version}") 228 | self.logger.info(f"{identifier} paddle_commit: {self.paddle_commit}") 229 | self.logger.info( 230 | f"{identifier} log_api_version: {self.autolog_version}") 231 | self.logger.info( 232 | "----------------------- Conf info -----------------------") 233 | self.logger.info( 234 | f"{identifier} runtime_device: {self.config_status['runtime_device']}" 235 | ) 236 | self.logger.info( 237 | f"{identifier} ir_optim: {self.config_status['ir_optim']}") 238 | self.logger.info(f"{identifier} enable_memory_optim: {True}") 239 | self.logger.info( 240 | f"{identifier} enable_tensorrt: {self.config_status['enable_tensorrt']}" 241 | ) 242 | self.logger.info( 243 | f"{identifier} enable_mkldnn: {self.config_status['enable_mkldnn']}" 244 | ) 245 | self.logger.info( 246 | f"{identifier} cpu_math_library_num_threads: {self.config_status['cpu_math_library_num_threads']}" 247 | ) 248 | self.logger.info( 249 | "----------------------- Model info ----------------------") 250 | self.logger.info(f"{identifier} model_name: {self.model_name}") 251 | self.logger.info(f"{identifier} precision: {self.precision}") 252 | self.logger.info( 253 | "----------------------- Data info -----------------------") 254 | self.logger.info(f"{identifier} batch_size: {self.batch_size}") 255 | self.logger.info(f"{identifier} input_shape: {self.data_shape}") 256 | self.logger.info(f"{identifier} data_num: {data_num}") 257 | self.logger.info( 258 | "----------------------- Perf info -----------------------") 259 | self.logger.info( 260 | f"{identifier} cpu_rss(MB): {cpu_rss_mb}, gpu_rss(MB): {gpu_rss_mb}, gpu_util: {gpu_util}%" 261 | ) 262 | self.logger.info(f"{identifier} total time spent(s): {total_time_s}") 263 | self.logger.info( 264 | f"{identifier} preprocess_time(ms): {preprocess_time_ms}, inference_time(ms): {inference_time_ms}, postprocess_time(ms): {postprocess_time_ms}" 265 | ) 266 | sys.exit(0) 267 | 268 | def print_help(self): 269 | """ 270 | print function help 271 | """ 272 | print("""Usage: 273 | ==== Print inference benchmark logs. ==== 274 | config = paddle.inference.Config() 275 | model_info = {'model_name': 'resnet50' 276 | 'precision': 'fp32'} 277 | data_info = {'batch_size': 1 278 | 'shape': '3,224,224' 279 | 'data_num': 1000} 280 | perf_info = {'preprocess_time_s': 1.0 281 | 'inference_time_s': 2.0 282 | 'postprocess_time_s': 1.0 283 | 'total_time_s': 4.0} 284 | resource_info = {'cpu_rss_mb': 100 285 | 'gpu_rss_mb': 100 286 | 'gpu_util': 60} 287 | log = PaddleInferBenchmark(config, model_info, data_info, perf_info, resource_info) 288 | log('Test') 289 | """) 290 | 291 | 292 | # if __name__ == "__main__": 293 | # get_os_info() 294 | # print(envs['os_info']) 295 | # get_cudnn_info() 296 | # print(envs['cudnn_version']) 297 | -------------------------------------------------------------------------------- /auto_log/device.py: -------------------------------------------------------------------------------- 1 | import psutil 2 | import os 3 | import json 4 | import time 5 | import multiprocessing 6 | import numpy as np 7 | 8 | 9 | class CpuInfo(object): 10 | @staticmethod 11 | def get_disk_info(path): 12 | G = 1024*1024 13 | diskinfo = psutil.disk_usage(path) 14 | info = "path:%s total:%dG, used:%dG, free:%dG, used_percent:%d%%"%(path, 15 | diskinfo.total/G, diskinfo.used/G, diskinfo.free/G, diskinfo.percent) 16 | return info 17 | 18 | @staticmethod 19 | def get_disk_partitions(): 20 | return psutil.disk_partitions() 21 | 22 | @staticmethod 23 | def get_current_process_pid(): 24 | pids = psutil.pids() 25 | return pids 26 | 27 | @staticmethod 28 | def get_process_info(pid): 29 | p = psutil.Process(pid) 30 | info = "name:{} pid:{} \nstatus:{} \ncreate_time:{} \ncpu_times:{} \nmemory_percent:{} \nmemory_info:{} \nio_counters:{} \nnum_threads:{}".format(p.name(), 31 | pid, p.status(), p.create_time(), p.cpu_times(), p.memory_percent(), p.memory_info(), p.io_counters(), p.num_threads()) 32 | return info 33 | 34 | @staticmethod 35 | def get_cpu_current_memory_mb(pid): 36 | info = {} 37 | p = psutil.Process(pid) 38 | mem_info = p.memory_info() 39 | info['cpu_rss'] = round(mem_info.rss/1024/1024, 4) 40 | info['memory_percent'] = round(p.memory_percent(), 4) 41 | return info 42 | 43 | 44 | class GpuInfo(object): 45 | def __init__(self): 46 | import pynvml 47 | import GPUtil 48 | # init 49 | pynvml.nvmlInit() 50 | 51 | def get_gpu_name(self): 52 | name = pynvml.nvmlDeviceGetName(handle) 53 | return name.decode('utf-8') 54 | 55 | def get_gpu_device(self): 56 | deviceCount = pynvml.nvmlDeviceGetCount() 57 | gpu_list = [] 58 | for i in range(deviceCount): 59 | handle = pynvml.nvmlDeviceGetHandleByIndex(i) 60 | gpu_list.append(i) 61 | return gpu_list 62 | 63 | def get_free_rate(self, gpu_id): 64 | handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id) 65 | info = pynvml.nvmlDeviceGetMemoryInfo(handle) 66 | free_rate = int((info.free / info.total) * 100) 67 | return free_rate 68 | 69 | def get_used_rate(self, gpu_id): 70 | handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id) 71 | info = pynvml.nvmlDeviceGetMemoryInfo(handle) 72 | used_rate = int((info.used / info.total) * 100) 73 | return used_rate 74 | 75 | def get_gpu_util(self, gpu_id): 76 | GPUs = GPUtil.getGPUs() 77 | gpu_util = GPUs[gpu_id].load 78 | return gpu_util 79 | 80 | def get_gpu_info(self, gpu_id): 81 | self.init() 82 | handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id) 83 | info = pynvml.nvmlDeviceGetMemoryInfo(handle) 84 | M = 1024*1024 85 | gpu_info = {} 86 | gpu_info['total'] = info.total/M 87 | gpu_info['free'] = info.free/M 88 | gpu_info['used'] = info.used/M 89 | gpu_info['util'] = self.get_gpu_util(gpu_id) 90 | return gpu_info 91 | 92 | def release(self): 93 | pynvml.nvmlShutdown() 94 | 95 | class GpuInfoV2(object): 96 | def __init__(self): 97 | self.gpu_info = {} 98 | self.default_att = ( 99 | 'index', 100 | 'uuid', 101 | 'name', 102 | 'timestamp', 103 | 'memory.total', 104 | 'memory.free', 105 | 'memory.used', 106 | 'utilization.gpu', 107 | 'utilization.memory' 108 | ) 109 | 110 | def get_gpu_info(self, gpu_id, nvidia_smi_path='nvidia-smi', no_units=True): 111 | """ 112 | The run time of get_gpu_info are about 70ms. 113 | """ 114 | keys = self.default_att 115 | nu_opt = '' if not no_units else ',nounits' 116 | cmd = '%s --query-gpu=%s --format=csv,noheader%s' % (nvidia_smi_path, ','.join(keys), nu_opt) 117 | f = os.popen(cmd) 118 | lines = f.readlines() 119 | lines = [ line.strip() for line in lines if line.strip() != '' ] 120 | 121 | gpu_info_list = [ { k: v for k, v in zip(keys, line.split(', ')) } for line in lines ] 122 | 123 | gpu_info={} 124 | gpu_info["memory.used"] = float(gpu_info_list[gpu_id]["memory.used"]) 125 | gpu_info["utilization.gpu"] = float(gpu_info_list[gpu_id]["utilization.gpu"]) 126 | return gpu_info 127 | 128 | class MemInfo(CpuInfo): 129 | def __init__(self, pids=None, gpu_id=None): 130 | 131 | self.pids = self.check_pid(pids) 132 | if gpu_id is not None: 133 | self.gpuinfo = GpuInfoV2() 134 | self.gpu_id = self.check_gpu_id(gpu_id) 135 | # self.gpuinfo.init() 136 | else: 137 | self.gpuinfo = None 138 | self.gpu_id = self.check_gpu_id(gpu_id) 139 | 140 | self.cpu_infos = {} 141 | self.gpu_infos = {} 142 | 143 | def get_cpu_mem(self): 144 | cpu_mem = {} 145 | for pid in self.pids: 146 | cpu_mem[pid] = self.get_cpu_current_memory_mb(pid) 147 | return cpu_mem 148 | 149 | def get_gpu_mem(self): 150 | if gpuinfo is None: 151 | return None 152 | else: 153 | gpu_info = {} 154 | for id in self.gpu_id: 155 | gpu_info[id] = self.gpuinfo.get_gpu_info(id) 156 | return gpu_info 157 | 158 | def check_pid(self, pids): 159 | if type(pids)==int: 160 | return [pids] 161 | elif type(pids)==tuple: 162 | return [p for p in pids] 163 | elif type(pids)==list: 164 | return pids 165 | else: 166 | raise ValueError("Expect list of int input about pids, but get {} with type {}".format(pids, type(pids))) 167 | 168 | def check_gpu_id(self, gpu_id): 169 | if gpu_id is None: 170 | return [] 171 | elif type(gpu_id)==int: 172 | return [gpu_id] 173 | elif type(gpu_id)==tuple: 174 | return [p for p in gpu_id] 175 | elif type(gpu_id)==list: 176 | return gpu_id 177 | else: 178 | raise ValueError("Expect list of int input about gpu_id, but get {} with type {}".format(gpu_id, type(gpu_id))) 179 | 180 | def summary_mem(self, return_str=True): 181 | cpu_infos = {} 182 | for p in self.pids: 183 | cpu_infos[str(p)] = self.get_cpu_current_memory_mb(p) 184 | 185 | gpu_infos = {} 186 | for g in self.gpu_id: 187 | gpu_infos[str(g)] = self.gpuinfo.get_gpu_info(g) 188 | 189 | if return_str is False: 190 | return cpu_infos, gpu_infos 191 | else: 192 | return json.dumps(cpu_infos) + "\n" + json.dumps(gpu_infos) 193 | 194 | def get_avg_mem_mb(self): 195 | cpu_infos, gpu_infos = self.summary_mem(return_str=False) 196 | if len(self.cpu_infos.keys()) < 1: 197 | self.cpu_infos = cpu_infos 198 | else: 199 | for p in self.pids: 200 | for k in cpu_infos[str(p)].keys(): 201 | v = cpu_infos[str(p)][k] 202 | self.cpu_infos[str(p)][k] = np.mean([v, self.cpu_infos[str(p)][k]]) 203 | if len(self.gpu_infos.keys()) < 1: 204 | self.gpu_infos = gpu_infos 205 | else: 206 | for g in self.gpu_id: 207 | for k in gpu_infos[str(g)].keys(): 208 | #self.gpu_infos[str(g)][k] = np.mean([v, self.gpu_infos[str(g)][k]]) 209 | self.gpu_infos[str(g)][k] = np.max([v, self.gpu_infos[str(g)][k]]) 210 | return self.cpu_infos, self.gpu_infos 211 | 212 | 213 | class SubprocessGetMem(object): 214 | def __init__(self, pid, gpu_id): 215 | self.mem_info = MemInfo(pid, gpu_id) 216 | 217 | def get_mem_subprocess_start(self, q, interval=0.0): 218 | while True: 219 | cpu_infos, gpu_infos = self.mem_info.get_avg_mem_mb() 220 | pid = os.getpid() 221 | q.put([cpu_infos, gpu_infos, pid]) 222 | time.sleep(interval) 223 | return 224 | 225 | def get_mem_subprocess_init(self, interval=0.0): 226 | ctx = multiprocessing.get_context('spawn') 227 | self.mem_q = ctx.Queue() 228 | 229 | results = [] 230 | self.mem_p = ctx.Process(target=self.get_mem_subprocess_start, args=(self.mem_q, interval)) 231 | self.mem_p.start() 232 | 233 | def get_mem_subprocess_run(self, interval=0.0): 234 | self.get_mem_subprocess_init(interval=interval) 235 | 236 | def get_mem_subprocess_end(self): 237 | self.cpu_infos, self.gpu_infos, subpid = self.mem_q.get() 238 | #self.mem_p.terminate() 239 | try: 240 | self.mem_p.kill() # python>=3.7 needed 241 | except: 242 | import os, signal 243 | import subprocess 244 | try: 245 | subprocess.Popen("kill -s 9 %i"%subpid , shell=True) # for linux 246 | except: 247 | subprocess.Popen("cmd.exe /k taskkill /F /T /PID %i"%subpid , shell=True) # for win 248 | 249 | 250 | 251 | 252 | # if __name__ == "__main__": 253 | # gpu_info = GpuInfoV2() 254 | # res = gpu_info.get_gpu_info(0) 255 | # print(res) 256 | 257 | # # print("----------------------------") 258 | # mem_info = MemInfo(11227, [0]) 259 | # res = mem_info.summary_mem() 260 | # print(res) 261 | 262 | -------------------------------------------------------------------------------- /auto_log/env.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import os 15 | import sys 16 | import distro 17 | import platform 18 | import subprocess 19 | 20 | envs_template = """ 21 | Paddle version: {paddle_version} 22 | Paddle With CUDA: {paddle_with_cuda} 23 | 24 | OS: {os_info} 25 | Python version: {python_version} 26 | 27 | CUDA version: {cuda_version} 28 | cuDNN version: {cudnn_version} 29 | Nvidia driver version: {nvidia_driver_version} 30 | """ 31 | 32 | envs = {} 33 | 34 | 35 | def get_paddle_info(): 36 | try: 37 | import paddle 38 | envs['paddle_version'] = paddle.__version__ 39 | envs['paddle_with_cuda'] = paddle.fluid.core.is_compiled_with_cuda() 40 | except: 41 | envs['paddle_version'] = None 42 | envs['paddle_with_cuda'] = None 43 | 44 | return envs['paddle_version'] 45 | 46 | 47 | def get_os_info(): 48 | plat = platform.system() 49 | if platform.system() == "Darwin": 50 | plat = "macOs" 51 | ver = platform.mac_ver()[0] 52 | elif platform.system() == "Linux": 53 | plat = distro.linux_distribution()[0] 54 | ver = distro.linux_distribution()[1] 55 | elif platform.system() == "Windows": 56 | plat = "Windows" 57 | ver = platform.win32_ver()[0] 58 | else: 59 | plat = None 60 | ver = None 61 | envs['os_info'] = "{0} {1}".format(plat, ver) 62 | return envs['os_info'] 63 | 64 | 65 | def get_python_info(): 66 | envs['python_version'] = sys.version.split(' ')[0] 67 | return envs['python_version'] 68 | 69 | 70 | def run_shell_command(cmd): 71 | p = subprocess.Popen( 72 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 73 | shell=True) 74 | out, err = p.communicate() 75 | 76 | if p.returncode==0: 77 | return out.decode('utf-8') 78 | else: 79 | return None 80 | 81 | 82 | def get_cuda_info(): 83 | out = run_shell_command('nvcc --version') 84 | if out: 85 | envs['cuda_version'] = out.split('V')[-1].strip() 86 | else: 87 | envs['cuda_version'] = None 88 | 89 | return envs['cuda_version'] 90 | 91 | 92 | def get_cudnn_info(): 93 | def _get_cudnn_ver(cmd): 94 | out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 95 | shell=True).communicate() 96 | out = run_shell_command(cmd) 97 | if out: 98 | return out.split(' ')[-1].strip() 99 | else: 100 | return None 101 | 102 | if platform.system() == "Windows": 103 | cudnn_dll_path = run_shell_command('where cudnn*') 104 | if cudnn_dll_path: 105 | cudnn_header_path = cudnn_dll_path.split('bin')[ 106 | 0] + r'include\cudnn.h' 107 | cmd = 'type "{0}" | findstr "{1}" | findstr /v "CUDNN_VERSION"' 108 | else: 109 | envs['cudnn_version'] = None 110 | return 111 | else: 112 | cudnn_header_path = run_shell_command( 113 | 'whereis "cudnn.h" | awk \'{print $2}\'') 114 | if cudnn_header_path: 115 | cudnn_header_path = cudnn_header_path.strip() 116 | cmd = 'cat "{0}" | grep "{1}" | grep -v "CUDNN_VERSION"' 117 | else: 118 | envs['cudnn_version'] = None 119 | return 120 | 121 | major = _get_cudnn_ver(cmd.format(cudnn_header_path, '#define CUDNN_MAJOR')) 122 | 123 | minor = _get_cudnn_ver(cmd.format(cudnn_header_path, '#define CUDNN_MINOR')) 124 | patch_level = _get_cudnn_ver( 125 | cmd.format(cudnn_header_path, 'CUDNN_PATCHLEVEL')) 126 | 127 | envs['cudnn_version'] = "{0}.{1}.{2}".format(major, minor, patch_level) 128 | 129 | return envs['cudnn_version'] 130 | 131 | 132 | def get_driver_info(): 133 | driver_ver = run_shell_command('nvidia-smi') 134 | if driver_ver: 135 | driver_ver = driver_ver.split('Driver Version:')[1].strip().split(' ')[ 136 | 0] 137 | else: 138 | driver_ver = None 139 | envs['nvidia_driver_version'] = driver_ver 140 | 141 | return envs['nvidia_driver_version'] 142 | 143 | 144 | def get_env_info(): 145 | get_paddle_info() 146 | get_os_info() 147 | get_python_info() 148 | get_cuda_info() 149 | get_cudnn_info() 150 | get_driver_info() 151 | return envs 152 | 153 | # if __name__ == '__main__': 154 | 155 | # res = get_env_info() 156 | # print('*' * 40 + envs_template.format(**res) + '*' * 40) 157 | 158 | -------------------------------------------------------------------------------- /auto_log/lite_autolog.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | class AutoLogger { 27 | public: 28 | AutoLogger(std::string model_name, 29 | std::string runtime_device, 30 | int cpu_threads, 31 | int batch_size, 32 | std::string input_shape, 33 | std::string model_precision, 34 | std::vector time_info, 35 | int img_num) { 36 | this->model_name_ = model_name; 37 | this->runtime_device_ = runtime_device; 38 | this->cpu_threads_ = cpu_threads; 39 | this->batch_size_ = batch_size; 40 | this->input_shape_ = input_shape; 41 | this->model_precision_ = model_precision; 42 | this->time_info_ = time_info; 43 | this->img_num_ = img_num; 44 | } 45 | void report() { 46 | std::cout << "----------------------- Config info -----------------------" << std::endl; 47 | std::cout << "runtime_device: " << (this->runtime_device_) << std::endl; 48 | std::cout << "cpu_num_threads: " << this->cpu_threads_ << std::endl; 49 | std::cout << "----------------------- Data info -----------------------" << std::endl; 50 | std::cout << "batch_size: " << this->batch_size_ << std::endl; 51 | std::cout << "input_shape: " << this->input_shape_ << std::endl; 52 | std::cout << "data_num: " << this->img_num_ << std::endl; 53 | std::cout << "----------------------- Model info -----------------------" << std::endl; 54 | std::cout << "model_name: " << this->model_name_ << std::endl; 55 | std::cout << "precision: " << this->model_precision_ << std::endl; 56 | std::cout << "----------------------- Perf info ------------------------" << std::endl; 57 | std::cout << "Total time spent(ms): " << std::accumulate(this->time_info_.begin(), this->time_info_.end(), 0) << std::endl; 58 | std::cout << "preprocess_time(ms): " << this->time_info_[0] / this->img_num_ 59 | << ", inference_time(ms): " << this->time_info_[1] / this->img_num_ 60 | << ", postprocess_time(ms): " << this->time_info_[2] / this->img_num_ << std::endl; 61 | } 62 | 63 | private: 64 | std::string model_name_; 65 | std::string runtime_device_; 66 | int cpu_threads_ = 1; 67 | int batch_size_ = 1; 68 | std::string input_shape_ = "dynamic"; 69 | std::string model_precision_ = "FP32"; 70 | std::vector time_info_; 71 | int img_num_; 72 | }; 73 | -------------------------------------------------------------------------------- /auto_log/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import time 16 | import numpy as np 17 | 18 | class Times(object): 19 | def __init__(self, keys=['preprocess_time', 'inference_time', 'postprocess_time'], 20 | warmup=0): 21 | super(Times, self).__init__() 22 | 23 | self.keys = keys 24 | self.warmup = warmup 25 | self.time_stamps = [] 26 | self.stamp_count = 0 27 | self.reset() 28 | 29 | def reset(self): 30 | self.time_stamps = [] 31 | self.times = {} 32 | for k in self.keys: 33 | self.times[k] = [] 34 | self.init() 35 | self.total_time = [] 36 | self.time_stamps = 0 37 | 38 | def init(self): 39 | self.stamp_count = 0 40 | self.time_stamps = [] 41 | 42 | def start(self, stamp=True): 43 | self.init() 44 | if stamp is True: 45 | self.time_stamps.append(time.time()) 46 | 47 | def end(self, stamp=False): 48 | if stamp is True: 49 | self.stamp() 50 | self.total_time.append(self.time_stamps[-1] - self.time_stamps[0]) 51 | self.init() 52 | 53 | def stamp(self): 54 | 55 | if len(self.time_stamps) <1: 56 | self.start() 57 | 58 | self.stamp_count += 1 59 | 60 | self.time_stamps.append(time.time()) 61 | 62 | if self.stamp_count > len(self.keys): 63 | self.keys.append(f"stamps_{self.stamp_count}") 64 | 65 | idx = (self.stamp_count - 1) % len(self.keys) 66 | _k = self.keys[idx] 67 | if _k in self.times.keys(): 68 | self.times[_k].append(self.time_stamps[-1] - self.time_stamps[-2]) 69 | else: 70 | self.times[_k] = [self.time_stamps[-1] - self.time_stamps[-2]] 71 | 72 | def set_keys(self, keys): 73 | self.keys = keys 74 | self.init() 75 | 76 | def _num_counts(self): 77 | return len(self.total_time) 78 | 79 | def _report_total_time(self, mode='avg'): 80 | if mode=='avg': 81 | return self.mean(self.total_time) 82 | elif mode=='sum': 83 | return self.sum(self.total_time) 84 | else: 85 | return self.total_time 86 | 87 | def value(self, key=None, mode=None): 88 | #TODO: 89 | # assert key in self.keys, f"Please set key:{key} as one of self.keys:{self.keys}" 90 | res = {} 91 | 92 | for k in self.keys: 93 | if mode=="mean": 94 | res[k] = self.mean(self.times[k]) 95 | elif mode=="sum": 96 | res[k] = self.sum(self.times[k]) 97 | else: 98 | res[k] = self.times[k] 99 | 100 | if key is None: 101 | return res 102 | 103 | for k in key: 104 | assert k in self.keys, f"Expect the element of {key} in self.keys: {self.keys}" 105 | return res 106 | 107 | def mean(self, lists): 108 | if len(lists) <= self.warmup: 109 | raise ValueError(f"The number {len(lists)} of time stamps must be larger than warmup: {self.warmup}") 110 | if len(lists) < 1: 111 | return 0. 112 | else: 113 | return np.mean(lists[self.warmup:]) 114 | 115 | def sum(self, lists): 116 | if len(lists) <= self.warmup: 117 | raise ValueError(f"The number {len(lists)} of time stamps must be larger than warmup: {self.warmup}") 118 | if len(lists) < 1: 119 | return 0. 120 | else: 121 | return np.sum(lists[self.warmup:]) 122 | 123 | 124 | # if __name__ == "__main__": 125 | # mytime = Times() 126 | # for i in range(5): 127 | # mytime.start() 128 | # time.sleep(0.1) 129 | # mytime.stamp() # 1 130 | # time.sleep(0.2) 131 | # mytime.stamp() # 2 132 | # time.sleep(0.3) 133 | # mytime.stamp() # 3 134 | # time.sleep(0.4) 135 | # mytime.stamp() # 4 136 | # mytime.end(stamp=True) # 5 137 | # print(mytime.times) 138 | # print("keys: ", mytime.keys) 139 | # print(mytime.value(mode="mean")) 140 | # print(mytime.value(mode="sum")) 141 | # print(mytime.value(mode=None)) 142 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | GPUtil 2 | psutil 3 | pynvml 4 | distro 5 | wheel 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | # python3.7 setup.py bdist_wheel 3 | 4 | with open('requirements.txt', encoding="utf-8-sig") as f: 5 | requirements = f.readlines() 6 | 7 | 8 | setup(name='auto_log', 9 | version='1.2.0', 10 | install_requires=requirements, 11 | license='Apache License 2.0', 12 | keywords='auto log', 13 | description="The AutoLog Contains automatic timing, statistics on CPU memory, GPU memory and other information, since generating logs and other functions.", 14 | url='https://github.com/LDOUBLEV/AutoLog', 15 | author='DoubleV', 16 | author_email='liuvv0203@gmail.com', 17 | packages=['auto_log'], 18 | ) 19 | --------------------------------------------------------------------------------