在C中保存图形到文件的最快方法

时间:2018-01-10 12:15:10

标签: c file graph

我遇到了这样的问题:我需要使用>保存一个大图。百万边缘到txt文件。每个边用一个包含3个整数的结构表示:from,to,cost。我的任务是编写一个程序,将以整个格式快速将整个图形保存到txt文件:"从成本\ n"。

我对这个方法感兴趣,怎么做。 我的想法是创建一个巨大的字符缓冲区,我将每个数字添加到缓冲区而不需要反转(首先我得到每个整数的位数,然后将每个数字添加到缓冲区,然后我添加空格/换行符号,直到添加最后一个数字为止。

然后我使用fwrite()函数将整个缓冲区保存到文件中。

尽管这种方法相对较快,但我看到程序更快。我的问题是:你知道更有效的方法来实现这个程序,以获得更快的结果吗?

该程序必须使用C语言。

columnMenuClick(args){
    if(args.item.id === 'gridclearsorting'){
        this.grid.clearSorting();
    }
}

获取位数的函数:

typedef struct {
   int edge_start; 
   int edge_count; 
   int parent; 
   int cost; 
} node_t;

typedef struct {
   graph_t *graph;
   node_t *nodes;
   int num_nodes; 
   int start_node; 
} dijkstra_t;

保存功能:

int getNumberOfDigitsBig(int x) {
  if (x >= 10000) {
        if (x >= 10000000) {
            if (x >= 100000000) {
                if (x >= 1000000000)
                    return 9;
                return 8;
            }
            return 7;
        }
        if (x >= 100000) {
            if (x >= 1000000)
                return 6;
            return 5;
        }
        return 4;
    }
    if (x >= 100) {
        if (x >= 1000)
            return 3;
        return 2;
    }
    if (x >= 10)
        return 1;
    return 0;
}

感谢您的关注。

3 个答案:

答案 0 :(得分:0)

我认为你需要的是printf的优化版本,它只处理正整数。我没有对它进行基准测试,但是我会尝试做尽可能少的比较和操作,所以我以这个函数结束:

int printint(FILE *fd, int n) {
    char buffer[32];     // an uint64_t uses max 20 chars in base 10 
    int i = sizeof(buffer);
    do {
        buffer[--i] = '0' + n%10;   // write digits from the right of buffer
        n /= 10;
    } while(n > 0);
    return fwrite(buffer + i, 1, sizeof(buffer) - i, fd);
}

然后我不会使用巨大的缓冲区,而只依赖于FILE *的默认缓冲

然后可以保存代码(或多或少只是从问题的示例开始):

    const dijkstra_t *const dij = (dijkstra_t*)dijkstra;

    if (dij) {
    FILE *f = fopen(filename, "w");
    if (f) {

    int numberOfNodes = dij->num_nodes;

    fputs("0 0 -1\n", f);

    for(int i = 1; i < numberOfNodes; i++) {
        const node_t *const node = &(dij->nodes[i]);

        fputc(' ', f);

        number = node->parent;
        //printf("parent = %d\n", number);
        if(number != -1) {
            printint(number, f);
        } else {
              fwrite("-1", 1, 2, f);

        }

        fputc('\n', f);

    }

  ret = fclose(f) == 0;
  free(buffer);
  }
 }

答案 1 :(得分:0)

使用“itoa”可以改善一点:

void gwf_i2a(char *d, int i, int l) {
  char *e = d + l;
  while (l > 0) {
    e--;
    l--;
    e[0] = '0' + (i % 10);
    i /= 10;
  }
}

原始时间:76次点击(7.6e-05秒)。

新时间:39次点击(3.9e-05秒)。

源:

#include <ctime>
#include <iostream>
#include <random>
#include <vector>

#define ZERO '0'

void gwf_i2a(char *d, int i, int l) {
  char *e = d + l;
  while (l > 0) {
    e--;
    l--;
    e[0] = '0' + (i % 10);
    i /= 10;
  }
}

typedef struct {
  int x, y, z;
} graph_t;

typedef struct {
  int edge_start;
  int edge_count;
  int parent;
  int cost;
} node_t;

typedef struct {
  graph_t *graph;
  node_t *nodes;
  int num_nodes;
  int start_node;
} dijkstra_t;

graph_t graph = {111, 222, 3456789};
node_t nodes[] = {{1, 1, 1, 9999}, {2, 2, 2, 8999}, {2, 2, 2, 1234567890}};
dijkstra_t data[] = {&graph, (node_t *)&nodes, 4, 0};

int getNumberOfDigits(int x) {
  if (x >= 100) {
    if (x >= 1000) return 3;
    return 2;
  }
  if (x >= 10) return 1;
  return 0;
}

int getNumberOfDigitsBig(int x) {
  if (x >= 10000) {
    if (x >= 10000000) {
      if (x >= 100000000) {
        if (x >= 1000000000) return 9;
        return 8;
      }
      return 7;
    }
    if (x >= 100000) {
      if (x >= 1000000) return 6;
      return 5;
    }
    return 4;
  }
  if (x >= 100) {
    if (x >= 1000) return 3;
    return 2;
  }
  if (x >= 10) return 1;
  return 0;
}

void save(const char *filename, const dijkstra_t *dijkstra) {
  int ret;
  const dijkstra_t *const dij = (dijkstra_t *)dijkstra;
  char *buffer;
  if (dij) {
    FILE *f = fopen(filename, "w");
    if (f) {
      int numberOfNodes = dij->num_nodes;
      long bufferLength = numberOfNodes * (9 * 3 + 3);
      buffer = (char *)malloc(bufferLength + 1);
      long bufferCounter = 0;

      int number;
      // printf("i = %d\n", number);
      int counter;
      int digits;

      buffer[bufferCounter++] = '0';
      buffer[bufferCounter++] = ' ';
      buffer[bufferCounter++] = '0';
      buffer[bufferCounter++] = ' ';
      buffer[bufferCounter++] = '-';
      buffer[bufferCounter++] = '1';
      buffer[bufferCounter++] = '\n';

      for (int i = 1; i < numberOfNodes; i++) {
        const node_t *const node = &(dij->nodes[i]);

        number = i;

        digits = getNumberOfDigits(number);
        counter = bufferCounter;

        do {
          buffer[counter + digits] = ZERO + number % 10;
          --digits;
          ++bufferCounter;
        } while (number /= 10);

        buffer[bufferCounter++] = ' ';

        number = node->cost;

        if (number != -1) {
          digits = getNumberOfDigitsBig(number);

          counter = bufferCounter;
          do {
            buffer[counter + digits] = ZERO + number % 10;
            digits = digits - 1;
            bufferCounter = bufferCounter + 1;
          } while (number /= 10);
        } else {
          buffer[bufferCounter++] = '-';
          buffer[bufferCounter++] = '1';
        }

        buffer[bufferCounter++] = ' ';

        buffer[bufferCounter++] = ' ';

        number = node->parent;

        if (number != -1) {
          digits = getNumberOfDigitsBig(number);

          counter = bufferCounter;
          do {
            buffer[counter + digits] = ZERO + number % 10;
            --digits;
            ++bufferCounter;
          } while (number /= 10);
        } else {
          buffer[bufferCounter++] = '-';
          buffer[bufferCounter++] = '1';
        }

        buffer[bufferCounter++] = '\n';
      }

      fwrite(buffer, 1, bufferCounter, f);

      ret = fclose(f) == 0;
      free(buffer);
    }
  }
}

void new_save(const char *filename, const dijkstra_t *dijkstra) {
  int ret;
  const dijkstra_t *const dij = (dijkstra_t *)dijkstra;
  char *buffer;
  if (dij) {
    FILE *f = fopen(filename, "w");
    if (f) {
      int numberOfNodes = dij->num_nodes;
      long bufferLength = numberOfNodes * (9 * 3 + 3);
      buffer = (char *)malloc(bufferLength + 1);
      long bufferCounter = 0;

      int number;
      int counter;
      int digits;

      buffer[bufferCounter++] = '0';
      buffer[bufferCounter++] = ' ';
      buffer[bufferCounter++] = '0';
      buffer[bufferCounter++] = ' ';
      buffer[bufferCounter++] = '-';
      buffer[bufferCounter++] = '1';
      buffer[bufferCounter++] = '\n';

      for (int i = 1; i < numberOfNodes; i++) {
        const node_t *const node = &(dij->nodes[i]);
        int len = getNumberOfDigits(i) + 1;

        gwf_i2a((char *)&buffer[bufferCounter], i, len);
        bufferCounter += len;
        buffer[bufferCounter++] = ' ';

        number = node->cost;

        if (number != -1) {
          len = getNumberOfDigitsBig(number) + 1;
          gwf_i2a((char *)&buffer[bufferCounter], number, len);
          bufferCounter += len;

        } else {
          buffer[bufferCounter++] = '-';
          buffer[bufferCounter++] = '1';
        }

        buffer[bufferCounter++] = ' ';

        buffer[bufferCounter++] = ' ';

        number = node->parent;

        if (number != -1) {
          digits = getNumberOfDigitsBig(number);

          counter = bufferCounter;
          do {
            buffer[counter + digits] = ZERO + number % 10;
            --digits;
            ++bufferCounter;
          } while (number /= 10);
        } else {
          buffer[bufferCounter++] = '-';
          buffer[bufferCounter++] = '1';
        }

        buffer[bufferCounter++] = '\n';
      }

      fwrite(buffer, 1, bufferCounter, f);

      ret = fclose(f) == 0;
      free(buffer);
    }
  }
}
void original() {
  clock_t t;
  t = clock();
  save("bogus.txt", data);
  t = clock() - t;
  std::cout << "original: " << t << " clicks (" << ((float)t) / CLOCKS_PER_SEC
            << " seconds)." << std::endl;
}
void new_test() {
  clock_t t;
  t = clock();
  new_save("new_bogus.txt", data);
  t = clock() - t;
  std::cout << "NEW: " << t << " clicks (" << ((float)t) / CLOCKS_PER_SEC
            << " seconds)." << std::endl;
}
int main(int argc, char **argv) {
  original();
  new_test();

  return 0;
}

答案 2 :(得分:0)

[改写于2018-01-13。] 标准I / O(printf()等)在将数字数据转换为文本形式方面确实相对较慢。这里的问题是输出表格行

  

&lt; node&gt; &lt; cost&gt; &lt; parent&gt;

其中所有三个都是十进制表示法的无符号(32位)整数,或-1。为简单起见,我们为UINT32_MAX保留值-1(4294967295)。

我建议采用双重方法:

  1. 从右到左构造每条记录。这样就无需检查数字中有多少位数。

  2. 一次缓冲多个记录。这减少了fwrite()次调用的次数,代价是适度的动态分配缓冲区。

    请注意,这意味着每个块中的记录必须从头到尾处理,以保持正确的顺序。

  3. 请考虑以下代码。请注意,我已将node_tdijkstra_t的定义缩减为实际使用区域的字段,以便可以按原样编译以下示例。另请注意,-1parent代替cost,必须使用UINT32_MAX,因为其类型现为uint32_t

    #include <stdlib.h>
    #include <stdint.h>
    #include <limits.h>
    #include <stdio.h>
    
    typedef struct {
        uint32_t    parent;     /* Use UINT32_MAX for -1 */
        uint32_t    cost;       /* Use UINT32_MAX for -1 */
    } node_t;
    
    typedef struct {
        node_t     *nodes;
        uint32_t    num_nodes;
    } dijkstra_t;
    
    /* This function will store an unsigned 32-bit value
       in decimal form, ending at 'end'.
       UINT32_MAX will be written as "-1", however.
       Returns a pointer to the start of the value.
    */
    static inline char *prepend_value(char *end, uint32_t value)
    {
        if (value == UINT32_MAX) {
            *(--end) = '1';
            *(--end) = '-';
        } else {
            do {
                *(--end) = '0' + (value % 10u);
                value /= 10u;
            } while (value);
        }
        return end;
    }
    
    /* Each record consists of three unsigned 32-bit integers,
       each at most 10 characters, with spaces in between
       and a newline at end. Thus, at most 33 characters. */
    #define  RECORD_MAXLEN  33
    /* We process records in chunks of 16384.
       Maximum number of records (nodes) is 2**32 - 2 - RECORD_CHUNK,
       or 4,294,950,910 in this case. */
    #define  RECORD_CHUNK   16384
    /* Each chunk of record is up to CHUNK_CHARS long.
       (Roughly half a megabyte in this case.) */
    #define  CHUNK_CHARS   (RECORD_MAXLEN * RECORD_CHUNK)
    
    /* Save the edges in a graph to a stream.
       Returns 0 if success, -1 if an error occurs.
    */
    int save_edges(dijkstra_t *dij, FILE *out)
    {
        if (dij && out && !ferror(out)) {
            const int       nodes = dij->num_nodes;
            const node_t   *node  = dij->nodes;
    
            const uint32_t  root_parent = dij->nodes->parent;
            const uint32_t  root_cost   = dij->nodes->cost;
    
            char           *buf, *end, *ptr;
            uint32_t        o;
    
            /* Allocate memory for the chunk buffer. */
            buf = malloc(CHUNK_CHARS);
            if (!buf)
                return -1;
    
            end = buf + CHUNK_CHARS;
    
            /* Temporarily, we reset the root node parent
               to UINT32_MAX and cost to 0, so that the
               very first record in the output is "0 0 -1". */
            dij->nodes->cost   = 0;
            dij->nodes->parent = UINT32_MAX;
    
            for (o = 0; o < nodes; o += RECORD_CHUNK) {
                uint32_t  i = (o + RECORD_CHUNK < nodes) ? o + RECORD_CHUNK : nodes;
    
                /* Fill buffer back-to-front. */
                ptr = end;
                while (i-->o) {
                    const node_t  *curr = node + i;
    
                    /* Format: <i> ' ' <cost> ' ' <parent> '\n' */
                    /* We construct the record from right to left. */
    
                    *(--ptr) = '\n';
                    ptr = prepend_value(ptr, curr->parent);
                    *(--ptr) = ' ';
                    ptr = prepend_value(ptr, curr->cost);
                    *(--ptr) = ' ';
                    ptr = prepend_value(ptr, i);
                }
    
                /* Write the chunk buffer out. */
                if (fwrite(ptr, 1, (size_t)(end - ptr), out) != (size_t)(end - ptr)) {
                    dij->nodes->cost = root_cost;
                    dij->nodes->parent = root_parent;
                    free(buf);
                    return -1;
                }
            }
    
            /* Reset root node, and free the buffer. */
            dij->nodes->cost = root_cost;
            dij->nodes->parent = root_parent;
            free(buf);
    
            /* Check for write errors. */
            if (fflush(out))
                return -1;
            if (ferror(out))
                return -1;
    
            /* Success. */
            return 0;
        }
    
        return -1;
    }
    

    如果我们可以使用来自<unistd.h>的POSIX低级I / O(open()close()write()fstat(),则可以实现额外的加速)。当目的地是管道或设备时,我们可以直接写入数据;当目标是文件时,我们应该写入st_blksize的倍数的块,以避免读 - 修改 - 写周期。与标准I / O不同,对于低级I / O,我们可以使用st_blksize的一个“溢出”缓冲区来完成,而无需在内存中复制整个块缓冲区。但是,由于问题没有标记为,我将不再进行进一步的讨论。

    OP表示他们自己的版本仍然更快。我发现很难相信,因为它比我上面的版本做了更多的工作。当我检查时,在我的机器上,一个大的数据集(比如100,000,000)不能在一个fwrite()调用中写入,因为它只进行部分写入;实际编写整个数据集需要一个循环。因此,在我看来,基准OP用来比较不同版本是非常可疑的。

    请考虑以下微基准测试。它生成一个单链表,并使用外部编译的save_graph()函数将其输出(到标准输出)。实现了三个版本: null ,它根本不保存任何内容; antonkretov ,用于OP的实施(适用于此处工作);我的 nominalanimal

    <强>生成文件

    CC      := gcc
    CFLAGS  := -std=c99 -O2 -Wall
    LDFLAGS :=
    BINS    := test-null test-antonkretov test-nominalanimal
    NODES   := 100000000
    
    .PHONY: all clean run
    
    all: clean $(BINS)
    
    clean:
            rm -f $(BINS) *.o
    
    %.o: %.c
            $(CC) $(CFLAGS) -c $^
    
    
    test-null: main.o data-null.o
            $(CC) $(CFLAGS) $^ -o $@
    
    test-antonkretov: main.o data-antonkretov.o
            $(CC) $(CFLAGS) $^ -o $@
    
    test-nominalanimal: main.o data-nominalanimal.o
            $(CC) $(CFLAGS) $^ -o $@
    
    run: $(BINS)
            @echo "Testing $(NODES) nodes."
            @./test-null $(NODES) > /dev/null
    
            @echo "Overhead (nothing saved):"
            @bash -c 'time ./test-null $(NODES) > /dev/null'
            @echo ""
    
            @echo "Anton Kretov:"
            @bash -c 'time ./test-antonkretov $(NODES) > /dev/null'
            @echo ""
    
            @echo "Nominal Animal:"
            @bash -c 'time ./test-nominalanimal $(NODES) > /dev/null'
            @echo ""
    

    请注意,此论坛将 Tab 转换为空格,而Makefile格式要求缩进使用空格,因此如果您将上述内容复制并粘贴到文件中,则需要运行以下操作: sed -e 's|^ *|\t|' -i Makefile来修复它。

    <强> data.h

    #ifndef   DATA_H
    #define   DATA_H
    #include <stdint.h>
    #include <limits.h>
    #include <stdio.h>
    
    #define  INVALID_COST    UINT32_MAX
    #define  INVALID_PARENT  UINT32_MAX
    
    typedef struct {
        uint32_t    parent;     /* Use INVALID_PARENT for -1 */
        uint32_t    cost;       /* Use INVALID_COST for -1 */
    } node_t;
    
    typedef struct {
        node_t     *nodes;
        uint32_t    num_nodes;
    } dijkstra_t;
    
    int save_graph(dijkstra_t *, FILE *);
    
    #endif /* DATA_H */
    

    data-null.c ,用于衡量运行时开销:

    #include "data.h"
    
    int save_graph(dijkstra_t *dij, FILE *out)
    {
        /* Does not do anything */
        return 0;
    }
    

    data-antonkretov.c ,OP的保存程序版本,用于比较:

    #include <stdlib.h>
    #include "data.h"
    
    int getNumberOfDigits(uint32_t x)
    {
        if (x >= 10000) {
            if (x >= 10000000) {
                if (x >= 100000000) {
                    if (x >= 1000000000)
                        return 9;
                    return 8;
                }
                return 7;
            }
            if (x >= 100000) {
                if (x >= 1000000)
                    return 6;
                return 5;
            }
            return 4;
        }
        if (x >= 100) {
            if (x >= 1000)
                return 3;
            return 2;
        }
        if (x >= 10)
            return 1;
        return 0;
    }
    
    int save_graph(dijkstra_t *dij, FILE *out)
    {
        uint32_t    numberOfNodes = dij->num_nodes;
        size_t      bufferLength = numberOfNodes * (size_t)33;
        size_t      bufferCounter = 0, counter;
        size_t      bytes;
        uint32_t    number, digits, i;
        char       *buffer;
    
        if ((size_t)(bufferLength / 33) != numberOfNodes)
            return -1;
    
        buffer = malloc(bufferLength);
        if (!buffer)
            return -1;
    
        buffer[bufferCounter++] = '0';
        buffer[bufferCounter++] = ' ';
        buffer[bufferCounter++] = '0';
        buffer[bufferCounter++] = ' ';
        buffer[bufferCounter++] = '-';
        buffer[bufferCounter++] = '1';
        buffer[bufferCounter++] = '\n';
    
        for (i = 1; i < numberOfNodes; i++) {
            const node_t *const node = dij->nodes + i;
    
            number = i;
            digits = getNumberOfDigits(number);
            counter = bufferCounter;
            do {
                buffer[counter + digits] = '0' + (number % 10u);
                --digits;
                ++bufferCounter;
            } while (number /= 10u);
    
            buffer[bufferCounter++] = ' ';
    
            number = node->cost;
            if (number != UINT32_MAX) {
                digits = getNumberOfDigits(number);
                counter = bufferCounter;
                do {
                    buffer[counter + digits] = '0' + (number % 10u);
                    --digits;
                    ++bufferCounter;
                } while (number /= 10u);
            } else {
                buffer[bufferCounter++] = '-';
                buffer[bufferCounter++] = '1';
            }
    
            buffer[bufferCounter++] = ' ';
    
            number = node->parent;
            if (number != UINT32_MAX) {
                digits = getNumberOfDigits(number);
                counter = bufferCounter;
                do {
                    buffer[counter + digits] = '0' + (number % 10u);
                    --digits;
                    ++bufferCounter;
                } while (number /= 10u);
            } else {
                buffer[bufferCounter++] = '-';
                buffer[bufferCounter++] = '1';
            }
    
            buffer[bufferCounter++] = '\n';
        }
    
        counter = 0;
        while (counter < bufferCounter) {
            bytes = fwrite(buffer + counter, 1, bufferCounter - counter, out);
            if (!bytes) {
                free(buffer);
                return -1;
            }
            counter += bytes;
        }
    
        free(buffer);
    
        return 0;
    }
    

    data-nominalanimal.c ,我的保存例程的前后版本:

    #include <stdlib.h>
    #include "data.h"
    
    /* This function will store an unsigned 32-bit value
       in decimal form, ending at 'end'.
       UINT32_MAX will be written as "-1", however.
       Returns a pointer to the start of the value.
    */
    static inline char *prepend_value(char *end, uint32_t value)
    {
        if (value == UINT32_MAX) {
            *(--end) = '1';
            *(--end) = '-';
        } else {
            do {
                *(--end) = '0' + (value % 10u);
                value /= 10u;
            } while (value);
        }
        return end;
    }
    
    /* Each record consists of three unsigned 32-bit integers,
       each at most 10 characters, with spaces in between
       and a newline at end. Thus, at most 33 characters. */
    #define  RECORD_MAXLEN  33
    /* We process records in chunks of 16384.
       Maximum number of records (nodes) is 2**32 - 2 - RECORD_CHUNK,
       or 4,294,950,910 in this case. */
    #define  RECORD_CHUNK   16384
    /* Each chunk of record is up to CHUNK_CHARS long.
       (Roughly half a megabyte in this case.) */
    #define  CHUNK_CHARS   (RECORD_MAXLEN * RECORD_CHUNK)
    
    /* Save the edges in a graph to a stream.
       Returns 0 if success, -1 if an error occurs.
    */
    int save_graph(dijkstra_t *dij, FILE *out)
    {
        if (dij && out && !ferror(out)) {
            const int       nodes = dij->num_nodes;
            const node_t   *node  = dij->nodes;
    
            const uint32_t  root_parent = dij->nodes->parent;
            const uint32_t  root_cost   = dij->nodes->cost;
    
            char           *buf, *end, *ptr;
            uint32_t        o;
    
            /* Allocate memory for the chunk buffer. */
            buf = malloc(CHUNK_CHARS);
            if (!buf)
                return -1;
    
            end = buf + CHUNK_CHARS;
    
            /* Temporarily, we reset the root node parent
               to UINT32_MAX and cost to 0, so that the
               very first record in the output is "0 0 -1". */
            dij->nodes->cost   = 0;
            dij->nodes->parent = UINT32_MAX;
    
            for (o = 0; o < nodes; o += RECORD_CHUNK) {
                uint32_t  i = (o + RECORD_CHUNK < nodes) ? o + RECORD_CHUNK : nodes;
    
                /* Fill buffer back-to-front. */
                ptr = end;
                while (i-->o) {
                    const node_t  *curr = node + i;
    
                    /* Format: <i> ' ' <cost> ' ' <parent> '\n' */
                    /* We construct the record from right to left. */
                    *(--ptr) = '\n';
                    ptr = prepend_value(ptr, curr->parent);
                    *(--ptr) = ' ';
                    ptr = prepend_value(ptr, curr->cost);
                    *(--ptr) = ' ';
                    ptr = prepend_value(ptr, i);
                }
    
                /* Write buffer. */
                if (fwrite(ptr, 1, (size_t)(end - ptr), out) != (size_t)(end - ptr)) {
                    dij->nodes->cost = root_cost;
                    dij->nodes->parent = root_parent;
                    free(buf);
                    return -1;
                }
            }
    
            /* Reset root node, and free the buffer. */
            dij->nodes->cost = root_cost;
            dij->nodes->parent = root_parent;
            free(buf);
    
            if (fflush(out))
                return -1;
            if (ferror(out))
                return -1;
    
            return 0;
        }
    
        return -1;
    }
    

    最后是主程序本身 main.c ,它生成数据并调用save_graph()函数:

    #include <stdlib.h>
    #include <inttypes.h>
    #include <limits.h>
    #include <string.h>
    #include "data.h"
    
    #define  EDGES_MAX  4294901759
    
    int main(int argc, char *argv[])
    {
        dijkstra_t graph;
        size_t     bytes;
        uint32_t   edges, i;
        char       dummy;
    
        if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
            fprintf(stderr, "\nUsage: %s EDGES\n\n", argv[0]);
            return EXIT_SUCCESS;
        }
        if (sscanf(argv[1], " %" SCNu32 " %c", &edges, &dummy) != 1 || edges < 1 || edges > EDGES_MAX) {
            fprintf(stderr, "%s: Invalid number of edges.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        bytes = (1 + (size_t)edges) * sizeof graph.nodes[0];
        if ((size_t)(bytes / (1 + (size_t)edges)) != sizeof graph.nodes[0]) {
            fprintf(stderr, "%s: Too many edges.\n", argv[1]);
            return EXIT_FAILURE;
        }
        graph.num_nodes = edges + 1;
        graph.nodes = malloc(bytes);
        if (!graph.nodes) {
            fprintf(stderr, "%s: Too many edges: out of memory.\n", argv[1]);
            return EXIT_FAILURE;
        }
    
        /* Generate a graph; no randomness, to keep timing steady. */
        graph.nodes[0].parent = INVALID_COST;
        graph.nodes[0].cost = 0;
        for (i = 1; i <= edges; i++) {
            graph.nodes[i].parent = i - 1;
            graph.nodes[i].cost = 1 + (i % 10);
        }
    
        /* Print graph. */
        if (save_graph(&graph, stdout)) {
            fprintf(stderr, "Write error!\n");
            return EXIT_FAILURE;
        }
    
        /* Done. */
        return EXIT_SUCCESS;
    }
    

    运行make clean run(或make NODES=100000000 clean run)重新编译基准测试,并测量其运行时间,以获得100,000,000个节点的图表。在我的机器上,输出是

    Testing 100000000 nodes.
    Overhead (nothing saved):
    
    real0m0.514s
    user0m0.297s
    sys0m0.217s
    
    Anton Kretov:
    
    real0m4.059s
    user0m3.379s
    sys0m0.680s
    
    Nominal Animal:
    
    real0m3.336s
    user0m3.151s
    sys0m0.184s
    

    表明我的速度明显更快。如果我们忽略了生成图表的开销,我花了大约2.8秒的实时时间将数据保存到/dev/null,而OP则需要大约3.5秒。换句话说,我的速度提高了20%。

    重要的是要注意两个测试确实产生完全相同的输出。例如,./test-nominalanimal 100000000 | sha256sum -./test-antonkretov 100000000 | sha256sum -都显示完全相同的SHA256校验和7504a1c97167701297c03c4aab8b0f20c5cac82a50128074d6e09c474353d0f8。 (您也可以将输出保存到文件中,并进行比较;两者的长度都是1,987,777,795字节,并且包含完全相同的数据。我确实检查了。)

    如果要运行将数据存储到存储的基准测试,为了使比较公平,您需要从冷存储开始。否则,运行基准测试的顺序将严重影响他们的时间。

相关问题