C++中非常大向量的初始化

2024-04-28 00:40:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我在python中创建了非常大的O(10M)浮点列表。我想在我的C++项目中使用这个查找表。将Python数组转换为C++的最简单、最有效的方法是什么?在

我的第一个想法是生成c++函数,它负责初始化这么长的向量,然后编译它。 python代码如下所示:

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

输出文件的大小为500 MB。无法编译(由于gcc崩溃而终止编译):

^{pr2}$

另一种方法是将Python数组存储到二进制文件中,然后用C++读取。但这很棘手。我读不懂。 我使用以下简单命令生成表:

file = open("models/BBDT_lookuptable.dat", 'wb')
table = numpy.array(classifier._lookup_table[:,0])
table.tofile(file)
file.close()

你能告诉我怎么做吗?我在谷歌上搜了一下,找不到足够的答案。在

你知道我怎么处理这么大的数组吗。在

我应该给你更详细地描述一下这个问题。 我使用Python来训练ML(SkSead)分类器,然后我想在C++中部署它。对于时间问题(执行速度是我研究的关键部分)我使用bonsai boosted decision trees的概念。在这种方法中,您将BDT转换为查找表。在


Tags: 方法tablefunctionbody数组filerowstd
3条回答

下面是一个简单的示例,说明如何将Python浮点数据写入二进制文件,以及如何在C中读取该数据。在

在savefloat.py在

#!/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))    

输出

^{pr2}$

装载浮动.c

/* 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所示的输出相同。在

如果您正在使用GNU工具,那么直接使用objcopy来实现Jean Francois建议的目标是相当容易的;结合PM2Ring的python脚本编写一个二进制数组,您可以执行:

objcopy -I binary test.data -B i386:x86-64 -O elf64-x86-64 testdata.o

(根据实际的处理器体系结构,可能需要调整)。该命令将使用以下符号创建一个名为testdata.o的新对象:

^{pr2}$

在链接程序中,所有这些符号都将显示为带有C链接的符号。{{cd4}不能被转换成cd3},但不能使用cd3}。这里是一个最小的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>接受两个迭代器,其中第一个指向第一个元素,第二个指向后面的一个元素;您可以在数组衰变为指针时使用这里的数组,指针可以用作迭代器:

std::vector<double> vec(_binary_test_data_start, _binary_test_data_end);

但是,对于大型数组,这只是不必要的复制。此外,仅使用C数组还有一个额外的好处,即它是延迟加载的;ELF可执行文件不会被读入内存,但它们会根据需要进行分页;二进制数组只有在被访问时才从文件加载到RAM中。在

正如您所注意到的,编译器在这种大数据数组上崩溃。在

除了读取二进制文件之外(因为您不想这样做),您可以与程序集文件链接。它仍然使可执行文件自给自足,而且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代码:

^{pr2}$

^ {CD2>}: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

这种方法的主要优点是,您可以用所需的格式定义任意多个符号。在

相关问题 更多 >