ref: Is changing a pointer considered an atomic action in C?
ref: http://www.csie.ntu.edu.tw/~r89004/hive/cache/page_1.html
Conclusion: It depends on platform. On Intel x86 system, pointer assignment operation is atomic if the pointer is put into one cache line(64 bytes in x86).
PS: Not only pointer, any data that can be put under one cache-line is atomic operation.
Cache 對速度有什麼影響呢?這可以由 latency 來表示。CPU 在從記憶體中讀取資料(或程式)時,會需要等待一段時間,這段時間就是 latency,通常用 cycle 數表示。例如,一般來說,如果資料已經在 L1 cache 中,則 CPU 在讀取資料時(這種情形稱為 L1 cache hit),CPU 是不需要多等的。但是,如果資料不在 L1 cache 中(這種情形稱為 L1 cache miss),則 CPU 就得到 L2 cache 去讀取資料了。這種情形下,CPU 就需要等待一段時間。如果需要的資料也不在 L2 cache 中,也就是 L2 cache miss,那麼 CPU 就得到主記憶體中讀取資料了(假設沒有 L3 cache)。這時候,CPU 就得等待更長的時間。
另外,cache 存取資料時,通常是分成很多小單位,稱為 cache line。例如,Pentium III 的 cache line 長度是 32 bytes。也就是說,如果 CPU 要讀取記憶體位址 0x00123456 的一個 32 bits word(即 4 bytes),且 cache 中沒有這個資料,則 cache 會將 0x00123440 ~ 0x0012345F 之間的 32 bytes 資料(即一整個 cache line 長度)都讀入 cache 中。所以,當 CPU 讀取連續的記憶體位址時,資料都已經讀到 cache 中了。
============ Test Code - pointer is put under cache line ============
#include stdio.h
#include stdlib.h
#include pthread.h
#include stdint.h
#include stdbool.h
uint64_t b = 1144644202170355111;
uint64_t *foo;
uint64_t *local;
pthread_t tid;
void *reader(void *arg)
{
while (true) {
local = foo;
if ((*local != a) && (*local != b)) {
printf("Not atomic %lu\n", *local);
} else {
//printf("atomic %lu\n", local);
}
}
}
void main()
{
bool switcher = false;
int rc;
rc = pthread_create(&tid, NULL, reader, &tid);
while (true)
{
if (switcher) {
foo = &a;
//foo = 1844674407370955161;
} else {
foo = &b;
//foo = 1144644202170355111;
}
switcher = !switcher;
}
pthread_exit(NULL);
}
============ Test Code - data is not put under one cache line ============
#include stdio.h
#include stdlib.h
#include pthread.h
#include stdint.h
#include stdbool.h
#define NUM_THREADS 5
uint64_t a = 1844674407370955161;
uint64_t b = 1144644202170355111;
struct test_s {
char pad[57]; // Force compiler not able to put this structure into one cache line
uint64_t foo;
}__attribute__((packed));
struct test_s test __attribute__((aligned(64))); // align to 64 bytes cache line
//uint32_t a __attribute__((aligned(4))) = 1234567890;
//uint32_t b __attribute__((aligned(4))) = 876543219;
//uint32_t foo __attribute__((aligned(4)));
//uint64_t *local;
//char a __attribute__((aligned(64))) = 'a';
//char b __attribute__((aligned(64))) = 'c';
//char foo __attribute__((aligned(64)));
//char local __attribute__((aligned(64)));
pthread_t tid[NUM_THREADS];
void *reader(void *arg)
{
long thread_id;
uint64_t local;
thread_id = *(long *)arg;
while (true) {
local = test.foo;
if ((local != a) && (local != b)) {
printf("Not atomic %lu thread_id=%ld\n", local, thread_id);
} else {
//printf("atomic %lu\n", local);
}
}
}
int main()
{
bool switcher = false;
int rc, i;
for (i = 0; i < NUM_THREADS; i ++) {
rc = pthread_create(&tid[i], NULL, reader, &i);
}
while (true)
{
if (switcher) {
test.foo = a;
//foo = 1844674407370955161;
} else {
test.foo = b;
//foo = 1144644202170355111;
}
switcher = !switcher;
}
pthread_exit(NULL);
return 0;
}
沒有留言:
張貼留言