MoProfiler

Badge

GitHub

GitHub followers GitHub repo size in bytes GitHub stars GitHub release Github commits (since latest release) Github All Releases GitHub Release Date

CI

Build Status Documentation Status Alert Status Coverage Lines of Code Maintainability Rating Reliability Rating Security Rating Vulnerabilities Bugs Duplicated Lines Code Smells Technical Debt

PyPi

PyPI PyPI - Wheel PyPI - Python Version PyPI - Format PyPI - Implementation PyPI - Status

其他

license Donate

概览

MoProfiler 是一个综合性能分析工具,支持内存分析、时间分析、秒表打点等功能。 可以有效量化 Python 代码的性能,从而找到性能瓶颈所在,使性能优化可以有的放矢。

安装方法

您可以通过 pip 进行安装,本包兼容 Python 2.7 & Python 3.5

pip install moprofiler>=1.1.0
使用说明

如开始的介绍,本工具集主要由三个子工具 时间分析器 , 内存分析器 , 秒表工具 组成, 下面将对其使用方式进行逐一介绍

三个子工具分别提供了一个 装饰器 ,其中秒表工具额外提供了一个 Mixin 类, 一般使用中会存在如下装饰场景:

  1. 独立函数
  2. 类方法
  3. 实例方法
  4. 静态方法
  5. 生成器方法

为方便使用,三个工具分别提供了一个 超级装饰器 ,即同时支持上述几类场景, 且同时支持 有参装饰无参装饰 ,当不传参时装饰器后不需增加 ()

其中秒表工具,当被装饰对象为 类方法实例方法 时,可通过让类继承 StopwatchMixin 类,来获得功能增强

时间分析器

该分析器可以统计出指定函数或方法中每行代码的执行时间消耗,从而找到瓶颈所在,用例如下:

from moprofiler import TimeProfiler


class QucikSort(object):
    """
    快速排序
    """
    def __init__(self, arr):
        self.arr = arr

    def sort(self, left=None, right=None):
        """排序"""
        left = 0 if not isinstance(left, (int, float)) else left
        right = len(self.arr) - 1 if not isinstance(right, (int, float)) else right
        if left < right:
            partition_index = self.partition(left, right)
            self.sort(left, partition_index - 1)
            self.sort(partition_index + 1, right)

    @TimeProfiler(print_res=False)
    def partition(self, left, right):
        """分区"""
        pivot = left
        index = pivot + 1
        i = index
        while i <= right:
            if self.arr[i] < self.arr[pivot]:
                self.swap(i, index)
                index += 1
            i += 1
        self.swap(pivot, index - 1)
        return index - 1

    def swap(self, i, j):
        """交换"""
        self.arr[i], self.arr[j] = self.arr[j], self.arr[i]

unsort_list = [3, 12, 12, 11, 15, 9, 12, 4, 15, 4, 2, 15, 7, 10, 12, 2, 3, 1, 14, 5, 7]
qs = QucikSort(unsort_list)
qs.sort()

# 从 1.1.0 版本开始支持的结果打印方式
qs.partition.print_stats()
print('结果: {}'.format(qs.arr))

执行结果如下:

Timer unit: 1e-06 s

Total time: 0.000344 s
File: tests/test_50_time_profiler_to_method.py
Function: partition at line 28

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    28                                               @TimeProfiler(print_res=False)
    29                                               def partition(self, left, right):
    30                                                   """分区"""
    31        15         17.0      1.1      4.9          pivot = left
    32        15         10.0      0.7      2.9          index = pivot + 1
    33        15          7.0      0.5      2.0          i = index
    34        93         63.0      0.7     18.3          while i <= right:
    35        78         58.0      0.7     16.9              if self.arr[i] < self.arr[pivot]:
    36        33         74.0      2.2     21.5                  self.swap(i, index)
    37        33         34.0      1.0      9.9                  index += 1
    38        78         47.0      0.6     13.7              i += 1
    39        15         26.0      1.7      7.6          self.swap(pivot, index - 1)
    40        15          8.0      0.5      2.3          return index - 1

结果:[1, 2, 2, 3, 3, 4, 4, 5, 7, 7, 9, 10, 11, 12, 12, 12, 12, 14, 15, 15, 15]

注意

