【问题标题】:Python to C++ Communication FailurePython 到 C++ 通信失败
【发布时间】:2021-12-13 22:06:06
【问题描述】:

我在 python 中有 4 个 numpy 数组。我将它们发送到一个衍生的 c++ 进程。这适用于小数组,但是当数组变得太大时,c++ 程序会读取(看起来与机器的相反字节顺序(小))一个不正确的值。

python/numpy 数组的典型值为 0.25、0.5、0.7 等。

c++ 程序正确接收这些,直到它达到数千个元素,然后 c++ 程序开始接收奇怪的值,这些值似乎是它应该得到的小端值的大端版本。它与同步无关,因为无论大小,中间数组都能完美地发送所有数据。这只是我的第一个也是最后一个数组这样做——即使所有 python/numpy 数组都被强制为同一类型。

一旦数组变得太大:c++ 接收的值如下:-4.34345e-266、-5.67456e-50 等。

有没有人猜测为什么会发生这种情况?我已经被困了一段时间了。

编辑:这似乎是一个平台问题。该代码在我的本地计算机(Ubuntu 18.04、python 3.6.9、g++ 7.5.0)上运行,但在 Amazon AWS(Ubuntu 18.04、python 3.6.9、g++ 7.5.0)上失败——看起来相同。我还应该考虑其他平台依赖项吗?

编辑 2:我将其范围缩小了更多。这似乎是在经过一定时间后发生的问题,而不是在特定数组大小之后发生的问题。这就是为什么我的本地机器在问题出现之前传输了所有数据的原因。 AWS 使用 vCPU(虚拟),进程之间的通信可能需要更长的时间,这就是为什么我在云服务器而不是我的本地服务器上注意到它的原因......关于可能导致这种情况的任何建议都是有帮助的。

# python

...

