การใช้ C ++ AtomicInt นี้ถูกต้องหรือไม่


9

สถานที่ตั้ง: ผมทำงานกับ ARM ฝังตัว (เกือบเปลือยโลหะ) สภาพแวดล้อมที่ฉันไม่ได้มี C ++ 11 (กับstd::atomic<int>) ใช้ได้ดังนั้นโปรดหลีกเลี่ยงคำตอบเช่น " เพียงแค่ใช้มาตรฐาน C ++std::atomic<int> " ฉันไม่สามารถ

การใช้งาน AtomicInt ARM นี้ถูกต้องหรือไม่ (ถือว่าสถาปัตยกรรม ARM คือARMv7-A )

คุณเห็นปัญหาการซิงโครไนซ์บ้างไหม? มันvolatileจำเป็น / มีประโยชน์หรือไม่?

// File: atomic_int.h

#ifndef ATOMIC_INT_H_
#define ATOMIC_INT_H_

#include <stdint.h>

class AtomicInt
{
public:
    AtomicInt(int32_t init = 0) : atom(init) { }
    ~AtomicInt() {}

    int32_t add(int32_t value); // Implement 'add' method in platform-specific file

    int32_t sub(int32_t value) { return add(-value); }
    int32_t inc(void)          { return add(1);      }
    int32_t dec(void)          { return add(-1);     }

private:
    volatile int32_t atom;
};

#endif
// File: arm/atomic_int.cpp

#include "atomic_int.h"

int32_t AtomicInt::add(int32_t value)
{
    int32_t res, prev, tmp;

    asm volatile(

    "try:    ldrex   %1, [%3]\n"     // prev = atom;
    "        add     %0, %1, %4\n"   // res = prev + value;
    "        strex   %2, %0, [%3]\n" // tmp = outcome(atom = res); // may fail
    "        teq     %2, #0\n"       // if (tmp)
    "        bne     try"            //     goto try; /* add failed: someone else modified atom -> retry */

    : "=&r" (res), "=&r" (prev), "=&r" (tmp), "+mo" (atom)  // output (atom is both in-out)
    : "r" (value)                                           // input
    : "cc");                                                // clobbers (condition code register [CPSR] changed)

    return prev; // safe return (local variable cannot be changed by other execution contexts)
}

นอกจากนี้ฉันกำลังพยายามที่จะใช้รหัสซ้ำนั่นคือเหตุผลที่ฉันแยกฟังก์ชันพื้นฐานเพียงอันเดียวเพื่อนำไปใช้ในโค้ดเฉพาะแพลตฟอร์ม ( add()เมธอดด้านในarm/atomic_int.cpp)

คือatomic_int.hจริงๆพกพามันเป็นข้ามแพลตฟอร์มที่แตกต่างกัน / สถาปัตยกรรม / คอมไพเลอร์? วิธีนี้เป็นไปได้หรือไม่? (ด้วยความเป็นไปได้ผมหมายถึงความเป็นไปได้สำหรับทุกแพลตฟอร์มที่จะรับประกันอะตอมมิกซิตี้โดยใช้add()วิธีการเพียงอย่างเดียว )

นี่คือการใช้ ARM GCC 8.3.1 ที่สอดคล้องกันของฟังก์ชั่นเดียวกัน เห็นได้ชัดว่าความแตกต่างที่แท้จริงเท่านั้นคือการปรากฏตัวของdmbก่อนและหลัง พวกเขาจำเป็นจริงๆในกรณีของฉัน? ทำไม? คุณมีตัวอย่างที่AtomicInt(ไม่มีdmb) ของฉันล้มเหลวหรือไม่?

ปรับปรุง: การใช้งานคงที่get()วิธีการลบออกเพื่อแก้ปัญหาอะตอมมิกและการจัดตำแหน่ง ตอนนี้พฤติกรรมเช่นมาตรฐานadd()fetchAndAdd()


volatileคำหลักใน C ++ หมายถึงอย่าปรับให้เหมาะสมผ่านตัวแปร ดังนั้นget()วิธีการได้รับประโยชน์จากมัน ถึงแม้ว่าโดยทั่วไปความผันผวนนั้นกำลังจะลดลงใน C ++ หากระบบของคุณไม่สามารถซิงค์ข้อมูล 32 บิตในตัวได้แสดงว่าคุณมีทางเลือกน้อย แต่ให้ใช้ mutexes - spinlock อย่างน้อยที่สุด
ALX23z

คุณใช้สถาปัตยกรรมแขนรุ่นใด armv-7?
Mike van Dyke

1
สิ่งนี้ไม่ได้ตอบคำถาม แต่ชื่อที่มีเครื่องหมายขีดล่างสองตัว ( __ATOMIC_INT_H_) และชื่อที่ขึ้นต้นด้วยขีดล่างตามด้วยตัวพิมพ์ใหญ่จะถูกสงวนไว้สำหรับการใช้งานโดยการใช้งาน อย่าใช้มันในรหัสของคุณ
Pete Becker

ชื่อสมาชิกatomicอาจไม่ได้ดีที่สุดเพื่อหลีกเลี่ยงความสับสนstd::atomicแม้ว่ามันจะเป็นคำถามที่ว่าทำไมคุณไม่ใช้ในกรณีใด ๆ
Clifford

เพิ่มสถาปัตยกรรม ARM เปลี่ยนชื่อ__ATOMIC_INT_H_ตัวระบุ
gentooise

คำตอบ:


2

หากคุณใช้gccคุณสามารถใช้ฟังก์ชั่นLegacy __syncBuilt-in สำหรับการเข้าถึงหน่วยความจำปรมาณู :

void add(int volatile& a, int value) {
    __sync_fetch_and_add(&a, value);
}

สร้าง :

add(int volatile&, int):
.L2:
        ldxr    w2, [x0]
        add     w2, w2, w1
        stlxr   w3, w2, [x0]
        cbnz    w3, .L2
        dmb     ish
        ret

น่าเสียดายที่ฉันไม่ได้ใช้gccและไม่ว่าในกรณีใดฉันไม่ต้องการผูกติดตั้งการใช้งานกับคอมไพเลอร์เฉพาะใด ๆ ขอบคุณสำหรับคำแนะนำของคุณอย่างน้อยก็บอกฉันว่าadd()ส่วนแขนของฉันควรจะถูกต้อง ความแตกต่างระหว่างldxrและldrexคืออะไร
gentooise

นี่คือ ARM8 (เช่น 64 บิต) มากกว่าหนึ่งในรุ่น 32 บิต
marko

ฉันจัดการเพื่อรับรหัสที่สอดคล้องกันโดยระบุสถาปัตยกรรมเป้าหมาย: การเชื่อมโยง ดูเหมือนว่า GCC จะใส่dmbก่อนและหลังldrex/ strexloop
gentooise

2
ฉันคิดว่านี่เป็นวิธีที่ดี แต่เพื่อให้คอมไพเลอร์เป็นอิสระเพียงไปที่godbolt.org/z/WB8rxwในฟังก์ชันที่คุณต้องการโดยใช้ gcc builtins และคัดลอกเอาต์พุตแอสเซมบลีที่สอดคล้องกัน ตรวจสอบให้แน่ใจว่าตรงกับพารามิเตอร์ -march กับเวอร์ชันเฉพาะของ ARM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.