作者 主题:块副本说明 (Read 4017 times)

0会员和1位客人正在查看此主题。

离线 siliconwizard.

  • 超级贡献者
  • ***
  • 帖子:6182
  • 国家: FR.
块复制指令
« on: 1月13日,2021年,01:20:38 AM»
刚刚在CPU中的块复制指令的潜在福利(或缺乏)潜在福利(或缺乏)。

内存块副本是典型程序中的*非常*的常见操作。有效的块拷贝似乎至关重要。 (我们还可以指代处理strcpy()和memcpy()的其他线程。)
一些CPU实现块副本指令(例如英特尔X86处理器),其他人没有。

随机思考:
* 作为一个"complex"有点指示,他们可能比RISC的事情更为一个CISC。
*在RISC处理器上,它可能更有意义"fuse"比实施更简单的指示"complex"指示。融合指令,超标执行,虽然不是微不足道的。
*块复制指令通常是不可中断的,可能需要多个周期"blocking"CPU可能会导致中断延迟和线程调度的问题。
*它们可以中断,但它会使CPU设计更复杂。
*可以使用DMA控制器,但它适合不同的IMO,将有额外的设置开销......

用于块副本的RISC-V(RV32I)汇编代码的示例'words'看起来(如果有),请不要犹豫提供更优化的代码):
[假设:A0包含n,a1源起始地址和a2目的地开始地址]
代码: [选择]
BlockCopy:
    beqz a0, BlockCopyEnd
    slli a0, a0, 2
    add t1, a1, a0
   
BlockCopyLoop:
    addi a1, a1, 4
    lw t2, -4(a1)
    addi a2, a2, 4
    sw t2, -4(a2)
    bgtu t1, a1, BlockCopyLoop

BlockCopyEnd:

那'S 8指令,以及副本循环中的5。

我们可以想象一系列块副本指令,只有不同的宽度(每个字节,每一个单词,每个单词副本),以及'N'在注册或立即。
例如,块复制指令处理单词和'N'在寄存器中看起来像:
代码: [选择]
memcpy a2, a1, a0

缺点"'N' in a register"版本:它们需要3个源寄存器。直接版本只需要2个。
这些指示不'实际上似乎非常复杂。

你的想法?
 

离线 遗体

  • 超级贡献者
  • ***
  • 帖子:6970
  • 国家: GB.
Re:块副本说明
« 回复#1开:2021.年1月13日,02:07:22»
我怀疑与合适的搜索引擎一起花费了几分钟将使您更加论文和博士学位。'LL注意在评估块复制指令与宏编码块副本上阅读。

没有经验数据的任何意见我'D将主要是数量'hand waving'我们在这里需要的最后一件事就是那种东西。然而,我会说我的 直觉 是,在一个精心设计的RISC CPU中,赢得了一块块拷贝教学'值得拥有的复杂性。
任何人都有一个注射器,我可以用来挤压魔法烟雾吗?
 

离线 T3SL4CO1L.

  • 超级贡献者
  • ***
  • 帖子:16712
  • 国家: 我们
  • 专家,模拟电子,PCB布局,EMC
    • 七晶体管实验室
Re:块副本说明
« 回复#2开: 1月13日,2021年,04:24:27»
我的意思是'据所有8086都通过微码来做。  In essence, you'VE将一些用户空间软件转移到"BIOS",除了没有那个's在CPU本身内更深层次。  It's喜欢拥有一个非常简短的库,内置于硬件中。

risc.往往有点详细说明在指令长度(优先于全总线宽度的固定长度编码,并且显然存在许多例外,在ISA级别(例如ARM拇指)或ISA内(具有全总线的指令 - 宽度参数,例如跳跃立即)),因此软件版本倾向于占用相当数量的空间。 但是那个空间都适合指令缓存,所以它'没有执行惩罚。 此类系统通常具有足够超过足够的程序存储器或缓存来处理它(只要您不行'介绍该记忆的成本,这是一个更大的交易在80年代和90年代说。

正如我所理解的那样,RISC的点是尽可能避免任何事情't在一个循环中完成。 因此,您非常常见地获取负载存储的架构,甚至非常华丽的架构,可以在该循环中做得很多(复杂的Alu Ops上的多个参数 - Gobs和硬件的硬件,深管道)。 但从来没有谦虚的小 添加[mem],imm 这需要读取修改写入周期。

并且因为RISC是速度聚焦,并且微码受相同的限制,它'不像一个人可以更快(鉴于相同的优化深度,显然)。 您唯一的成本似乎是节目空间。 和跳跃返回开销,如果使用它作为库函数而不是内含线(这只是一个简单的时空折衷)。

