Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.programming.threads > #956 > unrolled thread

Why is java consumer/producer so much faster than C++

Started byMelzzzzz <mel@zzzzz.com>
First post2012-07-22 23:59 +0200
Last post2012-08-14 22:48 +0000
Articles 14 — 7 participants

Back to article view | Back to comp.programming.threads


Contents

  Why is java consumer/producer so much faster than C++ Melzzzzz <mel@zzzzz.com> - 2012-07-22 23:59 +0200
    Re: Why is java consumer/producer so much faster than C++ Joshua Maurice <joshuamaurice@gmail.com> - 2012-07-22 15:42 -0700
      Re: Why is java consumer/producer so much faster than C++ Melzzzzz <mel@zzzzz.com> - 2012-07-23 01:28 +0200
        Re: Why is java consumer/producer so much faster than C++ Luca Risolia <luca.risolia@studio.unibo.it> - 2012-07-23 02:03 +0200
          Re: Why is java consumer/producer so much faster than C++ Melzzzzz <mel@zzzzz.com> - 2012-07-23 12:17 +0200
            Re: Why is java consumer/producer so much faster than C++ Luca Risolia <luca.risolia@studio.unibo.it> - 2012-07-24 00:33 +0200
    Re: Why is java consumer/producer so much faster than C++ Juha Nieminen <nospam@thanks.invalid> - 2012-07-23 06:37 +0000
      Re: Why is java consumer/producer so much faster than C++ Melzzzzz <mel@zzzzz.com> - 2012-07-23 12:33 +0200
        Re: Why is java consumer/producer so much faster than C++ Juha Nieminen <nospam@thanks.invalid> - 2012-07-23 11:46 +0000
          Re: Why is java consumer/producer so much faster than C++ Melzzzzz <mel@zzzzz.com> - 2012-07-23 15:33 +0200
    Re: Why is java consumer/producer so much faster than C++ Dombo <dombo@disposable.invalid> - 2012-07-23 22:57 +0200
      Re: Why is java consumer/producer so much faster than C++ Joshua Maurice <joshuamaurice@gmail.com> - 2012-07-23 21:54 -0700
        Re: Why is java consumer/producer so much faster than C++ Jorgen Grahn <grahn+nntp@snipabacken.se> - 2012-07-24 12:50 +0000
    Re: Why is java consumer/producer so much faster than C++ Ricardo Nabinger Sanchez <rnsanchez@wait4.org> - 2012-08-14 22:48 +0000

#956 — Why is java consumer/producer so much faster than C++

FromMelzzzzz <mel@zzzzz.com>
Date2012-07-22 23:59 +0200
SubjectWhy is java consumer/producer so much faster than C++
Message-ID<juht3o$a3a$1@news.albasani.net>
I have played little bit with new C++11 features and compared,
java performance to c++.
Actually this was meant to be GC vs RAII memory management,
but boiled down to speed of BlockingQueue class in java,
and mine in c++.
It seems that java implementation is so much more efficient
but I don't know why. I even tried c++ without dynamic 
memory management (except queue itself) and that is *even slower*.
Must be some quirks with a queue ;)

These are timings:
(java openjdk 1.7)
bmaxa@maxa:~/examples$ time java consprod

real	0m13.411s
user	0m19.853s
sys	0m0.960s

(c++ gcc 4.6.3)
bmaxa@maxa:~/examples$ time ./consprod

real	0m28.726s
user	0m34.518s
sys	0m6.800s

Example programs follow (I think implementations of 
blocking queues are similar):
// java
import java.util.concurrent.*;
import java.util.Random;

class Vars{
public final static int nitems = 100000000;
public final static Random rnd = new Random(12345);
public final static int size = 10000;
}

class Producer implements Runnable {
   private final BlockingQueue<Integer> queue;
   Producer(BlockingQueue<Integer> q) { queue = q; }
   public void run() {
     try {
       int i = Vars.nitems;
       while(i-- > 0) { queue.put(produce(i)); }
     } catch (InterruptedException ex) 
     {
       
     }
   }
   Integer produce(int i) { return new Integer(i); }
 }

 class Consumer implements Runnable {
   private final BlockingQueue<Integer> queue;
   Consumer(BlockingQueue<Integer> q) 
   { 
    queue = q; 
   }
   public void run() {
     try {
       Integer[] arr = new Integer[10000];
       int i = Vars.nitems;
       while(i-- > 0) { consume(queue.take(),arr); }
     } catch (InterruptedException ex) 
     {
     }
   }
   void consume(Integer x, Integer[] arr) 
   {
    arr[Vars.rnd.nextInt(Vars.size)] = x;
   }
 }

 public class consprod {
   public static void main(String [] args) {
   try{
     BlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(100000);
     Producer p = new Producer(q);
     Consumer c = new Consumer(q);
     new Thread(p).start();
     new Thread(c).start();
     } catch(Exception e)
     {
      e.printStackTrace();
     }
   }
 }
//-----------------------------------------
// c++
#include <condition_variable>
#include <mutex>
#include <thread>
#include <deque>
#include <cstdlib>

template <class T>
class BlockingQueue{
public:
    BlockingQueue(unsigned cap):capacity_(cap)
    {
    }
    void put(T t)
    {
        std::unique_lock<std::mutex> lock(m_);
        while(queue_.size() >= capacity_)c_full_.wait(lock);
        queue_.push_back(std::move(t));
        c_empty_.notify_one();
    }
    T take()
    {
        std::unique_lock<std::mutex> lock(m_);
        while(queue_.empty())c_empty_.wait(lock);
        T tmp = std::move(queue_.front());
        queue_.pop_front();
        c_full_.notify_one();
        return std::move(tmp);
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(m_);
        return queue_.empty();
    }
private:
    std::mutex m_;
    std::condition_variable c_empty_,c_full_;
    std::deque<T> queue_;
    unsigned capacity_;
};
 
