测量资源的加载和部署时间

在 Kanzi 中,资源分析使您能够测量在 Kanzi 应用程序中加载和部署资源和预设所需的时间。

在 Kanzi 中,资源是可以在应用程序的不同部分重用的项目。例如,Mesh Data 资源定义了 Model 节点的几何形状,您可以在不同的节点中使用相同的 Color Brush 来设置 Foreground Brush 或 Background Brush 属性。

使用预设件(预制模板)来组织应用程序的结构并创建一致的界面。您可以使用预设件创建应用程序的构建块,并简化应用程序的维护。

要使用资源剖析:

  1. 在应用程序配置中启用资源分析。请参阅 启用资源分析。

  2. 在应用程序代码中实现资源剖析数据的记录,然后运行应用程序,以收集并记录资源剖析数据。请参阅 记录资源剖析数据。

  3. 要测量在您的 Kanzi 应用程序中资源加载和部署的性能,请分析资源分析数据。请参阅 分析资源剖析数据。

启用资源分析

要开始资源剖析,您必须启用资源剖析类别。

要在应用程序配置中启用资源分析,请在以下任一位置使用 ProfilingCategoryFilter 设置:

  • application.cfg 文件:

    ProfilingCategoryFilter = "ResourceLoading=on"
    
  • Application::onConfigure 函数:

    configuration.profilingCategoryFilter = "ResourceLoading=on";
    

要启用或禁用应用程序期间的资源剖析,启用或禁用相应的剖析类别:

  • 要启用资源剖析:

    kzProfilingCategorySetRuntimeState(KZ_PROFILING_RESOURCE_LOADING_CATEGORY, KZ_PROFILING_ENABLED_CATEGORY)
    
  • 要禁用资源剖析:

    kzProfilingCategorySetRuntimeState(KZ_PROFILING_RESOURCE_LOADING_CATEGORY, KZ_PROFILING_DISABLED_CATEGORY)
    

在您启用资源剖析后,您可以收集资源剖析数据并将数据写入文件中。请参阅 记录资源剖析数据。

记录资源剖析数据

要分析资源在您的 Kanzi 应用程序中的加载和部署,记录资源分析数据。在记录资源分析数据之前,您必须启用资源分析。请参阅 启用资源分析。

在启用资源分析后,Kanzi 会分析应用程序中每个资源的加载和部署,并将分析数据存储在资源分析器中。在您的应用程序代码中实现收集的分析数据的日志记录。Kanzi 提供了 ResourceProfilingPtreePopulator 类,您可以使用该类将资源分析数据填充到 Boost 属性树中。然后,您可以使用 Boost 中可用的 JSON 格式化程序以 JSON 格式记录分析数据。

要记录资源剖析数据:

  1. 在您的应用程序代码中,包括记录资源剖析数据所需的头文件。

    // Defines the standard input/output stream objects.
    #include <kanzi/core/cpp/iostream.hpp>
    // Provides classes that implement string stream input/output operations.
    #include <kanzi/core/cpp/sstream.hpp>
    // Provides the functionality for populating a Boost property tree with resource profiling data.
    #include <kanzi/core.ui/resource/resource_profiling_ptree_populator.hpp>
    // Provides the functionality for translating a Boost property tree to JSON and writing it to a file.
    #include <boost/property_tree/json_parser.hpp>
    
  2. 向您的应用程序添加输出资源剖析数据的代码。

    例如,要将分析数据记录到 cout 中,在应用程序退出时 Kanzi 调用的回调 Application::onShutdown 中,添加以下代码:

    void onShutdown() override
    {
        // Populate the Boost property tree with resource profiling data.
        ptree resourceProfilingDataTree = ResourceProfilingPtreePopulator::populatePropertyTree(getResourceManager()->getResourceProfiler());
        // Write the property tree to string stream in JSON format.
        stringstream ss;
        boost::property_tree::json_parser::write_json(ss, resourceProfilingDataTree);
        // Boost json_writer considers "/" to be an escape character and adds "\" before each "/".
        // This affects how kzb URLs are displayed.
        // This code outputs json to cout replacing "\/" with "/".
        string str(ss.str());
        string_view strView(str);
        while (!strView.empty())
        {
            size_t backSlashPos = strView.find("\\/");
            if (backSlashPos == string_view::npos)
            {
                cout << strView;
                strView = string_view();
                continue;
            }
            cout << strView.substr(0, backSlashPos);
            strView = strView.substr(backSlashPos + 1);
        }
        cout << "\n";
    }
    
  3. 使用其中一个 Profiling build 配置构建您的应用程序并运行。退出应用程序时,您可以在控制台窗口中看到分析数据。

    要在 Windows 上构建并运行您的应用程序:

    1. 在 Visual Studio 中使用 Profiling 构建配置构建您的应用程序。

      ../../_images/profiling-configuration.png
    2. 当您在 Windows 上运行 Kanzi Profiling build 时,没有控制台窗口。要记录资源分析数据,请在计算机上打开命令行界面并运行应用程序,以便将应用程序输出重定向到您要存储资源分析信息的文件中:

      Application\build_vs<Version>\runtime\Profiling\My_application.exe > MyApplicationResourceProfiling.json
      

