Kode C ++ untuk menghasilkan DAG acak

Jan 11 2021

Saya menulis kode C ++ berikut beberapa waktu yang lalu untuk menghasilkan grafik acak untuk proyek yang saya kerjakan:

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

}

Kode memenuhi tujuannya untuk saya tetapi ada banyak ruang untuk perbaikan untuk kode ini. Harap beri tahu bagaimana kode ini dapat ditulis ulang untuk lebih mematuhi praktik C ++ yang diinginkan.

Jawaban

9 sehe Jan 13 2021 at 02:34

Kesalahan penting:

  • acak Anda tidak acak (seed it)

  • acak Anda tidak seragam (gunakan distribusi seragam daripada hanya mengambil modulus, yang akan memiringkan distribusi)

  • precedesering tidak dimulai; NoOfConfigssering tidak dimulai, dan tidak pernah digunakan?

  • Perulangan terakhir sebelum menulis file keluaran memodifikasi koleksi saat melakukan iterasi :

     for (size_t h = 0; h < nodes.size(); h++) {
         // ...
         nodes.push_back(forNewPE);
    

    Ini adalah anti pola. Anda hanya lolos begitu saja karena

         if (nodes[h].processid == 29) { break; }
    

    yang tentu saja menderita bilangan ajaib, dan dapat dengan mudah dimasukkan ke dalam kondisi loop sebagai gantinya:

     for (size_t h = 0; h < NoOfNodes; ++h) {
    
  • void PESpecificParameters(DAG_data Sample, int processorID) tidak digunakan.

    Ketika digunakan, itu tidak akan pernah berpengaruh (karena memiliki nilai kembalian netiher atau menyimpan referensi ke luar)

  • Sama dengan whenPEisGPU

  • Setelah menghapus kode duplikat, sepertinya PEParametersidentik dengan PESpecificParameters(lihat di bawah)

  • Demikian juga CreateandAssignEdgestidak terpakai dan sepertinya menduplikasi EdgeAssignment?

Catatan utama:

  • Penamaan! DAG_Datahampir tidak ada artinya. Model grafik Anda mewakili sesuatu dalam kehidupan nyata. Fakta bahwa ini adalah DAG seperti memanggil variabel "textstring" bukan "FirstName" dan "ZipCode"

  • Ekstrak beberapa fungsi. Gunakan mereka untuk

    • tanggung jawab terpisah,
    • tingkat abstraksi
    • mengurangi duplikasi
  • Secara opsional, kelompokkan fungsi terkait dengan datanya ke dalam kelas (lihat bagian "BONUS" di bawah)


Inilah pukulan demi pukulan dari hal-hal yang saya bahas:

  1. Gunakan peringatan (-Wall -Wextra -pedantic minimal) dan swat mereka:

    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 | }
    

    Perubahan:

    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. Menjalankan pemeriksaan modernisasi / keterbacaan menunjukkan banyak peringatan angka ajaib dan beberapa peningkatan mudah:

    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 ...
    

    Setidaknya segera ambil 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. Jangan memisahkan inisialisasi dari deklarasi secara tidak perlu.

    std::ofstream myFile;
    // 40 lines...
    myFile.open("TG_Data_30NewEdges.txt");
    

    Jangan mengelola sumber daya secara manual yang tidak perlu:

    myFile.close();
    

    Pola RAII C ++ berarti file akan selalu ditutup.

    {
        std::ofstream output("TG_Data_30NewEdges.txt");
    
        for (auto& DAG : All_DAGs)
        {
            // ...
        }
    }
    

    Perhatikan Saya juga mengganti nama myFilemenjadi sesuatu yang lebih deskriptif.

  4. Saatnya mengekstrak beberapa fungsi di atas:

    std::ofstream output("TG_Data_30NewEdges.txt");
    writeReport(output, All_DAGs);
    

    Dan kemudian di tempat lain:

    using DAGs = std::vector<DAG_data>;
    
    void writeReport(std::ostream& output, DAGs const& graphs) {
        for (auto& g : graphs) {
            // ...
        }
    }
    
  5. De-mistik loop

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

    Secara konvensional ditulis sebagai

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

    Atau, sejak c ++ 0x:

    for (Node& node : myTaskGraph) {
        // ...
    }
    
  6. Demikian pula loop yang membangun container mungkin harus membaca lebih lanjut seperti:

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

    Dan

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

    dll.

    Saya mungkin akan menulisnya sebagai std::generate_npanggilan dalam kehidupan nyata, tetapi mungkin kita secara alami akan tiba di sana, nanti di bawah

  7. Penamaan. Di suatu tempat di tengah jalan kode, tiba-tiba kita melihat sekilas apa yang sebenarnya kita hadapi:

    void generateEdges(std::vector<DAG_data> &myTaskGraph)
    

    Jadi, saya kira kita bisa memberi nama DAG_data Nodeatau Task(atau bahkan TaskNode?).

    Demikian juga, kami mendapatkan petunjuk halus di sini:

    if (Sample.processid == 0) {
        //parent process- so there will be no edges
    

    dan

    else if (node.processid == NoOfNodes - 1) {
        // sink process- so there will be no following edges
    

    Catatan Samping: Anda menggunakan parentseolah-olah itu berarti "tanpa tepi". Yang demonstratif tidak akurat, karena Anda segera lakukan menetapkan tepi follower. Apa yang Anda maksud adalah "orang tua tanpa orang tua", yang dalam DAG biasanya dikenal sebagai "root". Perhatikan juga, jika Anda memiliki DAG dengan hanya 1 root, mengapa tidak menyebutnya Tree?

    // file di bawah: penamaan itu penting

    Jadi, kita harus membuatnya lebih mudah dibaca:

    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. Faktanya, mungkin lebih baik menggabungkan semuanya:

    enum ProcessID : int {
        RootNodeId = 0,
        NoOfNodes  = 30,
        SinkNodeId = NoOfNodes -1,
    };
    

    Ini mengarah pada pengurangan besar semua angka ajaib ( = 0menjadi = RootNodeIddll).

    Namun, itu memaksa kita untuk mengatasi masalah dengan tugas "ajaib" lainnya:

    node.follow = rand() % NoOfEdges + 1;
    node.follow =
        (node.processid + 1) + (std::rand() % (29 - (node.processid) + 1));
    

    Maksud saya, kami akan membahasnya lagi (karena, ugh dan miring secara acak).

  9. Jadi, mari kita bahas secara acak! Anda memulai dengan benar:

    #include <random>
    

    tapi tidak pernah menggunakan apapun dari harta karun itu !

    std::mt19937 prng { std::random_device{} () };
    

    Sekarang kita memiliki UniformRandomBitGenerator dan kita telah melakukan seeding dengan aman!

    Mari buat beberapa fungsi pembantu yang akan membantu kita menghasilkan nomor yang terdistribusi secara seragam:

    Hasilkan angka hingga dan termasuk maks:

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

    Menambahkan short hand untuk sampel acak [1, max]:

    auto gen_positive(int max) {
        return gen_number(max, false);
    }
    

    Sekarang, untuk menghasilkan ProcessID kita memerlukan beberapa konversi dan kita dapat mengasumsikan beberapa default untuk batas jangkauan:

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

    Sekarang kita dapat mengubah ekspresi:

    // node.follow = rand() % NoOfEdges + 1;
    node.follow = gen_follower(FirstFollow, NoOfEdges);
    

    Dan

    // node.follow =
    //     (node.processid + 1) + (std::rand() % (29 - (node.processid) + 1));
    node.follow = gen_follower(node.processid+1);
    

    Jauh lebih sederhana, aman untuk tipe, dan seragam!

    Nah, ada beberapa hal aneh tentang ini.

    • Di mana follow- mana tersirat berasal dari ProcessIddomain. Namun, ekspresi gen_follower(FirstFollow, NoOfEdges)menggunakan NoOfEdgesbukannya NoOfNodes?! NoOfEdgesjuga hanya di-hardcode 10untuk satu panggilan ke EdgeAssignment.

      Apakah Anda yakin dimaksudkan untuk "sewenang-wenang" node pengikut batas untuk Root Node untuk [1..10]terlepas dari NoOfNodes?

      Karena pengikut berikutnya selalu dianggap "hilir", saya dapat menebak bahwa Anda ingin memilih dari partisi "10 pertama" hanya untuk meningkatkan kemungkinan subtugas menghasilkan "anak cucu". Jika demikian, namanya NoOfEdgesbenar-benar menyesatkan, dan bisa jadi seperti ini FirstGenerationNodes?)

    • Ada dua lokasi di mana hasil dari ekspresi ini dikoreksi:

       if (myTaskGraph[i].follow == 30) {
           myTaskGraph[i].follow -= 1;
       }
      
       if (Sample.follow == 30) {
           Sample.follow -= 1;
       }
      

      Jika itu rentang yang diinginkan, cukup perbaiki ekspresi Anda!

      Seperti yang tertulis, hal itu membuat kode sulit untuk dipahami, menyebarkan tanggung jawab ke seluruh fungsi (yang mengundang bug) dan juga mendistorsi distribusi: 29sekarang menjadi target tepi yang jauh lebih mungkin.

      Saya memilih untuk memperbaiki ekspresi agar sesuai dengan maksud yang tersirat dari komentar lain ini:

      // 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. Duplikasi kode. Pembuatan subtasks ( node.Node_config) diduplikasi, dengan beberapa perbedaan palsu yang mungkin merupakan bug, tetapi bisa disengaja?

    Misalnya:

    sub_task.number_Copies = rand() % 3 + 1;
    

    Satu dari tiga salinan dihilangkan +1yang kemungkinan besar merupakan bug.

    Dengan cara yang sama kita melihat satu salinan

    sub_task.execTime = rand() % static_cast<int>(node.ExecTime / 2);
    

    yang menambahkan +1. Kemungkinan ini menghindari nol execTime, dan merupakan kode bau bahwa ini juga seharusnya merupakan distribusi acak nyata seragam yang diketik kuat.

    Sulit untuk menebak apa yang sebenarnya ingin Anda execTimemaksud. Jika Anda bermaksud sedemikian rupa sehingga execTime node induk selalu menjumlahkan jumlah subtugasnya, itu jauh lebih mudah diungkapkan dengan beberapa logika bisnis, daripada memiliki data yang berlebihan dalam struktur data Anda dan menambahkan invarian yang tidak terdokumentasi (yang, sekali lagi, mengundang bug ).

    Untuk bersenang-senang, saya menambahkan bagaimana saya akan menulis distribusi dengan iseng:

        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. Untuk penarikan daya total tampaknya ada hal serupa yang terjadi. Mungkin Anda bisa mengganti powerDraw dengan fungsi:

    double powerDraw() const {
        return std::accumulate(begin(Node_config), end(Node_config), 0.);
    };
    

BONUS

Lebih jauh lagi, kami dapat membayangkan dunia di mana pembuatannya "otomatis", seperti halnya pelaporan:

  • Pertimbangkan untuk memindahkan generasi menjadi konstruktor:

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

    Catatan

    • kami menggunakan C ++ 11 NSMI untuk menghasilkan konstruktor default untuk kami
     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();
     };
    

    Sekarang, Nodedapat mengelompokkan dengan fungsi manipulasi itu:

    • Jenis ini mengasumsikan bahwa jenis tersebut belum digunakan di tempat lain. Jika bukan itu masalahnya, kita dapat membuat sub-kelas tipe dan manfaat dari pemotongan objek untuk mengonversi kembali ke kelas dasarnya.

    • Perhatikan juga bahwa beberapa tempat dalam kode (PEParameters, output dan EdgeAssignment) mengaktifkan perilaku pada PEid yang tampaknya hanya memiliki dua nilai yang valid. Saya telah mengubahnya menjadi enum yang mencerminkan fakta itu:

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

      Sebagai latihan bagi pembaca, mungkin masuk akal untuk beralih Nodeke beberapa jenis polimorfik alih-alih beralih sepanjang waktu:

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

      Atau menggunakan tipe virtual (warisan).

Daftar Demo

Semua revisi ada di sini dalam sebuah Intisari: https://gist.github.com/sehe/32c07118031a049042bd9fb469355caf/revisions

Hidup Di 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;
}

Cetakan, mis

DONE

Dan menghasilkan 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

Saya pikir akan lebih baik menggunakan static constexprmakro sini daripada preprocessor.

    //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;
    }

Dari manakah konstanta 29dan 30asalnya? Haruskah mereka diturunkan dari NoOfNodes?

Mungkin lebih baik menggunakan <random>pustaka C ++ daripada std::rand().

CreateandAssignEdges()dan EdgeAssignment()sangat mirip - menurut saya duplikasi dapat sangat dikurangi.

   //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;
    //}

Potongan kode yang diberi komentar seperti ini sering menjadi masalah, menjadi ketinggalan zaman dan tidak konsisten saat kode di sekitarnya berubah. Hapus, atau temukan cara untuk memastikannya dikompilasi dan diuji unit dengan kode lainnya.

    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;

Tidak ada kebutuhan nyata untuk menyiram myFilesetiap pernyataan - lebih memilih '\n'untuk std::endluntuk semua ini (dan sebagian besar / semua penggunaan sisa).