รหัส C ++ เพื่อสร้าง DAG แบบสุ่ม
ฉันเขียนโค้ด 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
หมายความว่าถัดจากความว่างเปล่า แบบจำลองกราฟของคุณแสดงถึงบางสิ่งในชีวิตจริง การที่มันเป็น DAG ก็เหมือนกับการเรียกตัวแปร "textstring" แทน "FirstName" และ "ZipCode"แยกบางฟังก์ชัน ใช้เพื่อ
- แยกความรับผิดชอบ
- ระดับของนามธรรม
- ลดความซ้ำซ้อน
เลือกที่จะจัดกลุ่มฟังก์ชันที่เกี่ยวข้องกับข้อมูลเป็นคลาส (ดูส่วน "โบนัส" ด้านล่าง)
นี่คือสิ่งที่ฉันพูดถึง:
ใช้คำเตือน (-Wall -Wextra -pedantic at least) และ swat:
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 ...
อย่างน้อยก็ใช้ ranged-for loop ทันที:
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();
รูปแบบ RAII ของ C ++ หมายความว่าไฟล์จะถูกปิดเสมอ
{ 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
ราวกับว่ามันหมายถึง "ไม่มีขอบ" ซึ่งไม่ถูกต้อง demonstratively เพราะทันทีที่คุณไม่ตั้งขอบติดตาม สิ่งที่คุณดูเหมือนจะหมายถึงคือ "ผู้ปกครองที่ไม่มีผู้ปกครอง" ซึ่งใน DAG มักเรียกว่า "ราก" โปรดทราบว่าหากคุณมี DAG ที่มีเพียง 1 รูททำไมไม่เรียกมันว่า Tree ล่ะ?// file under: การตั้งชื่อเป็นสิ่งสำคัญ
ดังนั้นเราควรทำให้อ่านง่ายขึ้น:
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{} () };
ตอนนี้เรามี UniformRandomBitGenerator ของเราแล้วและเราก็เพาะมันอย่างปลอดภัย!
มาสร้างฟังก์ชั่นตัวช่วยที่จะช่วยให้เราสร้างตัวเลขที่กระจายอย่างสม่ำเสมอ:
สร้างตัวเลขได้สูงสุดและรวมถึงสูงสุด:
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);
ที่เพิ่ม
+1
ไฟล์. ดูเหมือนว่าสิ่งนี้จะหลีกเลี่ยงศูนย์execTime
และเป็นกลิ่นรหัสที่ควรจะเป็นการแจกแจงแบบสุ่มจริงที่พิมพ์อย่างชัดเจนและสม่ำเสมอเดาได้ยากว่าแท้จริงแล้วคุณต้องการ
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; } }
สำหรับการดึงพลังงานทั้งหมดดูเหมือนจะมีสิ่งที่คล้ายกันเกิดขึ้น บางทีคุณสามารถแทนที่ powerDraw ด้วยฟังก์ชัน:
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 ที่สะท้อนความจริงนั้น:
enum Type { CPUNode, GPUNode }; Type PEid; // Processor's ID to which node is assigned
ในฐานะที่เป็นแบบฝึกหัดสำหรับผู้อ่านคุณควรเปลี่ยน
Node
เป็นโพลีมอร์ฟิกบางประเภทแทนที่จะสลับตลอดเวลา:using Node = std::variant<CPUNode, GPUNode>;
หรือใช้ประเภทเสมือน (การสืบทอด)
รายการสาธิต
การทบทวนทั้งหมดอยู่ที่นี่ใน Gist: https://gist.github.com/sehe/32c07118031a049042bd9fb469355caf/revisions
Live On Coliru
#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
ใช้สิ่งเหล่านี้ทั้งหมด (และส่วนใหญ่ / การใช้งานที่เหลือทั้งหมด)