C++ 提高驱动程序的时间效率

C++ 提高驱动程序的时间效率,c++,linked-list,processing-efficiency,C++,Linked List,Processing Efficiency,很抱歉,标题不清楚。本质上,我试图批准一个C++驱动程序的时间(和总体)效率: 使用ifstream逐行读取文件 对我的程序来说,行是分开处理的,这是至关重要的,所以我目前有4个单独的getline调用 程序使用字符串流将字符串行读入整数向量 最后,它将向量转换为整数的链表。是否有一种方法或函数可以直接将文件中的整数读入整数的ll 以下是驾驶员代码: int main(int argc, char *argv[]) { ifstream infile(argv[1]); vec

很抱歉,标题不清楚。本质上,我试图批准一个C++驱动程序的时间(和总体)效率:
  • 使用ifstream逐行读取文件

  • 对我的程序来说,行是分开处理的,这是至关重要的,所以我目前有4个单独的getline调用

  • 程序使用字符串流将字符串行读入整数向量

  • 最后,它将向量转换为整数的链表。是否有一种方法或函数可以直接将文件中的整数读入整数的ll

  • 以下是驾驶员代码:

    int main(int argc, char *argv[])
    {
        ifstream infile(argv[1]);
    
        vector<int> vals_add;
        vector<int> vals_remove;
    
        //Driver Code
        if(infile.is_open()){
    
            string line;
            int n;
            getline(infile, line);
            istringstream iss (line);
    
    
            getline(infile, line);
            istringstream iss2 (line);
            while (iss2 >> n){
                vals_add.push_back(n);
            }
    
            getline(infile, line);
            istringstream iss3 (line);
    
            getline(infile, line);
            istringstream iss4 (line);
            while (iss4 >> n){
                vals_remove.push_back(n);
            }
    
    
            int array_add[vals_add.size()];
            copy(vals_add.begin(), vals_add.end(), array_add);
    
    
            int array_remove[vals_remove.size()];
            copy(vals_remove.begin(), vals_remove.end(), array_remove);
    
    
    
            Node *ptr = CnvrtVectoList(array_add, sizeof(array_add)/sizeof(int));
            print(ptr);
            cout << "\n";
    
            for(int i = 0; i < vals_remove.size(); i++){
               deleteNode(&ptr, vals_remove[i]);
            }
    
    
            print(ptr);
            cout << "\n";
    
        }
    

    其中第2行和第4行必须作为单独的列表处理,第1行和第3行是列表的大小(它们必须动态分配内存,因此大小必须与输入保持一致)。

    有多个方面可以改进

    首先,删除不必要的代码:您没有使用
    iss
    iss3
    。接下来,您的
    array\u add
    array\u remove
    似乎是多余的。直接使用向量

    如果您大致知道平均要读取多少个值,请在向量中保留空间,以避免重复调整大小和复制(实际上,您的输入中似乎有这些数字;请使用这些信息,而不是将其丢弃!)。您还可以将
    while
    读取循环替换为
    std::copy
    std::istream\u迭代器

    您还没有展示如何实现
    CnvrtVectoList
    ,但通常情况下,由于缺乏局部性,链表的工作效率不高:它们会将数据扔到堆上。相邻容器(= vector)几乎总是更有效,甚至当您需要删除中间的元素时。尝试改用矢量,并仔细计时性能

    最后,您能对值进行排序吗?如果是这样,那么您可以使用对
    std::lower_bound
    的迭代调用或对
    std::set_difference
    的单个调用更有效地实现值的删除

    如果开销实际上是从文件中读取数字,请重新构造IO代码,不要单独读取行(这样可以避免许多冗余分配)。相反,直接扫描输入文件(可以选择使用缓冲区或内存映射),并手动跟踪遇到的换行符数量。然后,您可以使用该系列函数从输入读取缓冲区扫描数字

    或者,如果您可以假设输入是正确的,则可以使用文件中提供的信息避免读取单独的行:

    int add_num;
    infile >> add_num;
    std::copy_n(std::istream_iterator<int>(infile), std::inserter(your_list, std::end(your_list));
    
    int del_num;
    infile >> del_num;
    std::vector<int> to_delete(del_num);
    std::copy_n(std::istream_iterator<int>(infile), del_num, to_delete.begin());
    for (auto const n : del_num) {
        deleteNode(&ptr, n);
    }
    
    int-add\u-num;
    填充>>添加数量;
    std::copy_n(std::istream_迭代器(infle),std::inserter(您的_列表,std::end(您的_列表));
    int del_num;
    infle>>del_num;
    std::要删除的向量(del_num);
    std::copy_n(std::istream_迭代器(infle),del_num,to_delete.begin());
    用于(自动常数n:del_num){
    deleteNode(&ptr,n);
    }
    
    首先:为什么要使用一些自定义列表数据结构?很可能是不成熟的,即不支持分配器,因此很难适应良好的性能。对于双链接列表,只需使用
    std::list
    ,对于单链接列表,只需使用
    std::forward\u list
    。简单

    您似乎暗示了几个要求:

  • 类型为
    T
    (例如:一个
    int
    )的值将存储在一个链表中,
    std::list
    std::forward_list
    节点的原始列表)

  • 不应不必要地复制数据-即,不应重新分配内存块

  • 解析应该是可并行的,尽管这只有在I/O不会缩短CPU时间的快速数据源上才有意义

  • 这样的想法是:

  • 使用自定义分配器在可以存储多个列表节点的连续段中划分内存

  • 将整个文件解析为使用上述分配器的链接列表。该列表将根据需要分配内存段。每行换行都会启动一个新列表

  • 返回第2行和第4行列表(即第2行和第4行中的元素列表)

  • 值得注意的是,包含元素计数的行是不必要的。当然,该数据可以传递给分配器以预分配足够的内存段,但这不允许并行化,因为并行解析器不知道元素计数在哪里-只有在协调并行解析的数据后才能找到这些数据。是的,w只要稍加修改,这个解析就可以完全并行化。这多酷啊

    让我们从简单而简单的开始:解析文件以生成两个列表。下面的示例在数据集的内部生成的文本视图上使用
    std::istringstream
    ,但是
    parse
    当然也可以传递
    std::ifstream

    // https://github.com/KubaO/stackoverflown/tree/master/questions/linked-list-allocator-58100610
    #include <forward_list>
    #include <iostream>
    #include <sstream>
    #include <vector>
    
    using element_type = int;
    
    template <typename allocator> using list_type = std::forward_list<element_type, allocator>;
    
    template <typename allocator>
    std::vector<list_type<allocator>> parse(std::istream &in, allocator alloc)
    {
       using list_t = list_type<allocator>;
       std::vector<list_t> lists;
       element_type el;
       list_t *list = {};
       do {
          in >> el;
          if (in.good()) {
             if (!list) list = &lists.emplace_back(alloc);
             list->push_front(std::move(el));
          }
          while (in.good()) {
             int c = in.get();
             if (!isspace(c)) {
                in.unget();
                break;
             }
             else if (c=='\n') list = {};
          }
       } while (in.good() && !in.eof());
       for (auto &list : lists) list.reverse();
       return lists;
    }
    
    //https://github.com/KubaO/stackoverflown/tree/master/questions/linked-list-allocator-58100610
    #包括
    #包括
    #包括
    #包括
    使用元素_type=int;
    使用列表类型=std::转发列表的模板;
    模板
    std::vector parse(std::istream&in,分配器alloc)
    {
    使用列表t=列表类型;
    向量表;
    元素_型el;
    list_t*list={};
    做{
    在>>英语中;
    如果(in.good()){
    如果(!list)list=&lists.emplace_back(alloc);
    列表->向前推(标准::移动(el));
    }
    while(in.good()){
    int c=in.get();
    如果(!isspace(c)){
    in.unget();
    打破
    }
    else如果(c=='\n')list={};
    }
    }而(in.good()&&!in.eof());
    for(自动列表:list)list.reverse();
    退货清单;
    }
    
    然后,为了测试它:

    const std::vector<std::vector<element_type>> test_data = {
       {6, 18, 5, 20, 48, 2, 97},
       {3, 6, 9, 12, 28, 5, 7, 10}
    };
    
    template <typename allocator = std::allocator<element_type>>
    void test(const std::string &str, allocator alloc = {})
    {
       std::istringstream input{str};
       auto lists = parse(input, alloc);
       assert(lists.size() == 4);
       lists.erase(lists.begin()+2); // remove the 3rd list
       lists.erase(lists.begin()+0); // remove the 1st list
       for (int i = 0; i < test_data.size(); i++)
          assert(std::equal(test_data[i].begin(), test_data[i].end(), lists[i].begin()));
    }
    
    std::string generate_input()
    {
       std::stringstream s;
       for (auto &data : test_data) {
          s << data.size() << "\n";
          for (const element_type &el : data) s << el << " ";
          s << "\n";
       }
       return s.str();
    }
    
    const std::向量测试数据={
    {6, 18
    
    const std::vector<std::vector<element_type>> test_data = {
       {6, 18, 5, 20, 48, 2, 97},
       {3, 6, 9, 12, 28, 5, 7, 10}
    };
    
    template <typename allocator = std::allocator<element_type>>
    void test(const std::string &str, allocator alloc = {})
    {
       std::istringstream input{str};
       auto lists = parse(input, alloc);
       assert(lists.size() == 4);
       lists.erase(lists.begin()+2); // remove the 3rd list
       lists.erase(lists.begin()+0); // remove the 1st list
       for (int i = 0; i < test_data.size(); i++)
          assert(std::equal(test_data[i].begin(), test_data[i].end(), lists[i].begin()));
    }
    
    std::string generate_input()
    {
       std::stringstream s;
       for (auto &data : test_data) {
          s << data.size() << "\n";
          for (const element_type &el : data) s << el << " ";
          s << "\n";
       }
       return s.str();
    }
    
    class segment_allocator_base
    {
    protected:
       static constexpr size_t segment_size = 128;
       using segment = std::vector<char>;
       struct free_node {
          free_node *next;
          free_node() = delete;
          free_node(const free_node &) = delete;
          free_node &operator=(const free_node &) = delete;
          free_node *stepped_by(size_t element_size, int n) const {
             auto *p = const_cast<free_node*>(this);
             return reinterpret_cast<free_node*>(reinterpret_cast<char*>(p) + (n * element_size));
          }
       };
       struct segment_store {
          size_t element_size;
          free_node *free = {};
          explicit segment_store(size_t element_size) : element_size(element_size) {}
          std::forward_list<segment> segments;
       };
       template <typename T> static constexpr size_t size_for() {
          constexpr size_t T_size = sizeof(T);
          constexpr size_t element_align = std::max(alignof(free_node), alignof(T));
          constexpr auto padding = T_size % element_align;
          return T_size + padding;
       }
       struct pimpl {
          std::vector<segment_store> stores;
          template <typename T> segment_store &store_for() {
             constexpr size_t element_size = size_for<T>();
             for (auto &s : stores)
                if (s.element_size == element_size) return s;
             return stores.emplace_back(element_size);
          }
       };
       std::shared_ptr<pimpl> dp{new pimpl};
    };
    
    template<typename T>
    class segment_allocator : public segment_allocator_base
    {
       segment_store *d = {};
       static constexpr size_t element_size = size_for<T>();
       static free_node *advanced(free_node *p, int n) { return p->stepped_by(element_size, n); }
       static free_node *&advance(free_node *&p, int n) { return (p = advanced(p, n)); }
       void mark_free(free_node *free_start, size_t n)
       {
          auto *p = free_start;
          for (; n; n--) p = (p->next = advanced(p, 1));
          advanced(p, -1)->next = d->free;
          d->free = free_start;
       }
    public:
       using value_type = T;
       using pointer = T*;
       template <typename U> struct rebind {
          using other = segment_allocator<U>;
       };
       segment_allocator() : d(&dp->store_for<T>()) {}
       segment_allocator(segment_allocator &&o) = default;
       segment_allocator(const segment_allocator &o) = default;
       segment_allocator &operator=(const segment_allocator &o) {
          dp = o.dp;
          d = o.d;
          return *this;
       }
       template <typename U> segment_allocator(const segment_allocator<U> &o) :
          segment_allocator_base(o), d(&dp->store_for<T>()) {}
       pointer allocate(const size_t n) {
          if (n == 0) return {};
          if (d->free) {
             // look for a sufficiently long contiguous region
             auto **base_ref = &d->free;
             auto *base = *base_ref;
             do {
                auto *p = base;
                for (auto need = n; need; need--) {
                   auto *const prev = p;
                   auto *const next = prev->next;
                   advance(p, 1);
                   if (need > 1 && next != p) {
                      base_ref = &(prev->next);
                      base = next;
                      break;
                   } else if (need == 1) {
                      *base_ref = next; // remove this region from the free list
                      return reinterpret_cast<pointer>(base);
                   }
                }
             } while (base);
          }
          // generate a new segment, guaranteed to contain enough space
          size_t count = std::max(n, segment_size);
          auto &segment = d->segments.emplace_front(count);
          auto *const start = reinterpret_cast<free_node*>(segment.data());
          if (count > n)
             mark_free(advanced(start, n), count - n);
          else
             d->free = nullptr;
          return reinterpret_cast<pointer>(start);
       }
       void deallocate(pointer ptr, std::size_t n) {
          mark_free(reinterpret_cast<free_node*>(ptr), n);
       }
    
       using propagate_on_container_copy_assignment = std::true_type;
       using propagate_on_container_move_assignment = std::true_type;
    };
    
    int main()
    {  
       auto test_input_str = generate_input();
       std::cout << test_input_str << std::endl;
       test(test_input_str);
       test<segment_allocator<element_type>>(test_input_str);
       return 0;
    }