Latency to AWS Regions from Central China
Published At

Continuous Ping tests were conducted for the Hong Kong, Tokyo, Singapore, Seoul, US West, US East, and Frankfurt regions. The network latency performance of each region was recorded and analyzed, resulting in the following data.
The test environment was located in Xi’an, central China, using a China Mobile network. Tests were performed at 20-minute intervals, and in each round, 50 consecutive Ping requests were sent to each region.
Ping tests are primarily used to reflect basic network connectivity and latency and do not represent the full performance characteristics of real-world application requests.
Asian regions: Hong Kong, Tokyo, Singapore, and Seoul
Median request latency
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
P95
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
Jitter
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
Non-Asian regions: US West, US East, Frankfurt
Median request latency
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
P95
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
Jitter
X-axis: Time (UTC+8), Y-axis: Latency (ms)
Source: xc2f.com
The above data was collected using the following code.
const https = require("https")
const fs = require("fs")
const { performance } = require("perf_hooks")
const readline = require("readline")
// --- Configuration ---
const REGIONS = [
// --- Asia Pacific Hubs ---
{ name: "HongKong", url: "https://s3.ap-east-1.amazonaws.com" },
{ name: "Tokyo", url: "https://s3.ap-northeast-1.amazonaws.com" },
{ name: "Seoul", url: "https://s3.ap-northeast-2.amazonaws.com" },
{ name: "Singapore", url: "https://s3.ap-southeast-1.amazonaws.com" },
// --- North America Outlets ---
{ name: "US-West", url: "https://s3.us-west-2.amazonaws.com" }, // Oregon, primary subsea cable terminus
{ name: "US-East", url: "https://s3.us-east-1.amazonaws.com" }, // N. Virginia, largest AWS data center cluster
// --- Europe Outlet ---
{ name: "Frankfurt", url: "https://s3.eu-central-1.amazonaws.com" }, // Major European hub
]
const ITERATIONS = 50
const INTERVAL_MINUTES = 20
const MIN_GAP = 1000
const MAX_GAP = 2000
const DATE_LOCALE = "zh-CN"
const TIME_ZONE = "Asia/Shanghai"
const LOG_FILE = "aws_network.csv"
// --- Utility Logic ---
if (!fs.existsSync(LOG_FILE)) {
const header =
"Timestamp,Region,FirstReq(ms),Average(ms),Median(ms),P95(ms),P90(ms),Jitter(ms),LossRate,Min(ms),Max(ms),RawData\n"
fs.writeFileSync(LOG_FILE, header)
}
const sleepRandom = () => {
const ms = Math.floor(Math.random() * (MAX_GAP - MIN_GAP + 1)) + MIN_GAP
return new Promise((resolve) => setTimeout(resolve, ms))
}
const getStats = (arr, totalPlanned) => {
if (arr.length === 0)
return {
loss: "100%",
avg: 0,
median: 0,
p95: 0,
p90: 0,
jitter: 0,
min: 0,
max: 0,
}
const sorted = [...arr].sort((a, b) => a - b)
const sum = arr.reduce((a, b) => a + b, 0)
const lossRate =
(((totalPlanned - arr.length) / totalPlanned) * 100).toFixed(1) + "%"
const avg = (sum / arr.length).toFixed(2)
const median = (
sorted.length % 2 === 0
? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
: sorted[Math.floor(sorted.length / 2)]
).toFixed(2)
const p95 = sorted[Math.ceil(sorted.length * 0.95) - 1].toFixed(2)
const p90 = sorted[Math.ceil(sorted.length * 0.9) - 1].toFixed(2)
let jitterSum = 0
for (let i = 0; i < arr.length - 1; i++) {
jitterSum += Math.abs(arr[i + 1] - arr[i])
}
const jitter =
arr.length > 1 ? (jitterSum / (arr.length - 1)).toFixed(2) : "0.00"
return {
loss: lossRate,
avg,
median,
p95,
p90,
jitter,
min: sorted[0].toFixed(2),
max: sorted[sorted.length - 1].toFixed(2),
}
}
const request = (url) => {
return new Promise((resolve) => {
const start = performance.now()
https
.get(url, { timeout: 5000 }, (res) => {
res.on("data", () => {})
res.on("end", () => resolve(performance.now() - start))
})
.on("error", () => resolve(null))
.on("timeout", () => resolve(null))
})
}
function updateProgress(regionName, current, total, regionIndex, totalRegions) {
const percent = Math.round((current / total) * 100)
const barLength = 20
const completedLength = Math.round((current / total) * barLength)
const bar =
"█".repeat(completedLength) + "░".repeat(barLength - completedLength)
const remainingRegions = totalRegions - regionIndex - 1
const avgRegionTime = total * ((MIN_GAP + MAX_GAP) / 2 / 1000)
const currentRegionRemaining =
(total - current) * ((MIN_GAP + MAX_GAP) / 2 / 1000)
const totalRemainingSec = Math.round(
remainingRegions * avgRegionTime + currentRegionRemaining,
)
readline.cursorTo(process.stdout, 0)
process.stdout.write(
`[Region ${regionIndex + 1}/${totalRegions}] ${regionName.padEnd(10)} |${bar}| ${percent}% Est. Remaining: ${totalRemainingSec}s `,
)
}
const getCurrentTime = () =>
new Date().toLocaleString(DATE_LOCALE, {
hour12: false,
timeZone: TIME_ZONE,
})
// --- Main Task Execution ---
async function runTask() {
console.log(`\n[${getCurrentTime()}] 🚀 Starting Global Link Diagnostics...`)
for (let i = 0; i < REGIONS.length; i++) {
const region = REGIONS[i]
const latencies = []
const allAttempts = [] // Array to store every attempt (including nulls)
const timestamp = getCurrentTime()
const firstTime = await request(region.url)
const firstTimeStr = firstTime ? firstTime.toFixed(2) : "timeout"
for (let j = 0; j < ITERATIONS; j++) {
updateProgress(region.name, j + 1, ITERATIONS, i, REGIONS.length)
const time = await request(region.url)
if (time) {
latencies.push(time)
allAttempts.push(time.toFixed(2))
} else {
allAttempts.push("x") // Use 'x' to represent packet loss in raw data
}
if (j < ITERATIONS - 1) await sleepRandom()
}
const stats = getStats(latencies, ITERATIONS)
// Join all attempts into a single string separated by semicolons
const rawDataStr = allAttempts.join(";")
const logLine = `${timestamp},${region.name},${firstTimeStr},${stats.avg},${stats.median},${stats.p95},${stats.p90},${stats.jitter},${stats.loss},${stats.min},${stats.max},${rawDataStr}\n`
fs.appendFileSync(LOG_FILE, logLine)
readline.cursorTo(process.stdout, 0)
process.stdout.write(
`✅ ${region.name.padEnd(10)} | First: ${firstTimeStr.padStart(7)}ms | Med: ${stats.median.padStart(6)}ms | P95: ${stats.p95.padStart(6)}ms | P90: ${stats.p90.padStart(6)}ms | Jitter: ${stats.jitter.padStart(5)}ms | Loss: ${stats.loss}\n`,
)
}
console.log(
`\n[${getCurrentTime()}] Task cycle complete. Waiting ${INTERVAL_MINUTES} minutes...`,
)
setTimeout(runTask, INTERVAL_MINUTES * 60 * 1000)
}
runTask()The latency performance of China Telecom and China Unicom on CloudPing is provided for reference.
China Telecom

China Unicom