p = subprocess.Popen(
    'shell text to launch C++ process',
    shell=True, 
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

...

<loop through all arrays>
   array = arrays[i]
   
   p.stdin.write(<the size and type of array>)

   <looping through numpy array>
      value = array[x][y]
      b = struct.pack('<d', value)
      p.stdin.write(b)
   p.stdin.flush()
// c++

...

<loop through expected arrays>
    
    int size;
    r = fread(&size, sizeof(int), 1, stdin);

    <allocate array with size and type>

    <loop through size of array>
        double value;
        r = fread(&value, sizeof(double), 1, stdin);
        
        <input values into allocated array>

以下是发送前在 python 中的 unique 值,以及在接收后在 c++ 中的值(aid 是数组的 id):

py >> aid:  0  val :  1.5147748861987186e-07
py >> aid:  0  val :  0.00027064327098285035
py >> aid:  0  val :  0.00032440267655912817
py >> aid:  0  val :  0.00039440984799195717
py >> aid:  0  val :  0.00048666128622491974
py >> aid:  0  val :  0.0006088895533821487
py >> aid:  0  val :  0.0007693269194044289
py >> aid:  0  val :  0.0009713703111738414
py >> aid:  0  val :  0.0011995490513889394
py >> aid:  0  val :  0.0013989766453051436
py >> aid:  0  val :  0.0014816246426333445
py >> aid:  0  val :  0.000971370311173842
py >> aid:  0  val :  0.000608889553382149
py >> aid:  0  val :  0.0003944098479919573
py >> aid:  0  val :  0.0003244026765591282
py >> aid:  0  val :  0.0006091367065721885
py >> aid:  0  val :  1.1195904948938864
py >> aid:  0  val :  0.0006535893828657839
py >> aid:  0  val :  0.0006896927523924802
py >> aid:  0  val :  0.0007134077346636406
py >> aid:  0  val :  0.0007216922125962245
py >> aid:  0  val :  0.17
py >> aid:  0  val :  205.0
py >> aid:  0  val :  0.0016111429295754896
py >> aid:  0  val :  0.001897252365911768
py >> aid:  0  val :  0.0020511342434220143
py >> aid:  0  val :  0.0021170160163027038
py >> aid:  0  val :  0.0021201443585756935
py >> aid:  0  val :  2.407359807268656e-06
py >> aid:  0  val :  0.0011768390663802282
py >> aid:  0  val :  0.0013958354194843637
py >> aid:  0  val :  0.001670834944478645
py >> aid:  0  val :  0.002016004763800768
py >> aid:  0  val :  0.002444680031953682
py >> aid:  0  val :  0.0029617508597233667
py >> aid:  0  val :  0.0035475199335043986
py >> aid:  0  val :  0.004133617431826363
py >> aid:  0  val :  0.0045900107907456715
py >> aid:  0  val :  0.0047657186623283965
py >> aid:  0  val :  0.004133617431826364
py >> aid:  0  val :  0.0035475199335044
py >> aid:  0  val :  0.0024446800319536835
py >> aid:  0  val :  0.0016708349444786454
py >> aid:  1  val :  0.0
py >> aid:  1  val :  0.1978674776035768
py >> aid:  2  val :  295.15

c++ >> aid: 0 val: 1.51477e-07
c++ >> aid: 0 val: 0.000270643
c++ >> aid: 0 val: 0.000324403
c++ >> aid: 0 val: 0.00039441
c++ >> aid: 0 val: 0.000486661
c++ >> aid: 0 val: 0.00060889
c++ >> aid: 0 val: 0.000769327
c++ >> aid: 0 val: 0.00097137
c++ >> aid: 0 val: 0.00119955
c++ >> aid: 0 val: 0.00139898
c++ >> aid: 0 val: 0.00148162
c++ >> aid: 0 val: 0.00097137
c++ >> aid: 0 val: 0.00060889
c++ >> aid: 0 val: 0.00039441
c++ >> aid: 0 val: 0.000324403
c++ >> aid: 0 val: 0.000609137
c++ >> aid: 0 val: 1.11959
c++ >> aid: 0 val: 0.000653589
c++ >> aid: 0 val: 0.000689693
c++ >> aid: 0 val: 0.000713408
c++ >> aid: 0 val: 0.000721692
c++ >> aid: 0 val: 0.17
c++ >> aid: 0 val: 205
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: -1.11628e-125
c++ >> aid: 0 val: -1.11628e-125
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: -4.31009e+12
c++ >> aid: 0 val: -1.49167e-154
c++ >> aid: 0 val: -1.49167e-154
c++ >> aid: 0 val: -4.31009e+12
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: -4.31009e+12
c++ >> aid: 0 val: 3.27272e+181
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: 3.27272e+181
c++ >> aid: 0 val: 3.27272e+181
c++ >> aid: 0 val: 2.31398e-204
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: 2.31398e-204
c++ >> aid: 0 val: 2.31398e-204
c++ >> aid: 0 val: -3.46371e+65
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: -3.46371e+65
c++ >> aid: 0 val: -3.46371e+65
c++ >> aid: 0 val: 3.12744e+114
c++ >> aid: 0 val: -7.26344e+201
c++ >> aid: 0 val: 3.12744e+114
c++ >> aid: 0 val: 3.12744e+114
c++ >> aid: 0 val: 1.23842e+146
c++ >> aid: 0 val: 1.23842e+146
c++ >> aid: 0 val: 5.81017e-69
c++ >> aid: 0 val: 9.07095e+38
c++ >> aid: 0 val: 9.07095e+38
c++ >> aid: 0 val: -3.08372e+147
c++ >> aid: 0 val: -3.08372e+147
c++ >> aid: 0 val: 2.46041e+154
c++ >> aid: 0 val: 2.46041e+154
c++ >> aid: 0 val: -4.86069e-290
c++ >> aid: 0 val: -4.86069e-290
c++ >> aid: 0 val: 1.33577e-275
c++ >> aid: 0 val: 1.33577e-275
c++ >> aid: 0 val: 8.41193e+15
c++ >> aid: 0 val: 8.41193e+15
c++ >> aid: 0 val: 4.09165e-233
c++ >> aid: 0 val: 4.09165e-233
c++ >> aid: 0 val: -7.36835e+223
c++ >> aid: 0 val: -7.36835e+223
c++ >> aid: 0 val: -6.59221e+62
c++ >> aid: 0 val: -6.59221e+62
c++ >> aid: 0 val: -1.60471e-283
c++ >> aid: 0 val: -1.60471e-283
c++ >> aid: 0 val: -6.59221e+62
c++ >> aid: 0 val: -7.36835e+223
c++ >> aid: 0 val: -7.36835e+223
c++ >> aid: 0 val: 4.09165e-233
c++ >> aid: 0 val: 4.09165e-233
c++ >> aid: 0 val: 8.41193e+15
c++ >> aid: 0 val: 1.33577e-275
c++ >> aid: 0 val: 1.33577e-275
c++ >> aid: 0 val: -4.86069e-290
c++ >> aid: 0 val: 2.46041e+154
c++ >> aid: 0 val: 2.46041e+154
c++ >> aid: 0 val: -3.08372e+147
c++ >> aid: 0 val: 9.07095e+38
c++ >> aid: 0 val: 5.81017e-69
c++ >> aid: 1 val: 0
c++ >> aid: 1 val: 0.197867
c++ >> aid: 2 val: 295.15

这是正在发生的事情的示例。运行 python 代码,看看它是如何通信的。在创建此示例时,我注意到dim 的限制为144,这意味着我的计算机在失败之前可以传输的最大字节数为165888。 (编辑:这是由于读取和写入在同一个线程中时管道阻塞,正如下面的答案中所指出的,但我的实际代码使用线程并且是非阻塞的。问题似乎仍然是时间流逝相关,因为它适用于我的本地服务器

# python 3

import numpy
import subprocess
import struct
import sys
import time

s = subprocess.Popen(
    'g++ -Ofast -pthread -o script comm_test.cpp && ./script',
    shell=True, 
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    bufsize=-1,
)

dim = 4
a = numpy.arange(dim*dim, dtype=numpy.float64).reshape((dim, dim))
print(a)
bdim = dim.to_bytes(4, byteorder=sys.byteorder)
s.stdin.write(bdim)
for i in range(dim):
    for j in range(dim):
        b = struct.pack('<d', a[i][j])
        s.stdin.write(b)
s.stdin.flush()
while True:
    data = s.stderr.readline()
    text = '[c++] {}'.format(data.decode('utf-8'), end='')
    print(text)
    if 'done' in text:
        break

// comm_test.cpp

#include <iostream>
#include <stdio.h>
#include <streambuf>
using namespace std;

int main(){
    cerr << "started" << endl;
    int r, dim;
    r = fread(&dim, sizeof(int), 1, stdin);
    cerr << "dim: " << dim << endl;
    double* array = new double[dim*dim];
    for(int i = 0; i < dim*dim; i++){
        double val;
        r = fread(&val, sizeof(double), 1, stdin);
        array[i] = val;
        cerr << val << endl;
    }
    cerr << "done" << endl;
    return 0;
}

【问题讨论】:

  • “太大”到底是什么意思?你对它开始破坏的数组大小有一个估计吗?
  • @LayneBernardo 大约 30,000 个或更多元素
  • 这只是一种预感,我对 Python 没有太多经验,但我有点怀疑您可能遇到了 stdin 缓冲区的某种问题。作为故障排除步骤,您可以尝试禁用标准输入缓冲区,如下所述:stackoverflow.com/questions/3670323/…
  • @LayneBernardo 感谢您的输入,我尝试了非缓冲,但没有成功。
  • 您的代码过于粗略和不完整,无法确定任何事情。请给出一个合适的“Minimal Complete Reproducible Example”。您是否尝试过计算发送和接收的值的数量?

标签: python c++ amazon-web-services endianness fread


【解决方案1】:

我想通了。我在某个地方有一个循环,它在计时器上对 c++ 进程执行 ping 操作。它在所有初始数据传输完成之前运行,因此,ping 会在传输中间的某个地方发送几个字节的数据(因此我无法确定确切的错误点),这会抛出所有后续数据。我假设我的本地计算机 IPC 比 AWS Cloud vCPUs IPC 更快是正确的。这就是我的本地计算机正常工作的原因 - 初始数据传输能够超过计时器循环。

由于这对任何人都没有太大帮助,我可以发布一个简单的进程间 PIPE 通信的更好版本(带有线程,因此它是非阻塞的 [对传输的数据没有限制]):

# python 3

import numpy
import subprocess
import struct
import sys
import time
import threading
from time import perf_counter

s = subprocess.Popen(
    'g++ -Ofast -pthread -o script comm_test.cpp && ./script',
    shell=True, 
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
)

def reader(s):
    while True:
        data = s.stdout.readline()
        text = '[c++] {}'.format(data.decode('utf-8'), end='')
        print(text)
        if 'done' in text:
            break

r = threading.Thread(target=reader, args=(s,))
r.start()
dim = 1000
a = numpy.arange(dim*dim, dtype=numpy.float64).reshape((dim, dim))
print(a)
bdim = struct.pack('<i', dim)
s.stdin.write(bdim)
t1 = perf_counter()
for i in range(dim):
    for j in range(dim):
        val = a[i][j]
        val = numpy.float64(val)
        b = struct.pack('<d', val)
        s.stdin.write(b)
s.stdin.flush()
t2 = perf_counter()
r.join()
print('finished in {:.4f} seconds'.format(t2-t1))

// c++ comm_test.cpp

#include <iostream>
#include <stdio.h>
#include <streambuf>

using namespace std;

int main(){
    cerr << "started" << endl;
    int r, dim;
    r = fread(&dim, sizeof(int), 1, stdin);
    cerr << "dim: " << dim << endl;
    double* array = new double[dim*dim];
    for(int i = 0; i < dim*dim; i++){
        double val;
        r = fread(&val, sizeof(double), 1, stdin);
        array[i] = val;
        cerr << val << endl;
    }
    cerr << "done" << endl;
    return 0;
}

【讨论】:

    【解决方案2】:

    您的问题不在 Python 方面。您的问题是 C 应用程序正在填充其标准输出管道缓冲区并阻塞,因为您的 Python 代码在完成写入之前不会读取任何内容。

    要进行这种通信,您需要同时进行读取和写入。你可以用一个简单的 TCP 套接字来做到这一点,但你会遇到同样的问题;缓冲区不是无限大的。

    【讨论】:

    • 我的实际代码使用线程来分离读写操作,虽然我这样做的意图不同,所以我很感激这些知识。但我的问题似乎仍然来自错误的数据传输。该程序在 c++ 中接收数据,然后计算并将解决方案发送回 python(然后将数组发送到远程计算机)。它每次都完整地完成此过程,但在解决方案中产生nan 值(仅适用于 AWS 上的大型数组)。使用本地服务器时,它会产生正确的解决方案。
    猜你喜欢
    • 2018-01-09
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    • 2019-10-20
    相关资源
    最近更新 更多