J'ai fait un test pour comparer les performances de la file d'attente concurrente du canal golang et C++ tbb, j'ai installé 8 écrivain et 1 lecteur qui sont dans des threads différents. montre que golang est beaucoup plus rapide que la version C++ (quelle que soit la latence et la vitesse globale d'envoi/recv), est-ce vrai? ou une erreur dans mon code?pourquoi golang canal est beaucoup plus rapide que intel tbb concurrent_queue lors du test avec 8 producteur 1 consommateur
résultat golang, l'unité est microseconde
latence max: 1505, avg: 1073 envoi commencent: 1495593677683232, fin recv: 1495593677901854, temps: 218622
package main
import (
"flag"
"time"
"fmt"
"sync"
"runtime"
)
var (
producer = flag.Int("producer", 8, "producer")
consumer = flag.Int("consumer", 1, "consumer")
start_signal sync.WaitGroup
)
const (
TEST_NUM = 1000000
)
type Item struct {
id int
sendtime int64
recvtime int64
}
var g_vec[TEST_NUM] Item
func sender(out chan int, begin int, end int) {
start_signal.Wait()
runtime.LockOSThread()
println("i am in sender", begin, end)
for i:=begin; i < end; i++ {
item := &g_vec[i]
item.id = i
item.sendtime = time.Now().UnixNano()/1000
out<- i
}
println("sender finish")
}
func reader(out chan int, total int) {
//runtime.LockOSThread()
start_signal.Done()
for i:=0; i<total;i++ {
tmp :=<- out
item := &g_vec[tmp]
item.recvtime = time.Now().UnixNano()/1000
}
var lsum int64 = 0
var lavg int64 = 0
var lmax int64 = 0
var lstart int64 = 0
var lend int64 = 0
for _, item:= range g_vec {
if lstart > item.sendtime || lstart == 0 {
lstart = item.sendtime
}
if lend < item.recvtime {
lend = item.recvtime
}
ltmp := item.recvtime - item.sendtime
lsum += ltmp
if ltmp > lmax {
lmax = ltmp
}
}
lavg = lsum/TEST_NUM
fmt.Printf("latency max:%v,avg:%v\n", lmax, lavg)
fmt.Printf("send begin:%v,recv end:%v, time:%v", lstart, lend, lend-lstart)
}
func main() {
runtime.GOMAXPROCS(10)
out := make (chan int,5000)
start_signal.Add(1)
for i:=0 ;i<*producer;i++ {
go sender(out,i*TEST_NUM/(*producer), (i+1)*TEST_NUM/(*producer))
}
reader(out, TEST_NUM)
}
C++, seule une partie principale
concurrent_bounded_queue g_queue; max: 558.301, min: 3, avg: 403741 (unité est microseconde) départ: 1495594232068580, fin: 1495594233497618, longueur: 1429038
static void sender(int start, int end)
{
for (int i=start; i < end; i++)
{
using namespace std::chrono;
auto now = system_clock::now();
auto now_ms = time_point_cast<microseconds>(now);
auto value = now_ms.time_since_epoch();
int64_t duration = value.count();
Item &item = g_pvec->at(i);
item.id = i;
item.sendTime = duration;
//std::cout << "sending " << i << "\n";
g_queue.push(i);
}
}
static void reader(int num)
{
barrier.set_value();
for (int i=0;i<num;i++)
{
int v;
g_queue.pop(v);
Item &el = g_pvec->at(v);
using namespace std::chrono;
auto now = system_clock::now();
auto now_ms = time_point_cast<microseconds>(now);
auto value = now_ms.time_since_epoch();
int64_t duration = value.count();
el.recvTime = duration;
//std::cout << "recv " << item.id << ":" << duration << "\n";
}
// caculate the result.
int64_t lmax = 0;
int64_t lmin = 100000000;
int64_t lavg = 0;
int64_t lsum = 0;
int64_t lbegin = 0;
int64_t lend = 0;
for (auto &item : *g_pvec)
{
if (item.sendTime<lbegin || lbegin==0)
{
lbegin = item.sendTime;
}
if (item.recvTime>lend)
{
lend = item.recvTime;
}
lsum += item.recvTime - item.sendTime;
lmax = max(item.recvTime - item.sendTime, lmax);
lmin = min(item.recvTime - item.sendTime, lmin);
}
lavg = lsum/num;
std::cout << "max:" << lmax << ",min:" << lmin << ",avg:" << lavg << "\n";
std::cout << "start:" << lbegin << ",end:" << lend << ",length:" << lend-lbegin << "\n";
}
DEFINE_CODE_TEST(plain_queue_test)
{
g_pvec = new std::vector<Item>();
g_pvec->resize(TEST_NUM);
auto sf = barrier.get_future().share();
std::vector<std::thread> vt;
for (int i = 0; i < SENDER_NUM; i++)
{
vt.emplace_back([sf, i]{
sf.wait();
sender(i*TEST_NUM/SENDER_NUM, (i + 1)*TEST_NUM/SENDER_NUM);
});
}
std::cout << "create reader\n";
std::thread rt(bind(reader, TEST_NUM));
for (auto& t : vt)
{
t.join();
}
rt.join();
}
(moyens de couleur rouge cpu spin/overhead, le vert est inactif) du graphique de cpu de vtune j'ai senti que le canal de golang a un mutex plus efficace (par exemple a-t-il besoin d'un appel de système pour dormir un goroutine contre mutex de C++?)
Je vous suggère d'utiliser d'abord un véritable outil d'analyse comparative, comme celui intégré dans go. –
Lors de l'inspection, vous ne faites pas les mêmes choses dans les deux programmes. Pour n'en nommer que quelques-uns, la version C++ a des divisions entières et ce n'est pas le cas, et les variables n'ont pas le même niveau d'indirection. Ceux-ci peuvent finalement s'avérer non pertinents, mais jusqu'à ce que vous les référenciez réellement, nous ne le saurons pas –
Essayez également d'ajouter le travail de calcul réel/simulé dans les producteurs/consommateurs. Il peut arriver qu'à cause de goroutines, il soit exécuté dans un seul thread, ainsi la vraie synchronisation n'a pas lieu pour la version Go. En outre, la file d'attente de TBB est MPMC, tandis que Go pourrait l'optimiser pour le modèle MPSC de manière significative. – Anton