dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/wrr/edf.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright 2019 gRPC authors.
    21   *
    22   */
    23  
    24  package wrr
    25  
    26  import (
    27  	"container/heap"
    28  	"sync"
    29  )
    30  
    31  // edfWrr is a struct for EDF weighted round robin implementation.
    32  type edfWrr struct {
    33  	lock               sync.Mutex
    34  	items              edfPriorityQueue
    35  	currentOrderOffset uint64
    36  	currentTime        float64
    37  }
    38  
    39  // NewEDF creates Earliest Deadline First (EDF)
    40  // (https://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling) implementation for weighted round robin.
    41  // Each pick from the schedule has the earliest deadline entry selected. Entries have deadlines set
    42  // at current time + 1 / weight, providing weighted round robin behavior with O(log n) pick time.
    43  func NewEDF() WRR {
    44  	return &edfWrr{}
    45  }
    46  
    47  // edfEntry is an internal wrapper for item that also stores weight and relative position in the queue.
    48  type edfEntry struct {
    49  	deadline    float64
    50  	weight      int64
    51  	orderOffset uint64
    52  	item        interface{}
    53  }
    54  
    55  // edfPriorityQueue is a heap.Interface implementation for edfEntry elements.
    56  type edfPriorityQueue []*edfEntry
    57  
    58  func (pq edfPriorityQueue) Len() int { return len(pq) }
    59  func (pq edfPriorityQueue) Less(i, j int) bool {
    60  	return pq[i].deadline < pq[j].deadline || pq[i].deadline == pq[j].deadline && pq[i].orderOffset < pq[j].orderOffset
    61  }
    62  func (pq edfPriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
    63  
    64  func (pq *edfPriorityQueue) Push(x interface{}) {
    65  	*pq = append(*pq, x.(*edfEntry))
    66  }
    67  
    68  func (pq *edfPriorityQueue) Pop() interface{} {
    69  	old := *pq
    70  	*pq = old[0 : len(old)-1]
    71  	return old[len(old)-1]
    72  }
    73  
    74  func (edf *edfWrr) Add(item interface{}, weight int64) {
    75  	edf.lock.Lock()
    76  	defer edf.lock.Unlock()
    77  	entry := edfEntry{
    78  		deadline:    edf.currentTime + 1.0/float64(weight),
    79  		weight:      weight,
    80  		item:        item,
    81  		orderOffset: edf.currentOrderOffset,
    82  	}
    83  	edf.currentOrderOffset++
    84  	heap.Push(&edf.items, &entry)
    85  }
    86  
    87  func (edf *edfWrr) Next() interface{} {
    88  	edf.lock.Lock()
    89  	defer edf.lock.Unlock()
    90  	if len(edf.items) == 0 {
    91  		return nil
    92  	}
    93  	item := edf.items[0]
    94  	edf.currentTime = item.deadline
    95  	item.deadline = edf.currentTime + 1.0/float64(item.weight)
    96  	heap.Fix(&edf.items, 0)
    97  	return item.item
    98  }