如何发送(和接收)从 Swift 到 Objective c++ 到 C++ 的超大浮动数组,然后再返回到 Swift?

How can I send (and receive) extremely large floating arrays from Swift to Objective c++ to c++, and then back up to Swift?

我是一名 Swift 程序员,我的 objective-c++ 和 c++ 经验绝不能与我的 Swift 经验相提并论。我已经成功地从 Swift -> Objc++ -> c++(处理发生的地方)发送和接收小型浮点数组,然后返回到 Objc++ 和 Swift。我们有大量的工作正在用 C++ 执行,我们暂时不能迁移到 Swift 有几个原因(其中一个原因是 C++ 在完成这项工作时比 Swift 更有效)。

c++ 程序完成的工作是在非常大的浮点数组上。目前,这些浮点数数组由 C++ 程序通过 CSV 文件发送和使用。我们想取消这些 CSV 文件,以消除在磁盘上读写的需要。我们想直接处理浮点数数组。

以下代码适用于小型数组。但是,对于 300,000+ 的非常大的数组,由于内存问题,程序会在 iOS 设备上崩溃,然后在模拟器上,下面的 Objective-c++ 代码最多使用 59 GB!

有人可以解释并告诉我一种更好的方法来发送和接收从 Swift 到 Objc++ 到 c++ 然后再返回到 Swift 的大型浮点数组吗?

目前,我将省略.h 和.hpp 代码,只显示Swift、.mm 和.cpp 代码。

SWIFT CODE:
let objcCppWrapper = ObjcCppWrapper()
        let pointer: UnsafeMutablePointer<Float> = UnsafeMutablePointer(mutating: floatArray)
        if let cppArraySameSize = objcCppWrapper.cppProcessSwiftArray(pointer, number_elements: Int32(floatArray.count)) {
            let newArraySameSizePointer: UnsafeMutablePointer<Any> = UnsafeMutablePointer(mutating: cppArraySameSize)
            let size = Int32(floatArray.count)
            let floatsSameSizeAny : [Any] = Array(UnsafeBufferPointer(start: newArraySameSizePointer, count: Int(size)))
            print("Swift: 'floatsSameSizeAny' size is \(floatsSameSizeAny.count)")
        }
OBJECTIVE-C++ CODE:
-(NSArray *) cppProcessSwiftArray:(float [])array number_elements:(int )number_elements{

    //Transform the float array into a vector array
    std::vector<float> arrayVector(number_elements);

    for(int j=0; j < number_elements; j++){
        arrayVector[j] = array[j];
    }

    CppProcess cppProcess;
    std::vector<float>  cppArray = cppProcess.cppProcessSwiftArray(arrayVector);

    //USES WAYYYYYY TOO MUCH MEMORY! UP TO 59 GB ON SIMULATOR, CRASHES TERMINATES IN IOS DEVICE DU TO MEMORY ISSUE.
    NSArray *returnNsArray = [NSArray array];
    for(int j=0; j < cppArray.size(); j++){
        returnNsArray = [returnNsArray arrayByAddingObject:[NSNumber numberWithFloat:cppArray[j]]];
    }

    return returnNsArray;
}
C++ CODE:
vector<float> CppProcess::cppProcessSwiftArray(vector<float> arrayOfFloats) {

    cout << "CPP: Received an array is of size " << arrayOfFloats.size() << '\n';

    std::vector<float> returnedSameSizeArray(arrayOfFloats.size());

    //For the moment, only create an arbitrary array of same size and return it...
    for(int j=0; j < arrayOfFloats.size(); j++){
        returnedSameSizeArray[j] = j ;
    }

    return returnedSameSizeArray;
}

您的 Swift 代码展示了 Swift 指针和数组的错误用法。 您对 UnsafeMutablePointer.init(mutating:) 的使用可能会导致灾难性的结果,包括崩溃或变量的意外更改。

同样在C++代码中,为什么要按值传递vector?您最好使用引用以避免复制。

无论如何,如果你介意内存效率,最好关心两件事:

  • 永远不要使用NSArray来表示原始类型的数组

    NSArray of NSNumber[Float]

  • 占用内存十倍甚至更多
  • 尽量避免复制

    如你所见,复制会使内存消耗加倍


首先,重写您的 C++ 代码。使用指针而不是 vector 并避免不必要的复制。

C++代码:

void CppProcess::cppProcessSwiftArray(const float *arrayOfFloats, int count, float *outputArray) {
    cout << "CPP: Received an array is of size " << count << '\n';

    //For the moment, only create an arbitrary array of same size and return it...
    for(int j=0; j < count; j++){
        outputArray[j] = j ;
    }
}

指针可以很容易地桥接到 Swift,因此您的 Objective-C 代码几乎没有什么可做的。

Objective-C代码:

-(void)cppProcessSwiftArray:(const float * _Nonnull)array count:(NSInteger)count output:(float * _Nonnull)outputArray {
    CppProcess cppProcess;
    cppProcess.cppProcessSwiftArray(array, (int)count, outputArray);
}

然后就可以利用桥接Swift数组和指针的特性了。

Swift代码:

        let floatArray: [Float] = ...
        var resultArray: [Float] = Array(repeating: 0, count: floatArray.count)
        let objcCppWrapper = ObjcCppWrapper()
        objcCppWrapper.cppProcessSwiftArray(floatArray, count: floatArray.count, output: &resultArray)

感谢您的回答,它有效。

我在这里也找到了这个 'working' 代码。但是,您的代码效率更高。我认为我的代码的错误之一是在循环的每次迭代中都复制了 NSArray。

NSMutableArray *nsMutableArray = [[NSMutableArray alloc] initWithCapacity: 300000];
for (int i=0; i < 300000; i++) {
    [nsMutableArray insertObject:[NSNumber numberWithFloat:cppArray[i]] atIndex:i];
}

NSArray *returnNsArray = [nsMutableArray copy];
return returnNsArray;