How to Use Visual Studio to view unmanaged code

Source: Internet
Author: User

The author of this article is an MVP from the United States. This is the first article in his series of articles "under the cover, the purpose of this article is to optimize the code performance from the underlying perspective and serve as the technical basis for reading other articles by the author. Although such an approach seems too much at the beginning, it is understandable to readers. net)

We started with the foundation of using Visual Studio for unmanaged code debugging, so that you can learn more about future examples and make this article the basis of my future articles, although I also use windbg, Visual Studio has become a powerful debugging tool and is easier to use for simple code optimization problems.

When we need to tune code with high performance requirements, it is usually not the best practice to view Il, because the JIT optimizer will silently optimize our code, using reflector or ildasm, you can quickly find that the Il code generated in the release and debug modes is almost identical. So what makes the code in the release mode run so quickly? This is the result of JIT optimization. by viewing the managed code (IL code), we cannot see these optimizations, so we will look for clues Through native code (Local Code.

It must be noted that I do not advocate that we often do this. I do not agree with early optimization. You must make your code work first, and you must clearly know which code isNot worth itOptimized. When your code is complete, you can find the places that need to be accelerated. When you find that 10% of the code is used for 70% of the time, go back and optimize the 10% code. at the same time, you should always establish the judgment basis on the actual speed measurement, instead of simply reading the code. Finally, the data structure selection is more important than the underlying optimization.

Of course, it is interesting to understand the secrets hidden in the. NET underlying layer. Let's start to set up Visual Studio and experiment with a simple example.

First, we need some test code.

Static void main (string [] ARGs ){

For (INT I = 0; I <10; I ++ ){

Console. writeline ("Hello world! ");

}

}

To enable unmanaged code debugging, You Need To Set Visual Studio. Open the properties of the project and enter the debug tab. Select the "enable unmanaged code debugging" check box on this page.

(Note that this option is only valid for the current configuration, so we should set this option for all the configurations we use .) insert a breakpoint at the beginning of the loop and run the program. You will hit a breakpoint as usual. At this time, your screen should look 2 (TRANSLATOR: Missing figure). If you do not have a stack window, you can use menu-> Windows-> call stack (or Ctrl + d c) after opening the call stack, we can right-click it and select go to disassembly to enter the following code.

 static void Main(string[] args) {00000000  push        ebp00000001  mov         ebp,esp00000003  push        edi00000004  push        esi00000005  push        ebx00000006  sub         esp,38h00000009  xor         eax,eax0000000b  mov         dword ptr [ebp-10h],eax0000000e  xor         eax,eax00000010  mov         dword ptr [ebp-1Ch],eax00000013  mov         dword ptr [ebp-3Ch],ecx00000016  cmp         dword ptr ds:[00912DC8h],00000001d  je          000000240000001f  call        792B228E00000024  xor         esi,esi00000026  xor         edi,edi00000028  nopfor (int i = 0; i < 10; i++) {00000029  xor         esi,esi0000002b  nop0000002c  jmp         0000003D0000002e  nopConsole.WriteLine("Hello World!");0000002f  mov         ecx,dword ptr ds:[022B303Ch]00000035  call        785D90740000003a  nop}0000003b  nopfor (int i = 0; i < 10; i++) {0000003c  inc         esi0000003d  cmp         esi,0Ah00000040  setl        al00000043  movzx       eax,al00000046  mov         edi,eax00000048  test        edi,edi0000004a  jne         0000002E}0000004c  nop0000004d  lea         esp,[ebp-0Ch]00000050  pop         ebx00000051  pop         esi00000052  pop         edi00000053  pop

We are looking at the native code (Local Code) produced by JIT for our code. We can see how a simple loop runs at the native code level, if you have never studied native code, the common code may seem quite strange. Let's take a look at what happened here.

00000029 xor esi, ESI

2017002b NOP

0000002c JMP 0000003d

The above code initializes our counter in ESI. ESI is an index register that can be used to index arrays. You can see that there is an old "trick" to clear the counter 0, the Code does not put the 0 value into the register. Instead, it allows the Register itself to be unique or (XOR) itself to achieve the goal of clearing 0. The next line of Nop indicates "no operation ", and their role is the same as their name, and nothing is done. The Code will jump to 3D immediately. sometimes jump like this makes our code not run from top to bottom (just like in many advanced languages such as C, VB, and C ), if you jump to another part of this cycle, you can continue to analyze our code.

2017003c Inc ESI

In the first command after 3c, add a counter in ESI (you can see its value through the Register window or the key combination Ctrl + d r). In the first cycle, the code will skip this line, because the preceding jump command directly points to the terminal 3D

2017003d cmp esi, 0ah

00000040 SETl al

00000043 movzx eax, Al

00000046 mov EDI, eax

00000048 test EDI, EDI

0000004a JNE 0000002e

From 10000003d to 4A, it indicates the actual comparison of the cyclic stop value and the jump. If we do not reach this value (I <10), the last line will jump to 2E: this is 4A in the original book. It is a written mistake.) continue this cycle, that is, the place where the cycle begins.
2017002f mov ECx, dword ptr ds: [022b303ch]

00000035 call 785d9074

The first row above will load the string from the memory to the ECX register (this is a general register). Generally, ECx is always used to pass the first parameter to the method. In the instance method, ECX will always contain this, followed by EDX containing the second parameter, followed by a series of pushes used to import other parameters into the stack

The next statement executes the actual call. We will discuss how to find the called method later, but now we can see from the source code provided by Visual Studio, which is undoubtedly the console. writeline: the code then executes the auto-increment of the index, and returns the code to continue executing the code inside the loop.

However, our trivial examples have produced significant waste. The following is an example.
00000009 XOR eax, eax

10000000b mov dword ptr [ebp-10h], eax

0000000e XOR eax, eax

00000010 mov dword ptr [ebp-1Ch], eax

We set eax to 0 twice in a row. At this time, because we are running in debug mode, the optimization is not performed in debug mode. In other words, this code is only executed by JIT, however, JIT is not allowed to perform any intelligent optimization.

Next let's take a look at the optimized code:

Here are some questions about viewing the optimization code.
1) the JIT optimization is disabled by the debugger by default. (it took me a long time to realize that I have been watching the code that has not been optimized)
2) It is necessary to handle the impact of the "just my code" option on the optimized code.