Kanzi 将资源分析数据写入输出文件,其中包含:

  • Resource profiling contexts for the Kanzi Resource Manager 主线程 ("Resource manager main thread") 和加载线程 ("Resource manager loader thread N")。请参阅 资源剖析上下文。

  • 资源分析数据样本 ("ResourceProfilingDataSamples")。请参阅 资源剖析数据样本。

...
{
    "ThreadContexts": {
        "Resource manager loader thread 1": [
            ...
        ]
        "Resource manager main thread": [
            { resource profiling context of resourceX },
            { resource profiling context of resourceY },
            ...
            { resource profiling context of resourceZ }
        ],
        "Resource manager loader thread 0": [
            ...
            { resource profiling context of resourceY },
            ...
        ],
        "Resource manager loader thread 2": [
            ...
            ...
            { resource profiling context of resourceZ },
            ...
        ]
    },
    "ResourceProfilingDataSamples": [
        ...
        { profiling data sample for resourceX },
        { profiling data sample for resourceY },
        { profiling data sample for resourceZ },
        ...
   ]
}
...

关于为 滚动视图示例 记录的资源剖析数据示例,请参阅 分析资源剖析数据。

分析资源剖析数据

在您启用资源剖析和收集数据后,分析资源剖析程序采集的数据。

资源剖析数据包括:

  • Resource profiling contexts for the Kanzi Resource Manager 主线程 ("Resource manager main thread") 和加载线程 ("Resource manager loader thread N")。请参阅 资源剖析上下文。

  • 资源分析数据样本 ("ResourceProfilingDataSamples")。请参阅 资源剖析数据样本。

例如,收集和分析 滚动视图示例 应用程序的资源剖析数据。

资源剖析数据包括如下信息:

资源获取

通常在应用程序启动时,Kanzi 获取 StartupPrefab 预设模板,并将其实例化为 Screen 节点。为了优化加载资源的性能,在获取启动预设后,Kanzi 遍历 Screen 节点的子孙节点,并收集这些节点使用的所有资源的 URL。然后,Kanzi 异步获取这些资源。

例如:在 Scroll view 示例应用程序中,City 节点使用 City 网格。因为 City 节点是 Screen 节点的子孙节点之一,Kanzi 异步获取 City 网格。

../../_images/scroll-view-example-city-node.png

在为滚动视图示例应用程序收集的资源分析数据中,在 Resource manager main thread 资源分析上下文中,您可以找到 City 网格的 ResourceAcquire 上下文。上下文的 AcquireAsynchronously 属性设置为 true,显示 Kanzi 按预期异步获取了 City 网格。

ResourceAcquire 上下文的父级是 AcquireResourcesAsynchronously 上下文,它是 MainLoop 上下文的依赖上下文。MainLoop 上下文的 MainLoopCounter 属性的值为 "0",这意味着 Kanzi 在第一次主循环迭代期间异步获取了 City 网格资源。

