慢速插入链接列表需要多线程

Slow Inserts Linked List multi-threading needed?

本文关键字:多线程 列表 链接 插入      更新时间:2023-10-16

在代码bellow中,我正在寻找优化插入速度的方法,当时与数百万插入物打交道时。该代码运行良好,但执行大量插入时非常慢。我已经尝试了一些想法,但总是很慢。我想解决方案是使用多线程执行插入物,并使用全局变量" truct node* nodeobj"。如果您可以根据Code Bellow给我一个示例,我将非常感谢我,我对C,C 的多线程和同步有很少的经验。欢迎任何其他想法。

规则是:1-最终(插入所有数字之后(必须订购链接列表,这意味着每个节点 ->数据以最低数字启动到最大数字。2-呼叫者使用的是for循环,在插入函数内无法启动此循环。3-只要插入函数不自动添加插入物,这就是呼叫者的作业,就可以优化任何代码,呼叫者或插入功能。

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct Node {
    int data;
    struct Node* nextAddr;
};
struct Node* Insert(Node* p, int data);
void Print(struct Node* p);
void RevPrint(struct Node* p);
int main() {
    struct Node* nodeObj = NULL;
    printf("---------------------------------n"
        "Insert()n---------------------------------n");
    for (int i = 1; i < 1000000; i++) {
        nodeObj = Insert(nodeObj, i);
        printf(" %d", i);
    }
    printf("n---------------------------------n"
        "Print()n---------------------------------n");
    Print(nodeObj);
    printf("---------------------------------n"
        "RevPrint()n---------------------------------");
    RevPrint(nodeObj);
    printf("nPress any key to continue...");
    _getch();
}
struct Node* Insert(Node* _pToNode, int _nbr)
{
    Node *newValue = (struct Node*)malloc(sizeof(struct Node));
    newValue->data = _nbr;
    newValue->nextAddr = NULL;
    if (_pToNode == NULL) _pToNode = newValue;
    else {
        Node* pLocal = _pToNode;
        while (pLocal->nextAddr != NULL) {
            pLocal = pLocal->nextAddr;
        }
        pLocal->nextAddr = newValue;
    }
    return _pToNode;
}
void Print(struct Node* p) {
    if (p == NULL) {
        printf("n");
        return;
    }
    printf(" %d", p->data);
    Print(p->nextAddr);
}
void RevPrint(struct Node* p) {
    if (p == NULL) {
        printf("n");
        return;
    }
    RevPrint(p->nextAddr);
    printf(" %d", p->data);
}

谢谢。

在现代机器上不受欢迎的链接列表。L1和L2缓存喜欢向量和阵列。他们完全鄙视链接的列表。使用std::vector,而不是链接列表,(and 当然不是手工滚动的链接列表(。尝试.reserve((在矢量中足够的内存以容纳所有新内容。只需将所有内容推到向量的背面,然后使用并行执行对向量进行排序。C 17可以无痛地做到这一点。std::stable_sort非常好。

警告:这仅适用于尾部插入/附录:

int
main()
{
    struct Node* nodeObj = NULL;
    struct Node* nodeTail = NULL;
    // your other stuff ...
    for (int i = 1; i < 1000000; i++) {
        nodeObj = Insert(nodeObj, &nodeTail, i);
        printf(" %d", i);
    }
    // your other stuff ...
}
struct Node* Insert(Node* _pToNode, Node **tail,int _nbr)
{
    Node *newValue = (struct Node*)malloc(sizeof(struct Node));
    newValue->data = _nbr;
    newValue->nextAddr = NULL;
    if (_pToNode == NULL) {
        _pToNode = newValue;
    }
    else {
        (*tail)->nextAddr = newValue;
    }
    *tail = newValue;
    return _pToNode;
}

您可以使用具有头部和尾巴的"列表"结构(与单独的args(

清理。

更新:

非常酷/聪明,但不幸的是仍然很慢...

malloc ET。al。大量的小型分配可能会很慢。加快速度的一种方法是使用分配的子池(如Weathervane建议(。

正如我提到的,添加一个"列表"结构可以使事情变得更加整洁,我在两个地方使用了它。一旦承诺,您还可以添加其他东西,例如计数。另外,如果您选择的话,将来可以更轻松地从单连锁列表转换为双重连接。

旁注:带有双关联列表,插入稍微复杂得多,但是在列表的 middle 中的插入速度要快得多,因为您不喜欢必须遍历列表才能找到以前的指针(例如,节点中会有一个prev指针(。另外,RevPrintList将变得像PrintList一样简单。

请注意,[在我的系统上],反向打印毫无用处[和segfaulted],因此我将打印功能重新编码为 not> not 是递归的。

这是一个清理的版本,由于单个malloc调用的数量减少,因此应该更快地进行插入。

旁注:我没有添加malloc的必要支票等。返回null。

#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
    int data;
    struct Node_* next;
} Node;
typedef struct List_ {
    int count;
    Node* head;
    Node* tail;
} List;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
List freelist;
int
main()
{
    List nodelist = { 0, NULL, NULL };
    printf("---------------------------------n"
        "Insert()n---------------------------------n");
    for (int i = 1; i < 1000000; i++) {
        Insert(&nodelist, i);
#if 0
        printf(" %d", i);
#endif
    }
    printf("n---------------------------------n"
        "Print()n---------------------------------n");
#if 0
    Print(nodelist.head);
#else
    PrintList(&nodelist);
#endif
    printf("---------------------------------n"
        "RevPrint()n---------------------------------");
#if 0
    RevPrint(nodelist.head);
#else
    RevPrintList(&nodelist);
#endif
    printf("nPress any key to continue...");
#if 0
    _getch();
#else
    getchar();
#endif
}
Node*
NewNode(void)
{
    Node *node;
    // NOTE: adjust the count setup (e.g. 1000) to what ever value you want
    if (freelist.count <= 0) {
        freelist.count = 1000;
        freelist.head = calloc(freelist.count,sizeof(Node));
    }
    node = freelist.head++;
    freelist.count -= 1;
    return node;
}
Node*
Insert(List* list,int _nbr)
{
    Node *node = NewNode();
    node->data = _nbr;
    node->next = NULL;
    if (list->head == NULL) {
        list->head = node;
    }
    else {
        list->tail->next = node;
    }
    list->tail = node;
    list->count += 1;
    return node;
}
void
Print(Node* p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    printf(" %d", p->data);
    Print(p->next);
}
void
PrintList(List* list)
{
    Node *node;
    for (node = list->head;  node != NULL;  node = node->next)
        printf(" %d", node->data);
    printf("n");
}
void
RevPrint(Node* p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    RevPrint(p->next);
    printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
    Node **rlist = malloc(sizeof(Node**) * list->count);
    Node *node;
    int ridx;
    ridx = list->count - 1;
    for (node = list->head;  node != NULL;  node = node->next, --ridx)
        rlist[ridx] = node;
    for (ridx = 0;  ridx < list->count;  ++ridx) {
        node = rlist[ridx];
        printf(" %d",node->data);
    }
    printf("n");
    free(rlist);
}

更新#2:

您可以使Freelist成为列表的列表(使用具有不同的" Node" struct的列表,该结构将要列表而不是数字(,因此可以在完成程序完成后发布内存。

这是一个修改版本,可以这样做:

#include <stdio.h>
#include <stdlib.h>
//#include <conio.h>
typedef struct Node_ {
    int data;
    struct Node_* next;
} Node;
typedef struct List_ {
    int count;
    Node* head;
    Node* tail;
} List;
typedef struct Freelist_ {
    int count;
    Node* head;
    Node* tail;
    Node* avail;
} FreeList;
Node* NewNode(void);
Node* Insert(List* p, int data);
void Print(Node* p);
void PrintList(List *list);
void RevPrint(Node* p);
void RevPrintList(List *list);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
    List nodelist = { 0, NULL, NULL };
    printf("---------------------------------n"
        "Insert()n---------------------------------n");
    for (int i = 1; i < 1000000; i++) {
        Insert(&nodelist, i);
        // this printf will radically slow things down
#if 0
        printf(" %d", i);
#endif
    }
    printf("n---------------------------------n"
        "Print()n---------------------------------n");
#if 0
    Print(nodelist.head);
#else
    PrintList(&nodelist);
#endif
    printf("---------------------------------n"
        "RevPrint()n---------------------------------");
#if 0
    RevPrint(nodelist.head);
#else
    RevPrintList(&nodelist);
#endif
    // release all nodes back to the malloc free pool
    FreeAll();
    printf("nPress any key to continue...");
#if 0
    _getch();
#else
    getchar();
#endif
}
Node*
NewNode(void)
{
    Node *node;
    // NOTE: adjust the count setup (e.g. 1000) to what ever value you want
    if (freelist.count <= 0) {
        freelist.count = 1000;
        node = calloc(freelist.count,sizeof(Node));
        // maintain linked list of nodes that are at the _start_ of a
        // malloc area/arena
        if (freelist.head == NULL)
            freelist.head = node;
        else
            freelist.tail->next = node;
        freelist.tail = node;
        // burn the first node as a placeholder
        freelist.avail = node + 1;
        freelist.count -= 1;
    }
    node = freelist.avail++;
    freelist.count -= 1;
    return node;
}
void
FreeAll(void)
{
    Node* node;
    Node* next;
    for (node = freelist.head;  node != NULL;  node = next) {
        next = node->next;
        free(node);
    }
}
Node*
Insert(List* list,int _nbr)
{
    Node *node = NewNode();
    node->data = _nbr;
    node->next = NULL;
    if (list->head == NULL) {
        list->head = node;
    }
    else {
        list->tail->next = node;
    }
    list->tail = node;
    list->count += 1;
    return node;
}
void
Print(Node* p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    printf(" %d", p->data);
    Print(p->next);
}
void
PrintList(List* list)
{
    Node *node;
    for (node = list->head;  node != NULL;  node = node->next)
        printf(" %d", node->data);
    printf("n");
}
void
RevPrint(Node* p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    RevPrint(p->next);
    printf(" %d", p->data);
}
void
RevPrintList(List *list)
{
    Node **rlist = malloc(sizeof(Node**) * (list->count + 1));
    Node *node;
    int ridx;
    ridx = list->count - 1;
    for (node = list->head;  node != NULL;  node = node->next, --ridx)
        rlist[ridx] = node;
    for (ridx = 0;  ridx < list->count;  ++ridx) {
        node = rlist[ridx];
        printf(" %d",node->data);
    }
    printf("n");
    free(rlist);
}

更新#3:

这是一个版本,可以通过将reuse成员添加到FreeListFreeOne函数来添加节点的重用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <conio.h>
typedef struct Node_ {
    int data;
    struct Node_ *next;
} Node;
typedef struct List_ {
    int count;
    Node *head;
    Node *tail;
} List;
typedef struct Freelist_ {
    int count;
    Node *head;
    Node *tail;
    Node *avail;
    Node *reuse;
} FreeList;
Node *NewNode(void);
Node *Insert(List *p,int data);
void Print(Node *p);
void PrintList(List *list);
void RevPrint(Node *p);
void RevPrintList(List *list);
void FreeOne(Node *p);
void FreeAll(void);
FreeList freelist = { 0 };
int
main()
{
    List nodelist = { 0, NULL, NULL };
    printf("---------------------------------n" "Insert()n---------------------------------n");
    for (int i = 1; i < 1000000; i++) {
        Insert(&nodelist,i);
        // this printf will radically slow things down
#if 0
        printf(" %d",i);
#endif
    }
    printf("n---------------------------------n" "Print()n---------------------------------n");
#if 0
    Print(nodelist.head);
#else
    PrintList(&nodelist);
#endif
    printf("---------------------------------n" "RevPrint()n---------------------------------");
#if 0
    RevPrint(nodelist.head);
#else
    RevPrintList(&nodelist);
#endif
    // release all nodes back to the malloc free pool
    FreeAll();
    printf("nPress any key to continue...");
#if 0
    _getch();
#else
    getchar();
#endif
}
Node *
NewNode(void)
{
    FreeList *list;
    Node *node;
    list = &freelist;
    do {
        // try to reuse a node that has been released by FreeOne
        node = list->reuse;
        if (node != NULL) {
            list->reuse = node->next;
            node->next = NULL;
            break;
        }
        // NOTE: adjust the count setup (e.g. 1000) to what ever value you want
        if (list->count <= 0) {
            list->count = 1000;
            node = calloc(list->count,sizeof(Node));
            // maintain linked list of nodes that are at the _start_ of a
            // malloc area/arena
            if (list->head == NULL)
                list->head = node;
            else
                list->tail->next = node;
            list->tail = node;
            // burn the first node as a placeholder
            list->avail = node + 1;
            list->count -= 1;
        }
        // grab one from the current allocation array
        node = list->avail++;
        list->count -= 1;
    } while (0);
    return node;
}
void
FreeOne(Node *node)
{
    FreeList *list;
    list = &freelist;
    // push this node onto the front of the reuse list (i.e. it's fast)
    node->next = list->reuse;
    list->reuse = node;
}
void
FreeAll(void)
{
    Node *node;
    Node *next;
    for (node = freelist.head; node != NULL; node = next) {
        next = node->next;
        free(node);
    }
    memset(&freelist,0,sizeof(FreeList));
}
Node *
Insert(List *list,int _nbr)
{
    Node *node = NewNode();
    node->data = _nbr;
    node->next = NULL;
    if (list->head == NULL) {
        list->head = node;
    }
    else {
        list->tail->next = node;
    }
    list->tail = node;
    list->count += 1;
    return node;
}
void
Print(Node *p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    printf(" %d",p->data);
    Print(p->next);
}
void
PrintList(List *list)
{
    Node *node;
    for (node = list->head; node != NULL; node = node->next)
        printf(" %d",node->data);
    printf("n");
}
void
RevPrint(Node *p)
{
    if (p == NULL) {
        printf("n");
        return;
    }
    RevPrint(p->next);
    printf(" %d",p->data);
}
void
RevPrintList(List *list)
{
    Node **rlist = malloc(sizeof(Node **) * (list->count + 1));
    Node *node;
    int ridx;
    ridx = list->count - 1;
    for (node = list->head; node != NULL; node = node->next, --ridx)
        rlist[ridx] = node;
    for (ridx = 0; ridx < list->count; ++ridx) {
        node = rlist[ridx];
        printf(" %d",node->data);
    }
    printf("n");
    free(rlist);
}

我假设您提到的元素的顺序是通过比较订购的。IE。A&lt;b => a是在容器中b之前的。

如果该假设达到的最佳状态,您可以希望的是每个元素的日志(n(插入。因此,如果您想在数百万个元素中插入一个元素,则应期望进行大约20多个比较。如果您要插入的数百万美元有订单,您可以做得更好。

现在,基于指针的结构绝不是快速数据结构的答案。std ::向量应该是您的去。

要使用用于语句的快速插入结构,您需要一个非常特殊的迭代器,或者您需要将插入的内容重新排列到另一个向量。

总而言之,您想插入简单的二进制堆中。

多线程将不会更快地使它更快,除非您可以将类似的方案设计到插入。