我在python中创建了非常大的O(10M)浮点列表。我想在我的C ++项目中使用这个查找表。将此数组从python转移到C ++的最简单,最有效的方法是什么?
我的第一个想法是生成c ++函数,它负责初始化这样的长向量然后编译它。 python代码如上所示:
TextItem
输出文件的大小为500 MB。并且无法编译它(由于gcc崩溃而导致编译终止):
def generate_initLookupTable_function():
numbers_per_row = 100
function_body = """
#include "PatBBDTSeedClassifier.h"
std::vector<double> PatBBDTSeedClassifier::initLookupTable()
{
std::vector<double> indicesVector ={
"""
row_nb = 1
for bin_value in classifier._lookup_table[:,0]:
function_body += "\t" + str(bin_value) +" , "
if (row_nb % numbers_per_row) == 0:
function_body += "\n"
row_nb += 1
function_body += """\n };
return indicesVector;
}
"""
return function_body
另一个想法是将python数组存储到二进制文件中,然后用C ++读取它。但这很棘手。我无法正确阅读它。 我使用这么简单的命令生成表:
../src/PatBBDTSeedClassifier_lookupTable.cpp
lcg-g++-4.9.3: internal compiler error: Killed (program cc1plus)
0x409edc execute
../../gcc-4.9.3/gcc/gcc.c:2854
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
你能告诉我怎么办?我用Google搜索了,我无法找到足够的答案。
你知道我怎么能处理这么大的阵列。
我应该给你更详细的问题描述。 我使用python训练ML(sklearn)分类器,然后我想用C ++部署它。 Doe到计时问题(执行速度是我研究的一个关键部分)我使用bonsai boosted decision trees的想法。在这种方法中,您将BDT转移到查找表中。
答案 0 :(得分:4)
如果您正在使用GNU工具,直接使用objcopy
来实现Jean-Francois建议的那个很容易;结合编写二进制数组的PM2Ring的python脚本,您可以执行:
objcopy -I binary test.data -B i386:x86-64 -O elf64-x86-64 testdata.o
(取决于您的实际处理器架构,您可能需要调整)。该命令将使用以下符号创建名为testdata.o
的新对象:
0000000000000100 D _binary_test_data_end
0000000000000100 A _binary_test_data_size
0000000000000000 D _binary_test_data_start
所有这些符号将在链接程序中以C链接显示为符号。 size
不可用(它也会转换为地址),但可以使用*start
和*end
。这是一个最小的C ++程序:
#include <iostream>
extern "C" double _binary_test_data_start[];
extern "C" double _binary_test_data_end[0];
int main(void) {
double *d = _binary_test_data_start;
const double *end = _binary_test_data_end;
std::cout << (end - d) << " doubles in total" << std::endl;
while (d < end) {
std::cout << *d++ << std::endl;
}
}
_binary_test_data_end
实际上将超过数组_binary_test_data_start
中的最后一个元素。
使用g++ test.cc testdata.o -o program
编译+链接此程序(使用上面objcopy中的testdata.o)。
默认输出(cout
似乎不方便地截断小数):
% ./a.out
32 doubles in total
0
0.0625
0.125
0.1875
0.25
0.3125
0.375
0.4375
0.5
0.5625
0.625
0.6875
0.75
0.8125
0.875
0.9375
1
1.0625
1.125
1.1875
1.25
1.3125
1.375
1.4375
1.5
1.5625
1.625
1.6875
1.75
1.8125
1.875
1.9375
您还可以非常轻松地将这些值分配到矢量中; std::vector<double>
接受2个迭代器,其中第一个指向第一个元素,第二个指向后面的一个;你可以在这里使用数组,因为它们会衰减成指针,而指针可以用作迭代器:
std::vector<double> vec(_binary_test_data_start, _binary_test_data_end);
然而,对于大型阵列,这只是不必要的复制。此外,仅使用C数组还有一个额外的好处,即延迟加载; ELF可执行文件不会被读入内存,但会根据需要进行分页;二进制数组只有在访问时才从文件加载到RAM中。
答案 1 :(得分:2)
以下是如何将Python浮点数据写入二进制文件以及如何在C中读取数据的简单示例。为了对数据进行编码,我们使用struct
模块。
#!/usr/bin/env python3
from struct import pack
# The float data to save
table = [i / 16.0 for i in range(32)]
# Dump the table to stdout
for i, v in enumerate(table):
print('%d: %f' % (i, v))
# Save the data to a binary file
fname = 'test.data'
with open(fname, 'wb') as f:
for u in table:
# Pack doubles as little-endian
f.write(pack(b'<d', u))
<强>输出强>
0: 0.000000
1: 0.062500
2: 0.125000
3: 0.187500
4: 0.250000
5: 0.312500
6: 0.375000
7: 0.437500
8: 0.500000
9: 0.562500
10: 0.625000
11: 0.687500
12: 0.750000
13: 0.812500
14: 0.875000
15: 0.937500
16: 1.000000
17: 1.062500
18: 1.125000
19: 1.187500
20: 1.250000
21: 1.312500
22: 1.375000
23: 1.437500
24: 1.500000
25: 1.562500
26: 1.625000
27: 1.687500
28: 1.750000
29: 1.812500
30: 1.875000
31: 1.937500
/* Read floats from a binary file & dump to stdout */
#include <stdlib.h>
#include <stdio.h>
#define FILENAME "test.data"
#define DATALEN 32
int main(void)
{
FILE *infile;
double data[DATALEN];
int i, n;
if(!(infile = fopen(FILENAME, "rb")))
exit(EXIT_FAILURE);
n = fread(data, sizeof(double), DATALEN, infile);
fclose(infile);
for(i=0; i<n; i++)
printf("%d: %f\n", i, data[i]);
return 0;
}
上面的C代码产生与savefloat.py
所示相同的输出。
答案 2 :(得分:0)
正如您所注意到的,编译器在这样的大数据阵列上崩溃。
除了读取二进制文件(因为您不想这样做)之外,您可以做的是链接汇编文件。它仍然使可执行文件自给自足,并且GAS对大文件更加宽容。这是我使用python生成的一些asm文件的示例,它与经典gcc
汇总很好:
.section .rodata
.globl FT
.globl FT_end
FT:
.byte 0x46,0x54,0x5f,0x43,0x46,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x43,0x4f,0x4d,0x50
.byte 0x32,0x30,0x31,0x0,0x3,0x88,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
.byte 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
.byte 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0xe6,0x47,0x6,0x7,0x8,0x28,0x28
.byte 0x26,0x6,0x2a,0x6,0x6,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
FT_end:
这种技术允许我在可执行文件中嵌入一个80兆字节的二进制文件(100万个代码行),因为我没有文件系统来读取该环境中的数据文件(QEMU)
使用python代码进行真正的测试我终于可以挖掘出来了:
Python代码:
floats = [0.12,0.45,0.34,4.567,22.7]
import struct
contents = struct.pack('f'*len(floats), *floats)
outbase = "_extref"
output_file = "data.s"
fw = open(output_file,"w")
fw.write(""".section .rodata
.globl {0}
.globl {0}_end
{0}:
""".format(outbase,outbase))
eof = False
current_offset = 0
while not eof:
to_write = []
if current_offset==len(contents):
break
if current_offset<len(contents):
fw.write(".byte ")
for i in range(0,16):
if current_offset<len(contents):
to_write.append(hex(ord(contents[current_offset])))
current_offset+=1
else:
eof = True
break
if len(to_write)>0:
fw.write(",".join(to_write)+"\n")
fw.write(outbase+"_end:\n")
fw.close()
test.cpp
:C ++代码(C ++ 11,我努力争取指向asm部分的指针):
#include <iostream>
#include <vector>
#include <strings.h>
extern const float extref;
extern const float extref_end;
int main()
{
int size = (&extref_end - &extref);
std::cout << "nb_elements: " << size << std::endl;
std::vector<float> v(size);
memcpy(&v[0],&extref,sizeof(float)*size);
for (auto it : v)
{
std::cout << it << std::endl;
}
return 0;
}
Python代码生成data.s
文件。使用以下命令创建可执行文件:
g++ -std=c++11 test.cpp data.s
运行:
nb_elements: 5
0.12
0.45
0.34
4.567
22.7
此方法的主要优点是您可以使用所需的格式定义任意数量的符号。