当被装饰函数&方法被多次调用时,会复用该函数&方法对应的单例分析器, 所得的统计结果在上次的基础上累加后用于打印。若确实不关心累计结果, 仅需要使用全新的分析器进行分析可在装饰时使用 force_new_profiler 关键字参数实现, 具体参考其父类中的定义 ProfilerClassDecorator

内存分析器

该分析器可以统计出指定函数或方法中每行代码的执行内存消耗,从而找到瓶颈所在,用例如下:

from moprofiler import MemoryProfiler


class MemoryWaste(object):
    """
    浪费内存
    """
    @MemoryProfiler(print_res=False)
    def list_waste(self):
        """列表"""
        a = [1] * (10 ** 5)
        b = [2] * (2 * 10 ** 5)
        del b
        return a

    @classmethod
    @MemoryProfiler
    def dict_waste(cls, a):
        """字典"""
        ret = {}
        for i in a:
            ret[i] = i
        return ret

mw = MemoryWaste()
x = mw.list_waste()
mw.dict_waste(x)
mw.list_waste.print_stats()

执行结果如下:

Filename: tests/test_01_memory_profiler_to_method.py

Line #    Mem usage    Increment   Line Contents
================================================
    23     40.9 MiB     40.9 MiB       @classmethod
    24                                 @MemoryProfiler
    25                                 def dict_waste(cls, a):
    26                                     """字典"""
    27     40.9 MiB      0.0 MiB           ret = {}
    28     40.9 MiB      0.0 MiB           for i in a:
    29     40.9 MiB      0.0 MiB               ret[i] = i
    30     40.9 MiB      0.0 MiB           return ret


Filename: tests/test_01_memory_profiler_to_method.py

Line #    Mem usage    Increment   Line Contents
================================================
    15     38.6 MiB     38.6 MiB       @MemoryProfiler(print_res=False)
    16                                 def list_waste(self):
    17                                     """列表"""
    18     39.4 MiB      0.8 MiB           a = [1] * (10 ** 5)
    19     40.9 MiB      1.5 MiB           b = [2] * (2 * 10 ** 5)
    20     40.9 MiB      0.0 MiB           del b
    21     40.9 MiB      0.0 MiB           return a

注意

当被装饰函数&方法被多次调用时,会复用该函数&方法对应的单例分析器, 所得的统计结果在上次的基础上累加后用于打印。若确实不关心累计结果, 仅需要使用全新的分析器进行分析可在装饰时使用 force_new_profiler 关键字参数实现, 具体参考其父类中的定义 ProfilerClassDecorator

秒表工具

该秒表工具可以监控指定函数或方法的执行用时,当被装饰的方法继承了 StopwatchMixin 后,可以通过调用 dotting() 方法来进行日志打点,从而记录某个代码切片的用时。

由于打点多少可由开发者自行控制,故该工具与前述 时间分析器 的优势是,可用于生产环境。

import logging
import time

from moprofiler import StopwatchMixin, stopwatch

logging.basicConfig(
    level=logging.DEBUG,
    format='[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s')
LOG = logging.getLogger(__name__)


class zzz(StopwatchMixin):
    """测试方法装饰"""

    @staticmethod
    @stopwatch
    def orz_staticmethod():
        """静态方法"""
        for _i in range(2):
            time.sleep(0.25)

    @stopwatch
    def orz_instancemethod(self, x):
        """实例方法"""
        for _i in range(x):
            self.stopwatch.dotting()
            time.sleep(0.1)
        self.stopwatch.dotting()

    @classmethod
    @stopwatch(
        fmt='[性能] {name}, 参数列表: {args}, 耗时: {time_use:.8f}s, {foo}',
        logger=LOG,
        name='hakula',
        foo='matata')
    def orz_classmethod(cls, x):
        """类方法"""
        for _i in range(x):
            cls.stopwatch.dotting('定制打点输出{idx},当前 {time_diff:.8f}s,累计: {time_total:.8f}s')
            time.sleep(0.1)
        cls.stopwatch.dotting()

    @stopwatch(print_mem=True)
    def orz_instancemethod_generator(self, x):
        """实例方法生成器"""
        for _i in range(x):
            mute = True if _i == 2 else False
            self.stopwatch.dotting(mute=mute, memory=True)
            time.sleep(0.1)
            yield _i
        self.stopwatch.dotting(memory=True)

