Non-linear/inconsistent sendall() time when using python sockets on Raspberry Pi

Viewed 58

Background: I'm trying to send a NumPy array over LAN through a python socket on a Raspberry Pi 4 8GB. This array is quite large (480,640,3) with a dtype of uint8. I've noticed that when I am sending data uncompressed vs compressed, there is a large discrepancy in the time spent actually sending data. Without compression, I'm pushing around 40Mb/s through the network to my "server" PC. I'm not bandwidth limited, as I've hit around 75Mb/s in other use cases.

I've tried compressing and then sending data over the socket which does yield better results, but only marginally since there isn't much CPU overhead on this ARM processor. As you can see in the results below, there's an almost 2 order of magnitude difference between sending ~2x the amount of data over the socket (921768 vs ~520000 bytes). But since this compression takes a fair amount of CPU overhead, the serialization takes longer, so the total difference in total frame time is almost negligible. So as of right now, I'm stuck with around 10FPS either way I do it.

TLDR: Sending data over a socket in python seems to be non-linear unless I'm missing an underlying function call/cast that is taking up an exorbitant amount of time. Is there something that I'm missing here?

Client Code:

clientsocket=socket.socket()
clientsocket.connect(('10.0.1.18',8484)) # Static server IP
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,480)
cap.set(cv2.CAP_PROP_FPS, 30)
counter = 0
try:
    t1 = time.time()
    while True:
        ret, img = cap.read()
        
        t3 = time.time()
        # Uncomment this next line to compress the array
        #img = gzip.compress(img, compresslevel=1) # Compression level of 1 to reduce CPU overhead
        data = pickle.dumps(img)
        t4=time.time()

        packed = struct.pack("=L", len(data))+data
        t5 = time.time()
        clientsocket.sendall(packed)
        t6 = time.time()

        print("Serialization/processing time: " t4-t3)
        print("Sending time: ", t6-t5) # Execution time of sending the data
        print(len(packed), "\n") # Byte length of packed struct
        counter += 1

except Exception as e:
'''
Used for printing approximate framerate on connection close
'''
    t2 = time.time()
    print(e)
    print("Average of: {} FPS".format(counter / (t2-t1)))


Non-Compressed Results:

Serialization time:  0.01045680046081543
Sending time:  0.08290624618530273
Message Length: 921768 

Serialization time:  0.007626771926879883
Sending time:  0.07082009315490723
Message Length: 921768 

Serialization time:  0.0067784786224365234
Sending time:  0.09341287612915039
Message Length: 921768 

...

Serialization time:  0.0063440799713134766
Sending time:  0.0751504898071289
Message Length: 921768 

Serialization time:  0.010351896286010742
Sending time:  0.07989215850830078
Message Length: 921768 

Compressed Results:

Serialization time:  0.10198521614074707
Sending time:  0.0012769699096679688
Message Length: 526874 

Serialization time:  0.10913228988647461
Sending time:  0.0012683868408203125
Message Length: 525908 

Serialization time:  0.09717822074890137
Sending time:  0.001861572265625
Message Length: 525809 

...

Serialization time:  0.09045243263244629
Sending time:  0.001861572265625
Message Length: 525003 

Serialization time:  0.08880734443664551
Sending time:  0.0010662078857421875
Message Length: 525252 
0 Answers
Related