如何发送(和接收)从 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;
我是一名 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
ofNSNumber
比[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;