girijanandan nucha
girijanandan nucha

Reputation: 77

Atomic operations - C

Is it possible to have two variables incremented atomically. I have the following code and since it's a multi-processor, multi-threaded environment, cache invalidations becomes a performance bottleneck. So, I am trying to minimise the number of atomic operations.

__sync_add_and_fetch(&var1,1);
__sync_add_and_fetch(&var2,1);

I see that the first argument is a pointer, is it possible to achieve my case by using a structure?

P.S: I cannot use locks.

Upvotes: 5

Views: 26107

Answers (2)

Swiss Frank
Swiss Frank

Reputation: 2422

All but two Pentiums (AMD processors from like 2001, or something) support 128-bit atomic operations. That's 16 bytes.

Support for these operations is available in C++ via the atomic<> template, though one of the recent GCC versions had this turned off for 16-byte values and used a lock instead. However individual compilers have ways to access these and you can in principle write it in machine language as well.

The range of operations is limited, but you can in principle do whatever you want in regular C, and then replace the existing contents with new contents with a CAS operation. CAS just means: CPU, write value A into memory location B, if and only if B currently holds value C. So: read the values; do your math; then use CAS and use the CAS success code to break a do..while loop when it succeeds.

Upvotes: 1

Scheff&#39;s Cat
Scheff&#39;s Cat

Reputation: 20141

Atomic operations are very special and provide only limited support. Applying them to two variables sounds impossible for me.

Note, that it is even not garanteed that an atomic operation is really done with a resp. actomic operation (i.e. machine code command).

Out of the gcc doc. 5.47 Built-in functions for atomic memory access:

Not all operations are supported by all target processors. If a particular operation cannot be implemented on the target processor, a warning will be generated and a call an external function will be generated. The external function will carry the same name as the builtin, with an additional suffix '_n' where n is the size of the data type.

The external function probably emulates the atomic operation using a mutex.

But I guess, it would be possible with a "dirty hack" and only with certain limitations:

If 16 bit unsigned counters are sufficient, you could put two of them in one 32 bit variable where c1c2 += 0x00000001 increments one, c1c2 += 0x00010000 increments the other, and c1c2 += 0x00010001 increments both or using the atomic operations:

/* combined counters c1 and c2 */
static uint32_t c1c2 = 0;

/* count c1 atomically */
__sync_fetch_and_add(&c1c2, 0x00000001);
/* count c2 atomically */
__sync_fetch_and_add(&c1c2, 0x00010000);
/* count c1 AND c2 atomically */
__sync_fetch_and_add(&c1c2, 0x00010001);

This has to be combined with the appropriate bit shifting and masking to access the invidiual counter values.

Of course, counter overflow could be an issue. The same might work for two 32 bit counters on a 64 bit platform (considering that atomic operations are usually only available for "machine word" width).

Btw. while googling for background info, I stumbled over this: Why does __sync_add_and_fetch work for a 64 bit variable on a 32 bit system?. I found the hint that atomic operations may require sufficient alignment of variables to work properly (which I found worth to mention).

This might be the reason why the C11 Atomic Library provides dedicated types for atomic variables (e.g. atomic_uint_least32_t).

Upvotes: 8

Related Questions