ИЗМЕНИТЬ
Команды cmp, которые не используются, вызывают исключение NullPointerException.
Что представляют собой эти странные инструкции cmp [ecx], ecx в моем коде С#?
ОРИГИНАЛЬНАЯ ПОЧТА (еще больше изменений ниже)
Я пытаюсь понять способ компиляции кода JIT.
В памяти у меня есть поле 3 char. В С++ для сравнения двух таких полей я могу это сделать:
return ((*(DWORD*)p) & 0xFFFFFF00) == ((*(DWORD*)q) & 0xFFFFFF00);
MSVC 2010 будет генерировать что-то вроде этого (из памяти):
1 mov edx,dword ptr [rsp+8]
2 and edx,0FFFFFF00h
3 mov ecx,dword ptr [rsp]
4 and ecx,0FFFFFF00h
5 cmp edx,ecx
В С# я пытаюсь понять, как можно приблизиться к этому, насколько я могу. У нас есть записи, состоящие из большого количества 1,2,3,4,5,6,7,8 байтовых полей. Я проверил множество разных способов в С#, чтобы построить большую структуру, представляющую запись, используя меньшие структуры этих размеров. Я не удовлетворен кодом сборки. Прямо сейчас я играю с чем-то вроде этого:
[StructLayout(LayoutKind.Sequential, Size = 3)]
public unsafe struct KLF3
{
public fixed byte Field[3];
public bool Equals(ref KLF3 r)
{
fixed (byte* p = Field, q = r.Field)
{
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
}
}
}
Но у меня две проблемы. Проблема одна заключается в том, что компилятор генерирует много бесполезного выглядящего кода:
fixed (byte* p = Field, q = r.Field)
1 sub rsp,18h
2 mov qword ptr [rsp+8],0
3 mov qword ptr [rsp],0
4 cmp byte ptr [rcx],0
5 mov qword ptr [rsp+8],rcx
6 cmp byte ptr [rdx],0
7 mov qword ptr [rsp],rdx
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
8 mov rax,qword ptr [rsp+8]
9 mov edx,dword ptr [rax]
10 and edx,0FFFFFF00h
11 mov rax,qword ptr [rsp]
12 mov ecx,dword ptr [rax]
13 and ecx,0FFFFFF00h
14 xor eax,eax
15 cmp edx,ecx
16 sete al
17 add rsp,18h
18 ret
Линии 2,3,4,5,6,7 кажутся бесполезными, поскольку мы могли бы просто использовать регистр rcx и rdx и не нуждаться в строке 8 и строке 11. Строки 4 и 6 кажутся бесполезными, поскольку ничего не использует результат cmp. Я вижу много этих бесполезных cmps в .net-коде.
Проблема 2: я не могу заставить компилятор встроить функцию Equals. На самом деле мне нелегко видеть, что что-то идет внутрь.
Любые подсказки, чтобы получить это, чтобы скомпилировать лучше? Я использую Visual Studio 2010 и .net версии 4. Я работаю, чтобы получить 4.5 установленную и визуальную студию 2013 года, но это может занять несколько дней.
ИЗМЕНИТЬ
Итак, я попробовал кучу альтернатив
Это создает улучшающий код, но все же длинный:
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
public unsafe struct KLF31
{
public UInt16 pos0_1;
public byte pos2;
public bool Equals(ref KLF31 r)
{
return pos0_1 == r.pos0_1 && pos2 == r.pos2;
}
}
return pos0_1 == r.pos0_1 && pos2 == r.pos2;
00000000 mov r8,rdx
00000003 mov rdx,rcx
00000006 movzx ecx,word ptr [rdx]
00000009 movzx eax,word ptr [r8]
0000000d cmp ecx,eax
0000000f jne 0000000000000025
00000011 movzx ecx,byte ptr [rdx+2]
00000015 movzx eax,byte ptr [r8+2]
0000001a xor edx,edx
0000001c cmp ecx,eax
0000001e sete dl
00000021 mov al,dl
00000023 jmp 0000000000000027
00000025 xor eax,eax
00000027 rep ret
Это довольно скудно, за исключением того, что размер структуры составляет 4 байта вместо 3.
[StructLayout(LayoutKind.Explicit, Size = 3, Pack = 1)]
public unsafe struct KLF33
{
[FieldOffset(0)] public UInt32 pos0_3;
public bool Equals(ref KLF33 r)
{
return (pos0_3 & 0xFFFFFF00) == (r.pos0_3 & 0xFFFFFF00);
}
}
return (pos0_3 & 0xFFFFFF00) == (r.pos0_3 & 0xFFFFFF00);
00000000 mov rax,rdx
00000003 mov edx,dword ptr [rcx]
00000005 and edx,0FFFFFF00h
0000000b mov ecx,dword ptr [rax]
0000000d and ecx,0FFFFFF00h
00000013 xor eax,eax
00000015 cmp edx,ecx
00000017 sete al
0000001a ret
Этот выглядит точно так же, как и жуткий фиксированный массив char, как и ожидалось:
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
public unsafe struct KLF34
{
public byte pos0, pos1, pos2;
public bool Equals(ref KLF34 r)
{
fixed (byte* p = &pos0, q = &r.pos0)
{
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
}
}
}
fixed (byte* p = &pos0, q = &r.pos0)
00000000 sub rsp,18h
00000004 mov qword ptr [rsp+8],0
0000000d mov qword ptr [rsp],0
00000015 cmp byte ptr [rcx],0
00000018 mov qword ptr [rsp+8],rcx
0000001d cmp byte ptr [rdx],0
00000020 mov qword ptr [rsp],rdx
{
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
00000024 mov rax,qword ptr [rsp+8]
00000029 mov edx,dword ptr [rax]
0000002b and edx,0FFFFFF00h
00000031 mov rax,qword ptr [rsp]
00000035 mov ecx,dword ptr [rax]
00000037 and ecx,0FFFFFF00h
0000003d xor eax,eax
0000003f cmp edx,ecx
00000041 sete al
00000044 add rsp,18h
00000048 ret
ИЗМЕНИТЬ
В ответ на Ганса, вот пример кода.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ConsoleApplication2
{
[StructLayout(LayoutKind.Sequential, Size = 3)]
public unsafe struct KLF30
{
public fixed byte Field[3];
public bool Equals(ref KLF30 r)
{
fixed (byte* p = Field, q = r.Field)
{
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
}
}
public bool Equals1(ref KLF30 r)
{
fixed (byte* p = Field, q = r.Field)
{
return p[0] == q[0] && p[1] == q[1] && p[2] == q[2];
}
}
public bool Equals2(ref KLF30 r)
{
fixed (byte* p = Field, q = r.Field)
{
return p[0] == q[0] && p[1] == q[1] && p[2] == q[2];
}
}
}
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
public unsafe struct KLF31
{
public UInt16 pos0_1;
public byte pos2;
public bool Equals(ref KLF31 r)
{
return pos0_1 == r.pos0_1 && pos2 == r.pos2;
}
}
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
public unsafe struct KLF32
{
public fixed byte Field[3];
public bool Equals(ref KLF32 r)
{
fixed (byte* p = Field, q = r.Field)
{
return EqualsImpl(p, q);
}
}
private bool EqualsImpl(byte* p, byte* q)
{
return (*(uint*)p & 0xffffff) == (*(uint*)q & 0xffffff);
}
}
[StructLayout(LayoutKind.Explicit, Size = 3, Pack = 1)]
public unsafe struct KLF33
{
[FieldOffset(0)]
public UInt32 pos0_3;
public bool Equals(ref KLF33 r)
{
return (pos0_3 & 0xFFFFFF00) == (r.pos0_3 & 0xFFFFFF00);
}
}
[StructLayout(LayoutKind.Sequential, Size = 3, Pack = 1)]
public unsafe struct KLF34
{
public byte pos0, pos1, pos2;
public bool Equals(ref KLF34 r)
{
fixed (byte* p = &pos0, q = &r.pos0)
{
return ((*(UInt32*)p) & 0xFFFFFF00) == ((*(UInt32*)q) & 0xFFFFFF00);
}
}
}
[StructLayout(LayoutKind.Explicit)]
public struct Klf
{
[FieldOffset(0)] public char pos0;
[FieldOffset(1)] public char pos1;
[FieldOffset(2)] public char pos2;
[FieldOffset(3)] public char pos3;
[FieldOffset(4)] public char pos4;
[FieldOffset(5)] public char pos5;
[FieldOffset(6)] public char pos6;
[FieldOffset(7)] public char pos7;
[FieldOffset(0)] public UInt16 pos0_1;
[FieldOffset(2)] public UInt16 pos2_3;
[FieldOffset(4)] public UInt16 pos4_5;
[FieldOffset(6)] public UInt16 pos6_7;
[FieldOffset(0)] public UInt32 pos0_3;
[FieldOffset(4)] public UInt32 pos4_7;
[FieldOffset(0)] public UInt64 pos0_7;
}
[StructLayout(LayoutKind.Sequential, Size = 3)]
public unsafe struct KLF35
{
public Klf Field;
public bool Equals(ref KLF35 r)
{
return (Field.pos0_3 & 0xFFFFFF00) == (r.Field.pos0_3 & 0xFFFFFF00);
}
}
public unsafe class KlrAAFI
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct _AAFI
{
public KLF30 AirlineCxrCode0;
public KLF31 AirlineCxrCode1;
public KLF32 AirlineCxrCode2;
public KLF33 AirlineCxrCode3;
public KLF34 AirlineCxrCode4;
public KLF35 AirlineCxrCode5;
}
public KlrAAFI(byte* pData)
{
Data = (_AAFI*)pData;
}
public _AAFI* Data;
public int Size = sizeof(_AAFI);
}
class Program
{
static unsafe void Main(string[] args)
{
byte* foo = stackalloc byte[256];
var a1 = new KlrAAFI(foo);
var a2 = new KlrAAFI(foo);
var p1 = a1.Data;
var p2 = a2.Data;
//bool f01= p1->AirlineCxrCode0.Equals (ref p2->AirlineCxrCode0);
//bool f02= p1->AirlineCxrCode0.Equals1(ref p2->AirlineCxrCode0);
//bool f03= p1->AirlineCxrCode0.Equals2(ref p2->AirlineCxrCode0);
//bool f1 = p1->AirlineCxrCode1.Equals (ref p2->AirlineCxrCode1);
bool f2 = p1->AirlineCxrCode2.Equals (ref p2->AirlineCxrCode2);
//bool f3 = p1->AirlineCxrCode3.Equals (ref p2->AirlineCxrCode3);
//bool f4 = p1->AirlineCxrCode4.Equals (ref p2->AirlineCxrCode4);
//bool f5 = p1->AirlineCxrCode5.Equals (ref p2->AirlineCxrCode5);
//int q = f01 | f02 | f03 | f1 | f2 | f3 | f4 ? 0 : 1;
int q = f2 ? 0 : 1;
Console.WriteLine("{0} {1} {2} {3} {4} {5}",
sizeof(KLF30), sizeof(KLF31), sizeof(KLF32), sizeof(KLF33), sizeof(KLF34), sizeof(KLF35));
Console.WriteLine("{0}", q);
}
}
}
Когда я скомпилирую, что при всех комментариях, кроме f2, я получаю следующее:
var p1 = a1.Data;
0000007b mov rax,qword ptr [rdi+8]
var p2 = a2.Data;
0000007f mov rcx,qword ptr [rbx+8]
bool f2 = p1->AirlineCxrCode2.Equals (ref p2->AirlineCxrCode2);
00000083 cmp byte ptr [rax],0
00000086 add rax,10h
0000008c cmp byte ptr [rcx],0
0000008f add rcx,10h
00000093 xor edx,edx
00000095 mov qword ptr [rbp],rdx
00000099 mov qword ptr [rbp+8],rdx
0000009d cmp byte ptr [rax],0
000000a0 mov qword ptr [rbp],rax
000000a4 cmp byte ptr [rcx],0
000000a7 mov qword ptr [rbp+8],rcx
000000ab mov rax,qword ptr [rbp]
000000af mov rcx,qword ptr [rbp+8]
000000b3 mov edx,dword ptr [rax]
000000b5 and edx,0FFFFFFh
000000bb mov ecx,dword ptr [rcx]
000000bd and ecx,0FFFFFFh
000000c3 xor eax,eax
000000c5 cmp edx,ecx
000000c7 sete al
000000ca movzx ecx,al
000000cd movzx eax,cl
Если вы внимательно посмотрите на сборку, она будет указана, как указал Ханс, но большая часть этого asm ничего не делает. Посмотрите на все бесполезные выражения cmp до 000000c5. Посмотрите, сколько раз он перемещает одно и то же значение в и из rbp и rbp + 8. Может быть, я не понимаю полезности этого.
Если вы закомментируете все, кроме f1, я получаю следующее:
var p1 = a1.Data;
00000071 mov rdx,qword ptr [rdi+8]
var p2 = a2.Data;
00000075 mov r8,qword ptr [rbx+8]
bool f1 = p1->AirlineCxrCode1.Equals (ref p2->AirlineCxrCode1);
00000079 cmp byte ptr [rdx],0
0000007c cmp byte ptr [r8],0
00000080 movzx ecx,word ptr [rdx+8]
00000084 movzx eax,word ptr [r8+8]
00000089 cmp ecx,eax
0000008b jne 00000000000000A2
0000008d movzx ecx,byte ptr [rdx+0Ah]
00000091 movzx eax,byte ptr [r8+0Ah]
00000096 xor edx,edx
00000098 cmp ecx,eax
0000009a sete dl
0000009d movzx eax,dl
000000a0 jmp 00000000000000A4
000000a2 xor eax,eax
который все еще имеет бесполезный cmp instr 79, 7c, но намного меньше накладных расходов.
Кажется, что фиксированное генерирует много (бесполезных?) asm в этом случае.