I first saw a solution to this problem in my post on Vance Morrison (thanks to Vance, I have been troubled by the problem for a long time, finally, the original assemble method without source code is used ).

To solve this problem, follow these steps:

1) Open tools> Options> debugging> General

2) Make sure that 'support JIT Optimization on module load' is not selected

3) Make sure that 'enablejust my Code' is not selected.

Vance also recommends that you go to advanced build to set the release DLL to PDB only, then we can run this code in the same way as before.

Another way to view our code with JIT is to use the release mode, start the executable without the debugger, and then attach Visual Studio to the process for debugging.

We can optimize the code by using any method. The optimized code is as follows:

          for (int i = 0; i < 10; i++) {00000000  push        esi00000001  xor         esi,esiConsole.WriteLine("Hello World!");00000003  cmp         dword ptr ds:[02271084h],00000000a  jne         000000160000000c  mov         ecx,100000011  call        786FC65400000016  mov         ecx,dword ptr ds:[02271084h]0000001c  mov         edx,dword ptr ds:[0227307Ch]00000022  mov         eax,dword ptr [ecx]00000024  call        dword ptr [eax+000000D8h]for (int i = 0; i < 10; i++) {0000002a  add         esi,10000002d  cmp         esi,0Ah00000030  jl          0000000300000032  pop         esi}}00000033  ret

Wow, this time the Code is much less than the first time, and JIT optimization does work well. That's why checking the actual decompilation code rather than Il is so important, because JIT is often optimized by recognizing the mode in Il, alert readers may note that more passwords are generated within our cycle. It seems terrible at first, but it actually means that the optimizer has helped us to inline the console. the writeline method actually saves a lot of code. In the following post, I will talk about inline, but it is an important optimization.

Now we have prepared how to enjoy the optimized and unoptimized code in the debugger. I think this is a good start, in the following posts, I will lay a solid foundation for a better understanding of the general optimization process of JIT. We can also use some tools to see how they can help us get better code.

Hope to see you there.

Original article address

Http://codebetter.com/blogs/gregyoung/archive/2006/06/09/146298.aspx

Resources of the original article:

Http://en.wikipedia.org/wiki/X86

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.