随后的自然异常,任何地方高速缓存或多个公共汽车都可以填写这些危险。 原则上可以设计内存总线以用于某些基本修改操作。  I don'T当然知道实现细节,但某些AVR指令或外设寄存器以这种方式表明:某些IO位置可以在无循环惩罚中进行修改;或者一些外围设备存在执行相应操作的一组寄存器,并且可能是外围设备正在内部解决这些寄存器。 或者,例如,多个总线是常态的,所以即使在Mac [Dest],[SRC1],[SRC2](所有三个操作数是内存中,所有三个操作数是内存)的大规模吞吐量也可能具有甚至非常密集的操作。 无论如何,这些都是具体的架构,我对使用这些方法的架构(缓存和类型,多个总线)的架构很少的实用知识。

换句话说,我认为CISC倾向于实施块指令的原因是因为,由于它们的复杂指令解码和每条指令的多个内政循环,您处于执行肯定的非常常见的显着缺点 - 即块或串 - 对他们的操作。 因此提供快捷方式是有利的。

(有特殊的方面的案例被理解,如,不是'在几个奔腾上,写出循环可以比这更好 代表Stos.? 我觉得早期过渡到微型欧普架构的副作用?)

最后,差异在更简单的架构中消失;一世'前面的68k叫riscy,但也许是'毕竟没有良好的描述。 其指示往往比8086更快's?  Though that'S也主要原因是8086's stupid EA modes. 缺乏管道的建筑往往会表现得相同。 即使是AVR(坚定的RISC?)也对那些经典筹码的速度相比,尽管只有8位,还有更新的过程(虽然几乎不是尖<10nm or anything).

蒂姆
« 最后编辑:2021年1月13日,04:34:36 AM通过T3SL4Co1L »
七晶体管Labs,LLC
电子设计,从概念到原型。
将项目带到生活中?  Send me a message!
 

在线的 Ditbho.

  • 频繁的贡献者
  • **
  • 帖子:392
  • 国家: GB.
Re:块副本说明
« 回复#3开: 1月13日,2021年,06:51:43»
[..]因为RISC是集中的速度,而Microcode受到相同的限制,它'不像一个人可以更快(鉴于相同的优化深度,显然)。 您唯一的成本似乎是节目空间。 和跳跃返回开销,如果使用它作为库函数而不是内含线(这只是一个简单的时空折衷)。

总结了我的想法  :D
 

离线 布鲁尔特

  • 超级贡献者
  • ***
  • 帖子:2011
  • 国家: NZ.
  • 以前是Sifive,三星r&D
Re:块副本说明
« 回复#4开: 1月13日,2021年,08:21:03»
用于块副本的RISC-V(RV32I)汇编代码的示例'words'看起来(如果有),请不要犹豫提供更优化的代码):
[假设:A0包含n,a1源起始地址和a2目的地开始地址]
代码: [选择]
BlockCopy:
    beqz a0, BlockCopyEnd
    slli a0, a0, 2
    add t1, a1, a0
   
BlockCopyLoop:
    addi a1, a1, 4
    lw t2, -4(a1)
    addi a2, a2, 4
    sw t2, -4(a2)
    bgtu t1, a1, BlockCopyLoop

BlockCopyEnd:

那'S 8指令,以及副本循环中的5。

在典型的单一问题RISC管道上复制每个单词的5个时钟周期(假设分支被正确预测)。

您可以轻松地将其视为每4个字节的2个指令(周期)通过展开几次 - 四次将为您提供11个指令(2.75周期/字),16次为您复制16个单词的35条指令(2.19周期/字)

那's assuming you'在SRAM或L1缓存中复制。如果你'在DRAM中复制然后你'重新开始迅速击中内存带宽限制。

您需要一些开销来处理左字,未对准等。

newlib展开四次。 newlib nano是一个字节循环。

代码: [选择]
/* Nonzero if either X or Y is not aligned on a "long" boundary.  */
#define UNALIGNED(X, Y) \
  (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))

/* How many bytes are copied each iteration of the 4X unrolled loop.  */
#define BIGBLOCKSIZE    (sizeof (long) << 2)

/* How many bytes are copied each iteration of the word copy loop.  */
#define LITTLEBLOCKSIZE (sizeof (long))

/* Threshhold for punting to the byte copier.  */
#define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE)

void *
__inhibit_loop_to_libcall
memcpy (void *__restrict dst0,
const void *__restrict src0,
size_t len0)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
  char *dst = (char *) dst0;
  char *src = (char *) src0;

  void *save = dst0;

  while (len0--)
    {
      *dst++ = *src++;
    }

  return save;