z = zzz()
z.orz_staticmethod()
z.orz_instancemethod(5)
z.orz_classmethod(5)
_tmp = [i for i in z.orz_instancemethod_generator(5)]
assert _tmp == [i for i in range(5)]

执行结果如下:

[2019-01-08 19:13:26,019] INFO [moprofiler.stopwatch:120] [性能] orz_staticmethod, 耗时: 0.5062s
[2019-01-08 19:13:26,021] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(1): 0.0002s, 累计耗时: 0.0002s
[2019-01-08 19:13:26,127] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(2): 0.1054s, 累计耗时: 0.1056s
[2019-01-08 19:13:26,229] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(3): 0.1021s, 累计耗时: 0.2078s
[2019-01-08 19:13:26,333] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(4): 0.1045s, 累计耗时: 0.3123s
[2019-01-08 19:13:26,438] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(5): 0.1046s, 累计耗时: 0.4168s
[2019-01-08 19:13:26,542] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(6): 0.1045s, 累计耗时: 0.5213s
[2019-01-08 19:13:26,543] INFO [moprofiler.stopwatch:120] [性能] orz_instancemethod, 耗时: 0.5218s
[2019-01-08 19:13:26,544] INFO [test_02_stopwatch_mixin:214] 定制打点输出1,当前 0.00021791s,累计: 0.00021791s
[2019-01-08 19:13:26,647] INFO [test_02_stopwatch_mixin:214] 定制打点输出2,当前 0.10304499s,累计: 0.10326290s
[2019-01-08 19:13:26,751] INFO [test_02_stopwatch_mixin:214] 定制打点输出3,当前 0.10447907s,累计: 0.20774198s
[2019-01-08 19:13:26,856] INFO [test_02_stopwatch_mixin:214] 定制打点输出4,当前 0.10449409s,累计: 0.31223607s
[2019-01-08 19:13:26,961] INFO [test_02_stopwatch_mixin:214] 定制打点输出5,当前 0.10524797s,累计: 0.41748405s
[2019-01-08 19:13:27,065] INFO [test_02_stopwatch_mixin:214] [性能] 当前耗时(6): 0.1044s, 累计耗时: 0.5219s
[2019-01-08 19:13:27,066] INFO [test_02_stopwatch_mixin:120] [性能] hakula, 参数列表: (<class 'test_02_stopwatch_mixin.zzz'>, 5), 耗时: 0.52235889s, matata
[2019-01-08 19:13:27,069] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(1): 0.0019s, 累计耗时: 0.0019s, 当前变化:  1M, 累计变化:  1M
[2019-01-08 19:13:27,175] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(2): 0.1060s, 累计耗时: 0.1079s, 当前变化:  2M, 累计变化:  3M
[2019-01-08 19:13:27,384] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(4): 0.1041s, 累计耗时: 0.3167s, 当前变化:  2M, 累计变化:  6M
[2019-01-08 19:13:27,490] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(5): 0.1068s, 累计耗时: 0.4235s, 当前变化:  1M, 累计变化:  7M
[2019-01-08 19:13:27,594] INFO [moprofiler.stopwatch:214] [性能] 当前耗时(6): 0.1040s, 累计耗时: 0.5274s, 当前变化:  0M, 累计变化:  7M
[2019-01-08 19:13:27,598] INFO [moprofiler.stopwatch:120] [性能] orz_instancemethod_generator, 耗时: 0.5313s, 内存变化:  7M

时间分析器模块

提供用于时间性能分析的工具

class moprofiler.time.TimeProfiler(_function=None, stream=None, output_unit=None, stripzeros=False, **kwargs)[源代码]

基类:moprofiler.base.ProfilerClassDecorator

时间分析器的类装饰器

逐行分析被装饰函数每行的执行时间

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • stream (object) – 输出方式,默认为 stdout ,可指定为文件
  • output_unit (str) – 输出单位
  • stripzeros (bool) – 是否去零
profiler_factory

line_profiler.LineProfiler 的别名

print_stats()[源代码]

打印统计结果

moprofiler.time.time_profiler

此变量是为了向后兼容旧版本的命名

moprofiler.time.TimeProfiler 的别名

内存分析器模块

提供用于内存性能分析的工具

