理解ARM事务性内存扩展

5
ARM 事务性内存扩展有一个相当简单的使用说明:
sem_post:
    TSTART X0         // Start of outer transaction
    CBNZ   test_fail  // No reason for this routine to cancel or fail
    LDR    X1, [X2]   // X2 points to semaphore
    ADD    X1, X1, #1 // Increment semaphore value
    STR    X1, [X2].  // Store incremented value
    TCOMMIT           // Commit transaction and exit

我试图弄清楚的是,这些交易是否会因与代码其他部分中的交易发生冲突而被重放,并且它们是否会因与任何类型的访问发生冲突而被重放。为了详细说明,假设我们有这个例程:
sem_wait:
    TSTART X0          // Start of outer transaction
    CBNZ   retry_check // This routine checks for retry (RTRY) and restarts transaction
    LDR    X1, [X2]   // X2 points to semaphore
    CMP    X1, #0     // Check if semaphore is already used
    CBNZ   decrement  // If it's non-zero, we can decrement the semaphore
    TCANCEL #0xFF     // If it's zero, we gotta retry
decrement:
    SUB    X1, X1, #1 // Decrement semaphore value
    STR    X1, [X2].  // Store decremented value
    TCOMMIT           // Commit transaction and exit

因此,这个事务将在代码的另一个部分进行,但是将访问内存中的位置作为sem_post事务。

我的第一个问题:执行sem_post事务的线程是否可能由于同时执行sem_wait事务的线程而重复执行?

对于我的第二个问题,假设我们有一个像这样简单的例程:

break_semaphore:
    MOV X0, #0xFF
    STR X0, [X1]  // X1 points to semaphore

上述程序并不是一个事务,它只是在操作信号量。

我的第二个问题:执行 sem_post 事务的线程是否会由于对待更新和提交的位置进行的任何并发访问而重新播放?

为了明确起见,我完全理解这并不是 TME 指令的实际用法,锁更可能像这样实现: https://www.gem5.org/project/2020/10/27/tme.html

我更想知道的是事务实际上是如何线性化的:具有共同代码区域的两个事务,所有事务相互之间,还是与内存中的所有其他访问相关的事务?

1个回答

2
TME(Transactional Memory Extension)确实使访问共享内存区域线性化。在您的示例中,这些事务失败的原因不是因为它们执行相同的代码,而是由于共享内存地址。
根据ARM TME文档,任何与内存地址冲突的状态都会导致TSTART失败,并设置MEM位。在信号量示例的上下文中,由于没有回退代码调用sem_post,事务将取消程序执行状态并恢复到非事务状态。
出于类似的原因,事务不一定通过执行相同的代码进行线性化,因为它们可能引用不同的内存区域(即具有不同指针的多个信号量),这是完全合法的。
事务是否在彼此之间线性化更难以回答,因为它通常取决于硬件。例如,两个事务可以在不同的核心上合法地执行不同的内存对象,但如果两个事务试图在同一个核心和寄存器上执行(即使用超线程),则这种行为将更难以定义。

我现在明白了。我没有仔细阅读文档,没有注意到MEM位。那么,任何内存冲突都可能导致事务重试? - squirem
1
在事务状态下获取的每个缓存行都以独占模式完成(与C语言中的volatile变量相同),因此是的,这确实涉及到在事务状态下获取的任何地址(缓存块)的内存冲突。 - Sam Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接