#else
  char *dst = dst0;
  const char *src = src0;
  long *aligned_dst;
  const long *aligned_src;

  /* If the size is small, or either SRC or DST is unaligned,
     then punt into the byte copy loop.  This should be rare.  */
  if (!TOO_SMALL(len0) && !UNALIGNED (src, dst))
    {
      aligned_dst = (long*)dst;
      aligned_src = (long*)src;

      /* Copy 4X long words at a time if possible.  */
      while (len0 >= BIGBLOCKSIZE)
        {
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          *aligned_dst++ = *aligned_src++;
          len0 -= BIGBLOCKSIZE;
        }

      /* Copy one long word at a time if possible.  */
      while (len0 >= LITTLEBLOCKSIZE)
        {
          *aligned_dst++ = *aligned_src++;
          len0 -= LITTLEBLOCKSIZE;
        }

       /* Pick up any residual with a byte copier.  */
      dst = (char*)aligned_dst;
      src = (char*)aligned_src;
    }

  while (len0--)
    *dst++ = *src++;

  return dst0;
#endif /* not PREFER_SIZE_OVER_SPEED */
}

我实际上对这段代码有批评。一世'd将TOO_SMALL()测试删除为毫无意义("while (len0 >= BIGBLOCKSIZE)"会做同样的工作),但也有害,因为即使围绕一个单词拷贝循环一次(别别三次)会比一次复制一个字节更好。

通过现代编译器的Maqic,所有这些* foo ++通过从更新每循环更新一次的指针的偏移来转换为最佳代码。
 

离线 fanofeediy

  • 支持者
  • ****
  • 帖子:399
  • 国家: J.P
    • YouTube Channel.
Re:块副本说明
« 回复#5: 1月13日,2021年,上午10:14:39»
这可能是在RISC-V上没有特殊块拷贝指令的原因,但我可以想象的是最简单的指令是当RISC-V实现的管道进入更深时,写入注册商失速后会更加读取,并且变得更慢。

代码: [选择]
    lw t2, -4(a1)
    addi a2, a2, 4
    sw t2, -4(a2) <- will stall reading from register t2 for Read after write (RAW), if the implementation have more than 6 or 7 stages.

即使是X86的现代编译器也不会生成字符串复制指令,即使是指令也存在。
risc.-V是开放规范,因此它可以在未来的某个人中具有高度流水线的实施,并且可能risc-v希望从实施中保持自然。

我在RISC-V学习会议上写了类似的话题。
这是幻灯片的链接,第7页。
//1drv.ms/b/s!AnLZDi2WpU1Ogx8bmfm5HvzBGOm-?e=kZ0f8S

我在这里找到了舞台危险的漂亮图表。
//stackoverflow.com/questions/37396533/pipeline-stall-after-a-load-instruction-but-not-after-an-add-instruction

这是事件的链接。
//risc-v.connpass.com/event/194872/
 

离线 Westfw.

  • 超级贡献者
  • ***
  • 帖子:3387
  • 国家: 我们
Re:块副本说明
« 回复#6开启: 1月13日,2021年,10:52:57»
唔。 是否有任何实现的块拷贝指令副本将数据拆除以匹配内存和/或缓存实际实现的方式?

 

在线的 Ditbho.

  • 频繁的贡献者
  • **
  • 帖子:392
  • 国家: GB.
Re:块副本说明
« 回复#7: 1月13日,2021年,11:05:06»
唔。 是否有任何实现的块拷贝指令副本将数据拆除以匹配内存和/或缓存实际实现的方式?

你在想什么问题?


 

离线 安德姆

  • 超级贡献者
  • ***
  • 帖子:1198
  • 国家: FI.
Re:块副本说明
« 回复#8开: 1月13日,2021年,01:06:10 PM»
缺点"'N' in a register"版本:它们需要3个源寄存器。
如果将第三个寄存器用作对每个迭代更新的计数器,那将使指令中断无关不起作用。

编辑:那种类似于68010循环模式。
« 上次编辑:1月13日,2021年,01:10:57 PM Andersm »
 

在线的 Ditbho.

  • 频繁的贡献者
  • **
  • 帖子:392
  • 国家: GB.
Re:块副本说明
« 回复#9: 1月13日,2021年,01:31:10 PM»
那 kind of resembles the 68010 loop mode.

M68k这样做:

代码: [选择]
// Compare two blocks of memory for equality

     lea #Source,A0              // A0 points to source block
     lea #Destination,A1         // A1 points to destination block
     move.w #Count-1,D0          // Compare Count words
