编辑
未使用的 cmp 指令将导致 NullPointerException。
What are these strange cmp [ecx], ecx instructions doing in my C# code?
原始帖子(下面有更多编辑)
我正在尝试了解 JIT 编译代码的方式。
在内存中我有一个 3 个字符的字段。在 C++ 中比较两个这样的字段我可以这样做:
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
在 C# 中,我试图弄清楚如何尽可能接近它。我们的记录由很多 1、2、3、4、5、6、7、8 字节字段组成。我已经在 c# 中测试了很多不同的方法来构建一个更大的结构来表示使用这些大小的较小结构的记录。我对汇编代码不满意。现在我正在玩这样的东西:
[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。我在 .net 代码中看到很多这些无用的 cmps。
问题二是我无法让编译器内联 Equals 函数。事实上,我很难看到任何内联的东西。
有什么技巧可以让它更好地编译吗?我正在使用 visual studio 2010 和 .net 版本 4。我正在努力安装 4.5 和 visual studio 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
正如预期的那样,这个看起来就像蹩脚的固定字符数组:
[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
编辑
为了回应 Hans,这里是示例代码。
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
如果您仔细观察程序集,它会像 Hans 指出的那样被内联,但大部分 asm 什么都不做。查看000000c5之前的所有无用的cmp语句。查看将相同的值移入和移出 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,但开销少了很多。
在这种情况下,fixed 似乎会生成很多(无用的?)asm。
最佳答案
是的,优化器在这段代码上挣扎,它对固定不太满意。您可以通过编写一个单独的方法来解决这个问题:
public bool Equals(ref KLF3 r) {
fixed (byte* p = Field, q = r.Field) {
return EqualsImpl(p, q);
}
}
private unsafe bool EqualsImpl(byte* p, byte* q) {
return (*(uint*)p & 0xffffff) == (*(uint*)q & 0xffffff);
}
明智的做法是:
0000006b mov rax,qword ptr [rsp+20h]
00000070 mov rcx,qword ptr [rsp+28h]
00000075 mov edx,dword ptr [rax]
00000077 and edx,0FFFFFFh
0000007d mov ecx,dword ptr [rcx]
0000007f and ecx,0FFFFFFh
00000085 xor eax,eax
00000087 cmp edx,ecx
00000089 sete al
0000008c movzx ecx,al
0000008f movzx ecx,cl
在调用方方法中内联生成。同样重要的是,您分析一个没有通过 ref 传递参数的版本,应该更快,并且您当前的版本会导致太多事故。我更改了您的位掩码,它们在小端机器上应该是 0xffffff。
关于C# 比较 3 字节字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25536255/
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我几天前在我的rubyonrails2.3.2上安装了Sphinx和Thinking-Sphinx,基本搜索效果很好。这意味着,没有任何条件。现在,我想用一些条件过滤搜索。我有公告模型,索引如下所示:define_indexdoindexestitle,:as=>:title,:sortable=>trueindexesdescription,:as=>:description,:sortable=>trueend也许我错了,但我注意到只有当我将:sortable=>true语法添加到这些属性时,我才能将它们用作搜索条件。否则它找不到任何东西。现在,我还在使用acts_as_tag
假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案