AcquireResourcesAsynchronously 上下文具有依赖上下文,这些上下文与获取 Screen 节点的子节点使用的其他资源有关。

"Resource manager main thread": [
    {
        "ContextID": "217960184",
        "ContextName": "MainLoop",
        "DependenciesDuration": "359408700",
        "Duration": "389924400",
        "MainLoopCounter": "0",
        "TimeStamp": "200",
        "DependencyContexts": [
            {
            ...
            },
            {
                "ContextID": "217470528",
                "ContextName": "AcquireResourcesAsynchronously",
                "DependenciesDuration": "0",
                "Duration": "329800",
                "TimeStamp": "3697300",
                "DependencyContexts": [
                    ...
                    {
                        "AcquireAsynchronously": "true",
                        "ContextID": "216087856",
                        "ContextName": "ResourceAcquire",
                        "DependenciesDuration": "0",
                        "Duration": "9000",
                        "ResourceUrl": "kzb://scroll_view/Mesh Data/City",
                        "TimeStamp": "3750800"
                    },
                    ...

资源加载 (Resource loading)

Kanzi 加载线程并行加载那些设置为异步加载的资源。对于每个异步加载的资源,Kanzi 创建一个加载任务,并将任务放入加载队列中,加载线程从中挑选加载任务来执行。每当一个加载线程执行加载任务时,资源分析器为该加载线程创建一个 ResourceLoading 上下文。

ResourceLoading 上下文中的 City 网格是 "Resource manager loader thread 1" 线程的资源加载上下文之一。在 ResourceLoading 上下文中:

  • ResourceLoadingDuration 以纳秒为单位显示加载线程加载资源所需的时间。

  • TimeStamp 显示资源加载开始的时间。

  • LoadedResourceID 是已加载资源的唯一标识符。您可以使用该标识符在应用程序清除资源后再次加载相同资源的不同场合之间进行区分。

"Resource manager loader thread 1": [
    ...
    {
        "ContextID": "241268536",
        "ContextName": "ResourceLoading",
        "DependenciesDuration": "0",
        "Duration": "1475600",
        "LoadedResourceID": "217505936",
        "LoadedResourceType": "Kanzi.Mesh",
        "LoadedResourceUrl": "kzb://scroll_view/Mesh Data/City",
        "ResourceLoadingDuration": "1473400",
        "TimeStamp": "3764700"
    }
        ...

在加载线程完成加载 City 网格资源后,它将资源的加载任务放置到资源管理器的完成队列中,以便由 Kanzi 主线程进一步处理。当加载线程将已完成的任务放置到队列中时,Kanzi 主线程处理完成队列。

当调试时,您可以在 Kanzi 创建线程以及 Kanzi 将加载任务分配给具有 Domain::setOnThreadCreation 和 ResourceManager::setOnNewLoadingTask 的线程时提供您自己的回调。

资源部署

当 Kanzi 主线程处理 City 网格资源的加载任务时,资源分析器创建 FinishingQueue::processTask 上下文。在 FinishingQueue::processTask 上下文中,Description 属性显示加载任务的任务索引和完成队列中的任务数量。加载任务的处理包括资源部署,这是资源获取的最后阶段。ResourceDeployment 上下文是 FinishingQueue::processTask 上下文的依赖上下文。

"Resource manager main thread": [
    {
        "ContextID": "217960184",
        "ContextName": "MainLoop",
        "DependenciesDuration": "359408700",
        "Duration": "389924400",
        "MainLoopCounter": "0",
        "TimeStamp": "200",
        "DependencyContexts": [
                ...
                ]
            },
            ...
            {
                "ContextID": "242014232",
                "ContextName": "FinishingQueue::processTask",
                "DependenciesDuration": "43838400",
                "Description": "Task index 1, queue size 2",
                "Duration": "43914000",
                "TimeStamp": "59703300",
                "DependencyContexts": [
                    {
                        "ContextID": "241265456",
                        "ContextName": "ResourceDeployment",
                        "DependenciesDuration": "43118600",
                        "DeployedResourceID": "217505936",
                        "DeployedResourceType": "Kanzi.Mesh",
                        "DeployedResourceUrl": "kzb://scroll_view/Mesh Data/City",
                        "DeploymentDuration": "719800",
                        "Duration": "43839600",
                        "TimeStamp": "59705500",
                        "DependencyContexts": [
                            ...

City 网格使用一个 3D 资产和若干材料。Kanzi 在部署 City 网格时获取这些资源。那些 City 网格所依赖资源的 ResourceAcquire 上下文被作为依赖上下文添加到 City 网格的 ResourceDeployment 上下文中。

例如:在 City 网格的 ResourceDeployment 上下文的依赖树中,您可以看到 City 网格使用了 Park2 材质,而该材质又使用了 Phong 材质类型。在 City 网格的 ResourceDeployment 上下文中,DependenciesDuration 属性以纳秒为单位显示加载和部署 City 网格所依赖的资源所花费的时间。

"ContextID": "241265456",
"ContextName": "ResourceDeployment",
"DependenciesDuration": "43118600",
"DeployedResourceID": "217505936",
"DeployedResourceType": "Kanzi.Mesh",
"DeployedResourceUrl": "kzb://scroll_view/Mesh Data/City",
"DeploymentDuration": "719800",
"Duration": "43839600",
"TimeStamp": "59705500",
"DependencyContexts": [
    {
        "AcquireAsynchronously": "false",
        "ContextID": "216090208",
        "ContextName": "ResourceAcquire",
        "DependenciesDuration": "16896400",
        "Duration": "16919600",
        "ResourceUrl": "kzb://scroll_view/Materials/Park2",
        "TimeStamp": "59706300",
        "DependencyContexts": [
            {
                "ContextID": "241265368",
                "ContextName": "ResourceLoading",
                "DependenciesDuration": "0",
                "Duration": "1200",
                "LoadedResourceID": "241104440",
                "LoadedResourceType": "Kanzi.Material",
                "LoadedResourceUrl": "kzb://scroll_view/Materials/Park2",
                "ResourceLoadingDuration": "200",
                "TimeStamp": "59724500"
            },
            {
                "ContextID": "241265280",
                "ContextName": "ResourceDeployment",
                "DependenciesDuration": "16864100",
                "DeployedResourceID": "241104440",
                "DeployedResourceType": "Kanzi.Material",
                "DeployedResourceUrl": "kzb://scroll_view/Materials/Park2",
                "DeploymentDuration": "32100",
                "Duration": "16897400",
                "TimeStamp": "59726400",
                "DependencyContexts": [
                    {
                        "AcquireAsynchronously": "false",
                        "ContextID": "216092784",
                        "ContextName": "ResourceAcquire",
                        "DependenciesDuration": "16864100",
                        "Duration": "16882700",
                        "ResourceUrl": "kzb://scroll_view/Material Types/Phong",
                        "TimeStamp": "59731500",
                        "DependencyContexts": [
                            {
                                "ContextID": "241267480",
                                "ContextName": "ResourceLoading",
                                "DependenciesDuration": "0",
                                "Duration": "13800",
                                "LoadedResourceID": "241104544",
                                "LoadedResourceType": "Kanzi.ShaderProgram",
                                "LoadedResourceUrl": "kzb://scroll_view/Material Types/Phong",
                                "ResourceLoadingDuration": "13300",
                                "TimeStamp": "59740100"
                            },
                            {
                                "ContextID": "241265192",
                                "ContextName": "ResourceDeployment",
                                "DependenciesDuration": "0",
                                "DeployedResourceID": "241104544",
                                "DeployedResourceType": "Kanzi.ShaderProgram",
                                "DeployedResourceUrl": "kzb://scroll_view/Material Types/Phong",
                                "DeploymentDuration": "16850800",
                                "Duration": "16853300",
                                "TimeStamp": "59754300"
                            }
                        ]
                    }
                ]
            }
        ]
    },

另请参阅

资源剖析参考

测量应用程序性能

应用程序开发

资源 (Resources)

使用节点预设