rpt:
     cmpm.w (A0)+,(A1)+          // Compare pair of words
     dbne D0,rpt                 // Repeat until all done

DBNE:减少D0和 分支直到D0不等于零

在像PowerPC这样的RISC设计中,D0不是公共寄存器文件,而是一个专用寄存器。
所以管道完全了解什么'S没有需要查看分支预测的情况发生。

CMPM. 比较内存中的两个单词以进行平等,M68K ISA也具有类似的复杂性 移动 opcode 
代码: [选择]
     move.w (A0)+,(A1)+          // copy mem(p_src) into mem(p_trg)  and auto increment pointers

这对CISS有利。它's terrible and doesn'在RISC中实施时,添加任何收益。
除了PowerPC案例外,该案例除外,该案例然后增加额外的指令和额外的复杂性来管理硬件寄存器循环计数器。

如果MIP没有,有一个原因'有什么类似的东西  :D
« 最后编辑:1月13日,2021,01:37:44由Ditbho »
 

离线 TGZZZ.

  • 超级贡献者
  • ***
  • 帖子:13254
  • 国家: GB.
    • 玩得开心更多,更少
Re:块副本说明
« 回复#10: 1月13日,2021年,02:04:22 PM»
刚刚在CPU中的块复制指令的潜在福利(或缺乏)潜在福利(或缺乏)。

内存块副本是典型程序中的*非常*的常见操作。有效的块拷贝似乎至关重要。

指令执行时间不再是程序执行时间的确定性。主要内存延迟和带宽和缓存长度协议是杀手。

几十年前,当人们优化LAN协议堆栈到应用程序时,即加快内存复制的最佳方式非常详细。成功的技术是,不骄傲, 大学教师't 复制缓冲区 - 刚刚通过协议堆栈的相关指针。

那不是'在网络堆栈之类的特殊情况下,但在复杂的大应用程序中正确共享缓冲区/引用的特殊情况非常困难是固有的复杂性。最好的起点是一种为您提供照顾它的语言,即不是C / C ++。

此类应用程序的动态是C / C ++社区心爱的微币可能具有很大的复杂应用程序的一个原因。
« 上次编辑:1月13日,2021,02:06:55 PM由TGZZZ »
有谎言,该死的谎言,统计数据和ADC / DAC规范。
滑翔机飞行员's aphorism: "跨度没有替代品". Retort: "有一个替代品:技能+想象力。但是你可以 span".
正玩得开心 做更多,少
 

在线的 Ditbho.

  • 频繁的贡献者
  • **
  • 帖子:392
  • 国家: GB.
Re:块副本说明
« 回复#11开: 1月13日,2021年,02:11:22 PM»
最好的起点是一种为您提供照顾它的语言,即不是C / C ++

可能在这里生锈有点 :D
 

离线 Techman-001.

  • 频繁的贡献者
  • **
  • 帖子:716
  • 国家: Au.
  • 电子技术员过去50年
    • MECRISP Stellaris非官方Userdoc
Re:块副本说明
« 回复#12: 1月13日,2021年,02:13:15 PM»
Z80仅为此目的具有特殊命令:

块传输和搜索

     表9列出了极其强大的块传输说明。这些说明操作
     有三个寄存器。

     ·HL指向源位置
     ·de指向目的地位置
     ·BC是一个字节计数器


