It is very simple, and you don't need any code to work it out.
SoftwareSerial is completely synchronous (i.e., blocking) when writing. That means that the amount of time taken to peform the write is the time taken to send the data on the line plus a small function call overhead.
That means the time taken is:
1 / baud * 10 seconds
So at 9600 baud one bit is sent in 1/9600 of a second, and there are 10 bits (1 start, 8 data, and 1 stop bit), so it takes 1/960th of a second.
That's about 1ms per byte, plus a few extra clock cycles for calling the actual functions.
To put it in more detail, when you send a byte this happens:
- Set the line level to the start bit level
- Wait 1/9600th of a second
- Set the line level to the first data bit level
- Wait 1/9600th of a second
- Repeat 3&4 for the rest of the data bits
- Set the line level to the stop bit level
- Wait 1/9600th of a second
- Set the line level to the idle level
So you have 10 "wait 1/9600th of a second" in total, or a total time of 1/960th of a second.
The same can be said of receiving data. When the start bit arrives it triggers a pin change interrupt. A synchronous blocking interrupt routine then executes that does the same as above but with reading instead of writing. So that takes about 1ms to execute at 9600 baud - plus some extra to insert the incoming byte into a circular buffer, which you then read from in your sketch.
So the round-trip time of a single byte from unit A to unit B then from unit B to unit A can be estimated as:
1/960 + 1/960 + overheads
That means the minimum time will be 1/480 seconds plus whatever time is spent at the remote unit processing the byte to send data back to you, and a little bit for the function call overheads.
Incidentally, it's that last operation in the interrupt, the "adding the byte to a circular buffer" that means you can't reliably receive at 115200 baud with SoftwareSerial. That takes more time to process than the typical gap between bytes (could be zero - just the width of the stop bit...) that the next byte could have already started to arrive at the GPIO pin before the Arduino is capable of triggering the ISR again, so it misses the start bit.