问题标签 [basm]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
delphi - 英特尔 x86 汇编优化技术,用于将 8 位扩展为 0 或 1 的 8 个布尔字节
我正在学习汇编程序很长一段时间,我正在尝试重写一些简单的程序\函数来查看性能优势(如果有的话)。我的主要开发工具是 Delphi 2007,第一个示例将使用该语言,但它们也可以轻松翻译成其他语言。
问题陈述为:
我们给出了一个无符号字节值,其中八位中的每一位代表屏幕一行中的一个像素。每个单个像素可以是实心 (1) 或透明 (0)。换句话说,我们将 8 个像素打包在一个字节值中。我想以最年轻的像素(位)将落在数组的最低索引下的方式将这些像素解压缩到一个八字节数组中,依此类推。这是一个例子:
下面我介绍五种解决问题的方法。接下来我将展示他们的时间比较以及我是如何测量这些时间的。
我的问题包括两部分:
1.
我要求您详细回答有关方法DecodePixels4a
和DecodePixels4b
. 为什么方法4b
比4a
?
例如,如果由于我的代码未正确对齐而导致速度较慢,那么请告诉我给定方法中的哪些指令可以更好地对齐,以及如何做到这一点以不破坏该方法。
我想看看理论背后的真实例子。请记住,我正在学习汇编,我想从您的答案中获得知识,这使我将来能够编写更好的优化代码。
2.
你能写出比 更快的例程DecodePixels4a
吗?如果是这样,请展示它并描述您已采取的优化步骤。更快的例程是指在此处介绍的所有例程中在您的测试环境中运行时间最短的例程。
允许使用所有英特尔系列处理器以及与之兼容的处理器。
下面你会发现我写的例程:
这是我如何测试它们:
以下是我的机器(Win32 XP 上的 Intel® Pentium® E2180)的结果:
结果非常稳定 - 我所做的每次测试之间的时间变化只有几个百分点。这总是正确的:Time1 > Time3 > Time 2 > Time4b > Time4a
所以我认为 Time4a 和 Time4b 之间的差异取决于方法中的指令切换DecodePixels4b
。有时是 4%,有时是 10%,但4b
总是慢于4a
.
我正在考虑另一种使用 MMX 指令一次将八个字节写入内存的方法,但我无法找到将字节解压到 64 位寄存器中的快速方法。
感谢您的时间。
谢谢你们的宝贵意见。虽然我可以同时回答你们所有人,但不幸的是,与现代 CPU 相比,我只有一个“管道”并且当时只能执行一条指令“回复”;-) 所以,我会尝试总结一些事情在这里并在你的答案下写下额外的评论。
首先,我想说的是,在发布我的问题之前,我想出了 Wouter van Nifterick 提出的解决方案,它实际上比我的汇编代码慢得多。所以我决定不在这里发布该例程,但您可能会看到我在我的循环 Delphi 版本的例程中也采用了相同的方法。它在那里被评论是因为它给了我更糟糕的结果。
这对我来说是个谜。我用 Wouter 和 PhilS 的例程再次运行了我的代码,结果如下:
看看Time5的结果,是不是很奇怪?我想我有不同的 Delphi 版本,因为我生成的汇编代码与 Wouter 提供的不同。
第二次重大修改:
我知道为什么5
我的机器上的例行程序变慢了。我在编译器选项中检查了“范围检查”和“溢出检查”。我已经assembler
在例程9
中添加了指令,看看它是否有帮助。使用这个指令汇编程序似乎与 Delphi 内联变体一样好,甚至稍微好一点。
以下是最终结果:
第三次重大修改:
在@Pascal Cuoq和@j_random_hacker 看来,例程4a
之间执行时间的差异是由数据依赖性引起的。但是,基于我所做的进一步测试,我不得不不同意这种观点。4b
5
我还发明了4c
基于4a
. 这里是:
我会说它非常依赖数据。
这是测试和结果。我做了四次测试以确保没有意外。我还为 GJ (Time10a, Time10b) 提出的例程添加了新的时间。
如您所见4a
,4b
、4c
和的结果5
非常接近。这是为什么?因为我已经从 4a、4b 中删除了(4c 已经没有了)两条指令:push eax
和pop eax
. 因为我知道我不会在代码中的其他任何地方使用 eax 下的值,所以我不必保留它。现在我的代码只有一对推送/弹出,所以例程 5. 例程 5 保留 eax 的值,因为它首先在 ecx 下复制它,但它不保留 ecx。
所以我的结论是: 5 和 4a 和 4b 的时间执行差异(在第三次编辑之前)与数据相关性无关,而是由额外的一对推送/弹出指令引起的。
我对你的评论很感兴趣。
几天后,GJ 发明了比 PhiS 更快的程序(时间 10d)。干得好GJ!
c++ - 使用指针将代码转换为 Pascal 中的程序集 - Delphi
我在下面有这段代码,我想把它翻译成 ASM,也可以在 Delphi 中使用。
它工作正常,但是当我尝试它的汇编版本时:
它应该可以工作,因为在 C++ 中它以两种方式工作:
但它在德尔福中不起作用。
在 Assembly 版本中,它将数组乘以 4,因为它是数组中每个元素之间的偏移大小,所以两个版本是等价的。
所以,我想知道为什么它不适用于Delphi。在 Delphi 中,数组中整数值之间的偏移大小与 C++ 不同?
我已经尝试了许多偏移量,例如 1、2、4、6、8 等。以及许多类型的数组(指针数组;仅指针;整数数组等),并且我尝试了许多调用约定,并且 cdecl 是唯一适用于非 asm 版本的,但是对于 ASM,所有测试都不起作用。
谢谢。
delphi - Delphi标签和asm怪异?
我在 Delphi 7 中编写了一个 asm 函数,但它将我的代码转换为其他内容:
为什么会生成push ebx
and pop ebx
?为什么会这样mov eax, ebx
?
似乎它生成了部分堆栈帧,因为mov eax, ebx
.
这个简单的测试生成mov eax, edx
但不生成该堆栈帧:
似乎与label err
. 如果我删除它,我就不会得到这个mov eax, *
部分。
为什么会这样?
对Quality Central进行了错误报告。
delphi - 为什么Delphi编译器不内联汇编函数?
有时我会写很短的汇编函数,比如
这似乎是内联的最佳候选人:
但 Delphi 编译器不允许这样做。为什么?
更新:
感谢 ldsandon,有一份 5.5 年前的 QC 公开报告。该报告包含一些建议(如扩展 asm 指令)以简化编译器的 asm 内联。我更愿意在过程/函数级别引入“naked”指令,它告诉编译器它不必为过程创建堆栈框架,并且可以选择保留哪些寄存器(在 eax、edx 和 ecx 中)。
如果使用 BASM 代码进行高效内联过程的一般任务很困难(并且可能是不必要的),那么一个好主意是为最重要的情况启用内联(例如具有显式声明寄存器使用的裸函数)。
string - Delphi:将字符串放入编辑框中时访问冲突?
好吧,我正在研究 Delphi 中的一些内联程序集,并且程序集加密例程一切都很好,直到我尝试将 ShortString 解析到文本框中。
完整代码在这里:
如果我在“edit2.Text:= TCaption(key);”行上放置一个断点 我可以看到 ShortString “密钥”确实已正确加密,但背后也有很多奇怪的字符。
前 16 个字符是真正的加密。
加密 http://img831.imageshack.us/img831/365/29944312.png
谢谢!
string - Delphi 汇编函数返回一个长字符串
我正在尝试在 Delphi 中学习内联汇编编程,为此我发现这篇文章非常有帮助。
现在我想写一个汇编函数返回一个长字符串,特别是一个AnsiString
(为简单起见)。我已经写了
解释:
返回字符串的函数有一个不可见的var result: AnsiString
(在这种情况下)参数,因此,在函数的开头,eax
应该保存结果字符串的地址。然后我将edx
andecx
分别设置为 3 和 1252,然后调用System._LStrSetLength
. 实际上,我确实
其中 3 是字符串的新长度(字符 = 字节),1252 是标准windows-1252代码页。
然后,知道这eax
是字符串的第一个字符的地址,我简单地将字符串设置为“ABC”。但它不起作用 - 它给了我无意义的数据或 EAccessViolation。问题是什么?
更新
现在我们有两个看似有效的实现myfunc
,一个正在使用NewAnsiString
,一个正在使用LStrSetLength
。我不禁想知道它们是否都正确,因为它们不会破坏 Delphi 对字符串的内部处理(引用计数、自动释放等)。
delphi - 在 Delphi 中的 asm 过程结束时要恢复哪些 CPU 寄存器
用汇编代码编写Delphi程序或函数时,程序结束时必须保存哪些寄存器并恢复为原始值?
当从(内联)汇编代码调用另一个 Delphi 过程或函数时,我可以期望其他函数对寄存器做什么?哪些寄存器会恢复到原来的值,哪些不会?
(显然,相同的答案将适用于这两个问题)
我假设 Delphi 的默认调用约定。我知道它EAX
用于 32 位返回值。查看 SysUtils.pas 中的 asm 代码,似乎EBX
,ESI
和EDI
被推送和恢复,但其他不是。不过,我找不到任何关于此的文档。
delphi - delphi 汇编块中的异常行为
我在使用 Delphi 的内联汇编时遇到了一些奇怪的行为,如这个非常简短的程序所示:
这只是为了示例(mov
ing [asdf]
intoeax
没有多大作用,但它适用于示例)。如果您查看该程序的程序集,您会看到
已经变成
(由 OllyDbg 表示)显然崩溃了。但是,如果您这样做:
它更改为 mov eax, [ebp-4] 有效。为什么是这样?我通常使用 C++ 并且习惯于使用这样的实例变量,可能是我使用错误的实例变量。
编辑:是的,就是这样。更改mov eax, [asdf]
以mov eax, [Self.asdf]
解决问题。对于那个很抱歉。
delphi - 交换 Word 变量的字节(低/高)的过程
我有这个程序可以交换 Word 变量的字节(低/高)(它的作用与 System.Swap 函数相同)。该过程在编译器优化关闭时有效,但在它打开时无效。有人可以帮我吗?
delphi - 删除用纯汇编编写的函数的序言
我正在使用 Delphi 2010。是否可以告诉 Delphi 不要为函数生成序言?我正在写一些像这样的纯汇编函数:
我想告诉Delphi不要为这个函数生成序言和结语,就像C++的__declspec(naked)
特性一样。
所以没有人浪费他们的时间,我不需要帮助来让这些功能与序言一起工作;我已经可以做到了。这只是一个很大的不便,并且会使维护变得非常麻烦。我必须手动检查编译器生成的序言以查看它们的长度,如果发生变化,我的程序将崩溃。
我也知道我可以将函数编写为字节数组中的一系列字节,但这比必须找到 Delphi 序言的长度还要糟糕。