程序员初始化这三个寄存器之后,这四种指令中的任何一个都可以
用过的。负载和增量(LDI)指令从指向的位置移动一个字节
通过hl到de指向的位置。注册对HL和de是自动的
Cly递增,准备点指向以下位置。字节计数器(即,
寄存器对BC此时也递减。此指令是有价值的
必须移动数据块,但每个数据都需要其他类型的处理
移动。负载,增量和重复(LDIR)指令是LDI的扩展
操作说明。重复相同的负载和增量操作,直到字节计数器
达到零计数。结果,此单个指令可以移动任何数据块
从一个地方到任何其他地方。

由于使用了16位寄存器,因此块的大小可以长达64kb
(1KB = 1024位),可以从内存中的任何位置移动到任何其他位置。
此外,块可以重叠,因为数据上没有约束
用于三个寄存器对。

LDD和LDDR指令类似于LDI和LDIR。唯一的区别是
寄存器对HL和DE在每次移动后都会递减,从而开始块转移
从指定块的最高地址而不是最低的地址。

表10指定四个块搜索说明的操作代码。第一个,CPI(com-
削减和增量)将累加器中的数据与MEM-内容进行比较
或者y位置由注册HL指向。比较的结果存储在其中一个
然后递增标志位和HL寄存器对,并递增byte计数器(寄存器对
BC)递减。

http://www.zilog.com/docs/z80/um0080.pdf
Bluepill Diagnostics:  //sourceforge.net/projects/mecrisp-stellaris-folkdoc/files/stm32fxx-diagnostics-v1.5.bin
Cortex-M的Mecrisp-Stellaris;非官方DOC: //mecrisp-stellaris-folkdoc.sourceforge.io/
实时谈论真实的人,irc:freenode.net #mecrisp
 

离线 名义动物

  • 超级贡献者
  • ***
  • 帖子:2456
  • 国家: FI.
    • 我的主页和电子邮件地址
Re:块副本说明
« 回复#13开启: 1月13日,2021年,03:02:37 PM»
刚刚在CPU中的块复制指令的潜在福利(或缺乏)潜在福利(或缺乏)。
我也想知道并检查了这些。 我得出结论,即自动增量负载,存储和比较(如果使用单独的比较状态寄存器,而不同于例如,条件分支指令中固有的比较操作的RISC-V)将成为最大的区别。 如果在访问之前或之后发生增量,它甚至不会重要,也不会在负载和存储之间不同。 如果您探索实现最常用的操作所需的汇编 - Memset(); memcpy_inc()和memcpy_dec()(实现memcpy(),memmove(),memrepeat()); strncmp(),strnlen(),memcmp()等;以最常见的工作负载,你'LL看到地址递增/递减是所有这些的唯一部分,以及折叠到加载/存储/比较操作时,每个迭代保存最多的周期。

请注意,匹配访问大小的自动增量/自动分子是完全可接受的,并且大于字节/ CHAR访问可能必须对齐。 我个人希望看到8位和32/64位(本机寄存器大小)版本;其他一切都可以在软件中完成。

bzero()/ memset(,0,)只值得加快完整的Cachelines或页面,然后只有当您可以使用内存管理可以执行一些功能以有效地执行清算时,那么基本上才会有效。

有一个罕见的情况是将未对齐的内存复制以对齐内存,反之亦然,反之亦然。网络堆栈和二进制文件格式处理。 如果架构不支持未对齐的内存访问,则具有特殊的负载操作,该操作将目标寄存在一个字节上从另一个寄存器(以及商店的反向)加载字节,优选地使用可选的自动增量/自动分泌,会有所帮助。 这样,在32位架构上,这样的份数只需4个负载,1个存储,1个条件分支,每个32位字(或1个负载,4个商店,1个条件分支);但也可能对其他散点/聚集字节成分/提取有用。 请注意,Little-Endian字节订单使用自动增量,但大endian自动分子。

我相信你可以看到这些差异这些案例 - str *()和mem *()函数,还可以用于endian特定的未对准访问器函数(我打算将我的基本库提供默认为静态内联函数) - 但如果不是,我'D很乐意在伪组件中发布一些特定的例子。
 

在线的 Ditbho.

  • 频繁的贡献者
  • **
  • 帖子:392
  • 国家: GB.
Re:块副本说明
« 回复#14: 1月13日,2021年,03:24:18 PM»
自动增量负载,商店和比较[..]会产生最大的区别

所以基本上你想要这两个人,对吗?
代码: [选择]
load_postinc.size D_reg, (A_reg)   // load D_reg from mem[A_reg], and then increment A_reg+=size
store_postinc.size D_reg, (A_reg)  // store D_reg into mem[A_reg], and then increment A_reg+=size
size = {sizeof(u8),sizeof(u16),sizeof(u32)} = {1,2,4}字节
d_reg是数据寄存器。
a_reg是地址寄存器。

在RISC设计中,D_REG和A_REG都属于寄存器文件集。

mips没有'T有这种特殊的装载/商店指令,递增,但我认为这一点'即使对于纯RISC设计也是可能的工作并有意义。

但我不知道't添加任何东西,否则"RISC" will become a "CRISC" :D
« 最后编辑:1月13日,2021年,07:31:51 Ditbho »
 

离线 siliconwizard.

  • 超级贡献者
  • ***
  • 帖子:6182
  • 国家: FR.
Re:块副本说明
« 回复#15: 1月13日,2021年,03:55:39 PM»
从纯粹的基准,研究和可用代码中判断优化块副本,这肯定不是解决问题的恕我国语问题,至少不是在一般情况下。

我实际上发现了*很少有关于块拷贝说明的*文章。很少。您可以找到的大多数文章都是关于在装配中优化块副本,其中指令套件'T有专用的指示。如前所述,除了x86处理器的例外,您可以在其中找到各种比较,使用块副本说明(即x86遗留,通常在现代x86处理器上没有特别优化),或者有或没有循环展开等,现在即使优化具有特定的指令调度,矢量操作等,它需要特定的CPU特征,并且本身就是艺术。

@Bruce:拍摄RISC-V示例,循环展开(以及使用负载和存储操作的偏移量)确实将使它与专用控制器有效。缺点是:它'S不是一般解决方案,循环展开完全取决于代码,另一个:它使代码大小迅速增长,很多。它'当等同物是从8到可能数十的指令(带循环展开)时,难以击败1指令。代码大小不仅仅是一个尺寸问题,它也会不必要地填充指令缓存。

当然,如果代码大小没有问题,而且/或您拥有超标量CPU,和/或带矢量操作,和/或巨大的缓存,专用指令/专用控制器可能没有用。在其他情况下,我'我确信这一点可以是。

实施此类指令肯定会少于实现超标处理器和/或向量操作的较少。

@andersm:围绕中断问题的许多方法以及3个源问题确实。不是一个大问题。对于中断,具体而言,我'm认为它可能有趣的是,有一个可选择使块复制操作可中断或否:用于在某些时候实现对存储器的独占访问,与互斥互斥等相比,非中断版本肯定是高效的。

对于仍然这么想的人'是一个无用的功能,特别是在RISC CPU上,我找到了这个报告:
"用于内存到内存副本的硬件加速" - UCB / EECS-2017-2 - 由Randy Katz和Krste Asanovic的监督。一个有趣的阅读。

另一个读,更有趣但仍然处理问题:AN12628 - "优化内存复制例程", NXP.

最后,多么频繁"block copy"操作是,我认为很多人没有意识到多少。
他们肯定不仅仅是来自显式麦呢()的呼叫。随时移动数据(变量,对象,......),a"block copy"实际上发出。我没有 "benchmarks"给人(和我'd对看到一些)感兴趣,但我'某些移动记忆周围是大多数程序的非常重要的部分。即使您的特定MicroArch *和*编译器也允许副本作为专用指令/专用控制器的副本,这可能会保存重大的代码内存。无论如何,我建议阅读上述UCB报告。

 

离线 siliconwizard.

  • 超级贡献者
  • ***
  • 帖子:6182
  • 国家: FR.
Re:块副本说明
« 回复#16开: 1月13日,2021年,04:17:32 PM»
(...)
但我不知道't添加任何东西,否则"RISC" will become a "CRISC" :D

这是我在第一篇文章中提到的分数之一。但是想着它,我'm not sure it'真的是一个有效的观点。

而不是调用这样的指示"complex instructions",只需调用块副本功能"accelerator" - 通过单个指令可以接受它的部分,部分是扩展 - 突然间,所有都变得时髦,而且没有许多对象。
 

离线 名义动物

  • 超级贡献者
  • ***
  • 帖子:2456
  • 国家: FI.
    • 我的主页和电子邮件地址
Re:块副本说明
« 回复#17开: 1月13日,2021年,05:17:37 PM»
自动增量负载,商店和比较[..]会产生最大的区别
所以基本上你想要这两个人,对吗?
代码: [选择]
load_postinc.size D_reg, A_reg   // load D_reg from mem[A_reg], and then increment A_reg+=size
store_postinc.size D_reg, A_reg  // store D_reg into mem[A_reg], and then increment A_reg+=size
大小= {1,2,4}为{U8,U16,U32} I / O,D_REG,数据寄存器; A_REG,地址登记册;在RISC设计中,D_REG和A_REG都属于寄存器文件集。
I'd交易16位自动分手,我不'T CARE如果增量是过度投入或帖子的,但是的。

当DST时,Memmove()是自动分泌的>SRC在同一阵列中,并且在将数据插入排序阵列时非常常用。

单一指令,部分的一部分是扩展
那's what I'd prefer, too. 基本上,我有十种指导形式'd like to see:
  • 从存储器[reg2]将字节加载到reg1的最低有效字节,递增reg2
  • 从存储器[reg2]将字节加载到reg1的最低有效字节,递减reg2
  • 从存储器[reg2]将字节加载到reg1的最低有效字节,清除REG1的其他位,递增reg2
  • 从存储器[reg2]将字节加载到reg1的最低有效字节,清除REG1的其他位,递减一个reg2
  • 加载存储器[REG2]以注册REG1,通过寄存器大小递增REG2
  • 加载存储器[REG2]以注册REG1,递减REG2通过寄存器大小
  • 将Reg1的最低有效字节存储到内存[Reg2],递增Reg2
  • 将Reg1的最低有效字节存储在内存[Reg2],递减REG2
  • 将Reg1存储到内存[Reg2],递增Reg2按寄存器大小
  • 将REG1存储到内存[REG2],递减REG2寄存器大小
从前四个,任何一对都是可以接受的,两种形式都有其用途。 我的意思是,对于块存储器功能,保持上部比特不变或零尺度均匀;只是不做标志扩展。

I'VE总是对延伸一个字节的标志略微不喜欢,因为它很少需要。  In most code I'已经看到,它实际上是一个错误,其中来自字符串的单个字符被投射到int,并且底层的char类型才能签名。 块内存功能肯定不需要。

例如,在c中,<ctype.h>不得使用功能 char;需要一个明确的演员(未签名的char),但大多数程序员忘记/唐't know/don't care. 也就是说,以下代码是越野车:
代码: [选择]
static inline const char *skipspaces(const char *src) {
    if (src)
        while (isspace(*src))
            src++;
    return src;
}
而正确的版本是
代码: [选择]
static inline const char *skipspaces(const char *src) {
    if (src)
        while (isspace((unsigned char)(*src)))
            src++;
    return src;
}
从字节到完整寄存器的签名扩展唯一实际情况是当您有一个类型的变量 签名字符 或者 int8_t,并且您加载它以进行整数操作。 所以我确实需要对单个字节加载到寄存器的标志扩展;但不是内存块函数的一部分。
 
以下用户感谢此帖子: Ditbho.

离线 TGZZZ.

  • 超级贡献者
  • ***
  • 帖子:13254
  • 国家: GB.
    • 玩得开心更多,更少
Re:块副本说明
« 回复#18开启: 1月13日,2021年,07:03:55»

最后,多么频繁"block copy"操作是,我认为很多人没有意识到多少。
他们肯定不仅仅是来自显式麦呢()的呼叫。随时移动数据(变量,对象,......),a"block copy"实际上发出。我没有 "benchmarks"给人(和我'd对看到一些)感兴趣,但我'某些移动记忆周围是大多数程序的非常重要的部分。即使您的特定MicroArch *和*编译器也允许副本作为专用指令/专用控制器的副本,这可能会保存重大的代码内存。无论如何,我建议阅读上述UCB报告。

语言语义有很大的影响。有些语言授权复制,其他人唐't. T

在某种程度上,优化可以减少这样的开销,如果且才能才能在程序中任何位置没有别名(对特定数据)。
有谎言,该死的谎言,统计数据和ADC / DAC规范。
滑翔机飞行员's aphorism: "跨度没有替代品". Retort: "有一个替代品:技能+想象力。但是你可以 span".
正玩得开心 做更多,少
 

离线 siliconwizard.

  • 超级贡献者
  • ***
  • 帖子:6182
  • 国家: FR.
Re:块副本说明
« 回复#19: 1月13日,2021年,08:29:20 PM»

最后,多么频繁"block copy"操作是,我认为很多人没有意识到多少。
他们肯定不仅仅是来自显式麦呢()的呼叫。随时移动数据(变量,对象,......),a"block copy"实际上发出。我没有 "benchmarks"给人(和我'd对看到一些)感兴趣,但我'某些移动记忆周围是大多数程序的非常重要的部分。即使您的特定MicroArch *和*编译器也允许副本作为专用指令/专用控制器的副本,这可能会保存重大的代码内存。无论如何,我建议阅读上述UCB报告。

语言语义有很大的影响。有些语言授权复制,其他人唐't. T

在某种程度上,优化可以减少这样的开销,如果且才能才能在程序中任何位置没有别名(对特定数据)。

真实,虽然即便如此,数据副本也是IMO仍然很重要。你有什么语言?

I'd想查找或写入一个工具,允许在运行时识别数据副本,并耗尽统计信息,例如min,max,平均块大小和复制数据的总量,因此我们得到真正的数字而不是猜测。
 

离线 TGZZZ.

  • 超级贡献者
  • ***
  • 帖子:13254
  • 国家: GB.
    • 玩得开心更多,更少
Re:块副本说明
« 回复#20开: 1月13日,2021年,08:57:34 PM»

最后,多么频繁"block copy"操作是,我认为很多人没有意识到多少。
他们肯定不仅仅是来自显式麦呢()的呼叫。随时移动数据(变量,对象,......),a"block copy"实际上发出。我没有 "benchmarks"给人(和我'd对看到一些)感兴趣,但我'某些移动记忆周围是大多数程序的非常重要的部分。即使您的特定MicroArch *和*编译器也允许副本作为专用指令/专用控制器的副本,这可能会保存重大的代码内存。无论如何,我建议阅读上述UCB报告。

语言语义有很大的影响。有些语言授权复制,其他人唐't. T

在某种程度上,优化可以减少这样的开销,如果且才能才能在程序中任何位置没有别名(对特定数据)。

真实,虽然即便如此,数据副本也是IMO仍然很重要。你有什么语言?

C / C ++要求将STRUCTS / Objects复制为函数调用的参数。许多其他语言复制了对结构/对象的引用,但可以拥有它's own disadvantages.

Rust有一种有趣的方法来改善问题,但我没有't "kicked the tyres".

引用
I'd想查找或写入一个工具,允许在运行时识别数据副本,并耗尽统计信息,例如min,max,平均块大小和复制数据的总量,因此我们得到真正的数字而不是猜测。

大学教师'T忘记还测量工作集VS缓存大小的大小,以及缓存行对齐。
« 上次编辑:1月13日,2021年,09:00:02 PM由TGZZZ »
有谎言,该死的谎言,统计数据和ADC / DAC规范。
滑翔机飞行员's aphorism: "跨度没有替代品". Retort: "有一个替代品:技能+想象力。但是你可以 span".
正玩得开心 做更多,少
 

离线 布鲁尔特

  • 超级贡献者
  • ***
  • 帖子:2011
  • 国家: NZ.
  • 以前是Sifive,三星r&D
Re:块副本说明
« 回复#21开: 1月14日,2021年,01:49:16 AM»
C / C ++要求将STRUCTS / Objects复制为函数调用的参数。许多其他语言复制了对结构/对象的引用,但可以拥有它's own disadvantages.

是的,但关心性能的程序员永远不会为大型对象/结构做到这一点。
 

离线 renthraysk.

  • 常规贡献者
  • *
  • 帖子:105
  • 国家: GB.
Re:块副本说明
« 回复#22: 1月14日,2021年,02:37:00»
M68K具有Movem,块副本高于一定尺寸。可以在2条指令中复制48字节等48字节。

常规解决方案如果复制是性能瓶颈,则完全消除复制的需要。 AKA ZERO-COPY。

最现代的操作系统'S为文件和网络等内容具有零复制API。

 

离线 布鲁尔特

  • 超级贡献者
  • ***
  • 帖子:2011
  • 国家: NZ.
  • 以前是Sifive,三星r&D
Re:块副本说明
« 回复#23开:2021.年1月14日,03:27:05»
M68K具有Movem,块副本高于一定尺寸。可以在2条指令中复制48字节等48字节。

在实际的M68000上,每4字节复制18个周期,加上20个循环开销。你可以'显然,使用所有16​​个寄存器(64字节),因为您需要一些用于SRC和DST指针,最小值。无论您使用的寄存器,您可能必须在开始时将它们保存,并在以后重新加载它们。

好的,让我们'S说12寄存器。所以12 * 18 + 20 = 236个周期,以便在开始时保存并恢复它们。然后,每个48字节复制236个循环加上一些循环开销(DBNZ,Say,10个周期)。

一个简单的move.L(AN)+,(AN)+是20个周期和'除了SRC和DST指针之外,使用任何寄存器,所以Movem如果你不这样做很少'介意展开移动。循环一点。
« 上次编辑:2021年1月14日,03:29:22 AM by Brucehoult »
 

离线 TGZZZ.

  • 超级贡献者
  • ***
  • 帖子:13254
  • 国家: GB.
    • 玩得开心更多,更少
Re:块副本说明
« 回复#24开: 1月14日,2021年,10:18:30»
C / C ++要求将STRUCTS / Objects复制为函数调用的参数。许多其他语言复制了对结构/对象的引用,但可以拥有它's own disadvantages.

是的,但关心性能的程序员永远不会为大型对象/结构做到这一点。

是的,他们接受难点和问题(以C / C ++)的替代方案。

但这变得更加有问题,其中来自多人,项目,组织和公司必须合并的代码。

在这种情况下,语言语义可以并确实产生了很大的不同,特别是对于绝大多数程序员。

有谎言,该死的谎言,统计数据和ADC / DAC规范。
滑翔机飞行员's aphorism: "跨度没有替代品". Retort: "有一个替代品:技能+想象力。但是你可以 span".
正玩得开心 做更多,少
 


分享我

掘客  Facebook  诽谤  可口的  Technorati.  推特  谷歌  雅虎
SMF.