Unreal Engine 4 —— 使用反汇编来确定该进行优化的地方
2016-05-25 11:39
603 查看
这篇博客翻译自Robert Troughton的博客Using the Disassembler to Highlight Optimization Targets,已征得原作者同意。
This post is translated from English. You can find the original English language version here: http://coconutlizard.co.uk/blog/ue4/using-the-disassembler/
UE4中有很多的字符串处理的函数,这些函数会在UE4中的各种情况下被调用 —— 例如无论在编辑器、cooking或者运行游戏时,都会有一大堆的字符串函数被调用。
在最近的测试中,我们着重测试了一下
以上的代码看起来是无害的,只是一些很普通的字符串测试来判定InPath是相对路径(eg. “../engine/myfile.uasset”) 还是绝对路径 (eg. “c:\myfile.uasset”)。
为了研究这段代码,我启动Debugger,在函数中设置了断点并且查看其汇编代码。此时发生了非常恐怖的事,以下只是汇编代码中的一小段:
以上仅仅是整个函数的汇编代码的冰山一角,而整个函数也只有一行cpp代码,这简直是恐怖。
此外,这个代码不仅仅只是长而已,你应该可以看到有
还有,
因此,我做了如下事:
减少了StartsWith()函数的调用,转而使用更为直接的字符比较。
移除了runtime的TEXT()区块,取而代之的是在外部直接创建。
将其中RootPrefix测试设为在编辑器中才有效。
因此,我最终的代码如下:
对于.h文件,添加内容如下:
重新编译后,最终的汇编代码如下:
以上的汇编代码是在non-editor模式中的整个函数的汇编代码,我相信你我都能同意以上代码的性能要好得多。
优化这段代码还带来了一个很好的副作用:编译器会将这段逻辑进行inline操作,我们甚至不需要声明
我的最终测试表明这段代码的性能快了将近20倍,而代码资源占用量只是原来的10%。
This post is translated from English. You can find the original English language version here: http://coconutlizard.co.uk/blog/ue4/using-the-disassembler/
UE4中有很多的字符串处理的函数,这些函数会在UE4中的各种情况下被调用 —— 例如无论在编辑器、cooking或者运行游戏时,都会有一大堆的字符串函数被调用。
在最近的测试中,我们着重测试了一下
FPaths::IsRelative()函数,这个函数可以在Paths.cpp中被找到:
bool FPaths::IsRelative(const FString& InPath) { const bool IsRooted = InPath.StartsWith(TEXT("\\"), ESearchCase::CaseSensitive) || InPath.StartsWith(TEXT("/"), ESearchCase::CaseSensitive) || InPath.StartsWith(TEXT("root:/")) | (InPath.Len() >= 2 && FChar::IsAlpha(InPath[0]) && InPath[1] == TEXT(':')); return !IsRooted; }
以上的代码看起来是无害的,只是一些很普通的字符串测试来判定InPath是相对路径(eg. “../engine/myfile.uasset”) 还是绝对路径 (eg. “c:\myfile.uasset”)。
为了研究这段代码,我启动Debugger,在函数中设置了断点并且查看其汇编代码。此时发生了非常恐怖的事,以下只是汇编代码中的一小段:
00007FF6DFF97B4E mov ecx,2 00007FF6DFF97B53 xor edi,edi 00007FF6DFF97B55 xor edx,edx 00007FF6DFF97B57 mov r8d,ecx 00007FF6DFF97B5A mov qword ptr [rsp+70h],rbx 00007FF6DFF97B5F mov dword ptr [rbp+28h],edi 00007FF6DFF97B62 mov qword ptr [rbp-10h],rdi 00007FF6DFF97B66 mov qword ptr [rbp-8],2 00007FF6DFF97B6E call DefaultCalculateSlack (07FF6DFEC7DD0h) 00007FF6DFF97B73 movsxd rcx,eax 00007FF6DFF97B76 mov rax,qword ptr [rbp-10h] 00007FF6DFF97B7A mov dword ptr [rbp-4],ecx 00007FF6DFF97B7D test rax,rax 00007FF6DFF97B80 jne FPaths::IsRelative+46h (07FF6DFF97B86h) 00007FF6DFF97B82 test ecx,ecx 00007FF6DFF97B84 je FPaths::IsRelative+5Bh (07FF6DFF97B9Bh) 00007FF6DFF97B86 mov rdx,rcx 00007FF6DFF97B89 xor r8d,r8d 00007FF6DFF97B8C mov rcx,rax 00007FF6DFF97B8F add rdx,rdx 00007FF6DFF97B92 call FMemory::Realloc (07FF6DFF04CB0h) 00007FF6DFF97B97 mov qword ptr [rbp-10h],rax 00007FF6DFF97B9B lea rdx,[ToUpperAdjustmentTable+2ABCh (07FF6E1EFFB9Ch)] 00007FF6DFF97BA2 mov r8d,4 00007FF6DFF97BA8 mov rcx,rax 00007FF6DFF97BAB call FGenericPlatformString::Memcpy (07FF6DFED3CA0h) 00007FF6DFF97BB0 lea rdx,[rbp-10h] 00007FF6DFF97BB4 xor r8d,r8d 00007FF6DFF97BB7 mov rcx,rsi 00007FF6DFF97BBA mov ebx,1 00007FF6DFF97BBF call FString::StartsWith (07FF6DFEDD440h) 00007FF6DFF97BC4 test al,al 00007FF6DFF97BC6 jne FPaths::IsRelative+1BBh (07FF6DFF97CFBh)
以上仅仅是整个函数的汇编代码的冰山一角,而整个函数也只有一行cpp代码,这简直是恐怖。
此外,这个代码不仅仅只是长而已,你应该可以看到有
FMemory::Realloc()函数的调用。在整个汇编代码中,
FMemory::Realloc()函数调用了3次。与之对应的,
FMemory::Free()函数也出现了多次。
还有,
StartsWith()函数也不是一个很便宜的函数(注意
StartsWith()只有一个关于
FString的Implementation)。
因此,我做了如下事:
减少了StartsWith()函数的调用,转而使用更为直接的字符比较。
移除了runtime的TEXT()区块,取而代之的是在外部直接创建。
将其中RootPrefix测试设为在编辑器中才有效。
因此,我最终的代码如下:
// Paths.cpp #if WITH_EDITOR FString FPaths::RootPrefix = TEXT("root:/"); #endif // WITH_EDITOR bool FPaths::IsRelative(const FString& InPath) { const uint32 PathLen = InPath.Len(); const bool IsRooted = PathLen && ((InPath[0] == '/') || (PathLen >= 2 && ( ((InPath[0] == '\\') && (InPath[1] == '\\')) || (InPath[1] == ':' && FChar::IsAlpha(InPath[0])) #if WITH_EDITOR || (InPath.StartsWith(RootPrefix)) #endif // WITH_EDITOR )) ); return !IsRooted; }
对于.h文件,添加内容如下:
// Paths.h private: #if WITH_EDITOR static FString RootPrefix; #endif // WITH_EDITOR
重新编译后,最终的汇编代码如下:
00007FF7147B695A mov edx,dword ptr [r8+8] 00007FF7147B695E mov rsi,rcx 00007FF7147B6961 test edx,edx 00007FF7147B6963 je FPaths::ConvertRelativePathToFull+67h (07FF7147B6997h) 00007FF7147B6965 dec edx 00007FF7147B6967 je FPaths::ConvertRelativePathToFull+67h (07FF7147B6997h) 00007FF7147B6969 mov rax,qword ptr [r8] 00007FF7147B696C movzx ecx,word ptr [rax] 00007FF7147B696F cmp cx,2Fh 00007FF7147B6973 je FPaths::ConvertRelativePathToFull+0A9h (07FF7147B69D9h) 00007FF7147B6975 cmp edx,2 00007FF7147B6978 jb FPaths::ConvertRelativePathToFull+67h (07FF7147B6997h) 00007FF7147B697A cmp cx,5Ch 00007FF7147B697E jne FPaths::ConvertRelativePathToFull+56h (07FF7147B6986h) 00007FF7147B6980 cmp word ptr [rax+2],cx 00007FF7147B6984 je FPaths::ConvertRelativePathToFull+0A9h (07FF7147B69D9h) 00007FF7147B6986 cmp word ptr [rax+2],3Ah 00007FF7147B698B jne FPaths::ConvertRelativePathToFull+67h (07FF7147B6997h) 00007FF7147B698D call qword ptr [__imp_iswalpha (07FF716557B48h)] 00007FF7147B6993 test eax,eax 00007FF7147B6995 jne FPaths::ConvertRelativePathToFull+0A9h (07FF7147B69D9h)
以上的汇编代码是在non-editor模式中的整个函数的汇编代码,我相信你我都能同意以上代码的性能要好得多。
优化这段代码还带来了一个很好的副作用:编译器会将这段逻辑进行inline操作,我们甚至不需要声明
FORCEINLINE或者
INLINE宏!
我的最终测试表明这段代码的性能快了将近20倍,而代码资源占用量只是原来的10%。
相关文章推荐
- python 学习笔记十四 jQuery案例详解(进阶篇)
- 10个常见的Node.js面试题
- css3的背景坐标
- hard link vs soft link
- Android 使用第三方SDK—友盟实现分享功能
- Python2学习笔记(2)
- vim的最最最基本操作
- MySQL如何选择float, double, decimal
- MongoDB JAVA API Filters
- C++小里程
- SpringMVC Controller写法
- Ajax-基础篇
- Android Navigation TabBar控件实现多彩标签栏
- java.lang.OutOfMemoryError: PermGen space及其解决方法
- iOS上线流程
- 正则表达式
- 使用JMeter对Tomcat进行压力测试与Tomcat性能调优
- windows2008r2的时间同步小结
- linux下关闭网络命令
- 桌面显卡天梯图和桌面cpu天梯图