Jusqu'à maintenant j'utilisais std::queue
dans mon projet. J'ai mesuré le temps moyen de laquelle une opération spécifique sur cette file d'attente exige.
Les temps ont été mesurés sur 2 machines: Mon local de machine virtuelle Ubuntu et un serveur distant.
À l'aide de std::queue
, la moyenne était à peu près la même sur les deux machines: ~750 microsecondes.
Ensuite, j'ai "upgradé" l' std::queue
de boost::lockfree::spsc_queue
, j'ai donc pu se débarrasser de l'mutex la protection de la file d'attente. Sur mon local VM j'ai pu voir un énorme gain de performance, la moyenne est maintenant de 200 microsecondes. Sur la machine distante cependant, la moyenne est allé jusqu'à 800 microsecondes, ce qui est plus lent qu'il ne l'était avant.
J'ai d'abord pensé que cela pourrait être parce que la machine distante ne pouvait pas soutenir les sans verrouillage de la mise en œuvre:
De le Stimuler.Lockfree page:
Pas tout le matériel prend en charge le même ensemble d'instructions atomiques. Si elle n'est pas disponible dans le matériel, il peut être émulé dans le logiciel à l'aide de gardes. Cependant, cela a l'inconvénient évident de perdre le lock-bien gratuit.
Pour savoir si ces instructions sont pris en charge, boost::lockfree::queue
a une méthode appelée bool is_lock_free(void) const;
.
Toutefois, boost::lockfree::spsc_queue
n'a pas une fonction de ce genre, qui, pour moi, cela implique qu'elle ne repose pas sur le matériel et qui est toujours lockfree - sur n'importe quelle machine.
Quelle pourrait être la raison pour la perte de performance?
Exemple de code (Producteur/Consommateur)
// c++11 compiler and boost library required
#include <iostream>
#include <cstdlib>
#include <chrono>
#include <async>
#include <thread>
/* Using blocking queue:
* #include <mutex>
* #include <queue>
*/
#include <boost/lockfree/spsc_queue.hpp>
boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024>> queue;
/* Using blocking queue:
* std::queue<int> queue;
* std::mutex mutex;
*/
int main()
{
auto producer = std::async(std::launch::async, [queue /*,mutex*/]()
{
// Producing data in a random interval
while(true)
{
/* Using the blocking queue, the mutex must be locked here.
* mutex.lock();
*/
// Push random int (0-9999)
queue.push(std::rand() % 10000);
/* Using the blocking queue, the mutex must be unlocked here.
* mutex.unlock();
*/
// Sleep for random duration (0-999 microseconds)
std::this_thread::sleep_for(std::chrono::microseconds(rand() % 1000));
}
}
auto consumer = std::async(std::launch::async, [queue /*,mutex*/]()
{
// Example operation on the queue.
// Checks if 1234 was generated by the producer, returns if found.
while(true)
{
/* Using the blocking queue, the mutex must be locked here.
* mutex.lock();
*/
int value;
while(queue.pop(value)
{
if(value == 1234)
return;
}
/* Using the blocking queue, the mutex must be unlocked here.
* mutex.unlock();
*/
// Sleep for 100 microseconds
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
}
consumer.get();
std::cout << "1234 was generated!" << std::endl;
return 0;
}