![]() |
User Manual, Developers Guide and API Documentation |
![]() |
00001 /****************************************************************************** 00002 * WNS (Wireless Network Simulator) * 00003 * __________________________________________________________________________ * 00004 * * 00005 * Copyright (C) 2004-2006 * 00006 * Chair of Communication Networks (ComNets) * 00007 * Kopernikusstr. 16, D-52074 Aachen, Germany * 00008 * phone: ++49-241-80-27910 (phone), fax: ++49-241-80-22242 * 00009 * email: wns@comnets.rwth-aachen.de * 00010 * www: http://wns.comnets.rwth-aachen.de * 00011 ******************************************************************************/ 00012 00013 #include <WNS/scheduler/strategy/staticpriority/ProportionalFair.hpp> 00014 #include <WNS/scheduler/SchedulerTypes.hpp> 00015 00016 #include <vector> 00017 #include <map> 00018 #include <queue> 00019 #include <algorithm> 00020 #include <iostream> 00021 #include <iomanip> 00022 #include <math.h> 00023 00024 using namespace std; 00025 using namespace wns::scheduler; 00026 using namespace wns::scheduler::strategy; 00027 using namespace wns::scheduler::strategy::staticpriority; 00028 00029 // begin example "wimac.tutorial.experiment2.staticFactory.substrategy.ProportionalFair.cpp" 00030 STATIC_FACTORY_REGISTER_WITH_CREATOR(ProportionalFair, 00031 SubStrategyInterface, 00032 "ProportionalFair", 00033 wns::PyConfigViewCreator); 00034 // end example 00035 00036 ProportionalFair::ProportionalFair(const wns::pyconfig::View& config) 00037 : SubStrategy(config), 00038 blockSize(config.get<int>("blockSize")), 00039 historyWeight(config.get<float>("historyWeight")), 00040 scalingBetweenMaxTPandPFair(config.get<float>("scalingBetweenMaxTPandPFair")), 00041 rateFairness(config.get<bool>("rateFairness")), 00042 maxRateOfSubchannel(0.0), 00043 allUsers(), 00044 preferenceVariationDistribution(NULL) 00045 { 00046 assure(blockSize>0,"invalid blockSize="<<blockSize); 00047 MESSAGE_SINGLE(NORMAL, logger, "ProportionalFair(): constructed with blockSize="<<blockSize); 00048 pastDataRates.clear(); 00049 allUsers.clear(); 00050 preferenceVariationDistribution = new wns::distribution::Uniform(-1.0, 1.0); 00051 // historyWeight: 00052 // if 0 no history is taken into account -> maxThroughput Scheduler 00053 assure(scalingBetweenMaxTPandPFair>=0.0, "scalingBetweenMaxTPandPFair="<<scalingBetweenMaxTPandPFair<<" is out of bounds"); 00054 assure(scalingBetweenMaxTPandPFair<=1.0, "scalingBetweenMaxTPandPFair="<<scalingBetweenMaxTPandPFair<<" is out of bounds"); 00055 assure((historyWeight>=0.0)&&(historyWeight<1.0), "historyWeight="<<historyWeight<<" is out of bounds"); 00056 } 00057 00058 ProportionalFair::~ProportionalFair() 00059 { 00060 delete preferenceVariationDistribution; 00061 } 00062 00063 void 00064 ProportionalFair::initialize() 00065 { 00066 MESSAGE_SINGLE(NORMAL, logger, "ProportionalFair::initialize()"); 00067 wns::service::phy::phymode::PhyModeInterfacePtr phyMode = colleagues.registry->getPhyModeMapper()->getHighestPhyMode(); 00068 maxRateOfSubchannel = phyMode->getDataRate(); 00069 MESSAGE_SINGLE(NORMAL, logger, "ProportionalFair: maxRateOfSubchannel="<<maxRateOfSubchannel<<" bit/s"); 00070 assure(maxRateOfSubchannel>0.0, "unknown maxRateOfSubchannel"); 00071 } 00072 00073 std::priority_queue<ProportionalFair::UserPreference> 00074 ProportionalFair::calculateUserPreferences(UserSet activeUsers, bool txStrategy) const 00075 { 00076 std::map<UserID, ChannelQualityOnOneSubChannel> sinrs; 00077 00078 // preference for every user in a priority queue which automatically sorts 00079 std::priority_queue<UserPreference> preferences; 00080 assure(preferences.size()==0, "preferences.size() must be 0"); 00081 00082 for ( UserSet::const_iterator iter = activeUsers.begin(); 00083 iter != activeUsers.end(); ++iter) 00084 { 00085 UserID user = *iter; 00086 assure(user.isValid(), "No valid user"); 00087 00088 //determines the users SINRs depending on the tx/Rx mode of the strategy 00089 if (txStrategy) 00090 { 00091 sinrs[user] = colleagues.registry->estimateTxSINRAt(user); 00092 } 00093 else 00094 { 00095 sinrs[user] = colleagues.registry->estimateRxSINROf(user); 00096 } 00097 00098 // calculate PhyModeRate for available users 00099 wns::Ratio sinr(sinrs[user].carrier / sinrs[user].interference); 00100 wns::service::phy::phymode::PhyModeInterfacePtr phyMode = colleagues.registry->getPhyModeMapper()->getBestPhyMode(sinr); 00101 assure(phyMode->isValid(),"invalid PhyMode"); 00102 00103 // calculate userRate, which is the maximum possible data rate 00104 // for one subChannel for the best PhyMode available here 00105 float phyModeRate = phyMode->getDataRate(); // rate [b/s] of one subChannel 00106 float referenceRate; 00107 float pastDataRate = 1.0; 00108 00109 // get the past data rate for this user: 00110 // iterate over the global past data rates map of all users and 00111 // find the past data rate for this user 00112 // there is exactly one pastDataRate value per userID 00113 int weight = colleagues.registry->getTotalNumberOfUsers(user); 00114 for (std::map<UserID, float>::const_iterator iter = pastDataRates.begin(); 00115 iter != pastDataRates.end(); ++iter) 00116 { 00117 if (iter->first/*userID*/ == user) 00118 { 00119 float dataRate = iter->second; 00120 // a RN must get a better share of the bandwidth 00121 // here: proportional to its number of users: 00122 assure(weight>0, "numberOfUsers(" <<user.getName()<<")=" << weight); 00123 dataRate /= static_cast<float>(weight); 00124 // dataRate now has the meaning of a weight. 00125 pastDataRate = dataRate; 00126 } 00127 } // for all userIDs in pastDataRates 00128 00129 if (pastDataRate < 0.01) 00130 pastDataRate = 0.01; 00131 00132 // preference is achievable current user rate divided by a history 00133 // factor that takes the past throughput of this user into account 00134 00135 assure(maxRateOfSubchannel>0.0, "unknown maxRateOfSubchannel"); 00136 00137 // goal is either rate (true) or resource (false) fairness 00138 if (rateFairness == true) 00139 { 00140 referenceRate = maxRateOfSubchannel; 00141 } 00142 else 00143 { 00144 referenceRate = phyModeRate; 00145 } 00146 00147 // maxRateOfSubchannel is constant, with range [0..1][bit/s] 00148 float resultMaxThroughput = referenceRate / maxRateOfSubchannel; 00149 float resultPropFair = referenceRate / pastDataRate; // can be any range 00150 if (scalingBetweenMaxTPandPFair <= 0.5) { 00151 // variate the preference for each user, so that they differ a little bit (1%) 00152 // and the automatic sorting does not always give the same order 00153 // (would be a problem for identical preference weights). 00154 resultMaxThroughput *= (1 + 0.01*(*preferenceVariationDistribution)()); 00155 } 00156 float UserPref = 00157 (1.0-scalingBetweenMaxTPandPFair) * resultMaxThroughput 00158 +scalingBetweenMaxTPandPFair * resultPropFair; 00159 00160 MESSAGE_SINGLE(NORMAL, logger, "getPreference("<<user.getName()<<"): weight=" << weight << ", pastDataRate= "<<pastDataRate<<" bit/s, UserPreference= "<<UserPref<<" (resultMaxThroughput="<<resultMaxThroughput<<",resultProportionalFair="<<resultPropFair<<")"); 00161 00162 // calculate preferences for users and order them 00163 preferences.push(UserPreference(UserPref, user)); 00164 } 00165 return preferences; 00166 } 00167 00168 //std::map<UserID, Bit(int)> 00169 std::map<UserID, float> 00170 ProportionalFair::calculateBitsForConnections(const ConnectionSet& currentConnections) 00171 { 00172 std::map<UserID, float> bitsForUsers; 00173 00174 for ( wns::scheduler::ConnectionSet::const_iterator iter = currentConnections.begin(); 00175 iter != currentConnections.end(); 00176 ++iter ) 00177 { 00178 ConnectionID currentConnection = *iter; 00179 wns::scheduler::UserID user = colleagues.registry->getUserForCID(currentConnection); 00180 00181 Bit queueLength = 0; 00182 if (colleagues.queue->queueHasPDUs(currentConnection)) 00183 { 00184 queueLength = colleagues.queue->numBitsForCid(currentConnection); 00185 } 00186 00187 if (bitsForUsers.find(user) == bitsForUsers.end()) 00188 { 00189 bitsForUsers[user] = queueLength; 00190 } 00191 else 00192 { 00193 bitsForUsers[user] += queueLength; 00194 } 00195 } 00196 return bitsForUsers; 00197 } 00198 00199 // preferences should be a member 00200 wns::scheduler::ConnectionID 00201 ProportionalFair::getNextConnection(SchedulerStatePtr schedulerState, 00202 std::priority_queue<UserPreference> preferences) 00203 { 00204 wns::scheduler::ConnectionID next = -1; 00205 00206 while (!preferences.empty()) 00207 { 00208 int priority = schedulerState->currentState->getCurrentPriority(); 00209 const float preference = preferences.top().first; 00210 const UserID user = preferences.top().second; 00211 MESSAGE_SINGLE(NORMAL, logger, "Selected user="<<user.getName()); 00212 00213 ConnectionVector currentPrioConns = getConnectionsForPrio(priority, user); 00214 00215 if(!currentPrioConns.empty()) 00216 { 00217 next = getRandomConnection(currentPrioConns); 00218 MESSAGE_SINGLE(NORMAL, logger, "Selected connection with CID="<<next); 00219 return next; 00220 } 00221 preferences.pop(); 00222 } 00223 return next; 00224 } 00225 00226 // return connections for user belonging to current priority 00227 ConnectionVector 00228 ProportionalFair::getConnectionsForPrio(int currentPrio, const UserID user) 00229 { 00230 ConnectionVector currentPrioConns; 00231 ConnectionVector allRegisteredConns = colleagues.registry->getConnectionsForUser(user); 00232 for (ConnectionVector::const_iterator iter = allRegisteredConns.begin(); 00233 iter != allRegisteredConns.end(); 00234 ++iter) 00235 { 00236 wns::scheduler::ConnectionID currentConnection = *iter; 00237 // check if the connection has the current priority 00238 if (colleagues.registry->getPriorityForConnection(currentConnection) == currentPrio) 00239 { 00240 if (colleagues.queue->queueHasPDUs(currentConnection)) 00241 { 00242 currentPrioConns.push_back(currentConnection); 00243 } 00244 // else: this conection is empty, go to the next connection of this user 00245 } 00246 // else: this connection belongs to another, i.e. lower priority, go to the next connection of this user 00247 } 00248 return currentPrioConns; 00249 } 00250 00251 wns::scheduler::ConnectionID 00252 ProportionalFair::getRandomConnection(ConnectionVector currentPrioConns) 00253 { 00254 int numberOfConns = currentPrioConns.size(); 00255 wns::distribution::Uniform randomPositionDistribution(0.0, numberOfConns); 00256 float randomNumber = (randomPositionDistribution)(); 00257 int randomPosition = static_cast<int>(randomNumber); 00258 MESSAGE_SINGLE(NORMAL, logger, "Drew random number="<<randomNumber<< ", position="<< randomPosition << " out of "<< numberOfConns << " total connections."); 00259 assure(randomPosition<numberOfConns, "Random position of connections="<< randomPosition << " out of range of current connections size="<<numberOfConns); 00260 return currentPrioConns[randomPosition]; 00261 } 00262 00263 // maybe we could get rid of the phase length 00264 void 00265 ProportionalFair::updatePastDataRates(std::map<UserID, float> bitsBeforeThisFrame, 00266 std::map<UserID, float> bitsAfterThisFrame, 00267 simTimeType phaseLength) 00268 { 00269 UserID user; 00270 float bitsThisFrame = 0.0; 00271 float pastDataRate = 0.0; 00272 float currentRate = 0.0; 00273 00274 for (std::map<UserID, float>::const_iterator iter = bitsBeforeThisFrame.begin(); 00275 iter != bitsBeforeThisFrame.end(); ++iter) 00276 { 00277 user = iter->first; 00278 bitsThisFrame = bitsBeforeThisFrame[user] - bitsAfterThisFrame[user]; 00279 currentRate = bitsThisFrame / phaseLength; 00280 pastDataRate = pastDataRates[user]; 00281 00282 if (pastDataRates.find(user) != pastDataRates.end()) { 00283 pastDataRates[user] = (1.0-historyWeight) * currentRate + historyWeight * pastDataRates[user]; 00284 } else { 00285 // new user 00286 pastDataRates[user] = currentRate; 00287 } 00288 MESSAGE_SINGLE(NORMAL, logger, "updatePastDataRates("<<user.getName()<<","<<phaseLength<<"s): pastDataRate: new= "<< pastDataRates[user]<<" bit/s, old= "<<pastDataRate<<" bit/s, currentRate= "<<currentRate<<" bit/s"); 00289 } 00290 } 00291 00292 wns::scheduler::MapInfoCollectionPtr 00293 ProportionalFair::doStartSubScheduling(SchedulerStatePtr schedulerState, 00294 wns::scheduler::SchedulingMapPtr schedulingMap) 00295 00296 { 00297 MapInfoCollectionPtr mapInfoCollection = MapInfoCollectionPtr(new wns::scheduler::MapInfoCollection); // result datastructure 00298 UserSet allUsersInQueue = colleagues.queue->getQueuedUsers(); 00299 UserSet activeUsers = colleagues.registry->filterReachable(allUsersInQueue); 00300 ConnectionSet ¤tConnections = schedulerState->currentState->activeConnections; 00301 00302 MESSAGE_SINGLE(NORMAL, logger, "activeUsers= "<< activeUsers.size()<<" , currentConnections= "<<printConnectionSet(currentConnections)<<" "); 00303 00304 if (activeUsers.empty() || currentConnections.empty()) return mapInfoCollection; // nothing to do 00305 00306 simTimeType slotLength = schedulingMap->getSlotLength(); 00307 bool txStrategy = schedulerState->isTx; 00308 std::map<UserID, float> bitsBeforeThisFrame = calculateBitsForConnections(currentConnections); 00309 00310 MESSAGE_BEGIN(NORMAL, logger, m, "ProportionalFair"); 00311 for (std::map<UserID, float>::const_iterator iter = bitsBeforeThisFrame.begin(); 00312 iter != bitsBeforeThisFrame.end(); ++iter) 00313 { 00314 m << "\n User " << iter->first.getName() << " has " << iter->second; 00315 m << " queued bits."; 00316 } 00317 MESSAGE_END(); 00318 00319 std::map<UserID, float> pastDataRates; 00320 // make preferences a member, then no return value needed 00321 std::priority_queue<UserPreference> preferences = calculateUserPreferences(activeUsers, txStrategy); 00322 00323 // returns the connection of the user with the highest preference, i.e. lowest past data rate 00324 wns::scheduler::ConnectionID currentConnection = getNextConnection(schedulerState, preferences); 00325 //wns::scheduler::UserID user = colleagues.registry->getUserForCid(currentConnection); 00326 00327 bool spaceLeft= true; 00328 int pduCounter = 0; 00329 // static const noCID and then check for (currentConnection != noCID) 00330 while(spaceLeft && (currentConnection >= 0)) 00331 { 00332 // schedule blockSize PDUs for this CID 00333 while( colleagues.queue->queueHasPDUs(currentConnection) && spaceLeft) 00334 { 00335 spaceLeft = scheduleCid(schedulerState,schedulingMap,currentConnection,pduCounter,blockSize,mapInfoCollection); 00336 } // while PDUs in queue 00337 /*if (!colleagues.queue->queueHasPDUs(currentConnection)) 00338 { // exit because of queue empty (most probable case for low traffic) 00339 currentConnections.erase(currentConnection); 00340 if (currentConnections.empty()) break; // all queues empty 00341 }*/ 00342 currentConnection = getNextConnection(schedulerState, preferences); 00343 // user = colleagues.registry->getUserForCid(currentConnection); 00344 MESSAGE_SINGLE(NORMAL, logger, "doStartSubScheduling(): next connection="<<currentConnection); 00345 } // while(spaceLeft) 00346 MESSAGE_SINGLE(NORMAL, logger, "doStartSubScheduling(): ready: mapInfoCollection="<<mapInfoCollection.getPtr()<<" of size="<<mapInfoCollection->size()); 00347 00348 std::map<UserID, float> bitsAfterThisFrame = calculateBitsForConnections(currentConnections); 00349 00350 MESSAGE_BEGIN(NORMAL, logger, m, "ProportionalFair"); 00351 for (std::map<UserID, float>::const_iterator iter = bitsAfterThisFrame.begin(); 00352 iter != bitsAfterThisFrame.end(); ++iter) 00353 { 00354 m << "\n User " << iter->first.getName() << " has " << iter->second; 00355 m << " queued bits left after this frame."; 00356 } 00357 MESSAGE_END(); 00358 00359 assure(bitsBeforeThisFrame.size() == bitsAfterThisFrame.size(), "bitsBeforeThisFrame and bitsAfterThisFrame do not have the same number of users!"); 00360 updatePastDataRates(bitsBeforeThisFrame, bitsAfterThisFrame, slotLength); 00361 return mapInfoCollection; 00362 }
1.5.5