int main()
{
    BlockingQueue<std::unique_ptr<int>> produced(100000);
    const int nitems = 100000000;
    std::srand(12345);
    

    std::function<void()> f_prod = [&]() {
        int i = nitems;
        while(i-- > 0){ 
            produced.put(std::unique_ptr<int>(new int(i)));
        }   
    };
    
    std::thread producer1(f_prod); 

    std::function<void()> f_cons = [&]() {
        const int size = 10000;
        std::unique_ptr<int> arr[size];
        int i = nitems;
        while(i-- > 0)
        {
            arr[std::rand()%size] = produced.take();
        }
    };
    
    std::thread consumer1(f_cons); 

    producer1.join();
    consumer1.join();
}
// --------------------------------------

[toc] | [next] | [standalone]


#961

FromJoshua Maurice <joshuamaurice@gmail.com>
Date2012-07-22 15:42 -0700
Message-ID<ba6e5833-f9a2-428e-815b-aad0ebdf3c55@s6g2000pbi.googlegroups.com>
In reply to#956
On Jul 22, 2:59 pm, Melzzzzz <m...@zzzzz.com> wrote:
> I have played little bit with new C++11 features and compared,
> java performance to c++.
> Actually this was meant to be GC vs RAII memory management,
> but boiled down to speed of BlockingQueue class in java,
> and mine in c++.
> It seems that java implementation is so much more efficient
> but I don't know why. I even tried c++ without dynamic
> memory management (except queue itself) and that is *even slower*.
> Must be some quirks with a queue ;)
>
> These are timings:
> (java openjdk 1.7)
> bmaxa@maxa:~/examples$ time java consprod
>
> real    0m13.411s
> user    0m19.853s
> sys     0m0.960s
>
> (c++ gcc 4.6.3)
> bmaxa@maxa:~/examples$ time ./consprod
>
> real    0m28.726s
> user    0m34.518s
> sys     0m6.800s
>
> Example programs follow (I think implementations of
> blocking queues are similar):
> // java
> import java.util.concurrent.*;
> import java.util.Random;
>
> class Vars{
> public final static int nitems = 100000000;
> public final static Random rnd = new Random(12345);
> public final static int size = 10000;
>
> }
>
> class Producer implements Runnable {
>    private final BlockingQueue<Integer> queue;
>    Producer(BlockingQueue<Integer> q) { queue = q; }
>    public void run() {
>      try {
>        int i = Vars.nitems;
>        while(i-- > 0) { queue.put(produce(i)); }
>      } catch (InterruptedException ex)
>      {
>
>      }
>    }
>    Integer produce(int i) { return new Integer(i); }
>  }
>
>  class Consumer implements Runnable {
>    private final BlockingQueue<Integer> queue;
>    Consumer(BlockingQueue<Integer> q)
>    {
>     queue = q;
>    }
>    public void run() {
>      try {
>        Integer[] arr = new Integer[10000];
>        int i = Vars.nitems;
>        while(i-- > 0) { consume(queue.take(),arr); }
>      } catch (InterruptedException ex)
>      {
>      }
>    }
>    void consume(Integer x, Integer[] arr)
>    {
>     arr[Vars.rnd.nextInt(Vars.size)] = x;
>    }
>  }
>
>  public class consprod {
>    public static void main(String [] args) {
>    try{
>      BlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(100000);
>      Producer p = new Producer(q);
>      Consumer c = new Consumer(q);
>      new Thread(p).start();
>      new Thread(c).start();
>      } catch(Exception e)
>      {
>       e.printStackTrace();
>      }
>    }
>  }
> //-----------------------------------------
> // c++
> #include <condition_variable>
> #include <mutex>
> #include <thread>
> #include <deque>
> #include <cstdlib>
>
> template <class T>
> class BlockingQueue{
> public:
>     BlockingQueue(unsigned cap):capacity_(cap)
>     {
>     }
>     void put(T t)
>     {
>         std::unique_lock<std::mutex> lock(m_);
>         while(queue_.size() >= capacity_)c_full_.wait(lock);
>         queue_.push_back(std::move(t));
>         c_empty_.notify_one();
>     }
>     T take()
>     {
>         std::unique_lock<std::mutex> lock(m_);
>         while(queue_.empty())c_empty_.wait(lock);
>         T tmp = std::move(queue_.front());
>         queue_.pop_front();
>         c_full_.notify_one();
>         return std::move(tmp);
>     }
>     bool empty()
>     {
>         std::unique_lock<std::mutex> lock(m_);
>         return queue_.empty();
>     }
> private:
>     std::mutex m_;
>     std::condition_variable c_empty_,c_full_;
>     std::deque<T> queue_;
>     unsigned capacity_;
>
> };
>
> int main()
> {
>     BlockingQueue<std::unique_ptr<int>> produced(100000);
>     const int nitems = 100000000;
>     std::srand(12345);
>
>     std::function<void()> f_prod = [&]() {
>         int i = nitems;
>         while(i-- > 0){
>             produced.put(std::unique_ptr<int>(new int(i)));
>         }
>     };
>
>     std::thread producer1(f_prod);
>
>     std::function<void()> f_cons = [&]() {
>         const int size = 10000;
>         std::unique_ptr<int> arr[size];
>         int i = nitems;
>         while(i-- > 0)
>         {
>             arr[std::rand()%size] = produced.take();
>         }
>     };
>
>     std::thread consumer1(f_cons);
>
>     producer1.join();
>     consumer1.join();}


What g++ optimization options did you use? If you didn't compile with
proper optimization flags (e.g. at least -O2), then the numbers you
have are meaningless.

Also, you might be testing the difference between std::rand() and
Java's Random.

Also, why did you use dynamic allocation in the C++ code for an int?
If your original goal was to compare the Java way to the C++ way, you
are not doing it right. This is especially troubling after you
apparently went through the effort to make the queue support move-only
types. (Not sure. I'm still new to move-semantics.)

Also,
>         std::unique_ptr<int> arr[size];
...
>             arr[std::rand()%size] = produced.take();
I wonder if it's possible to optimize that into a simple assignment,
or whether there will be a call to delete() - or at least a branch to
test if the internal member pointer is null.

Just off the top of my head.

[toc] | [prev] | [next] | [standalone]


#962

FromMelzzzzz <mel@zzzzz.com>
Date2012-07-23 01:28 +0200
Message-ID<jui2a3$a3a$2@news.albasani.net>
In reply to#961
On Sun, 22 Jul 2012 15:42:55 -0700 (PDT)
Joshua Maurice <joshuamaurice@gmail.com> wrote:

> 
> 
> What g++ optimization options did you use? If you didn't compile with
> proper optimization flags (e.g. at least -O2), then the numbers you
> have are meaningless.

I have used -O2.

> 
> Also, you might be testing the difference between std::rand() and
> Java's Random.

Possibly.

> 
> Also, why did you use dynamic allocation in the C++ code for an int?

Actually with non dynamical allocation seems to be slower ;)

