Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8286421
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 8, 20262026-06-08T11:45:46+00:00 2026-06-08T11:45:46+00:00

I have an application where I have to increment some statistics counters in a

  • 0

I have an application where I have to increment some statistics counters in a multi-threaded method. The incrementing has to be thread-safe, so I decided to use the gcc atomic builtins __sync_add_and_fetch() function. Just to get an idea of their impact, I did some simple performance testing and noticed that these functions are much slower than simple pre/post incrementing.

Here is the test program that I created:

#include <iostream>
#include <pthread.h>
#include <time.h>

using namespace std;

uint64_t diffTimes(struct timespec &start, struct timespec &end)
{
  if(start.tv_sec == end.tv_sec)
  {
    return end.tv_nsec - start.tv_nsec;
  }
  else if(start.tv_sec < end.tv_sec)
  {
    uint64_t nsecs = (end.tv_sec - start.tv_sec) * 1000000000;
    return nsecs + end.tv_nsec - start.tv_nsec;
  }
  else
  {
    // this is actually an error
    return 0;
  }
}

void outputResult(const char *msg, struct timespec &start, struct timespec &end, uint32_t numIterations, uint64_t val)
{
  uint64_t diff = diffTimes(start, end);
  cout << msg << ": "
       << "\n\t iterations: " << numIterations
       << ", result: " << val
       << "\n\t times [start, end] =  [" << start.tv_sec << ", " << start.tv_nsec << "]"
       << "\n\t [" << end.tv_sec << ", " << end.tv_nsec << "]"
       << "\n\t [total, avg] = [" << diff
       << ", " << (diff/numIterations) << "] nano seconds"
       << endl;
}

int main(int argc, char **argv)
{
  struct timespec start, end;
  uint64_t val = 0;
  uint32_t numIterations = 1000000;

  //
  // NON ATOMIC pre increment
  //
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    ++val;
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("Non-Atomic pre-increment", start, end, numIterations, val);
  val = 0;

  //
  // NON ATOMIC post increment
  //
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    val++;
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("Non-Atomic post-increment", start, end, numIterations, val);
  val = 0;

  //
  // ATOMIC add and fetch
  //
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    __sync_add_and_fetch(&val, 1);
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("Atomic add and fetch", start, end, numIterations, val);
  val = 0;

  //
  // ATOMIC fetch and add
  //
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    __sync_fetch_and_add(&val, 1);
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("Atomic fetch and add", start, end, numIterations, val);
  val = 0;

  //
  // Mutex protected post-increment
  //
  pthread_mutex_t mutex;
  pthread_mutex_init(&mutex, NULL);
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    pthread_mutex_lock(&mutex);
    val++;
    pthread_mutex_unlock(&mutex);
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("Mutex post-increment", start, end, numIterations, val);
  val = 0;

  //
  // RWlock protected post-increment
  //
  pthread_rwlock_t rwlock;
  pthread_rwlock_init(&rwlock, NULL);
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
  for(uint32_t i = 0; i < numIterations; ++i)
  {
    pthread_rwlock_wrlock(&rwlock);
    val++;
    pthread_rwlock_unlock(&rwlock);
  }
  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);

  outputResult("RWlock post-increment", start, end, numIterations, val);
  val = 0;

  return 0;
}

And here are the results:

# ./atomicVsNonAtomic
Non-Atomic pre-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 1585375]
         [0, 1586185]
         [total, avg] = [810, 0] nano seconds
Non-Atomic post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 1667489]
         [0, 1667920]
         [total, avg] = [431, 0] nano seconds
Atomic add and fetch:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 1682037]
         [0, 16595016]
         [total, avg] = [14912979, 14] nano seconds
Atomic fetch and add:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 16617178]
         [0, 31499571]
         [total, avg] = [14882393, 14] nano seconds
Mutex post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 31526810]
         [0, 68515763]
         [total, avg] = [36988953, 36] nano seconds
RWlock post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 68547649]
         [0, 110877351]
         [total, avg] = [42329702, 42] nano seconds

Here is the gcc compilation:

g++ -o atomicVsNonAtomic.o -c -march=i686 -O2 -I. atomicVsNonAtomic.cc
g++ -o atomicVsNonAtomic atomicVsNonAtomic.o -lrt -lpthread

And related info and versions:

# gcc --version
gcc (GCC) 4.3.2
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# uname -a
Linux gtcba2v1 2.6.32.12-0.7-default #1 SMP 2010-05-20 11:14:20 +0200 i686 i686 i386 GNU/Linux

And now for the actual question 🙂 Is it normal that the atomic operations are so much slower?

The difference for one million iterations is:

  • simple post-increment: 431 nano seconds
  • atomic fetch and add operation: 14882393 nano seconds

Of course I understand that an atomic operation should be more costly, but this seems exaggerated. Just for completeness, I also checked a pthread mutex and rwlock. At least the atomic operations are faster then the pthread operations, but Im still wondering if I’ve done something wrong. I couldnt get it to link without specifying the -march=i686 option, maybe this has an impact?

UPDATE:

I took out the -O2 compiler optimization and was able to get more coherent results as follows:

# ./atomicVsNonAtomic
Non-Atomic pre-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 1647303]
         [0, 4171164]
         [total, avg] = [2523861, 2] nano seconds
Non-Atomic post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 4310230]
         [0, 7262704]
         [total, avg] = [2952474, 2] nano seconds
Atomic add and fetch:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 7285996]
         [0, 25919067]
         [total, avg] = [18633071, 18] nano seconds
Atomic fetch and add:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 25941677]
         [0, 44544234]
         [total, avg] = [18602557, 18] nano seconds
Mutex post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 44573933]
         [0, 82318615]
         [total, avg] = [37744682, 37] nano seconds
RWlock post-increment:
         iterations: 1000000, result: 1000000
         times [start, end] =  [0, 82344866]
         [0, 125124498]
         [total, avg] = [42779632, 42] nano seconds
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-08T11:45:50+00:00Added an answer on June 8, 2026 at 11:45 am

    The answer is that GCC optimizes your non-atomic increments away. When it sees a loop like:

    for (int i=0; i<N; i++) x++;
    

    it replaces it with:

    x += N;
    

    This can be seen in the generated assembly, which contains:

    call    clock_gettime
    leal    -32(%ebp), %edx
    addl    $1000000, -40(%ebp)     <- increment by 1000000
    adcl    $0, -36(%ebp)
    movl    %edx, 4(%esp)
    movl    $2, (%esp)
    call    clock_gettime
    

    So you are not measuring what you think you are.

    You can make your variable volatile to prevent this optimization. On my computer, after doing this, non-atomic access is about 8 times as fast as atomic access. When using a 32-bit variable instead of 64-bit (I’m compiling as 32-bit), the difference drops to about a factor of 3.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a multi-threaded .NET application that occasionally terminates without any message. When I
I have application with needs to have access to some sensitive data(in this case
I have application which needs to use a dll (also written by me) which
We have an application where we need to de-serialize some data from one stream
So I have a live Python application on App Engine that has far too
I'm recording some statistics in my application. One of the statistics is the size
I have a VB.NET application where various objects are going to access some common
I am maintaining some code that has a trigger on a table to increment
In an existing application, I have a table which has no primary key, which
We have an Yii-based PHP application which does some generic actions (like Share with

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.