Была у меня задача, отправлять на эхо сервер сообщения и получать их назад, при этом вести статистику по 1. Максимальному, минимальному, среднему времение пересылке сообщения. 2. Максимальному минимальному среднему числу пересылаемых сообщений вот... собстно это вылилось вот в такой шаблонный велосипед велосипед. Code: #ifndef __STATISTICS__ #define __STATISTICS__ #include <map> #include <iostream> #include <limits> #include <functional> // less<Key> #include <time.h> // clock_t //#include <boost/thread/mutex.hpp> //#include <boost/thread/condition_variable.hpp> #include <mutex> #include <thread> #include <boost/date_time/posix_time/posix_time.hpp> /* * Key - уникальный идентификтор для объекта статистики * Func - функцтор извлекающий уникальный идентификатор объекта статистики, возвращает Key * Item - объект статистики, передается в Func * Compare - функцтор для определение эквивалентности Key */ template <typename Key, typename Func, typename Item, typename Compare = std::less<Key> > class Statistics { public: Statistics(void); ~Statistics(void); // Сохранение инф. об отправляемом сообщении void sendMsg(const Item); // Сохранение инф. о принятом сообщение void recvMsg(const Item); void show(std::ostream&); private: typedef typename std::map<Key, boost::posix_time::ptime, Compare> repository; typedef typename repository::iterator repIterator; typedef typename repository::value_type repValueType; // статистика по одному сообщение void statisticsOfOneMsg(const boost::posix_time::ptime&, repIterator); // статистика в единицу времение void statisticsOfPerSec(const boost::posix_time::ptime&); // функтор для получения ключа объекта статистики Func func_; // функтор для установления эквивалентности Compare comp_; std::mutex mtx_; std::condition_variable cond_; // Статисстические данные для обного объекта статистики // максимальное время boost::posix_time::time_duration maxTotalTime_; // минимальное время boost::posix_time::time_duration minTotalTime_; // среднее время boost::posix_time::time_duration averageTotalTime_; // Статистические данные по периоду времение // максимальное, минимальное, среднее колличество фиксируесых объектов статистики за 1 секунду unsigned int maxSentMsgPerSec_; unsigned int minSentMsgPerSec_; unsigned int averageSentMsgPerSec_; // Вспомогательные переменные repository sendMsgTime_; // ntreott колличетсво элементов для сбора статистики unsigned int currectAddedMsg_; boost::posix_time::ptime seconds_; // колличество эллементов для сбора статистики за секунду unsigned int msgPerSecond_; // колличетсво секундных отрезков unsigned int currentnumberSecond_; }; //------------------------------ template <typename Key, typename Func, typename Item, typename Compare> Statistics<Key, Func, Item, Compare>::Statistics(void) :maxSentMsgPerSec_(std::numeric_limits<unsigned int>::min()), minSentMsgPerSec_(std::numeric_limits<unsigned int>::max()), averageSentMsgPerSec_(0), currectAddedMsg_(0), msgPerSecond_(0), currentnumberSecond_(0), seconds_(boost::posix_time::microsec_clock::local_time()), maxTotalTime_(0, 0, 0 ,0), minTotalTime_(24, 59, 59 , 59), averageTotalTime_(0, 0, 0 ,0) { } template <typename Key, typename Func, typename Item, typename Compare> Statistics<Key, Func, Item, Compare>::~Statistics(void) { } template <typename Key, typename Func, typename Item, typename Compare> void Statistics<Key, Func, Item, Compare>::sendMsg(const Item msg) { // Лочим объект на как можно меньше дейтсвий std::lock_guard<std::mutex> lock(this->mtx_); //sendMsgTime_[msg] = boost::posix_time::microsec_clock::local_time(); sendMsgTime_.insert( repValueType(func_(msg), boost::posix_time::microsec_clock::local_time())); this->cond_.notify_one(); //std::cout << "func_(msg) = " << func_(msg) << " | second = " << sendMsgTime_[func_(msg)] << std::endl; } template <typename Key, typename Func, typename Item, typename Compare> void Statistics<Key, Func, Item, Compare>::recvMsg(const Item msg) { // // Лочим объект на как можно меньше дейтсвий std::unique_lock<std::mutex> lock(this->mtx_); this->cond_.wait(lock, [this]()->bool{ return !(this->sendMsgTime_.empty()); }); repIterator item = this->sendMsgTime_.find(func_(msg)); //std::cout << item->first << " - " << item->second << std::endl; if(item != this->sendMsgTime_.end()) { boost::posix_time::ptime currentTime = boost::posix_time::microsec_clock::local_time(); this->statisticsOfOneMsg(currentTime, item); this->statisticsOfPerSec(currentTime); // не захломлем память this->sendMsgTime_.erase(item); } } template <typename Key, typename Func, typename Item, typename Compare> void Statistics<Key, Func, Item, Compare>::show(std::ostream& os) { std::lock_guard<std::mutex> lock(this->mtx_); os << "\t\t--- Statistics of one message ---" << std::endl; os << "minimum time to sent message: " << this->minTotalTime_ << std::endl; os << "maximum time to sent message: " << this->maxTotalTime_ << std::endl; os << "average time to sent message: " << this->averageTotalTime_ / ((this->currectAddedMsg_) ? this->currectAddedMsg_ : 1) << std::endl; os << "\t\t--- Statistics of per second ---" << std::endl; os << "currentnumberSecond_" << currentnumberSecond_ << std::endl; if(currentnumberSecond_) { // не учитываем сообщение набранные за последнюю не секунду т.к. она может быть не завершенной os << "> 1 secod" << std::endl; os << "minimum sent message per second: " << this->minSentMsgPerSec_ << std::endl; os << "maximum sent message per second: " << this->maxSentMsgPerSec_ << std::endl; os << "average sent message per second: " << (double)(this->averageSentMsgPerSec_) / this->currentnumberSecond_ << std::endl; } else { os << "< 1 secod" << std::endl; // если время сбора статистики < 1 секунды os << "minimum sent message per second: " << this->msgPerSecond_ << std::endl; os << "maximum sent message per second: " << this->msgPerSecond_ << std::endl; os << "average sent message per second: " << this->msgPerSecond_ << std::endl; } } template <typename Key, typename Func, typename Item, typename Compare> void Statistics<Key, Func, Item, Compare>::statisticsOfOneMsg(const boost::posix_time::ptime& currentTime, repIterator item) { // подразумевается что объект уже залочен // получаем время затраченное сообщением с момента отправки до момента приема, включа всю бизнес логику boost::posix_time::time_duration totalTimeMsg = currentTime - item->second; if(!totalTimeMsg.is_not_a_date_time()) { if(totalTimeMsg < this->minTotalTime_) { this->minTotalTime_ = totalTimeMsg; } if(totalTimeMsg > this->maxTotalTime_) { this->maxTotalTime_ = totalTimeMsg; } this->averageTotalTime_ += totalTimeMsg; ++(this->currectAddedMsg_); } else { throw "Bad time of one msg"; } } template <typename Key, typename Func, typename Item, typename Compare> void Statistics<Key, Func, Item, Compare>::statisticsOfPerSec(const boost::posix_time::ptime& currentTime) { // подразумевается что объект уже залочен boost::posix_time::time_duration perSecond = currentTime - this->seconds_; //std::cout << "currentTime = " << currentTime << std::endl; //std::cout << "this->seconds_ = " << this->seconds_ << std::endl; //std::cout << "perSecond = " << perSecond << " | Second = "<< perSecond.seconds() << std::endl; //std::cout << "msgPerSecond_ = " << msgPerSecond_ << std::endl; if(!perSecond.is_not_a_date_time() && perSecond.seconds() > 0) { if(this->msgPerSecond_ > 0) { // секунду прошла - фиксируем статистику, сбрасываем время, текущее сообение учитываем после сброса статистики if(this->msgPerSecond_ < this->minSentMsgPerSec_) { this->minSentMsgPerSec_ = this->msgPerSecond_; } if(this->msgPerSecond_ > this->maxSentMsgPerSec_) { this->maxSentMsgPerSec_ = this->msgPerSecond_; } this->averageSentMsgPerSec_ += this->msgPerSecond_; ++currentnumberSecond_; this->msgPerSecond_ = 0; this->seconds_ = boost::posix_time::microsec_clock::local_time(); //std::cout << "this->seconds_ = " << this->seconds_ << std::endl; } else { // первый заход, устанавливаем новое корректное время this->seconds_ = boost::posix_time::microsec_clock::local_time(); //std::cout << "this->seconds_ = " << this->seconds_ << std::endl; } } else if(perSecond.is_not_a_date_time()) { throw "Bad time of per second"; } //std::cout << msgPerSecond_ << std::endl; ++(this->msgPerSecond_); } #endif
Пример использования Code: // g++ main.cpp -o statistics -lpthread -lboost_date_time -lboost_thread -lboost_system -std=c++0x #include <iostream> //#include <boost/thread.hpp> #include <thread> #include "Statistics.h" #include <stdio.h> #include <stdlib.h> using namespace std; struct Myfunc { int operator()(int item) { return item; } }; void recv(Statistics<int, Myfunc, int>*, int msg); void send(Statistics<int, Myfunc, int>*, int msg); int main(int argc, char** argv) { int number = 0; if(argc == 2) { number = atoi(argv[argc - 1]); if (!number) return 0; cout << "num msg: " << number << endl; } int dfg = 0; /* boost::posix_time::ptime currentTime = boost::posix_time::microsec_clock::local_time(); map<int, int> temp; for(int i=0; i<dfg; i++) temp[i] = i; boost::posix_time::time_duration t = boost::posix_time::microsec_clock::local_time() - currentTime; cout << "Add time: " << t << "\t" << t.seconds() << " sec." << endl; cout << "Add of " << dfg / ((t.seconds()) ? t.seconds() : 1) << " per/sec" << endl; currentTime = boost::posix_time::microsec_clock::local_time(); for(int i=0; i<dfg; i++) { map<int, int>::iterator item = temp.find(i); temp.erase(item); } t = boost::posix_time::microsec_clock::local_time() - currentTime; cout << "Erase time: " << t << "\t" << t.seconds() << " sec." << endl; cout << "Erase of " << dfg / ((t.seconds()) ? t.seconds() : 1) << " per/sec" << endl; */ Statistics<int, Myfunc, int> st; thread th1(send, &st, number); thread th2(recv, &st, number); th1.join(); th2.join(); st.show(std::cout); return 0; } void recv(Statistics<int, Myfunc, int>* st, int number) { for(int i=0; i<number; i++) st->recvMsg(i); } void send(Statistics<int, Myfunc, int> *st, int number) { for(int i=0; i<number; i++) st->sendMsg(i); } то что закоменчено, это для сбора статистики о времение затрачиваемом на добавление и удаление элементов в/из контейнера map, что бы можно было сравнить с тем насколько замедляется это время при сборе статистики. Меня в первую очередь интересует ваша конструктивная критика, по поводу улучшения быстродействия, ибо оно мне кажется храмает в этой реализации. Ну и собстно, это можно использовать как хороший пример по написанию шаблонов, использованию boost и многопоточности.
Прошу минуточку вашего внимания. Меня интересует ваше мнение по поводу того как можно организовать замер пересылаемых данных за единицу времение. У меня это реализовано так (метод statisticsOfPerSec) Code: void Statistics<Key, Func, Item, Compare>::statisticsOfPerSec(const boost::posix_time::ptime& currentTime) { boost::posix_time::time_duration perSecond = currentTime - this->seconds_; if(!perSecond.is_not_a_date_time() && perSecond.seconds() > 0) { if(this->msgPerSecond_ > 0) { // секунду прошла - фиксируем статистику, сбрасываем время, текущее сообение учитываем после сброса статистики if(this->msgPerSecond_ < this->minSentMsgPerSec_) { this->minSentMsgPerSec_ = this->msgPerSecond_; } if(this->msgPerSecond_ > this->maxSentMsgPerSec_) { this->maxSentMsgPerSec_ = this->msgPerSecond_; } this->averageSentMsgPerSec_ += this->msgPerSecond_; ++currentnumberSecond_; this->msgPerSecond_ = 0; this->seconds_ = boost::posix_time::microsec_clock::local_time(); } else { // первый заход, устанавливаем новое корректное время this->seconds_ = boost::posix_time::microsec_clock::local_time(); } } else if(perSecond.is_not_a_date_time()) { throw "Bad time of per second"; } ++(this->msgPerSecond_); } 1. в переменной класса this->seconds_ фиксирует текущее время 2. при получении очередного сообщения проверяется разница между this->seconds_ и текущим временем local_time() 2.1 Если разница больше 1 секунды, значит секунда закончилась когдато давно и нужно сохранить статистику по последнему сообщение полученному в той секунде. После чего переменная this->seconds_ устанавливается на текущее время. А счетчик полученных сообщение сбрасывается на 0 3. увеличиваем счетчик принятых сообщение. так же добавлен учет того что время сбора статистики может быть вообще меньше секунды, тогда мы просто выводим колчестви полученных сообщений. При отображении статистики учитываются только статистика зафиксированная за полные секунды. Таким образом, если за время сбора статистики прошло 4.5 секунды, то статистика учитывается только за 4 секунды, так как последние 0.5 ее портят. Была еще идея, что бы из экзампляра класса запрускать поток в которы бы кидать каждое полученно сообщзение, а тот сам каждую секунду обновляет данные статистики. Но как реализовать ето секундное обновления я чет не придумалю Ибо sleep (1000) совсем не означает что поток будет спать ровно 1 секунду, можут и больше. Так же совсем не хочется просто соъхранять в памяти все сообщения с временем их получения и когда то потом произвоить подсчет. Ибо в таком случае будут сжираться все свободные ресурсы памяти.