> If your original goal was to compare the Java way to the C++ way, you
> are not doing it right. This is especially troubling after you
> apparently went through the effort to make the queue support move-only
> types. (Not sure. I'm still new to move-semantics.)

That was necessary in order to put unique_ptr in queue.

> 
> Also,
> >         std::unique_ptr<int> arr[size];
> ...
> >             arr[std::rand()%size] = produced.take();
> I wonder if it's possible to optimize that into a simple assignment,
> or whether there will be a call to delete() - or at least a branch to
> test if the internal member pointer is null.

I have removed that statement , removed called to rand, left 
only take, made int's non dynamic
and I got following time:

bmaxa@maxa:~/examples$ g++ -Wall -O2 -pthread -std=c++0x consprod1.cpp -o consprod1
consprod1.cpp: In lambda function:
consprod1.cpp:58:19: warning: unused variable ‘size’ [-Wunused-variable]
bmaxa@maxa:~/examples$ time ./consprod1

real	0m23.335s
user	0m22.177s
sys	0m16.417s

with java, I have removed rand too...
bmaxa@maxa:~/examples$ javac consprod.java
bmaxa@maxa:~/examples$ time java consprod

real	0m12.491s
user	0m21.001s
sys	0m0.524s

Still twice as fast as c++.
What it looks like, is that c++ spends much more time in syscalls
(futex) than java and that explains big difference.


[toc] | [prev] | [next] | [standalone]


#963

FromLuca Risolia <luca.risolia@studio.unibo.it>
Date2012-07-23 02:03 +0200
Message-ID<jui4cg$d5a$1@speranza.aioe.org>
In reply to#962
On 23/07/2012 01:28, Melzzzzz wrote:
> Still twice as fast as c++.
> What it looks like, is that c++ spends much more time in syscalls
> (futex) than java and that explains big difference.

A lock-free "BlockingQueue" would probably be more efficient.
You can also try to implement your Queue with std::array instead of 
std::deque and see how it performs.

In the C++ version change:

	while (queue_.size() >= capacity_)c_full_.wait(lock);
with
	c_full_.wait(lock, [this]{return queue_.size() < capacity_;});

and
	while (queue_.empty())c_empty_.wait(lock);
with
	c_empty_.wait(lock, [this]{return !queue_.empty();});

Also use a std::lock_guard in empty() instead of std::unique_lock. The 
former is faster.

(On a side note, the C++ version you have given does not have the best 
possible interface for a thread-safe queue)

[toc] | [prev] | [next] | [standalone]


#966

FromMelzzzzz <mel@zzzzz.com>
Date2012-07-23 12:17 +0200
Message-ID<juj8ca$ges$1@news.albasani.net>
In reply to#963
On Mon, 23 Jul 2012 02:03:28 +0200
Luca Risolia <luca.risolia@studio.unibo.it> wrote:

> On 23/07/2012 01:28, Melzzzzz wrote:
> > Still twice as fast as c++.
> > What it looks like, is that c++ spends much more time in syscalls
> > (futex) than java and that explains big difference.
> 
> A lock-free "BlockingQueue" would probably be more efficient.
> You can also try to implement your Queue with std::array instead of 
> std::deque and see how it performs.

I will try. Seems that java ArrayBlockingQueue is more efficient
because if that. 

> 
> In the C++ version change:
> 
> 	while (queue_.size() >= capacity_)c_full_.wait(lock);
> with
> 	c_full_.wait(lock, [this]{return queue_.size() < capacity_;});
> 
> and
> 	while (queue_.empty())c_empty_.wait(lock);
> with
> 	c_empty_.wait(lock, [this]{return !queue_.empty();});
> 
> Also use a std::lock_guard in empty() instead of std::unique_lock.
> The former is faster.

Yes, I got speed up of few seconds, thanks.

> 
> (On a side note, the C++ version you have given does not have the
> best possible interface for a thread-safe queue)

I have just copied Java BlockingQueue interface, which is of course
suited for Java. Don't know actually how ideal interface would look
like.


> 

[toc] | [prev] | [next] | [standalone]


#971

FromLuca Risolia <luca.risolia@studio.unibo.it>
Date2012-07-24 00:33 +0200
Message-ID<jukjf6$d5a$2@speranza.aioe.org>
In reply to#966
On 23/07/2012 12:17, Melzzzzz wrote:
>> (On a side note, the C++ version you have given does not have the
>> best possible interface for a thread-safe queue)
>
> I have just copied Java BlockingQueue interface, which is of course
> suited for Java. Don't know actually how ideal interface would look
> like.

In C++11 the ideal interface for a generic thread-safe and 
exception-safe queue should at least provide std::shared_ptr<T> take() 
and/or void take(T&). T take() is limited to those types having no-throw 
copy constructors or move-constructors. This also means that you should 
check the type at compile-time by using type traits. However, in your 
test case you used move-semantic everywhere, so that is not problem. 
Also note that the term "pop" instead of "take" is more in the spirit of 
C++.

[toc] | [prev] | [next] | [standalone]


#965

FromJuha Nieminen <nospam@thanks.invalid>
Date2012-07-23 06:37 +0000
Message-ID<juiret$jqb$1@speranza.aioe.org>
In reply to#956
In comp.lang.c++ Melzzzzz <mel@zzzzz.com> wrote:
> I even tried c++ without dynamic 
> memory management (except queue itself) and that is *even slower*.

If two C++ programs are otherwise identical, except that one uses
'new int' and the other just uses the ints by value, and the former
is faster than the latter, then there's something horribly wrong in
the way you are measuring, or something else. I don't think it's
physically possible for 'new int' to be faster than using ints by
value under any possible circumstance, even if we assumed a highly
optimized version of 'new' that does magic under the hood to be 10
times faster than the regular 'new'.

>        std::unique_lock<std::mutex> lock(m_);

>            produced.put(std::unique_ptr<int>(new int(i)));

>            arr[std::rand()%size] = produced.take();

I don't know what's the cause of the slowness in your program, but
I quoted above the three possible culprits I would first investigate
(which happen to be in order of likeliness).

Locks are slow. They are probably slower than 'new'.

'new' is slow, especially when compared to java's.

std::rand() is slow, although probably does not account for that much
slowness, but could be a minor contributing factor.

[toc] | [prev] | [next] | [standalone]


#967

FromMelzzzzz <mel@zzzzz.com>
Date2012-07-23 12:33 +0200
Message-ID<juj99h$ges$2@news.albasani.net>
In reply to#965
On Mon, 23 Jul 2012 06:37:17 +0000 (UTC)
Juha Nieminen <nospam@thanks.invalid> wrote:

> In comp.lang.c++ Melzzzzz <mel@zzzzz.com> wrote:
> > I even tried c++ without dynamic 
> > memory management (except queue itself) and that is *even slower*.
> 
> If two C++ programs are otherwise identical, except that one uses
> 'new int' and the other just uses the ints by value, and the former
> is faster than the latter, then there's something horribly wrong in
> the way you are measuring, or something else.

I think that pressure on condition variable is greater with non
dynamic allocation, therefore it is slower.
If I reduce queue capacity on eg 1000 (more pressure on condition
variable) than it is much slower than with capacity of 100000.


 I don't think it's
> physically possible for 'new int' to be faster than using ints by
> value under any possible circumstance, even if we assumed a highly
> optimized version of 'new' that does magic under the hood to be 10
> times faster than the regular 'new'.

That's because new is very fast and don;t take much time of program
in this example.

> 
> >        std::unique_lock<std::mutex> lock(m_);
> 
> >            produced.put(std::unique_ptr<int>(new int(i)));
> 
> >            arr[std::rand()%size] = produced.take();
> 
> I don't know what's the cause of the slowness in your program, but
> I quoted above the three possible culprits I would first investigate
> (which happen to be in order of likeliness).
> 
> Locks are slow. They are probably slower than 'new'.

I think that locks are not that slow in comparison to
condition variables. I have feeling that somehow
Java has less pressure on condition variables
in it's queue implementation.

> 
> 'new' is slow, especially when compared to java's.

In this example new takes very little time.

> 
> std::rand() is slow, although probably does not account for that much
> slowness, but could be a minor contributing factor.

This also takes little time.
Here is difference between new and rand and without.
Both versions are optimized now as per Luca's suggestion.


(with new and rand array assignment)
bmaxa@maxa:~/examples$ time ./consprod

real	0m25.127s
user	0m32.202s
sys	0m5.540s
(without)
bmaxa@maxa:~/examples$ time ./consprod1

real	0m23.883s
user	0m23.009s
sys	0m17.153s

Java:
(without rand and array assignment)
bmaxa@maxa:~/examples$ time java consprod

real	0m12.825s
user	0m21.833s
sys	0m0.592s

(with rand and array assignment)
bmaxa@maxa:~/examples$ time java consprod

real	0m15.571s
user	0m25.634s
sys	0m1.168s

Java pays higher price for random assignments, I think.

[toc] | [prev] | [next] | [standalone]


#968

FromJuha Nieminen <nospam@thanks.invalid>
Date2012-07-23 11:46 +0000
Message-ID<jujdit$uv9$1@speranza.aioe.org>
In reply to#967
In comp.lang.c++ Melzzzzz <mel@zzzzz.com> wrote:
> I think that pressure on condition variable is greater with non
> dynamic allocation, therefore it is slower.

That sentence doesn't make any kind of sense.

> I don't think it's
>> physically possible for 'new int' to be faster than using ints by
>> value under any possible circumstance, even if we assumed a highly
>> optimized version of 'new' that does magic under the hood to be 10
>> times faster than the regular 'new'.
> 
> That's because new is very fast and don;t take much time of program
> in this example.

'new' is a very slow operation in C++, and even if it weren't, it just
can't be faster than using an integer by value. It's physically impossible.
(The pointer that points to the allocated int is also an integral value at
the low level, so by allocating something dynamically you are only adding
to the amount of values being handled, and the overall complexity of the
program.)

Can you give any scenario where handling pointers would be faster than
handling ints?

>> 'new' is slow, especially when compared to java's.
> 
> In this example new takes very little time.

You are executing tens of thousands of 'new's. That is slow.

[toc] | [prev] | [next] | [standalone]


#969

FromMelzzzzz <mel@zzzzz.com>
Date2012-07-23 15:33 +0200
Message-ID<jujjqr$b3o$1@news.albasani.net>
In reply to#968
On Mon, 23 Jul 2012 11:46:37 +0000 (UTC)
Juha Nieminen <nospam@thanks.invalid> wrote:

> In comp.lang.c++ Melzzzzz <mel@zzzzz.com> wrote:
> > I think that pressure on condition variable is greater with non
> > dynamic allocation, therefore it is slower.
> 
> That sentence doesn't make any kind of sense.
I has sense in this context. I will provide example.

> 
> > I don't think it's
> >> physically possible for 'new int' to be faster than using ints by
> >> value under any possible circumstance, even if we assumed a highly
> >> optimized version of 'new' that does magic under the hood to be 10
> >> times faster than the regular 'new'.
> > 
> > That's because new is very fast and don;t take much time of program
> > in this example.
> 
> 'new' is a very slow operation in C++, and even if it weren't, it just
> can't be faster than using an integer by value. It's physically
> impossible. (The pointer that points to the allocated int is also an
> integral value at the low level, so by allocating something
> dynamically you are only adding to the amount of values being
> handled, and the overall complexity of the program.)

Yes, but overall complexity can be masked with something else like in
this case.

> 
> Can you give any scenario where handling pointers would be faster than
> handling ints?

This scenario is exactly example. My queue is limited to capacity
nodes. When capacity is reached, producer waits on condition
variable until it is emptied.
In first example queue was 100000 nodes so slow down with plain ints
isn't just that noticabe.
But if I put just 1000 nodes here is what happens:
(dynamic ints , unique_ptr, random function and array assignement)
bmaxa@maxa:~/examples$ time ./consprod

real	0m23.523s
user	0m32.390s
sys	0m10.581s

but look at this:
(no dynamic ints, no random function, no assignment)
:
bmaxa@maxa:~/examples$ time ./consprod1

real	0m35.654s
user	0m30.882s
sys	0m34.202s

it's more than 10 seconds slower! just ints!


> 
> >> 'new' is slow, especially when compared to java's.
> > 
> > In this example new takes very little time.
> 
> You are executing tens of thousands of 'new's. That is slow.

It is slow but waiting on condition is much slower so,
faster producer increases number of times condition waits,
so actually that makes program slower...


[toc] | [prev] | [next] | [standalone]


#970

FromDombo <dombo@disposable.invalid>
Date2012-07-23 22:57 +0200
Message-ID<500dba64$0$14708$5fc3050@news.tiscali.nl>
In reply to#956
Op 22-Jul-12 23:59, Melzzzzz schreef:
> I have played little bit with new C++11 features and compared,
> java performance to c++.
> Actually this was meant to be GC vs RAII memory management,
> but boiled down to speed of BlockingQueue class in java,
> and mine in c++.
> It seems that java implementation is so much more efficient
> but I don't know why. I even tried c++ without dynamic
> memory management (except queue itself) and that is *even slower*.
> Must be some quirks with a queue ;)
>
> These are timings:
> (java openjdk 1.7)
> bmaxa@maxa:~/examples$ time java consprod
>
> real	0m13.411s
> user	0m19.853s
> sys	0m0.960s
>
> (c++ gcc 4.6.3)
> bmaxa@maxa:~/examples$ time ./consprod
>
> real	0m28.726s
> user	0m34.518s
> sys	0m6.800s
>
> Example programs follow (I think implementations of
> blocking queues are similar):
> // java
> import java.util.concurrent.*;
> import java.util.Random;
>
> class Vars{
> public final static int nitems = 100000000;
> public final static Random rnd = new Random(12345);
> public final static int size = 10000;
> }
>
> class Producer implements Runnable {
>     private final BlockingQueue<Integer> queue;
>     Producer(BlockingQueue<Integer> q) { queue = q; }
>     public void run() {
>       try {
>         int i = Vars.nitems;
>         while(i-- > 0) { queue.put(produce(i)); }
>       } catch (InterruptedException ex)
>       {
>
>       }
>     }
>     Integer produce(int i) { return new Integer(i); }
>   }
>
>   class Consumer implements Runnable {
>     private final BlockingQueue<Integer> queue;
>     Consumer(BlockingQueue<Integer> q)
>     {
>      queue = q;
>     }
>     public void run() {
>       try {
>         Integer[] arr = new Integer[10000];
>         int i = Vars.nitems;
>         while(i-- > 0) { consume(queue.take(),arr); }
>       } catch (InterruptedException ex)
>       {
>       }
>     }
>     void consume(Integer x, Integer[] arr)
>     {
>      arr[Vars.rnd.nextInt(Vars.size)] = x;
>     }
>   }
>
>   public class consprod {
>     public static void main(String [] args) {
>     try{
>       BlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(100000);
>       Producer p = new Producer(q);
>       Consumer c = new Consumer(q);
>       new Thread(p).start();
>       new Thread(c).start();
>       } catch(Exception e)
>       {
>        e.printStackTrace();
>       }
>     }
>   }
> //-----------------------------------------
> // c++
> #include <condition_variable>
> #include <mutex>
> #include <thread>
> #include <deque>
> #include <cstdlib>
>
> template <class T>
> class BlockingQueue{
> public:
>      BlockingQueue(unsigned cap):capacity_(cap)
>      {
>      }
>      void put(T t)
>      {
>          std::unique_lock<std::mutex> lock(m_);
>          while(queue_.size() >= capacity_)c_full_.wait(lock);
>          queue_.push_back(std::move(t));
>          c_empty_.notify_one();
>      }
>      T take()
>      {
>          std::unique_lock<std::mutex> lock(m_);
>          while(queue_.empty())c_empty_.wait(lock);
>          T tmp = std::move(queue_.front());
>          queue_.pop_front();
>          c_full_.notify_one();
>          return std::move(tmp);
>      }
>      bool empty()
>      {
>          std::unique_lock<std::mutex> lock(m_);
>          return queue_.empty();
>      }
> private:
>      std::mutex m_;
>      std::condition_variable c_empty_,c_full_;
>      std::deque<T> queue_;
>      unsigned capacity_;
> };
>
> int main()
> {
>      BlockingQueue<std::unique_ptr<int>> produced(100000);
>      const int nitems = 100000000;
>      std::srand(12345);
>
>
>      std::function<void()> f_prod = [&]() {
>          int i = nitems;
>          while(i-- > 0){
>              produced.put(std::unique_ptr<int>(new int(i)));
>          }
>      };
>
>      std::thread producer1(f_prod);
>
>      std::function<void()> f_cons = [&]() {
>          const int size = 10000;
>          std::unique_ptr<int> arr[size];
>          int i = nitems;
>          while(i-- > 0)
>          {
>              arr[std::rand()%size] = produced.take();
>          }
>      };
>
>      std::thread consumer1(f_cons);
>
>      producer1.join();
>      consumer1.join();
> }
> // --------------------------------------

