임의 DAG를 생성하는 C ++ 코드

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라는 사실은 "FirstName"및 "ZipCode"대신 "textstring"변수를 호출하는 것과 같습니다.

  • 일부 기능을 추출하십시오. 사용하여

    • 별도의 책임,
    • 추상화 수준
    • 중복 감소
  • 선택적으로 관련 함수를 데이터와 함께 클래스로 그룹화합니다 (아래 "보너스"섹션 참조).


여기에 내가 언급 한 것들에 대한 타격이 있습니다.

  1. 경고 (최소 -Wall -Wextra -pedantic)를 사용하고 두 드리십시오.

    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 루프를 즉시 사용하십시오.

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

    C ++의 RAII 패턴은 파일이 항상 닫힙니다.

    {
        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가있는 경우이를 트리라고 부르지 않는 이유는 무엇입니까?

    // 파일 아래 : 이름 지정이 중요합니다.

    따라서 더 읽기 쉽게 만들어야합니다.

    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, max] 임의 샘플에 대한 짧은 손 추가 :

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

    훨씬 간단하고 형식이 안전하며 균일합니다!

    자, 이것에 대해 이상한 점이 있습니다.

    • 모든 곳 followProcessId도메인 에서 온 것을 의미 합니다. 그러나, 표현은 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. 아마도 이것은 0을 피하고 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, 출력 및 EdgeAssignment)는 분명히 두 개의 유효한 값만있는 PEid의 동작을 전환합니다. 그 사실을 반영하는 열거 형으로 변경했습니다.

      enum Type { CPUNode, GPUNode };
      Type      PEid;      // Processor's ID to which node is assigned
      

      독자를위한 연습으로 Node항상 전환하는 대신 일종의 다형성 유형 으로 변경 하는 것이 좋습니다.

      using Node = std::variant<CPUNode, GPUNode>;
      

      또는 가상 유형 (상속)을 사용합니다.

데모 목록

모든 수정 사항은 여기 요점에 있습니다. 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
++++++++++++++++++
=================
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;
    }

어디 상수를 할 2930에서 오는? NoOfNodes대신에 파생되어야합니까 ?

<random>.NET보다 C ++ 라이브러리 를 사용하는 것이 더 나을 수 있습니다 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(그리고 나머지 사용의 대부분 / 모두).