class moprofiler.memory.MemoryProfilerWrapper(**kw)[源代码]

基类:memory_profiler.LineProfiler

内存分析器封装

在原本分析器中增加统一的打印接口

print_stats(stream=None, precision=1)[源代码]

打印统计结果

参数:
  • stream (object) – 输出方式,默认为 stdout ,可指定为文件
  • precision (int) – 精度
class moprofiler.memory.MemoryProfiler(_function=None, stream=None, precision=1, backend='psutil', **kwargs)[源代码]

基类:moprofiler.base.ProfilerClassDecorator

内存分析器的类装饰器

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • stream (object) – 输出方式,默认为 stdout ,可指定为文件
  • precision (int) – 精度,默认为 1
  • backend (str) – 内存监控的 backend ,默认为 ‘psutil’
profiler_factory

MemoryProfilerWrapper 的别名

print_stats()[源代码]

打印统计结果

moprofiler.memory.memory_profiler

此变量是为了向后兼容旧版本的命名

moprofiler.memory.MemoryProfiler 的别名

秒表工具模块

提供用于计时打点的秒表工具

class moprofiler.stopwatch.Stopwatch[源代码]

基类:object

秒表类

time_buf = None

用来存储计时打点时间

mem_buf = None

用来存储打点内存

name = None

秒表的名称,可在装饰时设置,默认为使用被装饰方法的方法名

dkwargs = None

用来存储最终输出时使用的变量

dotting_param_pre = None

用来记录上次打点输出时的参数信息

logger = None

用来日志输出的 logger

logging_level = None

日志输出级别

final_fmt = None

输出最终计时结果的字符串模板

is_print_memory = None

是否打印内存

wrap_generator(func, wrap_param)[源代码]

封装一个生成器从而使用秒表对其进行观察

参数:
  • func (types.FunctionType or types.MethodType) – 被封装的函数,由解释器自动传入,不需关心
  • wrap_param (dict) – 封装时传入的参数字典
返回:

封装后的方法

返回类型:

types.FunctionType or types.MethodType

wrap_function(func, wrap_param)[源代码]

封装一个函数从而使用秒表对其进行观察

参数:
  • func (types.FunctionType or types.MethodType) – 被封装的函数,由解释器自动传入,不需关心
  • wrap_param (dict) – 封装时传入的参数字典
返回:

封装后的方法

返回类型:

types.FunctionType or types.MethodType

dotting(fmt='', logging_level=None, memory=False, mute=False, **kwargs)[源代码]

输出打点日志

会在打点时记录当前的时间&进程内存使用(若 memoryTrue),并将其与上次的打点记录做差, 分别获取时间差 time_diff 、内存差 memory_diff ,再将其与进入函数时的记录做差, 分别获取到时间差 time_total 、内存差 memory_total

将以上四个变量传入 fmt 模板中格式化后输出到 log 。 除上述变量外,您还可以使用装饰器参数中指定的变量。

注意仅当 memory 为真时,才会记录日志情况,否则将直接跳过。

日志输出模板中可使用变量如下:

  1. idx : 当前打点的序号,从 1 开始
  2. time_diff : 距上次打点间的时间差
  3. time_total : 距函数/方法开始时的时间差
  4. memory_diff : 距上次打点(memory=True)间的内存差,跳过未记录内存的时间打点,直到函数/方法进入时的内存记录
  5. memory_total : 距函数/方法开始时的内存差
参数:
  • fmt (str) – 用来输出打点日志的格式化模板,需使用 format 的占位符格式
  • logging_level (int) – 日志输出级别,默认使用装饰当前方法时设置的级别,若无则使用类属性中定义的默认值
  • memory (bool) – 是否记录内存使用,默认为 False
  • mute (bool) – 静默打点,默认为 False ,若设为 True ,则当次仅记录时间/内存,不执行任何输出逻辑
class moprofiler.stopwatch.StopwatchMixin[源代码]

基类:moprofiler.base.ProfilerMixin

秒表 Mixin 类

用以提供复杂的秒表功能,如:

  1. 针对需要多次调用的方法进行累加记录的场景
  2. 在一次代码执行流程中同时记录多个方法,并灵活控制记录结果的输出