In the C++ version at the end of the main function it waits for the 
threads to complete, in the Java version the threads are not joined at 
the end of the main function. I don't know if Java keeps the process 
alive until all threads have terminated, but if not it would explain the 
difference.

I'm a bit surpirsed that the C++ version compiles without complaints on 
your compiler because there is no return statement in the main() function.

[toc] | [prev] | [next] | [standalone]


#973

FromJoshua Maurice <joshuamaurice@gmail.com>
Date2012-07-23 21:54 -0700
Message-ID<a511940f-bb94-4aa5-a055-8a371065b550@oo8g2000pbc.googlegroups.com>
In reply to#970
On Jul 23, 1:57 pm, Dombo <do...@disposable.invalid> wrote:
> I'm a bit surpirsed that the C++ version compiles without complaints on
> your compiler because there is no return statement in the main() function.

It's an exception in C and C++ for main only. Running off the end of
main is allowed, and is equivalent to return 0, itself equivalent to
exit(0), IIRC.

[toc] | [prev] | [next] | [standalone]


#975

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2012-07-24 12:50 +0000
Message-ID<slrnk0t6g8.rk7.grahn+nntp@frailea.sa.invalid>
In reply to#973
["Followup-To:" header set to comp.lang.c++.]
On Tue, 2012-07-24, Joshua Maurice wrote:
> On Jul 23, 1:57 pm, Dombo <do...@disposable.invalid> wrote:
>> I'm a bit surpirsed that the C++ version compiles without complaints on
>> your compiler because there is no return statement in the main() function.
>
> It's an exception in C and C++ for main only. Running off the end of
> main is allowed, and is equivalent to return 0,

