【GAD翻译馆】UE4:文件分析工具(FIle Profiler)的改进
翻译:王成林(麦克斯韦的麦斯威尔) 审校:黄秀美(厚德载物)
你可以从这里查看并下载这篇博客中的代码。
目前的文件分析工具
UE4有一个用于分析文件IO的工具——不过如Michael Caine所说的: “并没有很多人知道它”。至少在网上关于它的信息非常少。如果你想试试,只需在运行项目时在命令行中添加 “-ProfileFile” 。这会覆盖IPlatformFile对于FProfiledPlatformFile的使用,将数据收集标签附加到文件IO操作函数。为了可以看见数据分析,你只需使用控制台命令 “Profile File”,然后会出现一个类似于下图的窗口:
该窗口显示的内容是GPU分析工具。不幸的是,分析工具的数据不是那么容易看懂:
· 它糟糕地将文件的相对路径和绝对路径混在一起了(如下图)
· 不可能从信息中得到有用的概括(只给你了一长串的文件操作)
· 你只能在编辑器或者游戏运行时才能看到数据(任何信息都不会保存到硬盘中)
用于处理收集到的数据和显示I/O分析工具窗口的代码位于LevelTick.cpp的FFileProfileWrapperExec () 中。含有 “Exec()”的函数允许命令从程序中的其他位置发出,目前它的功能是对从程序开始到用户输入命令时记录的文件IO活动拍一个快照。
文件分析工具的改进
第一个简单的改进是增强路径的可读性。为此我们使用虚幻引擎的FPaths:: ConvertRelativePathToFull()方法。我们不在被覆盖的文件IO函数中进行改动,而是在DisplayProfileData()中进行,避免做重复转换(这是由于文件IO方法的层级结构)。所以我们将:-
1 2 | TSharedPtr<fprofiledfilestatsfilebase> FileStat = ProfileData[ Index ]; double FileDurationMs = 0.0;</fprofiledfilestatsfilebase> |
修改为:-
1 2 3 4 | TSharedPtr<fprofiledfilestatsfilebase> FileStat = ProfileData[Index]; // Convert to full paths for ease of reading FileStat->Name = FPaths::ConvertRelativePathToFull(FileStat->Name); double FileDurationMs = 0.0;</fprofiledfilestatsfilebase> |
另外还有几处改动可以使我们更好地使用文件分析工具收集到的数据。简单来讲我们希望能够以一种更加方便用户的方式来显示数据,能够使用命令保存和重新载入数据,以及最好在退出程序时保存数据。
虽然我们可以创建一个酷炫的Slate窗口来显示IO数据,但实际上我们希望看到一个数据表单,并且可以按照一项特定的统计数据进行排序。你最爱的电子表格程序当然就有这个功能——所以在虚幻引擎中重新写一个显示窗口只不过是做无用功罢了。另外我们还希望将数据存储在硬盘中以便以后引用,所以使用一个CSV文件可以解决我们这两个问题。要分析的数据被收集到一个含有FProfiledFileStatsBase结构体的树状图中,然后被转换并显示在当前文件I/O窗口中,通过对树状图进行遍历然后创建一个“事件名称”的列表,其中每个文件都有一个相关时间。我们仅仅添加了一个对每个IO事件进行计数并将数据写到文件的功能。
接下来我们先不要乱改LevelTick.cpp,而是趁这个机会将整个FFileProfileWrapperExec类移动到它自己的h/cpp文件中。你可以从我们上传的文件中找到那些文件,并将它们放到你的Engine/Source/Runtime/Engine/Privatefolder中。从LevelTick.cpp中删除旧的FFileProfileWrapperExec代码,最后有一些不需要的包含文件,将它们从LevelTick.cpp中删除:-
1 2 3 4 5 | // NOT NEEDED #include "HAL/IPlatformFileProfilerWrapper.h" // NOT NEEDED #if !UE_BUILD_SHIPPING // NOT NEEDED #include "VisualizerEvents.h" // NOT NEEDED #include "STaskGraph.h" // NOT NEEDED #endif |
在新的FileProfileWrapperExec.h/cpp文件的开头我们修改了命令行解析代码,加入了一个 “Quit” 指令。这设置了一个布尔型变量,阻止文件I/O窗口打开,使文件分析数据在程序终止时在不生成弹窗的情况下被保存。另外,这意味着文件分析工具可以在诸如烘焙这样的自动过程或者命令行中运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool FFileProfileWrapperExec::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) { // Internal UE4 command line - use to take a snapshot of the file IO data recorded from startup to the current time point. // (The file profiler is activated by running the application with 'ProfileFile' as a flag.) if (FParse::Command(&Cmd, TEXT( "ProfileFile" ))) { // 'Quit' command is issued in FEngineLoop::AppPreExit() if (FParse::Command(&Cmd, TEXT( "Quit" ))) { bShouldSuppressIOVisualiser = true ; } } …. } |
为了使文件分析工具在退出时将变化写到文件中,我们需要在程序退出阶段一个合适的时间点发出“Quit”命令。这一步骤可以在内存分析工具(MallocProfiler)开始关闭时实现,我们在FEngineLoop:: AppPreExit()(LaunchEngineLoop.cpp)的前面加上StaticExec(),如下所示:-
1 2 3 4 5 6 7 8 9 10 11 12 13 | void FEngineLoop::AppPreExit( ) { UE_LOG(LogExit, Log, TEXT( "Preparing to exit." ) ); FCoreDelegates::OnPreExit.Broadcast(); MALLOC_PROFILER( GMalloc->Exec(nullptr, TEXT( "MPROF STOP" ), *GLog); ); #if !UE_BUILD_SHIPPING FSelfRegisteringExec::StaticExec(nullptr, TEXT( "ProfileFile Quit" ), *GLog); #endif // !UE_BUILD_SHIPPING #if WITH_ENGINE ... } |
剩下的关于FFileProfileWrapperExec()类的改动相对不那么令人激动了,主要是处理CSV的格式和保存。我们已经对数据进行了一番处理,显示了文件大小和从中读取数据量之间的对比,另外还保存了各文件中的原始数据的总数。
设置好这些后,如果你使用“-ProfileFIle”命令行选项运行你的项目,你会在程序退出后在\UnrealEngine\<你的项目名>\Saved\FileProfiler路径下得到一个CSV文件。使用控制台命令“ProfileFile”,程序会生成到这一时刻的所有IO活动的一张快照,你可以在内置的IO可视器窗口看到它(只不过文件名更加好认了些!)
有了这个,你现在可以看到所有的文件操作并可以将它们以各种方式进行排序以便分析。
在下一篇博客中,我们将分析快照中的一些数据——并且为你展示它如何帮助我们追踪漏洞,优化文件使用以及更多。
如果你有任何想法或者问题请在下方评论区留言!
【版权声明】
原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权。