รหัส C ++ เพื่อสร้าง DAG แบบสุ่ม

Jan 11 2021

ฉันเขียนโค้ด 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 ++ ที่ต้องการได้ดีขึ้น

คำตอบ

9 sehe Jan 13 2021 at 02:34

ข้อผิดพลาดที่สำคัญ:

  • การสุ่มของคุณไม่ใช่การสุ่ม (เพาะเมล็ด)

  • การสุ่มของคุณไม่สม่ำเสมอ (ใช้การแจกแจงแบบสม่ำเสมอแทนที่จะใช้โมดูลัสซึ่งจะบิดเบือนการแจกแจง)

  • 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"

  • แยกบางฟังก์ชัน ใช้เพื่อ

    • แยกความรับผิดชอบ
    • ระดับของนามธรรม
    • ลดความซ้ำซ้อน
  • เลือกที่จะจัดกลุ่มฟังก์ชันที่เกี่ยวข้องกับข้อมูลเป็นคลาส (ดูส่วน "โบนัส" ด้านล่าง)


นี่คือสิ่งที่ฉันพูดถึง:

  1. ใช้คำเตือน (-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++)
    
  2. การเรียกใช้การตรวจสอบความทันสมัย ​​/ การอ่านจะแสดงคำเตือนจำนวนเวทย์มนตร์จำนวนมากและการปรับปรุงที่ง่าย:

    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;
    }
    
  3. อย่าแยกการเริ่มต้นออกจากการประกาศโดยไม่จำเป็น

    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สิ่งที่สื่อความหมายได้ดีกว่า

  4. ถึงเวลาแยกฟังก์ชั่นบางอย่างสำหรับข้างต้น:

    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) {
            // ...
        }
    }
    
  5. ลบความลึกลับของลูป

    unsigned int i = 0;
    while (i != myTaskGraph.size()) {
        // ...
        i++;
    }
    

    เขียนตามอัตภาพเป็น

    for (size_t i = 0; i < myTaskGraph.size(); ++i) {
        // ...
    }
    

    หรือนับตั้งแต่ c ++ 0x:

    for (Node& node : myTaskGraph) {
        // ...
    }
    
  6. ในทำนองเดียวกันลูปที่สร้างคอนเทนเนอร์ควรอ่านเพิ่มเติมเช่น:

    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เรียกในชีวิตจริง แต่บางทีเราอาจจะไปถึงที่นั่นโดยธรรมชาติในภายหลัง

  7. การตั้งชื่อ. ที่ไหนสักแห่งครึ่งหนึ่งของรหัสทันใดนั้นเราก็เห็นสิ่งที่เรากำลังจัดการอยู่:

    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?
    
  8. ในความเป็นจริงอาจจะเป็นการดีกว่าที่จะรวมสิ่งทั้งหมด:

    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));
    

    ฉันหมายความว่าเราจะจัดการกับทางเดินเหล่านั้น (เพราะเอ่อและเบ้แบบสุ่ม)

  9. ดังนั้นเรามาสุ่มกันเถอะ! คุณเริ่มต้นอย่างถูกต้อง:

    #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);
      
  10. การทำสำเนารหัส การสร้างงานย่อย ( 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;
            }
        }
    
  11. สำหรับการดึงพลังงานทั้งหมดดูเหมือนจะมีสิ่งที่คล้ายกันเกิดขึ้น บางทีคุณสามารถแทนที่ 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
++++++++++++++++++
=================
1 TobySpeight Jan 11 2021 at 18:32
#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ใช้สิ่งเหล่านี้ทั้งหมด (และส่วนใหญ่ / การใช้งานที่เหลือทั้งหมด)