moprofiler.stopwatch.stopwatch(_function=None, print_args=False, logger=None, print_mem=False, fmt='', name='', logging_level=20, **dkwargs)[源代码]

返回秒表监控下的函数或方法

通过额外的关键字参数,支持配置自定义的值到输出模板中

日志输出模板中可使用变量如下:

  1. name : 当前秒表名称
  2. args : 被装饰函数/方法的位置参数
  3. kwargs : 被装饰函数/方法
  4. time_use : 函数/方法执行耗时
  5. mem_use : 函数/方法执行内存
参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • print_args (bool) – 是否打印被装饰函数的参数列表,若含有较长的参数,可能造成日志过长,开启时请注意
  • logger (logging.Logger) – 可传入指定的日志对象,便于统一输出样式,默认使用该模块中的全局 logger
  • print_mem (bool) – 是否在方法退出时打印内存信息,默认为 False
  • fmt (str) – 用于格式化输出的模板,可在了解所有内置参数变量后自行定制输出样式,若指定该参数则会忽略 print_args
  • name (str) – 关键字参数,被装饰方法代理生成的 stopwatch 所使用的名称,默认为使用被装饰方法的方法名
  • logging_level (int) – 打印日志的级别,默认为 INFO
返回:

装饰后的函数

返回类型:

types.FunctionType or types.MethodType

基础模块

提供用于性能分析的相关基类&函数定义

moprofiler.base.proxy(obj, prop, prop_name)[源代码]

为 object 对象代理一个属性

参数:
  • obj (object) – 被代理的对象
  • prop (object) – 代理返回的属性
  • prop_name (str) – 被代理的属性名
返回:

被代理之后的对象

返回类型:

object

moprofiler.base.get_callargs(func, *args, **kwargs)[源代码]

找到层层装饰器下最里层的函数的 callargs

参数:
  • func (function) – 被装饰过的函数
  • args (list) – 调用函数时的位置参数
  • kwargs (dict) – 调用函数时的关键字参数
返回:

调用参数字典

返回类型:

dict

moprofiler.base.is_instance_or_subclass(self_or_cls, super_cls)[源代码]

判断对象或类是否继承了指定类

参数:
  • self_or_cls (object) – 对象或类
  • super_cls (class) – 父类
返回:

判断结果

返回类型:

bool

class moprofiler.base.ProfilerMixin[源代码]

基类:object

分析器 Mixin 的基类

class moprofiler.base.ClassDecoratorBase(_function=None, fake_method=True)[源代码]

基类:object

类装饰器初始化

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • fake_method (bool) – 是否将被装饰后的类装饰器伪装成方法,默认为是。 注意,伪装后仍然可以正常调用类装饰器中额外定义的对象属性, 此参数仅用于装饰类方法时使用,装饰函数时无效
func

被封装函数的 getter 方法

class moprofiler.base.ProfilerClassDecorator(_function=None, print_res=True, force_new_profiler=False, profiler_args=None, profiler_kwargs=None, **kwargs)[源代码]

基类:moprofiler.base.ClassDecoratorBase

分析器的类装饰器初始化

增加分析器相关的供外部使用的公共属性

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • print_res (bool) – 是否在被装饰对象退出后立刻打印分析结果,默认为 True 。 当需要将多次调用结果聚集后输出时,可设为 False ,并通过调用被装饰函数/方法 (装饰后将被替换为 ProfilerClassDecorator )的 print_stats() 方法进行结果输出
  • force_new_profiler (bool) – 是否强制使用新的分析器,默认为
  • profiler_args (tuple) – 分析器工厂的位置参数列表
  • profiler_kwargs (dict) – 分析器工厂的关键字参数字典
profiler_factory

用于生产分析器的工厂

profiler = None

分析器实例对象

print_stats()[源代码]

打印统计结果

抽象方法,需子类实现

Shortcut

注意

此文档中列出的为 moprofiler 对外提供功能的捷径说明,从而缩短调用路径

提供针对时间、内存的分析器,以及秒表日志打点工具

class moprofiler.TimeProfiler(_function=None, stream=None, output_unit=None, stripzeros=False, **kwargs)[源代码]

基类:moprofiler.base.ProfilerClassDecorator

时间分析器的类装饰器