Sounds right.

> itself equivalent to exit(0), IIRC.

But this does not.  Calling exit() prevent objects from being
destroyed properly.

Unless you mean "equivalent to returning from main (thereby destroying
a lot of objects) and then calling exit(0)".

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#998

FromRicardo Nabinger Sanchez <rnsanchez@wait4.org>
Date2012-08-14 22:48 +0000
Message-ID<k0ekjp$suv$1@dont-email.me>
In reply to#956
On Sun, 22 Jul 2012 23:59:20 +0200, Melzzzzz wrote:

> I have played little bit with new C++11 features and compared,
> java performance to c++.
> Actually this was meant to be GC vs RAII memory management,
> but boiled down to speed of BlockingQueue class in java,
> and mine in c++.
> It seems that java implementation is so much more efficient
> but I don't know why. I even tried c++ without dynamic 
> memory management (except queue itself) and that is *even slower*.
> Must be some quirks with a queue

As others mentioned, most of your CPU time is being wasted on memory
management and synchronization.  I've compiled your source with GCC-4.7.1,
-O2, and ran through perf (from Linux kernel).  The output follows.

Cheers!

# captured on: Tue Aug 14 19:22:47 2012
# os release : 3.2.23
# perf version : 3.2.13
# arch : x86_64
# nrcpus online : 4
# nrcpus avail : 4
# cpudesc : Intel(R) Core(TM) i3 CPU 540 @ 3.07GHz
# cpuid : GenuineIntel,6,37,5
# total memory : 3897640 kB

