在Z80机器代码中,一种将缓冲区初始化为固定值的廉价技术,比如所有空白.所以一大堆代码可能看起来像这样.
LD HL, DESTINATION ; point to the source
LD DE, DESTINATION + 1 ; point to the destination
LD BC, DESTINATION_SIZE - 1 ; copying this many bytes
LD (HL), 0X20 ; put a seed space in the first position
LDIR ; move 1 to 2, 2 to 3...
Run Code Online (Sandbox Code Playgroud)
结果是DESTINATION的内存块完全填满了空白.我已经尝试了memmove和memcpy,并且无法复制这种行为.我希望memmove能够正确地完成它.
memmove和memcpy为什么会这样?
有没有合理的方法来进行这种数组初始化?
我已经知道char array [size] = {0}用于数组初始化
我已经知道memset将为单个字符完成工作.
还有什么其他方法可以解决这个问题?
Ned*_*der 12
memmove
并且memcpy
不要那样工作,因为它不是用于移动或复制内存的有用语义.在Z80中能够填充内存很方便,但为什么你会期望一个名为"memmove"的函数用单个字节填充内存?这是为了移动内存块.无论块如何重叠,它都是为了得到正确的答案(源字节移动到目的地)而实现的.它有助于为移动内存块获得正确的答案.
如果你想填充内存,请使用memset,它可以满足您的需求.
dev*_*fix 11
使用堆栈可以更快地消隐内存区域.尽管LDI和LDIR的使用非常普遍,但David Webb(以各种方式推动ZX Spectrum,如包括边界在内的全屏幕倒计时)提出了这种技术,速度提高了4倍:
上面的解释取自David Webbs游戏Starion的评论.
Z80例程可能看起来像这样:
DI ; disable interrupts which would write to the stack.
LD HL, 0
ADD HL, SP ; save stack pointer
EX DE, HL ; in DE register
LD HL, 0
LD C, 0x18 ; Screen size in pages
LD SP, 0x4000 ; End of screen
PAGE_LOOP:
LD B, 128 ; inner loop iterates 128 times
LOOP:
PUSH HL ; effectively *--SP = 0; *--SP = 0;
DJNZ LOOP ; loop for 256 bytes
DEC C
JP NZ,PAGE_LOOP
EX DE, HL
LD SP, HL ; restore stack pointer
EI ; re-enable interrupts
Run Code Online (Sandbox Code Playgroud)
但是,这个例程的速度要快两倍.LDIR每21个周期复制一个字节.内循环每24个循环复制两个字节 - 11个循环PUSH HL
,13 个循环DJNZ LOOP
.要获得近4倍的速度,只需展开内循环:
LOOP:
PUSH HL
PUSH HL
...
PUSH HL ; repeat 128 times
DEC C
JP NZ,LOOP
Run Code Online (Sandbox Code Playgroud)
这是每两个字节非常接近11个周期,比每个LDIR字节的21个周期快约3.8倍.
毫无疑问,这项技术已被多次重新发明.例如,它出现在1980年的子逻辑的飞行模拟器1中,用于TRS-80.
我相信这符合C和C++的设计理念.正如Bjarne的Stroustrup的一次说的C++设计的主要指导原则之一就是"你不要用什么,你不支付".而丹尼斯里奇可能没有用完全相同的词语说出来,我相信这也是一个指导原则,告知他的C设计(以及后来人的C设计).现在您可能会认为如果您分配内存,它应该自动初始化为NULL,我倾向于同意您的看法.但这需要机器周期,如果你在每个周期都很关键的情况下进行编码,这可能不是一个可接受的权衡.基本上C和C++试图避开你的方式 - 因此如果你想要初始化的东西,你必须自己做.
memmove和memcpy为什么会这样?
可能是因为没有针对Z80硬件的特定的现代C++编译器?写一个.;-)
这些语言没有指定给定硬件如何实现任何东西.这完全取决于编译器和库的程序员.当然,为每个可以想象的硬件配置编写一个自己的,高度指定的版本是很多工作.那就是原因.
有没有合理的方法来进行这种数组初始化?有没有合理的方法来进行这种数组初始化?
好吧,如果一切都失败了,你总是可以使用内联汇编.除此之外,我期望std::fill
在良好的STL实施中表现最佳.是的,我完全清楚我的期望太高,而且std::memset
在实践中往往表现更好.
这可以在 x86 汇编中同样轻松地完成。事实上,它归结为与您的示例几乎相同的代码。
mov esi, source ; set esi to be the source
lea edi, [esi + 1] ; set edi to be the source + 1
mov byte [esi], 0 ; initialize the first byte with the "seed"
mov ecx, 100h ; set ecx to the size of the buffer
rep movsb ; do the fill
Run Code Online (Sandbox Code Playgroud)
然而,如果可以的话,一次设置多个字节会更有效。
最后,memcpy
/memmove
不是您要寻找的,它们用于将内存块从一个区域复制到另一个区域(memmove 允许源和目标成为同一缓冲区的一部分)。memset
用您选择的字节填充一个块。
您展示的Z80序列是最快的方式 - 1978年.那是30年前.从那时起,处理器已经取得了很大进展,而今天这是最慢的方式.
Memmove设计为在源和目标范围重叠时工作,因此您可以将一块内存向上移动一个字节.这是C和C++标准指定行为的一部分.Memcpy未指定; 它可能与memmove完全相同,或者可能有所不同,具体取决于编译器决定如何实现它.编译器可以自由选择比memmove更有效的方法.
归档时间: |
|
查看次数: |
2748 次 |
最近记录: |