逐行分析被装饰函数每行的执行时间

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • stream (object) – 输出方式,默认为 stdout ,可指定为文件
  • output_unit (str) – 输出单位
  • stripzeros (bool) – 是否去零
profiler_factory

line_profiler.LineProfiler 的别名

print_stats()[源代码]

打印统计结果

class moprofiler.MemoryProfiler(_function=None, stream=None, precision=1, backend='psutil', **kwargs)[源代码]

基类:moprofiler.base.ProfilerClassDecorator

内存分析器的类装饰器

参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • stream (object) – 输出方式,默认为 stdout ,可指定为文件
  • precision (int) – 精度,默认为 1
  • backend (str) – 内存监控的 backend ,默认为 ‘psutil’
profiler_factory

MemoryProfilerWrapper 的别名

print_stats()[源代码]

打印统计结果

class moprofiler.StopwatchMixin[源代码]

基类:moprofiler.base.ProfilerMixin

秒表 Mixin 类

用以提供复杂的秒表功能,如:

  1. 针对需要多次调用的方法进行累加记录的场景
  2. 在一次代码执行流程中同时记录多个方法,并灵活控制记录结果的输出
moprofiler.stopwatch(_function=None, print_args=False, logger=None, print_mem=False, fmt='', name='', logging_level=20, **dkwargs)[源代码]

返回秒表监控下的函数或方法

通过额外的关键字参数,支持配置自定义的值到输出模板中

日志输出模板中可使用变量如下:

  1. name : 当前秒表名称
  2. args : 被装饰函数/方法的位置参数
  3. kwargs : 被装饰函数/方法
  4. time_use : 函数/方法执行耗时
  5. mem_use : 函数/方法执行内存
参数:
  • _function (types.FunctionType or types.MethodType) – 被封装的对象,由解释器自动传入,不需关心
  • print_args (bool) – 是否打印被装饰函数的参数列表,若含有较长的参数,可能造成日志过长,开启时请注意
  • logger (logging.Logger) – 可传入指定的日志对象,便于统一输出样式,默认使用该模块中的全局 logger
  • print_mem (bool) – 是否在方法退出时打印内存信息,默认为 False
  • fmt (str) – 用于格式化输出的模板,可在了解所有内置参数变量后自行定制输出样式,若指定该参数则会忽略 print_args
  • name (str) – 关键字参数,被装饰方法代理生成的 stopwatch 所使用的名称,默认为使用被装饰方法的方法名
  • logging_level (int) – 打印日志的级别,默认为 INFO
返回:

装饰后的函数

返回类型:

types.FunctionType or types.MethodType

moprofiler.time_profiler

此变量是为了向后兼容旧版本的命名

moprofiler.time.TimeProfiler 的别名

moprofiler.memory_profiler

此变量是为了向后兼容旧版本的命名

moprofiler.memory.MemoryProfiler 的别名

发布说明

1.1.0 (2019-02-20 13:42:05)
Feature
  1. 重构时间&内存分析器的设计,去除 Mixin 类,简化使用方法
1.0.3 (2019-01-27 13:47:22)
Feature
  1. 为时间&内存分析器的装饰器增加 force_new_profiler 关键字参数,实现强制使用新分析器的功能
1.0.2 (2019-01-13 22:12:14)
Feature
  1. 实现对函数&静态方法使用 时间分析器 装饰后,在外部打印分析结果的功能
  2. 实现对函数&静态方法使用 内存分析器 装饰后,在外部打印分析结果的功能
Bugfix
  1. 修复 CI 过程中设置全局包版本号环境变量失败的 Bug
1.0.1.post0 (2019-01-13 15:53:12)
Optimize
  1. 优化 rtfd 文档编写
  2. 完善 Travis CI 配置,并整理徽章
  3. 添加 SonarCloud 代码质量分析 CI
  4. 完善单元测试覆盖率
1.0.1 (2019-01-08 19:27:20)
Feature
  1. 为秒表工具增加内存监控的功能,便于查看两个打点间的内存变化
1.0.0 (2019-01-05 21:13:20)
Feature
  1. 实现对 line_profiler 封装的装饰器及相应的 TimeProfilerMixin
  2. 实现对 memory-profiler 封装的装饰器及相应的 MemoryProfilerMixin
  3. 实现用于打点计时的秒表工具,方便记录函数的关键执行节点,以及耗时