简体   繁体   English

如何在 QGIS Python 插件中分析性能?

[英]How to profile performance in a QGIS Python plugin?

Is it possible to use kernprof.py, line_profiler.py, or something similar to profile a QGIS plugin?是否可以使用 kernprof.py、line_profiler.py 或类似于配置 QGIS 插件的东西? I can't run the plugin outside of QGIS because the plugin requires state from QGIS & will make calls to the QGIS API.我无法在 QGIS 之外运行该插件,因为该插件需要来自 QGIS 的状态并且会调用 QGIS API。

It seems like I might be able to modify the plugin's initializer to call kernprof, to call back to the plugin and pass the state all the way through, but I can't wrap my head around it.似乎我可以修改插件的初始化程序以调用 kernprof,回调插件并一直传递状态,但我无法理解它。

Does anyone have experience with running a Python profiler from inside another tool?有没有人有从另一个工具中运行 Python 分析器的经验?

I used a simpler way to profile my plugin using cProfile.我使用了一种更简单的方法来使用 cProfile 来分析我的插件。 In the constructor of main class of plugin (that is returned in classFactory), I used this code:在插件的主类(在 classFactory 中返回)的构造函数中,我使用了以下代码:

 self.pr = cProfile.Profile()
 self.pr.enable()

and in unload method of the class or any where some one needs print the profile stats:并在类的卸载方法或任何需要打印配置文件统计信息的地方:

    self.pr.disable()
    s = io.StringIO()
    sortby = SortKey.CUMULATIVE
    ps = pstats.Stats(self.pr, stream=s).sort_stats(sortby)
    ps.print_stats() 

remember to use following code for imports:请记住使用以下代码进行导入:

import cProfile, pstats, io
from pstats import SortKey

It's possible to use line_profiler while running your script inside QGIS.在 QGIS 中运行脚本时可以使用line_profiler

You need to import it inside the main file of your plugin along your other imports, then add profile = line_profiler.LineProfiler() before your main class, add the decorator @profile just before your main function to profile and finally add profile.print_stats(stream=stream) just before the return of the function.您需要将它与其他导入一起导入到插件的主文件中,然后在主类之前添加profile = line_profiler.LineProfiler() ,在主函数之前添加装饰器@profile到配置文件,最后添加profile.print_stats(stream=stream)就在函数返回之前。

I suppose there is other ways to do it, but it's the way I found that works good enough for me.我想还有其他方法可以做到这一点,但我发现这种方法对我来说已经足够好了。

Below is an example for a Processing plugin:以下是处理插件的示例:

import os

import line_profiler
profile = line_profiler.LineProfiler() 

class processingScriptExample(QgsProcessingAlgorithm):
    
    INPUT_directory = 'INPUT_directory'
    
    def initAlgorithm(self, config):
        self.addParameter(QgsProcessingParameterNumber(self.INPUT_directory,
                                                       self.tr('Output directory'),
                                                       QgsProcessingParameterFile.Folder))
    
    @profile
    def processAlgorithm(self, parameters, context, feedback):
        directory = self.parameterAsInt(parameters, self.INPUT_directory, context)
        
        ls = []
        for ii in range(1000000):
            ls.append(ii)
        
        ls = [ii for ii in range(1000000)]
        
        path_profiling = os.path.join(directory, "line_profiling.txt")
        with open(path_profiling, 'w') as stream:
            profile.print_stats(stream=stream)

        return {'Profiling file': path_profiling}

The resulting file:结果文件:

Timer unit: 1e-07 s

Total time: 1.31260 s
File: C:\OSGeo4W\profiles\default/python/plugins\test\algo_test.py
Function: processAlgorithm at line 70

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    70                                               @profile
    71                                               def processAlgorithm(self, parameters, context, feedback):
    72         1        248.0    248.0      0.0          directory = self.parameterAsInt(parameters, self.INPUT_directory, context)
    73                                           
    74         1          8.0      8.0      0.0          ls = []
    75   1000001    5054594.0      5.1     38.5          for ii in range(1000000):
    76   1000000    6633146.0      6.6     50.5              ls.append(ii)
    77                                                   
    78         1    1418416.0 1418416.0     10.8          ls = [ii for ii in range(1000000)]
    79                                                   
    80         1        561.0    561.0      0.0          path_profiling = os.path.join(directory, "line_profiling.txt")
    81         1      19001.0  19001.0      0.1          with open(path_profiling, 'w') as stream:
    82                                                       profile.print_stats(stream=stream)
    83                                           
    84                                                   return {"Profiling file":path_profiling}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM