C 需要一个快速的方法将大量的double转换为string

C 需要一个快速的方法将大量的double转换为string,c,postgresql,hpc,C,Postgresql,Hpc,我正在为一个高速计算程序编写一个结果输出模块 我的计划是: 我的任务是以相对较快的速度将结果插入数据库(PostgreSQL) 我使用libpq的[COPY FROM STDIN],有人告诉我这是最快的方法 该方法需要将结果转换为char*格式 结果如下所示: 未来106年的月度现金流(总计1272倍) 每个实体大约有14个现金流 约2800个实体(2790个用于测试数据) 数据库中的表如下所示: 表的每一行包含一个实体 有一些前缀用于标识不同的实体 现金流是前缀后面的双数组(PGSQL中的fl

我正在为一个高速计算程序编写一个结果输出模块

我的计划是:

  • 我的任务是以相对较快的速度将结果插入数据库(PostgreSQL)
  • 我使用libpq的[COPY FROM STDIN],有人告诉我这是最快的方法
  • 该方法需要将结果转换为char*格式
  • 结果如下所示:

  • 未来106年的月度现金流(总计1272倍)
  • 每个实体大约有14个现金流
  • 约2800个实体(2790个用于测试数据)
  • 数据库中的表如下所示:

  • 表的每一行包含一个实体
  • 有一些前缀用于标识不同的实体
  • 现金流是前缀后面的双数组(PGSQL中的float8[]类型)
  • 以下是在数据库中创建表的代码:

    create table AgentCF(
    PlanID     int4,
    Agent      int4,
    Senario    int4,
    RM_Prev    float8[], DrvFac_Cur float8[], Prem       float8[],
    Comm       float8[], CommOR     float8[], FixExp     float8[],
    VarExp     float8[], CIRCFee    float8[], SaftyFund  float8[],
    Surr       float8[], Benefit_1  float8[], Benefit_2  float8[],
    Benefit_3  float8[], Benefit_4  float8[], Benefit_5  float8[],
    Benefit_6  float8[], Benefit_7  float8[], Benefit_8  float8[],
    Benefit_9  float8[], Benefit_10 float8[]
    );
    
    为准备插入现金流的功能提供代码:

    void AsmbCF(char *buffer, int size, int ProdNo, int i, int Pos, int LineEnd)
    {
        int     j, Step = sizeof(nodecf) / sizeof(double), PosST, Temp;
        double *LoopRate = &AllHeap[ProdNo].Heap.AgentRes[i].CF.NodeCF[0].Prem;
        strcpy_s(buffer, size, "{");
        for (j = 0; j < TOTLEN / 10; j++) {
            PosST = j * 10 * Step + Pos;
            sprintf_s(&buffer[strlen(buffer)], size - strlen(buffer), "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,",
                LoopRate[PosST],
                LoopRate[PosST + 1 * Step],
                LoopRate[PosST + 2 * Step],
                LoopRate[PosST + 3 * Step],
                LoopRate[PosST + 4 * Step],
                LoopRate[PosST + 5 * Step],
                LoopRate[PosST + 6 * Step],
                LoopRate[PosST + 7 * Step],
                LoopRate[PosST + 8 * Step],
                LoopRate[PosST + 9 * Step]
            );
        }
        Temp = j * 10;
        PosST = Temp * Step + Pos;
        sprintf_s(&buffer[strlen(buffer)], size - strlen(buffer), "%f", LoopRate[PosST]);
        Temp = Temp + 1;
        for (j = Temp; j < TOTLEN; j++) {
            PosST = j * Step + Pos;
            sprintf_s(&buffer[strlen(buffer)], size - strlen(buffer), ",%f", LoopRate[PosST]);
        }
        if (LineEnd) {
            strcat_s(buffer, size, "}\n");
        }
        else {
            strcat_s(buffer, size, "}\t");
        }
    }
    
    void AsmbCF(字符*缓冲区、整数大小、整数ProdNo、整数i、整数Pos、整数LineEnd)
    {
    int j,Step=sizeof(nodecf)/sizeof(double),PosST,Temp;
    double*LoopRate=&AllHeap[ProdNo]。Heap.AgentRes[i]。CF.NodeCF[0]。Prem;
    strcpy_s(缓冲区,大小,“{”);
    对于(j=0;j
    以下是速度测试的代码:

    void ThreadOutP(LPVOID pM)
    {
        char       *buffer = malloc(BUFFLEN), sql[SQLLEN];
        int         Status, ProdNo = (int)pM, i, j, ben;
        PGconn     *conn = NULL;
        PGresult   *res;
        clock_t     begin, end;
    
        fprintf_s(fpOutP, "PlanID %d Start inseting...\n", AllHeap[ProdNo].PlanID);
        begin = clock();
        DBConn(&conn, CONNSTR, fpOutP);
    
    #pragma region General cashflow
        //============================== Data Query ==============================
        //strcpy_s(&sql[0], SQLLEN, "COPY AgentCF(PlanID,Agent,Senario,Prem,Comm,CommOR,CIRCFee,SaftyFund,FixExp,VarExp,Surr");
        //for (ben = 1; ben <= AllHeap[ProdNo].Heap.TotNo.NoBenft; ben++) {
        //  strcat_s(&sql[0], SQLLEN, ",Benefit_");
        //  _itoa_s(ben, &sql[strlen(sql)], sizeof(sql) - strlen(sql), 10);
        //}
        //strcat_s(&sql[0], SQLLEN, ") FROM STDIN;");
        //res = PQexec(conn, &sql[0]);
        //if (PQresultStatus(res) != PGRES_COPY_IN) {
        //  fprintf_s(fpOutP, "Not in COPY_IN mode\n");
        //}
        //PQclear(res);
        //============================== Data Apply ==============================
        for (i = 0; i < AllHeap[ProdNo].MaxAgntPos + AllHeap[ProdNo].Heap.TotNo.NoSensi; i++) {
            sprintf_s(buffer, BUFFLEN, "%d\t%d\t%d\t", AllHeap[ProdNo].PlanID, AllHeap[ProdNo].Heap.AgentRes[i].Agent, AllHeap[ProdNo].Heap.AgentRes[i].Sensi);
            //Status = PQputCopyData(conn, buffer, (int)strlen(buffer));
            //if (1 != Status) {
            //  fprintf_s(fpOutP, "PlanID %d inserting error for agent %d\n", AllHeap[ProdNo].PlanID, AllHeap[ProdNo].Heap.AgentRes[i].Agent);
            //}
            for (j = 0; j < 8 + AllHeap[ProdNo].Heap.TotNo.NoBenft; j++) {
                if (j == 7 + AllHeap[ProdNo].Heap.TotNo.NoBenft) {
                    AsmbCF(buffer, BUFFLEN, ProdNo, i, j, 1);
                }
                else {
                    AsmbCF(buffer, BUFFLEN, ProdNo, i, j, 0);
                }
                //Status = PQputCopyData(conn, buffer, (int)strlen(buffer));
                //if (1 != Status) {
                //  fprintf_s(fpOutP, "PlanID %d inserting error for agent %d\n", AllHeap[ProdNo].PlanID, AllHeap[ProdNo].Heap.AgentRes[i].Agent);
                //}
            }
        }
        //Status = PQputCopyEnd(conn, NULL);
    #pragma endregion
    
    #pragma region K cashflow
    
    #pragma endregion
    
        PQfinish(conn);
        FreeProd(ProdNo);
        free(buffer);
        end = clock();
        fprintf_s(fpOutP, "PlanID %d inserted, total %d rows inserted, %d millisecond cost\n", AllHeap[ProdNo].PlanID, i, end - begin);
        AllHeap[ProdNo].Printed = 1;
    }
    
    void ThreadOutP(LPVOID pM)
    {
    char*buffer=malloc(BUFFLEN),sql[SQLLEN];
    int状态,ProdNo=(int)pM,i,j,ben;
    PGconn*conn=NULL;
    PGresult*res;
    时钟开始,结束;
    fprintf_s(fpOutP,“PlanID%d开始插入…\n”,AllHeap[ProdNo].PlanID);
    开始=时钟();
    DBConn(&conn、CONNSTR、FPOUPT);
    #布拉格马地区一般现金流
    //========================================数据查询==============================
    //strcpy_s(&sql[0],SQLLEN,“复制代理(PlanID、代理、Senario、Prem、Comm、CommOR、CIRCFee、SaftyFund、FixExp、VarExp、Surr”);
    //对于(ben=1;ben
    将大量double转换为字符串的快速方法

    对于完整的
    double
    范围应用,请使用
    sprintf(buf,“%a”,一些双精度)
    。如果需要十进制输出,请使用
    %e”

    任何其他代码只有在其精度或允许的输入范围合理的情况下才会更快

    通常的方法是将
    double x
    转换为某个大范围的整数并将其转换为字符串。这意味着对
    x
    的限制尚未通过OP明确表达

    即使其他一些方法看起来更快,但随着代码的发展或移植,它也可能不会更快



    OP需要发布的是用于客观性能评估的速度测试代码。

    实际上有几种更快的方法可以准确地将浮点数表示为字符串,其中之一就是

    比较了C和C++中的几种算法,其中包含了比 SaMPTF更快的5.7X。


    然而,同一回购协议的作者()提供了自己的C++单头实现,它被要求是9.1x更快,大概是因为更多的函数是完全内联的。我相信把这个代码移植到C应该是微不足道的,因为它不使用任何特殊的C++语法。< /P> < P> > CHOX的回答,我做了以下的功能:

    __inline char* dbltoa(char* buff, double A, int Precision)
    {
        int     Temp;
        char   *ptr;
    
        Temp = (int)A;
        _itoa_s(Temp, buff, 50, 10);
        ptr = buff + strlen(buff);
        ptr[0] = '.';
        Temp = (int)((A - Temp) * pow(10, Precision));
        _itoa_s(Temp, ptr + 1, 50, 10);
        return ptr + strlen(ptr);
    }
    
    并更新了生成现金流字符串的函数:

    void AsmbCF(char *buffer, int size, int ProdNo, int i, int Pos, int LineEnd)
    {
        int     j, Step = sizeof(nodecf) / sizeof(double), PosST, Temp;
        double *LoopRate = &AllHeap[ProdNo].Heap.AgentRes[i].CF.NodeCF[0].Prem;
        char   *ptr;
        strcpy_s(buffer, size, "{");
        ptr = buffer + 1;
        for (j = 0; j < TOTLEN; j++) {
            PosST = j * Step + Pos;
            ptr = dbltoa(ptr, LoopRate[PosST], 8);
            ptr[0] = ',';
            ptr++;
        }
        ptr[-1] = 0;
        if (LineEnd) {
            strcat_s(buffer, size, "}\n");
        }
        else {
            strcat_s(buffer, size, "}\t");
        }
    }
    
    void AsmbCF(字符*缓冲区、整数大小、整数ProdNo、整数i、整数Pos、整数LineEnd)
    {
    int j,Step=sizeof(nodecf)/sizeof(double),PosST,Temp;
    double*LoopRate=&AllHeap[ProdNo]。Heap.AgentRes[i]。CF.NodeCF[0]。Prem;
    char*ptr;
    strcpy_s(缓冲区,大小,“{”);
    ptr=buffer+1;
    对于(j=0;j

    不使用insert的测试结果为4558毫秒,而使用insert tooks的测试结果为29260毫秒(可能是数据库的并行运行使其在比率上不等效)。

    我对原始代码做了一些簿记:



    现在,我不知道什么是
    TOTLEN
    ,但是在不断增长的字符串上调用strlen()和friends会导致二次行为,请参阅


    • 在优化之前,请先了解/测量(或思考)
    • 正确使用时,
      snprintf()
      是溢出安全的;请阅读手册页并使用返回值
    • strxxx_x()
      函数几乎毫无用处,它们的存在只是为了取悦PHB
    full double(完全双精度)表示不带小数的双精度??我现在尝试%e,我也尝试使用itoa编写代码,因为我使用的是英特尔的cpu,应该有fild指令。@Shore“full double range”并不表示不带小数的双精度。典型的
    double
    有大约2^64个不同的值。快速输出应该可以
      Total score("function" calls):
        2 + 4*TOTLEN * strlen()
        1 + 2*TOTLEN * sprintf() 
        1 * strcat()
    
      Estimated string() cost:
        3 + 4* size * (TOTLEN*TOTLEN) / 2 (measured in characters)
    
      Estimated sprintf() cost:
        2 * TOTLEN (measured in %lf conversions)
        2 * size (measured in characters)