如何有效地将大字符串从 Python 传递到 C++ 扩展方法?

Posted

技术标签:

【中文标题】如何有效地将大字符串从 Python 传递到 C++ 扩展方法?【英文标题】:How to pass a large string from Python to a C++ extension method efficiently? 【发布时间】:2018-12-31 18:32:34 【问题描述】:

简介

我正在处理需要处理大量文本数据的项目。许多相当大(数百 MB)的文本文件。 python是必需的(不要问为什么)。我想使用 C++ 扩展来提高性能。我决定选择 SWIG。我有一个模式匹配算法,它比通常的 python "string".find("pattern") 快得多。当我看到它用作 python 扩展时要慢得多时,我感到很惊讶。它不应该发生。我想我很接近找到原因,但需要您的帮助。

问题

现在,我用包含类的方法编写了一个简单的扩展,它什么都不做(只需将字符串作为参数并返回数值(函数中不进行任何处理):

nothing.h:

#ifndef NOTHING_H
#define NOTHING_H

#include <string.h>
#include <iostream>

using namespace std;

    class nothing 
        protected:
            int zm = 5;
        public:
            virtual int do_nothing(const char *empty);
    ;

#endif

nothing.cpp

#include "nothing.h"

int nothing::do_nothing(const char *empty) 
    return this->zm;

nothing.i

%module nothing
%include <std_string.i>

using std::string;
using namespace std;
%
    #include "nothing.h"
%


class nothing 
    protected:
        int zm = 5;
    public:
        virtual int do_nothing(const char *empty);
;

test.py

import nothing
import time

data = ""
with open('../hugefile', 'rb') as myfile:
    data=myfile.read().decode(errors='replace')

n = len(data)

zm = nothing.nothing()
start = time.time()
res = zm.do_nothing(data)
end = time.time()
print("Nothing time: ".format(end - start))


zm = nothing.nothing()
start = time.time()
res = data.find("asdasdasd")
end = time.time()
print("Find time   : ".format(end - start))

编译步骤:

swig -c++ -py3 -extranative -python nothing.i
g++ -fpic -lstdc++ -O3 -std=c++11 -c nothing.cpp nothing_wrap.cxx -I/usr/include/python3.7m
g++ -shared nothing.o nothing_wrap.o -o _nothing.so

输出:

$ python3 test.py
Nothing time: 0.3149874210357666
Find time   : 0.09926176071166992

如您所见,尽管没有什么比 find() 快得多,但它却慢了很多!

知道这是否可以以某种方式解决吗?对我来说,数据似乎被转换或复制了。

为什么我认为整个数据都被复制了?因为如果稍微将函数 do_nothing() 更改为(我省略了标题):

int nothing::do_nothing()  // removed the argument
    return this->zm;

那么结果如预期:

$ python3 test.py
Nothing time: 4.291534423828125e-06
Find time   : 0.10114812850952148

【问题讨论】:

Python 必须在调用之前创建一个 unmanaged 对象/char */字符串(是的,这意味着分配和复制数据)。 我想知道是否使用 non-Unicode 'string' / byte-array(所有 Python 3 字符串都是 Unicode,这是对 Python 2.x 的更改)是否允许SWIG 一个不复制的机会..?或者,也许接受 Python [string] 对象本身而不进行隐式本​​机转换? 你在寻找类似this的东西吗? 我认为您正在寻找更像这样的东西:***.com/a/16998687/168175 【参考方案1】:

您可能希望将文件名传递给 C 并在那里打开并搜索它。您正在读取字节,将这些字节转换为 unicode,然后转换回定时部分内的字节。您可以阅读此处的文档以了解其内部结构。

https://docs.python.org/3/c-api/unicode.html

如果文件是 utf-8,则通过删除解码将其保留为字节,或者仅传递文件名并将其加载到 C 中。

【讨论】:

谢谢,但我需要对字符串而不是文件进行操作。我将对这些字符串进行更多操作,因此每次从磁盘保存和加载都不是我的问题的好选择。我只需要将字符串的引用传递给 c++ 扩展。

以上是关于如何有效地将大字符串从 Python 传递到 C++ 扩展方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地将大文件加载到 IndexedDB 存储中?我的应用程序在超过 100,000 行时崩溃

如何安全地将字符串引用从 c# 传递到 c++?

如何以异步方式有效地将变量从 Matlab 传递到 GPU?

如何正确地将浮点指针从 C 库传递到其 C# 包装器

c ++将大向量作为输出传递

如何正确地将日期时间从 c# 传递到 sql?