【发布时间】:2018-12-28 07:08:08
【问题描述】:
我正在为一个高速计算程序编写一个结果输出模块。
我的计划是:
- 我的任务是以相对较快的速度将结果插入数据库 (PostgreSQL)。
- 我使用 libpq 的 [COPY FROM STDIN],有人告诉我这是最快的方法。
- 该方法需要将结果转换为 char* 格式。
虽然结果如下所示:
- 未来 106 年的月度现金流(总计 1272 倍)。
- 每个条目大约有 14 个现金流。
- 大约 2800 个实体(测试数据为 2790 个)。
数据库中的表是这样的:
- 表格的每一行都包含一个实体。
- 有一些前缀来标识不同的实体。
- CashFlows 是跟在前缀后面的双数组(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[]
);
为准备插入的 CashFlow 的函数提供代码:
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 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;
}
请注意,我禁用了涉及插入的代码。
测试结果为:
- 仅组装字符串的成本为 45930 毫秒。
- 组装字符串和插入的成本为 54829 毫秒。
因此,大部分成本在于将 double 转换为 char。
所以想问问有没有更快的方法把double序列转成string,因为相比计算成本,瓶颈其实是结果的输出。
顺便说一下,我的平台是 Windows 10、PostgreSQL 11、Visual Studio 2017。
非常感谢!
【问题讨论】:
-
你在 strlen() 和 strcat() [en.wikipedia.org/wiki/…] 上花费了很多时间。你知道 snprintf() 返回一个可用的值吗?
标签: c postgresql hpc