Latency to AWS Regions from Central China

Published At

ping aws data

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

CloudPing,China Telecom

China Unicom

CloudPing,China Unicom