diff --git a/README.md b/README.md index 7d14b1c..d7f6e43 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ func main() { s.DownloadTest() s.UploadTest() fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed) + s.Context.Reset() // reset counter } } ``` diff --git a/example/main.go b/example/main.go index 6f78f23..95cc224 100644 --- a/example/main.go +++ b/example/main.go @@ -32,7 +32,7 @@ func main() { checkError(s.UploadTest()) fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed) - //speedtest.GlobalDataManager.Reset() + s.Context.Reset() } } diff --git a/speedtest.go b/speedtest.go index e0ea7ad..8b404e4 100644 --- a/speedtest.go +++ b/speedtest.go @@ -3,12 +3,12 @@ package main import ( "context" "fmt" + "gopkg.in/alecthomas/kingpin.v2" "os" "strconv" "time" "github.com/showwin/speedtest-go/speedtest" - "gopkg.in/alecthomas/kingpin.v2" ) var ( diff --git a/speedtest/data_manager.go b/speedtest/data_manager.go index 47ce8b8..a56bb66 100644 --- a/speedtest/data_manager.go +++ b/speedtest/data_manager.go @@ -35,6 +35,7 @@ type Manager interface { // Wait for the upload or download task to end to avoid errors caused by core occupation Wait() Reset() + Snapshots() *Snapshots SetNThread(n int) Manager } @@ -74,7 +75,8 @@ type DataManager struct { DownloadRateSequence []int64 UploadRateSequence []int64 - DataGroup []*DataChunk + SnapshotStore *Snapshots + Snapshot *Snapshot sync.Mutex repeatByte *[]byte @@ -94,9 +96,11 @@ func NewDataManager() *DataManager { nThread: runtime.NumCPU(), captureTime: time.Second * 10, rateCaptureFrequency: time.Millisecond * 100, + Snapshot: &Snapshot{}, } ret.dFn = &funcGroup{manager: ret} ret.uFn = &funcGroup{manager: ret} + ret.SnapshotStore = newRecentSnapshots(maxSnapshotSize) return ret } @@ -257,7 +261,7 @@ func (dm *DataManager) NewChunk() Chunk { var dc DataChunk dc.manager = dm dm.Lock() - dm.DataGroup = append(dm.DataGroup, &dc) + *dm.Snapshot = append(*dm.Snapshot, &dc) dm.Unlock() return &dc } @@ -297,10 +301,15 @@ func (dm *DataManager) SetNThread(n int) Manager { return dm } +func (dm *DataManager) Snapshots() *Snapshots { + return dm.SnapshotStore +} + func (dm *DataManager) Reset() { dm.totalDownload = 0 dm.totalUpload = 0 - dm.DataGroup = []*DataChunk{} + dm.SnapshotStore.push(dm.Snapshot) + dm.Snapshot = &Snapshot{} dm.DownloadRateSequence = []int64{} dm.UploadRateSequence = []int64{} dm.dFn.fns = []func(){} @@ -495,4 +504,40 @@ func sampleVariance(vector []int64) (mean, variance, stdDev, min, max int64) { return } -var GlobalDataManager = NewDataManager() +const maxSnapshotSize = 10 + +type Snapshot []*DataChunk + +type Snapshots struct { + sp []*Snapshot + maxSize int +} + +func newRecentSnapshots(size int) *Snapshots { + return &Snapshots{ + sp: make([]*Snapshot, 0, size), + maxSize: size, + } +} + +func (rs *Snapshots) push(value *Snapshot) { + if len(rs.sp) == rs.maxSize { + rs.sp = rs.sp[1:] + } + rs.sp = append(rs.sp, value) +} + +func (rs *Snapshots) Latest() *Snapshot { + if len(rs.sp) > 0 { + return rs.sp[len(rs.sp)-1] + } + return nil +} + +func (rs *Snapshots) All() []*Snapshot { + return rs.sp +} + +func (rs *Snapshots) Clean() { + rs.sp = make([]*Snapshot, 0, rs.maxSize) +} diff --git a/speedtest/data_manager_test.go b/speedtest/data_manager_test.go index c279179..14c956e 100644 --- a/speedtest/data_manager_test.go +++ b/speedtest/data_manager_test.go @@ -9,7 +9,7 @@ import ( func BenchmarkDataManager_NewDataChunk(b *testing.B) { dmp := NewDataManager() - dmp.DataGroup = make([]*DataChunk, 64) + dmp.Reset() for i := 0; i < b.N; i++ { dmp.NewChunk() } @@ -41,10 +41,11 @@ func TestDataManager_AddTotalDownload(t *testing.T) { } func TestDataManager_GetAvgDownloadRate(t *testing.T) { - GlobalDataManager.totalDownload = 3000000 - GlobalDataManager.captureTime = time.Second * 10 + dm := NewDataManager() + dm.totalDownload = 3000000 + dm.captureTime = time.Second * 10 - result := GlobalDataManager.GetAvgDownloadRate() + result := dm.GetAvgDownloadRate() if result != 2.4 { t.Fatal() } @@ -52,16 +53,19 @@ func TestDataManager_GetAvgDownloadRate(t *testing.T) { func TestDynamicRate(t *testing.T) { - oldDownTotal := GlobalDataManager.GetTotalDownload() - oldUpTotal := GlobalDataManager.GetTotalUpload() + server, _ := CustomServer("http://shenzhen.cmcc.speedtest.shunshiidc.com:8080/speedtest/upload.php") + //server, _ := CustomServer("http://192.168.5.237:8080/speedtest/upload.php") + + oldDownTotal := server.Context.Manager.GetTotalDownload() + oldUpTotal := server.Context.Manager.GetTotalUpload() - GlobalDataManager.SetRateCaptureFrequency(time.Millisecond * 100) - GlobalDataManager.SetCaptureTime(time.Second) + server.Context.Manager.SetRateCaptureFrequency(time.Millisecond * 100) + server.Context.Manager.SetCaptureTime(time.Second) go func() { for i := 0; i < 2; i++ { time.Sleep(time.Second) - newDownTotal := GlobalDataManager.GetTotalDownload() - newUpTotal := GlobalDataManager.GetTotalUpload() + newDownTotal := server.Context.Manager.GetTotalDownload() + newUpTotal := server.Context.Manager.GetTotalUpload() downRate := float64(newDownTotal-oldDownTotal) * 8 / 1000 / 1000 upRate := float64(newUpTotal-oldUpTotal) * 8 / 1000 / 1000 @@ -71,16 +75,13 @@ func TestDynamicRate(t *testing.T) { } }() - server, _ := CustomServer("http://shenzhen.cmcc.speedtest.shunshiidc.com:8080/speedtest/upload.php") - //server, _ := CustomServer("http://192.168.5.237:8080/speedtest/upload.php") - err := server.DownloadTest() if err != nil { fmt.Println("Warning: not found server") //t.Error(err) } - GlobalDataManager.Wait() + server.Context.Manager.Wait() err = server.UploadTest() if err != nil { diff --git a/speedtest/request_test.go b/speedtest/request_test.go index d922ba7..e82e4f9 100644 --- a/speedtest/request_test.go +++ b/speedtest/request_test.go @@ -9,9 +9,6 @@ import ( ) func TestDownloadTestContext(t *testing.T) { - GlobalDataManager.Reset() - GlobalDataManager.SetRateCaptureFrequency(time.Millisecond) - GlobalDataManager.SetCaptureTime(time.Second) idealSpeed := 0.1 * 8 * float64(runtime.NumCPU()) * 10 / 0.1 // one mockRequest per second with all CPU cores delta := 0.15 latency, _ := time.ParseDuration("5ms") @@ -21,6 +18,10 @@ func TestDownloadTestContext(t *testing.T) { Context: defaultClient, } + server.Context.Manager.Reset() + server.Context.SetRateCaptureFrequency(time.Millisecond) + server.Context.SetCaptureTime(time.Second) + err := server.downloadTestContext( context.Background(), mockRequest, @@ -37,10 +38,6 @@ func TestDownloadTestContext(t *testing.T) { } func TestUploadTestContext(t *testing.T) { - GlobalDataManager.Reset() - GlobalDataManager.SetRateCaptureFrequency(time.Millisecond * 10) - GlobalDataManager.SetCaptureTime(time.Second) - idealSpeed := 0.1 * 8 * float64(runtime.NumCPU()) * 10 / 0.1 // one mockRequest per second with all CPU cores delta := 0.15 // tolerance scope (-0.05, +0.05) @@ -51,6 +48,10 @@ func TestUploadTestContext(t *testing.T) { Context: defaultClient, } + server.Context.Manager.Reset() + server.Context.SetRateCaptureFrequency(time.Millisecond) + server.Context.SetCaptureTime(time.Second) + err := server.uploadTestContext( context.Background(), mockRequest, @@ -68,7 +69,7 @@ func TestUploadTestContext(t *testing.T) { func mockRequest(ctx context.Context, s *Server, w int) error { fmt.Sprintln(w) - dc := GlobalDataManager.NewChunk() + dc := s.Context.Manager.NewChunk() // (0.1MegaByte * 8bit * nConn * 10loop) / 0.1s = n*80Megabit // sleep has bad deviation on windows // ref https://github.com/golang/go/issues/44343 diff --git a/speedtest/speedtest.go b/speedtest/speedtest.go index 5a64f05..0b02f05 100644 --- a/speedtest/speedtest.go +++ b/speedtest/speedtest.go @@ -11,7 +11,7 @@ import ( ) var ( - version = "1.6.6" + version = "1.6.7" DefaultUserAgent = fmt.Sprintf("showwin/speedtest-go %s", version) ) @@ -168,7 +168,7 @@ func WithUserConfig(userConfig *UserConfig) Option { func New(opts ...Option) *Speedtest { s := &Speedtest{ doer: http.DefaultClient, - Manager: GlobalDataManager, + Manager: NewDataManager(), } // load default config s.NewUserConfig(&UserConfig{UserAgent: DefaultUserAgent})