|
| 1 | +--- |
| 2 | +title: "Diagnosing slow data transfer rates for my USB C hub" |
| 3 | +categories: |
| 4 | + - Blog |
| 5 | +tags: |
| 6 | + - Tech |
| 7 | + - Programming |
| 8 | +--- |
| 9 | + |
| 10 | +Data transfer to my 1TB SSD felt abnormally slow with my USB C hub, but really fast with my girlfriend's hub. On top of that, when I was using my USB C hub, my monitor would occasionally cut out, but with my girlfriend's, I never had that issue. |
| 11 | + |
| 12 | +I *suspect* the hub is the issue, but I'm going to try to test that by measuring data transfer rates. I really wouldn't want the physical transfer rate to be limiting anything I'm working on. |
| 13 | + |
| 14 | +# Writing a benchmarking script |
| 15 | + |
| 16 | +I asked ChatGPT for help with this, and they suggested I use the `dd` command to write a test file on my 1TB SSD. The `dd` command measures how long that takes, which is pretty neat. |
| 17 | + |
| 18 | +```bash |
| 19 | +$ dd if=/dev/zero of=/Volumes/1TB\ Manual/testfile bs=1m count=4096 |
| 20 | +4096+0 records in |
| 21 | +4096+0 records out |
| 22 | +4294967296 bytes transferred in 129.563073 secs (33149625 bytes/sec) |
| 23 | +``` |
| 24 | + |
| 25 | +The value we're really interested in is the data transfer rate, `33149625` in this case, so we can pipe the results into `awk`. |
| 26 | + |
| 27 | +```bash |
| 28 | +$ dd if=/dev/zero of=/Volumes/1TB\ Manual/testfile bs=1m count=4096 2>&1 | awk '/bytes\/sec/ {gsub(/[^0-9]/, "", $(NF-1)); print $(NF-1)}' |
| 29 | +33025085 |
| 30 | +``` |
| 31 | + |
| 32 | +Finally, we can divide that by $1024^2$ to get the result in $\text{MB}/\text{s}$. |
| 33 | + |
| 34 | +```bash |
| 35 | +$ dd if=/dev/zero of=/Volumes/1TB\ Manual/testfile bs=1m count=4096 2>&1 | awk '/bytes\/sec/ {gsub(/[^0-9]/, "", $(NF-1)); print $(NF-1)}' | bc -l <<< "scale=2; $(cat) / 1048576" |
| 36 | +``` |
| 37 | + |
| 38 | +This is getting a little complicated, and I want to run the same test for reading too, so I'll stick this logic in a script with ChatGPT's help. The script adds the following: |
| 39 | + |
| 40 | +1. Read test |
| 41 | +2. Cleaning up the test file |
| 42 | +3. Passing in the path as an argument |
| 43 | + |
| 44 | +```zsh |
| 45 | +#!/usr/bin/env zsh --no-rcs |
| 46 | + |
| 47 | +# Check if volume path argument is provided |
| 48 | +if [[ -z "$1" ]]; then |
| 49 | + echo "Usage: $0 <path-to-volume>" |
| 50 | + exit 1 |
| 51 | +fi |
| 52 | + |
| 53 | +# Validate the provided path |
| 54 | +if [[ ! -d "$1" ]]; then |
| 55 | + echo "Error: '$1' is not a valid directory or does not exist." |
| 56 | + exit 1 |
| 57 | +fi |
| 58 | + |
| 59 | +# Define test file path and size |
| 60 | +VOLUME_PATH="$1" |
| 61 | +TEST_FILE="$(mktemp "$VOLUME_PATH/testfile.XXXXXX.tmp")" |
| 62 | +TEST_SIZE="4096" # Size in MiB (4GB) |
| 63 | + |
| 64 | +# Function to clean up the test file |
| 65 | +function cleanup() { |
| 66 | + if [[ -f "$TEST_FILE" ]]; then |
| 67 | + echo "Cleaning up test file..." |
| 68 | + rm -f "$TEST_FILE" |
| 69 | + fi |
| 70 | +} |
| 71 | + |
| 72 | +# Set up traps to ensure cleanup |
| 73 | +trap cleanup EXIT # Run cleanup on script exit |
| 74 | +trap cleanup INT TERM HUP # Run cleanup on interruption (Ctrl+C, termination, or hangup) |
| 75 | + |
| 76 | +# Function to run dd and extract speed in MB/s |
| 77 | +function run_dd_test() { |
| 78 | + local direction=$1 # "write" or "read" |
| 79 | + local dd_command=$2 |
| 80 | + echo "Running $direction test..." |
| 81 | + |
| 82 | + # Run dd, extract speed in bytes/sec, and convert to MB/s |
| 83 | + SPEED=$(eval "$dd_command" 2>&1 | \ |
| 84 | + awk '/bytes\/sec/ {gsub(/[^0-9]/, "", $(NF-1)); printf "%.2f", $(NF-1) / 1048576}') |
| 85 | + |
| 86 | + echo "$direction speed: $SPEED MB/s" |
| 87 | +} |
| 88 | + |
| 89 | +# Perform write test |
| 90 | +run_dd_test "write" "dd if=/dev/zero of='$TEST_FILE' bs=1m count=$TEST_SIZE" |
| 91 | + |
| 92 | +# Perform read test |
| 93 | +run_dd_test "read" "dd if='$TEST_FILE' of=/dev/null bs=1m" |
| 94 | + |
| 95 | +echo "Benchmark completed." |
| 96 | +``` |
| 97 | + |
| 98 | +Now, I just have to put this in my ["dotfiles" repository](https://github.com/Jython1415/dotfiles) and use it to figure out what's going on with the hub. |
| 99 | + |
| 100 | +# Recording results from different connections |
| 101 | + |
| 102 | +I'm currently using an [M2 MacBook Air](https://en.wikipedia.org/wiki/MacBook_Air_(Apple_silicon)#M2_and_M3_(2022–present)). There are 2 USB C ports on the left side, so I'll refer to them as the "A" and "B" ports, with A being closer to the charging port. My USB C hub (An older version of the [Chosure USB hub](https://a.co/d/eULor79) ([archive.today](http://archive.today/TAT5z))) has 3 USB A ports, and I'll refer to them as "1", "2", "3", with "1" being closest to the USB C port, and "3" being furthest. Here are the read–write speeds I recorded for each of them. |
| 103 | + |
| 104 | +| Connection | Write | Read | |
| 105 | +| ---------- | ----- | ----- | |
| 106 | +| A1 | 32.29 | 31.90 | |
| 107 | +| A2 | 32.41 | 31.70 | |
| 108 | +| A3 | 32.62 | 31.70 | |
| 109 | +| B1 | 31.54 | 31.44 | |
| 110 | +| B2 | 31.82 | 31.52 | |
| 111 | +| B3 | 31.48 | 31.47 | |
| 112 | + |
| 113 | +Read speeds seem *marginally* faster on A than B. Other than that, I don't see any differences that I'd suspect would turn out to be statistically significant with further testing. |
| 114 | + |
| 115 | +Since one of my initial issues was with when I had my display connected as well, I'm going to repeat the test on A1 with my display connected and playing something, to see if that changes the transfer rate at all. |
| 116 | + |
| 117 | +| Connection | Write | Read | |
| 118 | +| ---------- | ----- | ----- | |
| 119 | +| A1 | 32.59 | 31.88 | |
| 120 | +| A2 | 32.44 | 31.82 | |
| 121 | +| A3 | 32.41 | 31.83 | |
| 122 | + |
| 123 | +No real difference, and I'm still getting the display issue like before. I'll have to try with my girlfriend's USB C hub and see if that makes any difference. She has a "ANKER PowerExpand Direct 7-in-2 USB-C PD Media Hub". The hub plugs into both USB ports on my MacBook Air, so no A/B distinction this time. There are two USB A ports, so I'll be testing both of them, starting with the one closer to the HDMI port. |
| 124 | + |
| 125 | +This test is with the display connected as well. |
| 126 | + |
| 127 | +| Connection | Write | Read | |
| 128 | +| ---------- | ------ | -------- | |
| 129 | +| 1 | 385.43 | 374.41 | |
| 130 | +| 2 | 394.25 | 14614.17 | |
| 131 | + |
| 132 | +The read speed seems outlandish? Like is there some sort of caching going on that is messing with the test? I'm going to record the values anyway, but I'm suspicious of their validity. |
| 133 | + |
| 134 | +# Comparing results with the advertised values |
| 135 | + |
| 136 | +With the ANKER hub plugged in, I see "Up to 5 Gb/s" for System Information > Hardware > USB > USB 3.1 Bus > USB3.0 Hub > Speed. |
| 137 | + |
| 138 | +With The Chosure hub plugged in, I see "Up to 480 Mb/s" for System Information > Hardware > USB > USB 3.1 Bus > USB 2.1 Hub > Speed. |
| 139 | + |
| 140 | +So, there's a 10x difference in speed, for one, and the Chosure USB is listed as a USB 2.1 hub instead of a 3.1 hub. I'm not sure what the significance of that is. |
| 141 | + |
| 142 | +The Chosure hub seems 10x slower than their advertised 5 Gb/s, and the ANKER hub seems to be inline with their promotional material. |
| 143 | + |
| 144 | +# Conclusion |
| 145 | + |
| 146 | +- Having a tool to benchmark issues like this will be handy in the future. |
| 147 | +- I should probably get another hub... after checking their advertised data transfer rate more carefully. |
| 148 | +- Something is definitely going with the read test on the 2nd port of ANKER. I should follow-up on that... |
0 commit comments