यादृच्छिक डीएजी उत्पन्न करने के लिए सी ++ कोड
मैंने जिस प्रोजेक्ट पर काम कर रहा था, उसके लिए यादृच्छिक रेखांकन उत्पन्न करने के लिए कुछ समय पहले निम्नलिखित C ++ कोड लिखा था:
#include "stdafx.h"
#include <iostream>
#include <algorithm> // std::min_element, std::max_element
#include <sstream>
#include <fstream>
#include <string>
#include <iterator>
#include <random>
#include <vector>
#define NoOfNodes 30
struct GPU_data
{
int number_Copies;
int workItems;
int workGroups;
bool memory;
double power_consumption;
double execTime;
};
struct DAG_data
{
int processid; //Node's ID
int PEid; //Processor's ID to which node is assigned
std::vector<GPU_data> Node_config;
int precede;
int follow; //nodes following this node
int noOfCopies;
double transData;
double ExecTime;
double powerDraw;
};
void CreateandAssignEdges(DAG_data Sample, int NoOfEdges)
{
unsigned int i = 0;
if (Sample.processid == 0)
{
//parent process- so there will be no edges
Sample.precede = 0;
Sample.follow = rand()% NoOfEdges + 1;
}
else if (Sample.processid == NoOfNodes - 1)
{
//sink process- so there will be no following edges
Sample.follow = 0;
}
else
{
//which nodes will the edges connect to (Anywhere from among the following nodes, including the sink node)
Sample.follow = (Sample.processid + 1) + (std::rand() % (29 - (Sample.processid) + 1));
if (Sample.follow == 30)
{
Sample.follow -= 1;
}
}
}
DAG_data EdgeAssignment(DAG_data Sample, int NoOfEdges)
{
unsigned int i = 0;
if (Sample.processid == 0)
{
//parent process- so there will be no edges
Sample.precede = 0;
Sample.follow = rand() % NoOfEdges + 1;
return Sample;
}
else if (Sample.processid == NoOfNodes - 1)
{
//sink process- so there will be no following edges
Sample.follow = 0;
return Sample;
}
else
{
//which nodes will the edges connect to (Anywhere from among the following nodes, including the sink node)
Sample.follow = (Sample.processid + 1) + (std::rand() % (29 - (Sample.processid) + 1));
return Sample;
}
}
//Sample->precede = rand() % NoOfEdges;
//Sample->follow = rand() % NoOfEdges;
////Preceding and following edges of a node should not be the same.
//while (Sample->precede > Sample->follow || Sample->precede == Sample->follow)
//{
// //assign both edges again
// Sample->follow = rand() % NoOfEdges;
// Sample->precede = rand() % NoOfEdges;
//}
void whenPEisGPU(DAG_data Sample, int processorID)
{
GPU_data emptySet;
int i = 0;
int NoOfConfigs = rand() % 5;
GPU_data* sub_tasks = &emptySet;
while (i != NoOfConfigs)
{
sub_tasks->memory = rand() % 1;
sub_tasks->number_Copies = rand() % 3;
sub_tasks->workGroups = rand() % 10 +1;
sub_tasks->workItems = rand() % (sub_tasks->workGroups * 2) + 1;
sub_tasks->power_consumption = rand() % 250;
sub_tasks->execTime = rand() % (int)(Sample.ExecTime / 2);
Sample.Node_config.push_back(*sub_tasks);
i++;
}
}
void PESpecificParameters(DAG_data Sample, int processorID)
{
if (processorID == 0)
{
Sample.ExecTime = rand() % 100;
Sample.powerDraw = 0.0;
Sample.noOfCopies = 0;
}
else if (processorID == 1)
{
Sample.PEid = processorID;
//whenPEisGPU(Sample, processorID);
int i = 0;
int NoOfConfigs = rand() % 5;
GPU_data sub_tasks;
while (i != NoOfConfigs)
{
sub_tasks.memory = rand() % 1;
sub_tasks.number_Copies = rand() % 3+1;
sub_tasks.workGroups = rand() % 10 + 1;
sub_tasks.workItems = rand() % (sub_tasks.workGroups * 2) + 1;
sub_tasks.power_consumption = rand() % 250;
sub_tasks.execTime = rand() % (int)(Sample.ExecTime / 2);
Sample.Node_config.push_back(sub_tasks);
i++;
}
}
}
DAG_data PEParameters(DAG_data Sample, int processorID)
{
if (processorID == 0)
{
Sample.ExecTime = rand() % 100;
Sample.powerDraw = 0.0;
Sample.noOfCopies = 0;
return Sample;
}
else if (processorID == 1)
{
Sample.PEid = processorID;
//whenPEisGPU(Sample, processorID);
int i = 0;
int NoOfConfigs = rand() % 5;
GPU_data sub_tasks;
while (i != NoOfConfigs)
{
sub_tasks.memory = rand() % 1;
sub_tasks.number_Copies = rand() % 3 + 1;
sub_tasks.workGroups = rand() % 10 + 1;
sub_tasks.workItems = rand() % (sub_tasks.workGroups * 2) + 1;
sub_tasks.power_consumption = rand() % 250;
sub_tasks.execTime = rand() % (int)(Sample.ExecTime / 2) + 1;
Sample.Node_config.push_back(sub_tasks);
i++;
}
return Sample;
}
}
void generateEdges(std::vector<DAG_data> &myTaskGraph)
{
unsigned int i = 0;
while (i != myTaskGraph.size())
{
for (unsigned int j = (myTaskGraph[i].processid)+1; j < myTaskGraph.size(); j++)
{
if (myTaskGraph[i].follow == 30)
{
myTaskGraph[i].follow -= 1;
}
//create an edge between the current node and any of its following nodes according to the following random number
if (rand() % 100 < 30)
{
myTaskGraph[i].follow = j;
break;
}
}
i++;
}
}
int main()
{
DAG_data emptyDAG;
unsigned int i = 0;
std::ofstream myFile;
std::vector<DAG_data> All_DAGs;
while (i != NoOfNodes)
{
DAG_data DAG1;
DAG1.processid = i;
DAG1.transData = i + 1;
DAG1.PEid = 0;
DAG1= PEParameters(DAG1, DAG1.PEid);
DAG1= EdgeAssignment(DAG1, 10);
All_DAGs.push_back(DAG1);
//DAG1.Node_config.clear();
i++;
}
generateEdges(All_DAGs);
for (int h = 0; h < All_DAGs.size(); h++)
{
if (h % 2 != 0)
{
DAG_data forNewPE =PEParameters(All_DAGs[h], 1);
All_DAGs.push_back(forNewPE);
All_DAGs[h].Node_config.clear();
if (All_DAGs[h].processid ==29)
{
break;
}
}
}
myFile.open("TG_Data_30NewEdges.txt");
for (int i = 0; i < All_DAGs.size(); i++)
{
myFile << "Node id: " << All_DAGs[i].processid << std::endl;
myFile << "Following Edge: " << All_DAGs[i].follow << std::endl;
myFile << "Transfer Data: " << All_DAGs[i].transData << std::endl;
myFile << "Node PE: " << All_DAGs[i].PEid << std::endl;
if (All_DAGs[i].PEid == 0)
{
myFile << "Execution time: " << All_DAGs[i].ExecTime << std::endl;
}
else
{
myFile << "-------------------------------" << std::endl;
for (int j = 0; j < All_DAGs[i].Node_config.size(); j++)
{
myFile << "Execution time: " << All_DAGs[i].Node_config[j].execTime << std::endl;
myFile << "Copies: " << All_DAGs[i].Node_config[j].number_Copies << std::endl;
myFile << "Memory: " << All_DAGs[i].Node_config[j].memory << std::endl;
myFile << "Work-Items: " << All_DAGs[i].Node_config[j].workItems << std::endl;
myFile << "Work-Groups: " << All_DAGs[i].Node_config[j].workGroups << std::endl;
myFile << "Power: " << All_DAGs[i].Node_config[j].power_consumption << std::endl;
myFile << "++++++++++++++++++" << std::endl;
}
}
myFile << "=================" << std::endl;
}
myFile.close();
std::cout << "DONE NOW." << std::endl;
std::cin.get();
}
कोड ने मेरे लिए अपने उद्देश्य को पूरा किया लेकिन इस कोड में सुधार के लिए बहुत जगह है। कृपया सलाह दें कि वांछित C ++ प्रथाओं का बेहतर पालन करने के लिए इस कोड को कैसे फिर से लिखा जा सकता है।
जवाब
महत्वपूर्ण त्रुटियां:
आपका यादृच्छिक यादृच्छिक नहीं है (इसे बीज दें)
आपका रैंडम एकसमान नहीं है (केवल मापांक लेने के बजाय एक समान वितरण का उपयोग करें, जो वितरण को तिरछा करेगा)
precede
अक्सर असंगठित होता है;NoOfConfigs
अक्सर अनधिकृत है, और कभी इस्तेमाल नहीं किया जाता है?आउटपुट फ़ाइल लिखने से पहले अंतिम लूप पुनरावृत्ति करते समय संग्रह को संशोधित करता है :
for (size_t h = 0; h < nodes.size(); h++) { // ... nodes.push_back(forNewPE);
यह एक विरोधी पैटर्न है। आप केवल इसकी वजह से दूर हो जाते हैं
if (nodes[h].processid == 29) { break; }
कौन सा पाठ्यक्रम जादू की संख्या से ग्रस्त है, और आसानी से इसके बजाय लूप की स्थिति में रखा जा सकता है:
for (size_t h = 0; h < NoOfNodes; ++h) {
void PESpecificParameters(DAG_data Sample, int processorID)
उपयोग नहीं होता है।जब उपयोग किया जाता है, तो इसका कभी भी कोई प्रभाव नहीं होगा (क्योंकि इसमें नेटिहेर रिटर्न मान हैं और न ही बाहर की किसी भी चीज़ का संदर्भ है)
के जैसा
whenPEisGPU
डुप्लिकेट कोड हटाने के बाद, ऐसा लगता है कि
PEParameters
यह समान थाPESpecificParameters
(नीचे देखें)इसी तरह
CreateandAssignEdges
अप्रयुक्त था और लगता है नकलEdgeAssignment
?
प्रमुख नोट:
नामकरण!
DAG_Data
कुछ भी नहीं के बगल में मतलब है। आपका ग्राफ मॉडल वास्तविक जीवन में कुछ का प्रतिनिधित्व करता है । तथ्य यह है कि यह एक डीएजी है, "फर्स्टनाम" और "जिपोड" के बजाय "टेक्स्टस्ट्रिंग" चर को कॉल करने जैसा है।कुछ कार्य निकालें। उनका उपयोग करें
- अलग-अलग जिम्मेदारियां,
- अमूर्तता का स्तर
- दोहराव को कम करें
कक्षाओं में उनके डेटा के साथ वैकल्पिक रूप से संबंधित समूह (नीचे "अनुभाग" देखें)
यहाँ मेरे द्वारा संबोधित चीजों के प्रहार से एक झटका लगता है:
चेतावनियों का उपयोग करें (कम से कम -Wextra-upantic) और उन्हें स्वाट करें:
test.cpp:43:18: warning: unused variable ‘i’ [-Wunused-variable] 43 | unsigned int i = 0; test.cpp:74:18: warning: unused variable ‘i’ [-Wunused-variable] 74 | unsigned int i = 0; test.cpp:119:39: warning: unused parameter ‘processorID’ [-Wunused-parameter] 119 | void whenPEisGPU(DAG_data Sample, int processorID) test.cpp:259:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<DAG_data>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 259 | for (int h = 0; h < All_DAGs.size(); h++) test.cpp:277:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<DAG_data>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 277 | for (int i = 0; i < All_DAGs.size(); i++) test.cpp:290:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘std::vector<GPU_data>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare] 290 | for (int j = 0; j < All_DAGs[i].Node_config.size(); j++) test.cpp:204:1: warning: control reaches end of non-void function [-Wreturn-type] 204 | }
परिवर्तन:
CreateandAssignEdges: - unsigned int i = 0; EdgeAssignment: - unsigned int i = 0; -void whenPEisGPU(DAG_data Sample, int processorID) +void whenPEisGPU(DAG_data Sample, int /*processorID*/) PEParameters: + throw std::range_error("processorID"); - for (int h = 0; h < All_DAGs.size(); h++) + for (size_t h = 0; h < All_DAGs.size(); h++) - for (int i = 0; i < All_DAGs.size(); i++) + for (size_t i = 0; i < All_DAGs.size(); i++) - for (int j = 0; j < All_DAGs[i].Node_config.size(); j++) + for (size_t j = 0; j < All_DAGs[i].Node_config.size(); j++)
आधुनिकीकरण / पठनीयता जांच चलाने से कई जादुई संख्या की चेतावनी और कुछ आसान सुधार दिखाई देते हैं:
clang-apply-replacements version 9.0.0 clang-tidy-9 -header-filter=.* -checks=-*,readability-*,modernize-*,-modernize-use-trailing-return-type -export-fixes /tmp/tmp6CfbSr/tmpYGk6CX.yaml -p=/home/sehe/Projects/stackoverflow /home/sehe/Projects/stackoverflow/test.cpp /home/sehe/Projects/stackoverflow/test.cpp:59:66: warning: 29 is a magic number; consider replacing it with a named constant [readability-magic-numbers] Sample.follow = (Sample.processid + 1) + (std::rand() % (29 - (Sample.processid) + 1)); ^ /home/sehe/Projects/stackoverflow/test.cpp:61:30: warning: 30 is a magic number; consider replacing it with a named constant [readability-magic-numbers] if (Sample.follow == 30) ^ /home/sehe/Projects/stackoverflow/test.cpp:81:5: warning: do not use 'else' after 'return' [readability-else-after-return] else if (Sample.processid == NoOfNodes - 1) ^~~~~ /home/sehe/Projects/stackoverflow/test.cpp:92:66: warning: 29 is a magic number; consider replacing it with a named constant [readability-magic-numbers] Sample.follow = (Sample.processid + 1) + (std::rand() % (29 - (Sample.processid) + 1)); ^ /home/sehe/Projects/stackoverflow/test.cpp:119:32: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] int NoOfConfigs = rand() % 5; ^ /home/sehe/Projects/stackoverflow/test.cpp:123:29: warning: implicit conversion 'int' -> bool [readability-implicit-bool-conversion] sub_tasks->memory = rand() % 1; ^ (( ) != 0) /home/sehe/Projects/stackoverflow/test.cpp:125:42: warning: 10 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks->workGroups = rand() % 10 +1; ^ /home/sehe/Projects/stackoverflow/test.cpp:127:49: warning: 250 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks->power_consumption = rand() % 250; ^ /home/sehe/Projects/stackoverflow/test.cpp:138:36: warning: 100 is a magic number; consider replacing it with a named constant [readability-magic-numbers] Sample.ExecTime = rand() % 100; ^ /home/sehe/Projects/stackoverflow/test.cpp:148:36: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] int NoOfConfigs = rand() % 5; ^ /home/sehe/Projects/stackoverflow/test.cpp:152:32: warning: implicit conversion 'int' -> bool [readability-implicit-bool-conversion] sub_tasks.memory = rand() % 1; ^ (( ) != 0) /home/sehe/Projects/stackoverflow/test.cpp:154:45: warning: 10 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks.workGroups = rand() % 10 + 1; ^ /home/sehe/Projects/stackoverflow/test.cpp:156:52: warning: 250 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks.power_consumption = rand() % 250; ^ /home/sehe/Projects/stackoverflow/test.cpp:170:36: warning: 100 is a magic number; consider replacing it with a named constant [readability-magic-numbers] Sample.ExecTime = rand() % 100; ^ /home/sehe/Projects/stackoverflow/test.cpp:177:5: warning: do not use 'else' after 'return' [readability-else-after-return] else if (processorID == 1) ^~~~~ /home/sehe/Projects/stackoverflow/test.cpp:182:36: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] int NoOfConfigs = rand() % 5; ^ /home/sehe/Projects/stackoverflow/test.cpp:186:32: warning: implicit conversion 'int' -> bool [readability-implicit-bool-conversion] sub_tasks.memory = rand() % 1; ^ (( ) != 0) /home/sehe/Projects/stackoverflow/test.cpp:188:45: warning: 10 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks.workGroups = rand() % 10 + 1; ^ /home/sehe/Projects/stackoverflow/test.cpp:190:52: warning: 250 is a magic number; consider replacing it with a named constant [readability-magic-numbers] sub_tasks.power_consumption = rand() % 250; ^ /home/sehe/Projects/stackoverflow/test.cpp:211:42: warning: 30 is a magic number; consider replacing it with a named constant [readability-magic-numbers] if (myTaskGraph[i].follow == 30) ^ /home/sehe/Projects/stackoverflow/test.cpp:216:26: warning: 100 is a magic number; consider replacing it with a named constant [readability-magic-numbers] if (rand() % 100 < 30) ^ /home/sehe/Projects/stackoverflow/test.cpp:216:32: warning: 30 is a magic number; consider replacing it with a named constant [readability-magic-numbers] if (rand() % 100 < 30) ^ /home/sehe/Projects/stackoverflow/test.cpp:246:36: warning: 10 is a magic number; consider replacing it with a named constant [readability-magic-numbers] DAG1= EdgeAssignment(DAG1, 10); ^ /home/sehe/Projects/stackoverflow/test.cpp:264:41: warning: 29 is a magic number; consider replacing it with a named constant [readability-magic-numbers] if (All_DAGs[h].processid ==29) ^ /home/sehe/Projects/stackoverflow/test.cpp:274:5: warning: use range-based for loop instead [modernize-loop-convert] for (size_t i = 0; i < All_DAGs.size(); i++) ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (auto & All_DAG : All_DAGs) 7510 warnings generated. Suppressed 7485 warnings (7485 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. Applying fixes ...
कम से कम रंग-बिरंगे लूप को तुरंत लें:
for (auto& DAG : All_DAGs) { myFile << "Node id: " << DAG.processid << std::endl; myFile << "Following Edge: " << DAG.follow << std::endl; myFile << "Transfer Data: " << DAG.transData << std::endl; myFile << "Node PE: " << DAG.PEid << std::endl; if (DAG.PEid == 0) { myFile << "Execution time: " << DAG.ExecTime << std::endl; } else { myFile << "-------------------------------" << std::endl; for (auto& cfg : DAG.Node_config) { myFile << "Execution time: " << cfg.execTime << std::endl; myFile << "Copies: " << cfg.number_Copies << std::endl; myFile << "Memory: " << cfg.memory << std::endl; myFile << "Work-Items: " << cfg.workItems << std::endl; myFile << "Work-Groups: " << cfg.workGroups << std::endl; myFile << "Power: " << cfg.power_consumption << std::endl; myFile << "++++++++++++++++++" << std::endl; } } myFile << "=================" << std::endl; }
घोषणा से अनावश्यक रूप से अलग न करें।
std::ofstream myFile; // 40 lines... myFile.open("TG_Data_30NewEdges.txt");
अनावश्यक रूप से मैन्युअल रूप से संसाधन प्रबंधन न करें:
myFile.close();
C ++ का RAII पैटर्न का मतलब है कि फ़ाइल हमेशा बंद रहेगी।
{ std::ofstream output("TG_Data_30NewEdges.txt"); for (auto& DAG : All_DAGs) { // ... } }
नोट मैंने भी
myFile
कुछ और वर्णनात्मक नाम दिया ।उपरोक्त के लिए कुछ कार्य निकालने का समय:
std::ofstream output("TG_Data_30NewEdges.txt"); writeReport(output, All_DAGs);
और फिर कहीं और:
using DAGs = std::vector<DAG_data>; void writeReport(std::ostream& output, DAGs const& graphs) { for (auto& g : graphs) { // ... } }
डी-मिस्ट्री लूप्स
unsigned int i = 0; while (i != myTaskGraph.size()) { // ... i++; }
के रूप में पारंपरिक रूप से लिखा गया है
for (size_t i = 0; i < myTaskGraph.size(); ++i) { // ... }
या, जब से c ++ 0x:
for (Node& node : myTaskGraph) { // ... }
इसी तरह कंटेनर बनाने वाले छोरों को संभवतः अधिक पढ़ना चाहिए:
Nodes nodes(NoOfNodes); size_t i = 0; for (auto& current : nodes) { current.processid = i; current.transData = i + 1; current.PEid = 0; i++; current = PEParameters(current, current.PEid); current = EdgeAssignment(current, 10); }
तथा
void whenPEisGPU(Node& node, int /*processorID*/) { int NoOfConfigs = rand() % 5; node.Node_config.assign(NoOfConfigs, {}); for (auto& sub_task : node.Node_config) { sub_task.memory = ((rand() % 1) != 0); sub_task.number_Copies = rand() % 3; sub_task.workGroups = rand() % 10 +1; sub_task.workItems = rand() % (sub_task.workGroups * 2) + 1; sub_task.power_consumption = rand() % 250; sub_task.execTime = rand() % (int)(node.ExecTime / 2); } }
आदि।
मैं शायद उन्हें
std::generate_n
वास्तविक जीवन में कॉल के रूप में लिखूंगा , लेकिन शायद हम स्वाभाविक रूप से वहां पहुंचेंगे, बाद में नीचेनामकरण। कहीं-कहीं कोड आधा हो जाता है, अचानक हमें एक झलक मिलती है कि हम वास्तव में क्या कर रहे हैं:
void generateEdges(std::vector<DAG_data> &myTaskGraph)
तो, मुझे लगता है कि हम नाम
DAG_data
Node
याTask
(या भीTaskNode
?) सकता है।इसी तरह, हम यहां सूक्ष्म संकेत प्राप्त करते हैं:
if (Sample.processid == 0) { //parent process- so there will be no edges
तथा
else if (node.processid == NoOfNodes - 1) { // sink process- so there will be no following edges
साइड नोट: आप उपयोग करते हैं
parent
जैसे कि इसका अर्थ है "कोई किनारा नहीं"। जो प्रदर्शनकारी रूप से गलत है, क्योंकि आप तुरंत एक अनुयायी बढ़त निर्धारित करते हैं । आपको जो प्रतीत होता है वह "माता-पिता के बिना माता-पिता" है, जो कि एक डीएजी में आमतौर पर "मूल" के रूप में जाना जाता है। ध्यान दें, कि यदि आपके पास केवल 1 रूट वाला DAG है, तो इसे ट्री क्यों न कहें?// फ़ाइल के तहत: नामकरण महत्वपूर्ण है
इसलिए, हमें इसे और अधिक पठनीय बनाना चाहिए:
using ProcessID = int; static constexpr size_t NoOfNodes = 30; static constexpr ProcessID RootNodeId = 0; static constexpr ProcessID SinkNodeId = NoOfNodes - 1; // ... static bool constexpr IsSink(ProcessID id) { return SinkNodeId == id; } static bool constexpr IsSink(Node const& node) { return IsSink(node.processid); } // etc?
वास्तव में, शायद पूरी चीज़ को संयोजित करना बेहतर है:
enum ProcessID : int { RootNodeId = 0, NoOfNodes = 30, SinkNodeId = NoOfNodes -1, };
इससे सभी मैजिक नंबर (
= 0
हो जाता है= RootNodeId
) की भारी कमी हो जाती है ।हालाँकि, यह हमें समस्या को अन्य "जादू" असाइनमेंट के साथ संबोधित करने के लिए मजबूर करता है:
node.follow = rand() % NoOfEdges + 1; node.follow = (node.processid + 1) + (std::rand() % (29 - (node.processid) + 1));
मेरा मतलब है, हम उन रास्तों को संबोधित करने जा रहे थे (क्योंकि, ऊ और तिरछी यादृच्छिक)।
तो, चलो यादृच्छिक पता! आपने सही ढंग से शुरुआत की:
#include <random>
लेकिन उस खजाने से एक चीज़ का इस्तेमाल नहीं किया !
std::mt19937 prng { std::random_device{} () };
अब हमारे पास हमारी यूनिफ़ॉर्मग्रैंडबीटगेंसर है और हम सुरक्षित रूप से इसका बीजारोपण करते हैं!
आइए कुछ सहायक कार्य बनाएं जो समान रूप से वितरित संख्याओं को उत्पन्न करने में हमारी मदद करेंगे:
अधिकतम और सहित संख्या उत्पन्न करें:
auto gen_number(int max, bool includeZero = true) { using Dist = std::uniform_int_distribution<>; using Param = Dist::param_type; static Dist dist; auto min = includeZero? 0:1; assert(max >= min); return dist(prng, Param(min, max)); }
[1, अधिकतम] यादृच्छिक नमूने के लिए एक छोटा हाथ जोड़ना:
auto gen_positive(int max) { return gen_number(max, false); }
अब, ProcessID उत्पन्न करने के लिए हमें कुछ रूपांतरणों की आवश्यकता है और हम सीमा सीमाओं के लिए कुछ चूक मान सकते हैं:
ProcessID gen_follower(int from = FirstFollow, int to = NoOfNodes) { using T = std::underlying_type_t<ProcessID>; using Dist = std::uniform_int_distribution<T>; using Param = Dist::param_type; static Param full{static_cast<T>(FirstFollow), static_cast<T>(NoOfNodes)}; static Dist dist(full); return static_cast<ProcessID>(dist(prng, Param(from, to))); }
अब हम भावों को फिर से लिख सकते हैं:
// node.follow = rand() % NoOfEdges + 1; node.follow = gen_follower(FirstFollow, NoOfEdges);
तथा
// node.follow = // (node.processid + 1) + (std::rand() % (29 - (node.processid) + 1)); node.follow = gen_follower(node.processid+1);
बहुत सरल, टाइप-सेफ और यूनिफ़ॉर्म!
अब, इस बारे में कुछ अजीब बातें हैं।
हर जगह डोमेन
follow
से होना निहित हैProcessId
। हालाँकि, के बजाय अभिव्यक्तिgen_follower(FirstFollow, NoOfEdges)
का उपयोग करता है ?! केवल एक कॉल के लिए भी हार्डकोड किया गया है ।NoOfEdges
NoOfNodes
NoOfEdges
10
EdgeAssignment
क्या आप सुनिश्चित हैं कि रूट नोड के लिए "मनमानी" सीमा अनुयायी नोड्स की
[1..10]
परवाह किए बिना आप का मतलब हैNoOfNodes
?चूंकि बाद के अनुयायियों को हमेशा "डाउन-स्ट्रीम" लिया जाता है, इसलिए मैं अनुमान लगा सकता हूं कि आप "ग्रैंड चिल्ड्रन" बनने के लिए सबटुक की संभावना को बढ़ाने के लिए केवल "पहले 10" विभाजन से चुनना चाहते थे। यदि हां, तो नाम
NoOfEdges
पूरी तरह से भ्रामक है, और ऐसा कुछ हो सकता हैFirstGenerationNodes
? 'ऐसे दो स्थान हैं जहाँ इन अभिव्यक्तियों के परिणाम को सही किया जा रहा है:
if (myTaskGraph[i].follow == 30) { myTaskGraph[i].follow -= 1; } if (Sample.follow == 30) { Sample.follow -= 1; }
यदि यह वांछित सीमा है, तो बस अपने भाव ठीक करें!
जैसा कि लिखा गया है कि यह कोड को समझने में कठिन बनाता है, फ़ंक्शंस में ज़िम्मेदारी फैलाता है (जो बग को आमंत्रित करता है) और वितरण को और भी अधिक
29
सीमित कर देता है : अब एक बहुत अधिक संभावना वाला लक्ष्य है।मैंने इस अन्य टिप्पणी से निहित इरादे से मेल खाने के लिए अभिव्यक्ति को ठीक करने के लिए चुना:
// which nodes will the edges connect to (Anywhere from among the // following nodes, including the sink node) node.follow = gen_follower(node.processid+1, SinkNodeId);
कोड दोहराव। सबटैक्शंस (
node.Node_config
) की पीढ़ी को डुप्लिकेट किया गया है, जिसमें कुछ सहज अंतर हैं जो बग हो सकते हैं, लेकिन इरादतन हो सकते हैं?जैसे:
sub_task.number_Copies = rand() % 3 + 1;
तीन प्रतियों में से एक को छोड़ दिया जाता है
+1
जो कि बग की संभावना है।इसी तरह की शैली में हम इसकी एक प्रति देखते हैं
sub_task.execTime = rand() % static_cast<int>(node.ExecTime / 2);
जो जोड़ता है a
+1
। संभवतः यह शून्य से बचा जाता हैexecTime
, और एक कोड गंध है कि यह भी एक मजबूत-टाइप, समान वास्तविक यादृच्छिक वितरण होना चाहिए था।यह अनुमान लगाना कठिन है कि आप वास्तव में क्या चाहते हैं
execTime
। यदि आप इसका मतलब यह मानते हैं कि माता-पिता का नोड हमेशा निष्पादित होता है, तो वे अपने उप-योगों का योग बनाते हैं, जो कि कुछ व्यावसायिक तर्क के साथ व्यक्त करने के लिए आसान है, बजाय आपके डेटास्ट्रक्चर में डेटा को निरर्थक करने और अनिर्दिष्ट अनियंत्रित (जो, फिर से, बग को आमंत्रित करते हैं) ) का है।मज़े के लिए, मैंने जोड़ा कि मैं वितरण को किस तरह लिखूंगा:
void distributeExecTime(Node& node) { std::vector<double> weights; std::uniform_real_distribution<> dist; std::generate_n( back_inserter(weights), node.Node_config.size(), [&dist] { return dist(prng); }); auto total_w = std::accumulate(begin(weights), end(weights), 0.); for (size_t i = 0; i < weights.size(); ++i) { node.Node_config[i].execTime = (weights[i]/total_w) * node.ExecTime; } }
कुल पावर ड्रॉ के लिए ऐसी ही बातें चल रही हैं। शायद आप पावरड्राइव को किसी फ़ंक्शन से बदल सकते हैं:
double powerDraw() const { return std::accumulate(begin(Node_config), end(Node_config), 0.); };
बक्शीश
किनारे पर जा रहे हैं, हम एक ऐसी दुनिया की कल्पना कर सकते हैं जहाँ जनरेटिंग "स्वचालित" हो, जैसा कि रिपोर्टिंग है:
निर्माण में चलती पीढ़ी पर विचार करें:
struct GPU_data { int number_Copies = gen_positive(3); int workGroups = gen_positive(10); // order is important! int workItems = gen_positive(workGroups * 2); bool memory = odds(50); double power_consumption = gen_real(249); double execTime = 0; // see distributeExecTime };
ध्यान दें
- हम अपने लिए डिफ़ॉल्ट कंस्ट्रक्टर बनाने के लिए C ++ 11 NSMI का उपयोग कर रहे हैं
struct Node { enum Type { CPUNode, GPUNode }; Type PEid; // Processor's ID to which node is assigned ProcessID processid; // Node's ID Configs sub_tasks; ProcessID follow = RootNodeId; // nodes following this node double transData = 0; double ExecTime = 0; explicit Node(int id, int NoOfEdges = 10) : PEid(CPUNode), processid(ProcessID(id)), transData(id + 1) { PEParameters(); EdgeAssignment(NoOfEdges); } explicit Node(Node const& node) : PEid(GPUNode), processid(node.processid), sub_tasks(), follow(node.follow), transData(node.transData), ExecTime(node.ExecTime) { PEParameters(); } double powerDraw() const; bool isGPU() const { return PEid == GPUNode; } private: void PEParameters(); void EdgeAssignment(int NoOfEdges); void distributeExecTime(); };
अब,
Node
इसके साथ समूह कार्य कर सकता है:इस प्रकार का अनुमान है कि प्रकार पहले से कहीं और उपयोग में नहीं हैं। यदि ऐसा नहीं होता है, तो हम इसके आधार वर्ग में वापस बदलने के लिए ऑब्जेक्ट स्लाइसिंग से प्रकारों को लाभ और उप-वर्ग कर सकते हैं।
नोट के रूप में अच्छी तरह से कोड (PEParameters, output और EdgeAssignment) में कई स्थानों पर PEID पर व्यवहार बदल जाता है, जिसमें स्पष्ट रूप से केवल दो वैध मान होते हैं। मैंने उस तथ्य को दर्शाते हुए एक प्रतिरूप होने के लिए बदल दिया है:
enum Type { CPUNode, GPUNode }; Type PEid; // Processor's ID to which node is assigned
पाठक के लिए एक अभ्यास के रूप में, यह
Node
हर समय स्विच करने के बजाय किसी प्रकार के बहुरूपी प्रकार में बदलने के लिए समझ में आता है :using Node = std::variant<CPUNode, GPUNode>;
या वर्चुअल प्रकार (इनहेरिटेंस) का उपयोग करना।
डेमो लिस्टिंग (s)
सभी रिविज़न यहाँ एक Gist में हैं: https://gist.github.com/sehe/32c07118031a049042bd9fb469355caf/revisions
कोलिरु पर रहते हैं
#include <iostream>
#include <algorithm> // std::min_element, std::max_element
#include <fstream>
#include <string>
#include <random>
#include <vector>
#include <cassert>
namespace {
static std::mt19937 prng { std::random_device{} () };
enum ProcessID : int {
RootNodeId /*= 0 */,
NoOfNodes = 30,
FirstFollow = RootNodeId +1,
SinkNodeId = NoOfNodes -1,
};
auto gen_number(int max, bool includeZero = true) {
using Dist = std::uniform_int_distribution<>;
using Param = Dist::param_type;
static Dist dist;
auto min = includeZero? 0:1;
assert(max >= min);
return dist(prng, Param(min, max));
}
auto gen_positive(int max) {
return gen_number(max, false);
}
ProcessID gen_follower(int from = FirstFollow, int to = NoOfNodes) {
using T = std::underlying_type_t<ProcessID>;
using Dist = std::uniform_int_distribution<T>;
using Param = Dist::param_type;
static Param full{static_cast<T>(FirstFollow), static_cast<T>(NoOfNodes)};
static Dist dist(full);
return static_cast<ProcessID>(dist(prng, Param(from, to)));
}
bool odds(int percentage) {
if (percentage == 100)
return true;
assert(percentage > 0 && percentage < 100);
return std::discrete_distribution<bool>(percentage, 100-percentage)(prng);
}
double gen_real(double mean = 100.0, double stddev = 0) {
if (stddev == 0)
stddev = mean/4;
assert(stddev>0);
return std::normal_distribution(mean, stddev)(prng);
}
}
struct GPU_data {
int number_Copies = gen_positive(3);
int workGroups = gen_positive(10); // order is important!
int workItems = gen_positive(workGroups * 2);
bool memory = odds(50);
double power_consumption = gen_real(249);
double execTime = 0; // see distributeExecTime
};
using Configs = std::vector<GPU_data>;
struct Node {
enum Type { CPUNode, GPUNode };
Type PEid; // Processor's ID to which node is assigned
ProcessID processid; // Node's ID
Configs sub_tasks;
ProcessID follow = RootNodeId; // nodes following this node
double transData = 0;
double ExecTime = 0;
explicit Node(int id, int NoOfEdges = 10)
: PEid(CPUNode),
processid(ProcessID(id)),
transData(id + 1)
{
PEParameters();
EdgeAssignment(NoOfEdges);
}
explicit Node(Node const& node)
: PEid(GPUNode),
processid(node.processid),
sub_tasks(),
follow(node.follow),
transData(node.transData),
ExecTime(node.ExecTime)
{
PEParameters();
}
double powerDraw() const {
double total = 0;
for (auto& sub: sub_tasks) {
total += sub.power_consumption;
}
return total;
};
bool isGPU() const { return PEid == GPUNode; }
private:
void PEParameters() {
switch(PEid) {
case CPUNode:
ExecTime = gen_real(100.0);
break;
case GPUNode:
sub_tasks.resize(gen_number(5));
distributeExecTime();
break;
default:
throw std::range_error("PEid");
}
}
void EdgeAssignment(int NoOfEdges) {
if (processid == RootNodeId) {
// parent process- so there will be no edges
follow = gen_follower(FirstFollow, NoOfEdges);
}
else if (processid == SinkNodeId) {
// sink process- so there will be no following edges
follow = RootNodeId;
}
else {
// which nodes will the edges connect to (Anywhere from among the
// following nodes, including the sink node)
follow = gen_follower(processid+1, SinkNodeId);
}
}
void distributeExecTime() {
std::vector<double> weights;
std::uniform_real_distribution<> dist;
std::generate_n(
back_inserter(weights),
sub_tasks.size(),
[&dist] { return dist(prng); });
auto total_w = std::accumulate(begin(weights), end(weights), 0.);
for (size_t i = 0; i < weights.size(); ++i) {
sub_tasks[i].execTime = (weights[i]/total_w) * ExecTime;
}
}
};
using Nodes = std::vector<Node>;
void generateEdges(Nodes& nodes) {
for (Node& node : nodes) {
// Create an edges to following nodes given 30% odds
for (size_t j = node.processid+1; j < nodes.size(); j++) {
if (odds(30)) {
node.follow = static_cast<ProcessID>(j);
break;
}
}
}
}
static std::ostream& operator<<(std::ostream& os, Node const& n);
int main() {
Nodes nodes;
for (auto id = 0; id < NoOfNodes; ++id) {
nodes.emplace_back(id);
}
generateEdges(nodes);
for (size_t h = 0; h < NoOfNodes; h++) {
if (h % 2 == 0)
continue;
nodes.emplace_back(nodes[h]);
nodes[h].sub_tasks.clear();
}
std::ofstream output("TG_Data_30NewEdges.txt");
for (auto& n : nodes) {
output << n << "=================\n";
}
std::cout << "DONE" << std::endl;
}
static std::ostream& operator<<(std::ostream& os, GPU_data const& cfg) {
return os
<< "Execution time: " << cfg.execTime << "\n"
<< "Copies: " << cfg.number_Copies << "\n"
<< "Memory: " << cfg.memory << "\n"
<< "Work-Items: " << cfg.workItems << "\n"
<< "Work-Groups: " << cfg.workGroups << "\n"
<< "Power: " << cfg.power_consumption << "\n";
}
static std::ostream& operator<<(std::ostream& os, Node const& n) {
os << "Node id: " << n.processid << "\n"
<< "Following Edge: " << n.follow << "\n"
<< "Transfer Data: " << n.transData << "\n"
<< "Node powerDraw: " << n.powerDraw() << "\n"
<< "Node PE: " << n.PEid << "\n";
if (n.isGPU()) {
os << "-------------------------------\n";
for (auto& cfg : n.sub_tasks) {
os << cfg << "++++++++++++++++++\n";
}
} else {
os << "Execution time: " << n.ExecTime << "\n";
}
return os;
}
प्रिंट, उदाहरण के लिए
DONE
और TG_Data_30NewEdges.txt उत्पन्न करता है:
Node id: 0
Following Edge: 1
Transfer Data: 1
Node powerDraw: 1020.61
Node PE: 1
-------------------------------
Execution time: 12.2428
Copies: 1
Memory: 1
Work-Items: 10
Work-Groups: 9
Power: 229.989
++++++++++++++++++
Execution time: 39.2756
Copies: 1
// ...
// 825 lines snipped
// ...
Copies: 3
Memory: 1
Work-Items: 3
Work-Groups: 9
Power: 235.512
++++++++++++++++++
=================
#define NoOfNodes 30
मुझे लगता है कि static constexpr
प्रीप्रोसेसर मैक्रो के बजाय यहां उपयोग करना बेहतर होगा ।
//which nodes will the edges connect to (Anywhere from among the following nodes, including the sink node) Sample.follow = (Sample.processid + 1) + (std::rand() % (29 - (Sample.processid) + 1)); if (Sample.follow == 30) { Sample.follow -= 1; }
कहाँ स्थिरांक करना 29
और 30
से आते हैं? क्या उन्हें NoOfNodes
इसके बजाय व्युत्पन्न किया जाना चाहिए?
C ++ <random>
लाइब्रेरी का उपयोग करना बेहतर हो सकता है std::rand()
।
CreateandAssignEdges()
और EdgeAssignment()
बहुत समान हैं - मुझे लगता है कि दोहराव को बहुत कम किया जा सकता है।
//Sample->precede = rand() % NoOfEdges; //Sample->follow = rand() % NoOfEdges; ////Preceding and following edges of a node should not be the same. //while (Sample->precede > Sample->follow || Sample->precede == Sample->follow) //{ // //assign both edges again // Sample->follow = rand() % NoOfEdges; // Sample->precede = rand() % NoOfEdges; //}
इस तरह से टिप्पणी की गई कोड के टुकड़े अक्सर एक समस्या बन जाते हैं, तारीख से बाहर हो जाते हैं और आस-पास के कोड के रूप में असंगत हो जाते हैं। या तो इसे हटा दें, या यह सुनिश्चित करने का एक तरीका खोजें कि यह बाकी कोड के साथ संकलित और इकाई-परीक्षण किया जाए।
myFile << "Node id: " << All_DAGs[i].processid << std::endl; myFile << "Following Edge: " << All_DAGs[i].follow << std::endl; myFile << "Transfer Data: " << All_DAGs[i].transData << std::endl; myFile << "Node PE: " << All_DAGs[i].PEid << std::endl;
myFile
प्रत्येक कथन को फ्लश करने की कोई वास्तविक आवश्यकता नहीं है - इन सभी के लिए पसंद '\n'
करें std::endl
(और शेष सभी उपयोगों में से अधिकांश / सभी)।