func quantile(x []int, p float64) float64 {
	if len(x) == 0 {
		return 0
	}
	N := float64(len(x))
	if p < 2.0/(3.0*(N+1.0/3.0)) {
		return float64(x[0])
	}
	if p >= (N-1.0/3.0)/(N+1.0/3.0) {
		return float64(x[len(x)-1])
	}
	h := (N+1.0/3.0)*p + 1.0/3.0
	fh := math.Floor(h)
	xl := x[int(fh)-1]
	xr := x[int(fh)]
	return float64(xl) + (h-fh)*float64(xr-xl)
}
https://en.wikipedia.org/wiki/Quantile formula R-8
package ht
import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/errorlist"
)
func init() {
	RegisterCheck(&Latency{})
}
type Latency struct {
	N int `json:",omitempty"`
	Concurrent int `json:",omitempty"`
	Limits string `json:",omitempty"`
	IndividualSessions bool `json:",omitempty"`
	SkipChecks bool `json:",omitempty"`
	DumpTo string `json:",omitempty"`
	limits []latLimit
}
type latLimit struct {
	q   float64
	max time.Duration
}
func (L *Latency) Execute(t *Test) error {
	var dumper io.Writer
	switch L.DumpTo {
	case "":
		dumper = ioutil.Discard
	case "stdout":
		dumper = os.Stdout
	case "stderr":
		dumper = os.Stderr
	default:
		file, err := os.OpenFile(L.DumpTo, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			return err
		}
		defer file.Close()
		buffile := bufio.NewWriter(file)
		defer buffile.Flush()
		dumper = buffile
	}
	csvWriter := csv.NewWriter(dumper)
	defer csvWriter.Flush()
	tests, err := L.produceTestSet(t)
	if err != nil {
		return err
	}
	averageRT := L.warmup(tests)
	offset := averageRT / time.Duration(L.Concurrent)
	conc := L.Concurrent
	resultCh := make(chan latencyResult, 3*L.Concurrent)
	data := make([]latencyResult, L.N)
	done := make(chan bool)
	started := time.Now()
	go func() {
		for i := 0; i < len(data) && time.Since(started) < 3*time.Minute; i++ {
			data[i] = <-resultCh
		}
		close(done)
	}()
	wg := &sync.WaitGroup{}
	for i := 0; i < conc; i++ {
		wg.Add(1)
		go func(ex *Test, id int) {
			for running := true; running; {
				ex.Run()
				lr := latencyResult{
					status:   ex.Result.Status,
					started:  ex.Result.Started,
					duration: ex.Response.Duration,
					execBy:   id,
				}
				select {
				case <-done:
					running = false
				default:
					select {
					case resultCh <- lr:
					default:
					}
				}
			}
			wg.Done()
		}(tests[i], i)
		time.Sleep(offset)
	}
	wg.Wait()
	var errs errorlist.List
	counters := make([]int, Bogus+1)
	seen := uint64(0)  
	for _, res := range data {
		counters[res.status]++
		seen |= 1 << uint(res.execBy)
	}
	if counters[NotRun] > 0 {
		errs = errs.Append(fmt.Errorf("Check timed out, got only %d measurements",
			L.N-counters[NotRun]))
	} else if counters[Pass] != L.N {
		errs = errs.Append(fmt.Errorf("Got %d Fail, %d Error, %d Bogus",
			counters[Fail], counters[Error], counters[Bogus]))
	}
	if seen != (uint64(1)<<uint(conc))-1 {
		errs = errs.Append(fmt.Errorf("Not all %d concurrent workers did provide a result (%x)",
			L.Concurrent, seen))
	}
	if L.DumpTo != "" {
		fields := make([]string, 7)
		fields[0] = t.Name
		fields[1] = fmt.Sprintf("%d", L.Concurrent)
		if len(data) == L.N {
			fields[2] = "check completed"
		} else {
			fields[2] = "stopped early"
		}
		for _, d := range data {
			fields[3] = d.status.String()
			fields[4] = fmt.Sprintf("%d", d.execBy)
			fields[5] = d.started.Format(time.RFC3339Nano)
			fields[6] = d.duration.Truncate(10 * time.Microsecond).String()
			csvWriter.Write(fields)
		}
	}
	latencies := make([]int, len(data))
	for i, r := range data {
		latencies[i] = int(r.duration) / int(time.Millisecond)
	}
	sort.Ints(latencies)
	for _, lim := range L.limits {
		lat := time.Millisecond * time.Duration(quantile(latencies, lim.q))
		t.infof("Latency quantil (conc=%d) %0.2f%% ≤ %d ms",
			conc, lim.q*100, lat/time.Millisecond)
		if lat > lim.max {
			errs = errs.Append(fmt.Errorf("%.2f%% = %s > limit %s",
				100*lim.q, lat, lim.max))
		}
	}
	return errs.AsError()
}
func (L *Latency) produceTestSet(t *Test) ([]*Test, error) {
	tests := make([]*Test, L.Concurrent)
	for i := range tests {
		cpy, err := Merge(t)
		if err != nil {
			return nil, err
		}
		cpy.Name = fmt.Sprintf("Latency-Test %d", i+1)
		checks := []Check{}
		for _, c := range t.Checks {
			if _, lt := c.(*Latency); L.SkipChecks || lt {
				continue
			}
			checks = append(checks, c)
		}
		cpy.Checks = checks
		cpy.Execution.Verbosity = 0
		if t.Jar != nil {
			if L.IndividualSessions {
				cpy.Jar, _ = cookiejar.New(nil)
			} else {
				cpy.Jar = t.Jar
			}
		}
		tests[i] = cpy
	}
	return tests, nil
}
func (L *Latency) warmup(tests []*Test) time.Duration {
	wg := &sync.WaitGroup{}
	started := time.Now()
	prewarmed := 0
	for prewarm := 0; prewarm < 2; prewarm++ {
		for _, t := range tests {
			prewarmed++
			wg.Add(1)
			go func(ex *Test) {
				ex.Run()
				wg.Done()
			}(t)
		}
		wg.Wait()
	}
	return time.Since(started) / time.Duration(prewarmed)  
}
type latencyResult struct {
	status   Status
	started  time.Time
	duration time.Duration
	execBy   int
}