# Overhead  Command        Shared Object  Symbol
    10.90%     lala  [kernel.kallsyms]    [k] _raw_spin_lock
    10.34%     lala  libc-2.15.so         [.] _int_free
     8.84%     lala  libpthread-2.15.so   [.] pthread_cond_signal@@GLIBC_2.3.2
     7.53%     lala  libc-2.15.so         [.] free
     7.28%     lala  libc-2.15.so         [.] _int_malloc
     6.89%     lala  libc-2.15.so         [.] malloc
     6.49%     lala  libpthread-2.15.so   [.] pthread_mutex_lock
     4.85%     lala  libpthread-2.15.so   [.] __pthread_mutex_unlock_usercnt
     3.20%     lala  libpthread-2.15.so   [.] __lll_lock_wait
     3.18%     lala  [kernel.kallsyms]    [k] hash_futex
     2.49%     lala  [kernel.kallsyms]    [k] system_call
     2.40%     lala  [kernel.kallsyms]    [k] futex_wait_setup
     2.02%     lala  [kernel.kallsyms]    [k] copy_user_generic_string
     1.99%     lala  libc-2.15.so         [.] __random
     1.81%     lala  libpthread-2.15.so   [.] __lll_unlock_wake
     1.76%     lala  lala                 [.] main::{lambda()#2}::operator()() const
     1.60%     lala  lala                 [.] std::_Function_handler<void (), main::{lambda()#1}>::_M_invoke(std::_Any_data const&)
     1.43%     lala  [kernel.kallsyms]    [k] futex_wake
     1.39%     lala  [kernel.kallsyms]    [k] do_futex
     1.32%     lala  [kernel.kallsyms]    [k] system_call_after_swapgs
     1.21%     lala  [kernel.kallsyms]    [k] sys_futex
     1.21%     lala  [kernel.kallsyms]    [k] get_futex_key
     1.15%     lala  [kernel.kallsyms]    [k] try_to_wake_up
     1.03%     lala  [kernel.kallsyms]    [k] futex_wait
     0.58%     lala  [kernel.kallsyms]    [k] sysret_check
     0.46%     lala  libpthread-2.15.so   [.] pthread_cond_wait@@GLIBC_2.3.2
     0.40%     lala  [kernel.kallsyms]    [k] get_futex_value_locked
     0.40%     lala  libstdc++.so.6.0.17  [.] operator new(unsigned long)
     0.33%     lala  [kernel.kallsyms]    [k] select_task_rq_fair
     0.31%     lala  libc-2.15.so         [.] __random_r
     0.29%     lala  libpthread-2.15.so   [.] _L_lock_965
     0.29%     lala  [kernel.kallsyms]    [k] __schedule
     0.24%     lala  [kernel.kallsyms]    [k] drop_futex_key_refs.isra.12
     0.23%     lala  libstdc++.so.6.0.17  [.] std::condition_variable::notify_one()
     0.20%     lala  lala                 [.] pthread_mutex_unlock@plt
     0.20%     lala  [kernel.kallsyms]    [k] get_futex_key_refs.isra.10
     0.16%     lala  [kernel.kallsyms]    [k] system_call_fastpath
     0.16%     lala  libpthread-2.15.so   [.] _L_unlock_567
     0.15%     lala  [kernel.kallsyms]    [k] task_waking_fair
     0.14%     lala  lala                 [.] _ZNSt18condition_variable10notify_oneEv@plt
     0.13%     lala  libstdc++.so.6.0.17  [.] malloc@plt
     0.13%     lala  [kernel.kallsyms]    [k] wake_futex
     0.12%     lala  lala                 [.] pthread_mutex_lock@plt
     0.11%     lala  [kernel.kallsyms]    [k] intel_pmu_disable_all
     0.11%     lala  libstdc++.so.6.0.17  [.] pthread_cond_signal@plt
     0.11%     lala  libpthread-2.15.so   [.] __pthread_mutex_cond_lock
     0.10%     lala  libc-2.15.so         [.] rand
     0.10%     lala  libpthread-2.15.so   [.] pthread_mutex_unlock
     0.10%     lala  lala                 [.] rand@plt
     0.09%     lala  [kernel.kallsyms]    [k] default_send_IPI_mask_sequence_phys
     0.08%     lala  lala                 [.] _Znwm@plt
     0.07%     lala  [kernel.kallsyms]    [k] _raw_spin_lock_irqsave
     0.07%     lala  [kernel.kallsyms]    [k] finish_task_switch
     0.07%     lala  [kernel.kallsyms]    [k] native_sched_clock
     0.07%     lala  [kernel.kallsyms]    [k] futex_wait_queue_me
     0.06%     lala  [kernel.kallsyms]    [k] intel_pmu_enable_all
     0.06%     lala  [kernel.kallsyms]    [k] cpuacct_charge
     0.05%     lala  libc-2.15.so         [.] __lll_lock_wait_private
     0.05%     lala  [kernel.kallsyms]    [k] plist_add
     0.05%     lala  lala                 [.] _ZdlPv@plt
     0.05%     lala  [kernel.kallsyms]    [k] sched_clock_cpu
     0.05%     lala  [kernel.kallsyms]    [k] hrtick_update
     0.05%     lala  [kernel.kallsyms]    [k] update_curr
     0.04%     lala  [kernel.kallsyms]    [k] pick_next_task_stop
     0.04%     lala  [kernel.kallsyms]    [k] hrtick_start_fair
     0.04%     lala  libpthread-2.15.so   [.] __pthread_disable_asynccancel
     0.04%     lala  libstdc++.so.6.0.17  [.] operator delete(void*)
     0.04%     lala  [kernel.kallsyms]    [k] dequeue_task_fair
     0.04%     lala  libstdc++.so.6.0.17  [.] free@plt
     0.04%     lala  [kernel.kallsyms]    [k] irq_exit
     0.03%     lala  [kernel.kallsyms]    [k] pick_next_task_fair
     0.03%     lala  [kernel.kallsyms]    [k] local_clock
     0.03%     lala  lala                 [.] void std::deque<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > >::_M_push_back_aux<std::unique_ptr<int, std::default_delete<int> > >(std::unique_ptr<int, std::default_delete<int> >&&)
     0.03%     lala  [kernel.kallsyms]    [k] rcu_note_context_switch
     0.03%     lala  [kernel.kallsyms]    [k] _raw_spin_lock_irq
     0.03%     lala  [kernel.kallsyms]    [k] find_next_bit
     0.03%     lala  [kernel.kallsyms]    [k] dequeue_entity
     0.03%     lala  [kernel.kallsyms]    [k] update_cfs_shares
     0.03%     lala  [kernel.kallsyms]    [k] pick_next_task_rt
     0.02%     lala  [kernel.kallsyms]    [k] account_entity_dequeue
     0.02%     lala  [kernel.kallsyms]    [k] put_prev_task_fair
     0.02%     lala  [kernel.kallsyms]    [k] __perf_event_task_sched_out
     0.02%     lala  [kernel.kallsyms]    [k] dequeue_task
     0.02%     lala  libpthread-2.15.so   [.] __pthread_enable_asynccancel
     0.02%     lala  libc-2.15.so         [.] __lll_unlock_wake_private
     0.02%     lala  [kernel.kallsyms]    [k] update_min_vruntime
     0.02%     lala  [kernel.kallsyms]    [k] update_rq_clock
     0.02%     lala  [kernel.kallsyms]    [k] update_context_time.isra.48
     0.02%     lala  [kernel.kallsyms]    [k] perf_pmu_disable
     0.02%     lala  [kernel.kallsyms]    [k] apic_timer_interrupt
     0.02%     lala  [kernel.kallsyms]    [k] schedule
     0.02%     lala  libstdc++.so.6.0.17  [.] std::condition_variable::wait(std::unique_lock<std::mutex>&)
     0.02%     lala  [kernel.kallsyms]    [k] free_hot_cold_page
     0.02%     lala  [kernel.kallsyms]    [k] pick_next_task_idle
     0.02%     lala  [kernel.kallsyms]    [k] do_softirq
     0.01%     lala  [kernel.kallsyms]    [k] ktime_get
     0.01%     lala  [kernel.kallsyms]    [k] update_cfs_load
     0.01%     lala  [kernel.kallsyms]    [k] perf_event_context_sched_in
     0.01%     lala  [kernel.kallsyms]    [k] __perf_event_task_sched_in
     0.01%     lala  [kernel.kallsyms]    [k] perf_pmu_rotate_start.isra.44
     0.01%     lala  [kernel.kallsyms]    [k] _raw_spin_unlock_irqrestore
     0.01%     lala  [kernel.kallsyms]    [k] hrtimer_interrupt
     0.01%     lala  libc-2.15.so         [.] __memmove_ssse3_back
     0.01%     lala  [kernel.kallsyms]    [k] ctx_sched_out
     0.01%     lala  [kernel.kallsyms]    [k] physflat_send_IPI_mask
     0.01%     lala  [kernel.kallsyms]    [k] intel_pmu_nhm_enable_all
     0.01%     lala  [kernel.kallsyms]    [k] do_timer
     0.01%     lala  [kernel.kallsyms]    [k] smp_apic_timer_interrupt
     0.01%     lala  [kernel.kallsyms]    [k] x86_pmu_disable
     0.01%     lala  [kernel.kallsyms]    [k] plist_del
     0.01%     lala  [kernel.kallsyms]    [k] deactivate_task
     0.01%     lala  [kernel.kallsyms]    [k] run_timer_softirq
     0.01%     lala  libc-2.15.so         [.] _L_lock_4111
     0.01%     lala  [kernel.kallsyms]    [k] rcu_enter_nohz
     0.01%     lala  [kernel.kallsyms]    [k] __rcu_pending
     0.01%     lala  [kernel.kallsyms]    [k] perf_event_task_sched_out
     0.01%     lala  [kernel.kallsyms]    [k] native_smp_send_reschedule
     0.01%     lala  [kernel.kallsyms]    [k] __do_softirq
     0.01%     lala  [kernel.kallsyms]    [k] profile_tick
     0.01%     lala  [kernel.kallsyms]    [k] __run_hrtimer
     0.01%     lala  [kernel.kallsyms]    [k] scheduler_tick
     0.01%     lala  [kernel.kallsyms]    [k] task_tick_fair
     0.01%     lala  [kernel.kallsyms]    [k] tick_sched_timer
     0.01%     lala  [kernel.kallsyms]    [k] perf_event_task_tick
     0.01%     lala  [kernel.kallsyms]    [k] x86_pmu_enable
     0.01%     lala  [kernel.kallsyms]    [k] acct_update_integrals
     0.00%     lala  libc-2.15.so         [.] _L_unlock_12586
     0.00%     lala  libpthread-2.15.so   [.] _L_cond_lock_973
     0.00%     lala  [kernel.kallsyms]    [k] hrtimer_forward
     0.00%     lala  [kernel.kallsyms]    [k] effective_load.isra.70
     0.00%     lala  [kernel.kallsyms]    [k] update_process_times
     0.00%     lala  [kernel.kallsyms]    [k] __unqueue_futex
     0.00%     lala  [kernel.kallsyms]    [k] perf_pmu_enable
     0.00%     lala  [kernel.kallsyms]    [k] clear_buddies
     0.00%     lala  [kernel.kallsyms]    [k] rcu_check_callbacks
     0.00%     lala  lala                 [.] _ZNSt18condition_variable4waitERSt11unique_lockISt5mutexE@plt
     0.00%     lala  [kernel.kallsyms]    [k] update_cpu_load
     0.00%     lala  [kernel.kallsyms]    [k] rcu_process_callbacks
     0.00%     lala  [kernel.kallsyms]    [k] raise_softirq
     0.00%     lala  [kernel.kallsyms]    [k] jiffies_to_timeval
     0.00%     lala  [kernel.kallsyms]    [k] timerqueue_add
     0.00%     lala  [kernel.kallsyms]    [k] ret_from_sys_call
     0.00%     lala  [kernel.kallsyms]    [k] flush_tlb_others_ipi
     0.00%     lala  [kernel.kallsyms]    [k] rb_insert_color
     0.00%     lala  [kernel.kallsyms]    [k] rcu_process_gp_end.isra.27
     0.00%     lala  [kernel.kallsyms]    [k] rcu_exit_nohz
     0.00%     lala  [kernel.kallsyms]    [k] hrtimer_run_queues
     0.00%     lala  libc-2.15.so         [.] _L_lock_12561
     0.00%     lala  [kernel.kallsyms]    [k] idle_cpu
     0.00%     lala  [kernel.kallsyms]    [k] do_lookup
     0.00%     lala  [kernel.kallsyms]    [k] calc_global_load
     0.00%     lala  [kernel.kallsyms]    [k] lookup_page_cgroup
     0.00%     lala  [kernel.kallsyms]    [k] irq_enter
     0.00%     lala  [kernel.kallsyms]    [k] force_quiescent_state
     0.00%     lala  [kernel.kallsyms]    [k] dyntick_save_progress_counter
     0.00%     lala  [kernel.kallsyms]    [k] run_posix_cpu_timers
     0.00%     lala  [kernel.kallsyms]    [k] __math_state_restore
     0.00%     lala  [kernel.kallsyms]    [k] clockevents_program_event
     0.00%     lala  [kernel.kallsyms]    [k] account_process_tick
     0.00%     lala  [kernel.kallsyms]    [k] find_busiest_group
     0.00%     lala  [kernel.kallsyms]    [k] call_softirq
     0.00%     lala  [kernel.kallsyms]    [k] rb_next
     0.00%     lala  [kernel.kallsyms]    [k] wake_up_state
     0.00%     lala  [kernel.kallsyms]    [k] printk_tick
     0.00%     lala  [kernel.kallsyms]    [k] find_vma_prev
     0.00%     lala  [kernel.kallsyms]    [k] __alloc_pages_nodemask
     0.00%     lala  [kernel.kallsyms]    [k] tick_program_event
     0.00%     lala  [kernel.kallsyms]    [k] update_shares
     0.00%     lala  [kernel.kallsyms]    [k] cpuacct_update_stats
     0.00%     lala  [e1000e]             [k] e1000_irq_enable
     0.00%     lala  [kernel.kallsyms]    [k] sched_clock_tick
     0.00%     lala  [kernel.kallsyms]    [k] irq_work_run
     0.00%     lala  [kernel.kallsyms]    [k] clear_page_c
     0.00%     lala  [kernel.kallsyms]    [k] account_user_time
     0.00%     lala  [kernel.kallsyms]    [k] fib_get_table
     0.00%     lala  [kernel.kallsyms]    [k] ehci_work
     0.00%     lala  [kernel.kallsyms]    [k] __current_kernel_time
     0.00%     lala  [kernel.kallsyms]    [k] __rcu_process_callbacks
     0.00%     lala  [kernel.kallsyms]    [k] rb_erase
     0.00%     lala  [kernel.kallsyms]    [k] __percpu_counter_add
     0.00%     lala  [kernel.kallsyms]    [k] update_vsyscall
     0.00%     lala  [kernel.kallsyms]    [k] mem_cgroup_pgfault
     0.00%     lala  libstdc++.so.6.0.17  [.] std::thread::join()
     0.00%     lala  libpthread-2.15.so   [.] __read_nocancel
     0.00%     lala  libpthread-2.15.so   [.] start_thread
     0.00%     lala  [kernel.kallsyms]    [k] __mmdrop


-- 
Ricardo Nabinger Sanchez           http://rnsanchez.wait4.org/
  "Left to themselves, things tend to go from bad to worse."

[toc] | [prev] | [standalone]


Back to top | Article view | comp.programming.threads


csiph-web