Skip to content

Commit f6a20de

Browse files
committed
Change request queue from a heap to a sorted array
1 parent fcba110 commit f6a20de

File tree

5 files changed

+435
-234
lines changed

5 files changed

+435
-234
lines changed

Source/Core/RequestQueue.js

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
define([
2+
'./Check',
3+
'./defineProperties'
4+
], function(
5+
Check,
6+
defineProperties) {
7+
'use strict';
8+
9+
/**
10+
* Priority queue for the {@link RequestScheduler} implemented as a sorted array.
11+
* The request with the highest priority is placed at index 0 and the request
12+
* with lowest priority is placed at index <code>length - 1</code>.
13+
* <p>
14+
* A lower <code>request.priority</code> value indicates that the request has higher priority. See {@link Request#priority}.
15+
* </p>
16+
*
17+
* @alias RequestQueue
18+
* @constructor
19+
* @private
20+
*
21+
* @param {Number} maximumLength The maximum length of the queue.
22+
*/
23+
function RequestQueue(maximumLength) {
24+
//>>includeStart('debug', pragmas.debug);
25+
Check.typeOf.number('maximumLength', maximumLength);
26+
//>>includeEnd('debug');
27+
28+
this._array = new Array(maximumLength);
29+
this._length = 0;
30+
this._maximumLength = maximumLength;
31+
}
32+
33+
defineProperties(RequestQueue.prototype, {
34+
/**
35+
* Gets the length of the queue.
36+
*
37+
* @memberof RequestQueue.prototype
38+
*
39+
* @type {Number}
40+
* @readonly
41+
*/
42+
length : {
43+
get : function() {
44+
return this._length;
45+
}
46+
}
47+
});
48+
49+
/**
50+
* Get the request at the given index.
51+
*
52+
* @param {Number} index The index of the request.
53+
*
54+
* @return {Request} The request at the given index.
55+
*/
56+
RequestQueue.prototype.get = function(index) {
57+
//>>includeStart('debug', pragmas.debug);
58+
Check.typeOf.number.greaterThanOrEquals('index', index, 0);
59+
Check.typeOf.number.lessThan('index', index, this._length);
60+
//>>includeEnd('debug');
61+
return this._array[index];
62+
};
63+
64+
/**
65+
* Insert a request into the queue. If the length would grow greater than the maximum length
66+
* of the queue, the lowest priority request is removed and returned.
67+
*
68+
* @param {Request} request The request to insert.
69+
*
70+
* @return {Request|undefined} The request that was removed from the queue if the queue is at full capacity.
71+
*/
72+
RequestQueue.prototype.insert = function(request) {
73+
//>>includeStart('debug', pragmas.debug);
74+
Check.defined('request', request);
75+
//>>includeEnd('debug');
76+
77+
var array = this._array;
78+
var previousLength = this._length;
79+
var length = this._length;
80+
var maximumLength = this._maximumLength;
81+
82+
if (length < maximumLength)
83+
{
84+
++this._length;
85+
}
86+
87+
if (previousLength === 0)
88+
{
89+
array[0] = request;
90+
return;
91+
}
92+
93+
var removedRequest;
94+
var lastIndex = previousLength - 1;
95+
96+
if (previousLength === maximumLength) {
97+
var lastRequest = array[lastIndex];
98+
if (request.priority >= lastRequest.priority) {
99+
// The array is full and the priority value of this request is too high to be inserted.
100+
return request;
101+
}
102+
// The array is full and the inserted request pushes off the last request
103+
removedRequest = lastRequest;
104+
--lastIndex;
105+
}
106+
107+
while (lastIndex >= 0 && request.priority < array[lastIndex].priority) {
108+
array[lastIndex + 1] = array[lastIndex]; // Shift element to the right
109+
--lastIndex;
110+
}
111+
array[lastIndex + 1] = request;
112+
113+
return removedRequest;
114+
};
115+
116+
/**
117+
* Call the given function for each request in the queue.
118+
*
119+
* @type {RequestQueue~ForEachCallback} The function to call.
120+
*/
121+
RequestQueue.prototype.forEach = function(callback) {
122+
//>>includeStart('debug', pragmas.debug);
123+
Check.typeOf.func('callback', callback);
124+
//>>includeEnd('debug');
125+
126+
var array = this._array;
127+
var length = this._length;
128+
for (var i = 0; i < length; ++i) {
129+
callback(array[i]);
130+
}
131+
};
132+
133+
/**
134+
* Sorts the queue.
135+
*/
136+
RequestQueue.prototype.sort = function() {
137+
var array = this._array;
138+
var length = this._length;
139+
140+
// Use insertion sort since our array is small and likely to be mostly sorted already.
141+
// Additionally length may be smaller than the array's actual length, so calling array.sort will lead to incorrect results for uninitialized values.
142+
for (var i = 1; i < length; ++i) {
143+
var j = i;
144+
while ((j > 0) && (array[j - 1].priority > array[j].priority)) {
145+
var temp = array[j - 1];
146+
array[j - 1] = array[j];
147+
array[j] = temp;
148+
--j;
149+
}
150+
}
151+
};
152+
153+
/**
154+
* Remove <code>length</code> number of requests from the top of the queue.
155+
*
156+
* @param {Number} length The number of requests to remove.
157+
*/
158+
RequestQueue.prototype.remove = function(length) {
159+
//>>includeStart('debug', pragmas.debug);
160+
Check.typeOf.number.greaterThanOrEquals('length', length, 0);
161+
Check.typeOf.number.lessThanOrEquals('length', length, this._length);
162+
//>>includeEnd('debug');
163+
if (length === 0) {
164+
return;
165+
}
166+
if (length === this._length) {
167+
this._length = 0;
168+
return;
169+
}
170+
171+
// Shift remaining requests back to the left
172+
var array = this._array;
173+
for (var i = length; i < this._length; ++i) {
174+
array[i - length] = array[i];
175+
}
176+
this._length -= length;
177+
};
178+
179+
/**
180+
* The callback to use in forEach.
181+
* @callback RequestQueue~ForEachCallback
182+
* @param {Request} request The request.
183+
*/
184+
185+
return RequestQueue;
186+
});

Source/Core/RequestScheduler.js

+39-43
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ define([
55
'./defined',
66
'./defineProperties',
77
'./Event',
8-
'./Heap',
98
'./isBlobUri',
109
'./isDataUri',
10+
'./RequestQueue',
1111
'./RequestState'
1212
], function(
1313
Uri,
@@ -16,16 +16,12 @@ define([
1616
defined,
1717
defineProperties,
1818
Event,
19-
Heap,
2019
isBlobUri,
2120
isDataUri,
21+
RequestQueue,
2222
RequestState) {
2323
'use strict';
2424

25-
function sortRequests(a, b) {
26-
return a.priority - b.priority;
27-
}
28-
2925
var statistics = {
3026
numberOfAttemptedRequests : 0,
3127
numberOfActiveRequests : 0,
@@ -35,12 +31,8 @@ define([
3531
numberOfActiveRequestsEver : 0
3632
};
3733

38-
var priorityHeapLength = 20;
39-
var requestHeap = new Heap({
40-
comparator : sortRequests
41-
});
42-
requestHeap.maximumLength = priorityHeapLength;
43-
requestHeap.reserve(priorityHeapLength);
34+
var requestQueueLength = 20;
35+
var requestQueue = new RequestQueue(requestQueueLength);
4436

4537
var activeRequests = [];
4638
var numberOfActiveRequestsByServer = {};
@@ -112,29 +104,28 @@ define([
112104
},
113105

114106
/**
115-
* The maximum size of the priority heap. This limits the number of requests that are sorted by priority. Only applies to requests that are not yet active.
107+
* The maximum length of the priority queue. This limits the number of requests that are sorted by priority. Only applies to requests that are not yet active.
116108
*
117109
* @memberof RequestScheduler
118110
*
119111
* @type {Number}
120112
* @default 20
113+
*
114+
* @private
121115
*/
122-
priorityHeapLength : {
116+
requestQueueLength : {
123117
get : function() {
124-
return priorityHeapLength;
118+
return requestQueueLength;
125119
},
126120
set : function(value) {
127-
// If the new length shrinks the heap, need to cancel some of the requests.
128-
// Since this value is not intended to be tweaked regularly it is fine to just cancel the high priority requests.
129-
if (value < priorityHeapLength) {
130-
while (requestHeap.length > value) {
131-
var request = requestHeap.pop();
132-
cancelRequest(request);
133-
}
121+
// Cancel all requests and resize the queue
122+
var length = requestQueue.length;
123+
for (var i = 0; i < length; ++i) {
124+
var request = requestQueue.get(i);
125+
cancelRequest(request);
134126
}
135-
priorityHeapLength = value;
136-
requestHeap.maximumLength = value;
137-
requestHeap.reserve(value);
127+
requestQueue = new RequestQueue(value);
128+
RequestScheduler.requestQueue = requestQueue;
138129
}
139130
}
140131
});
@@ -242,21 +233,19 @@ define([
242233
}
243234
activeRequests.length -= removeCount;
244235

245-
// Update priority of issued requests and resort the heap
246-
var issuedRequests = requestHeap.internalArray;
247-
var issuedLength = requestHeap.length;
248-
for (i = 0; i < issuedLength; ++i) {
249-
updatePriority(issuedRequests[i]);
250-
}
251-
requestHeap.resort();
236+
// Update priority of issued requests and resort the queue
237+
requestQueue.forEach(updatePriority);
238+
requestQueue.sort();
252239

253240
// Get the number of open slots and fill with the highest priority requests.
254241
// Un-throttled requests are automatically added to activeRequests, so activeRequests.length may exceed maximumRequests
255242
var openSlots = Math.max(RequestScheduler.maximumRequests - activeRequests.length, 0);
256243
var filledSlots = 0;
257-
while (filledSlots < openSlots && requestHeap.length > 0) {
258-
// Loop until all open slots are filled or the heap becomes empty
259-
request = requestHeap.pop();
244+
var processedRequests = 0;
245+
var totalRequests = requestQueue.length;
246+
while (filledSlots < openSlots && processedRequests < totalRequests) {
247+
// Loop until all open slots are filled or the queue becomes empty
248+
request = requestQueue.get(processedRequests++);
260249
if (request.cancelled) {
261250
// Request was explicitly cancelled
262251
cancelRequest(request);
@@ -272,6 +261,7 @@ define([
272261
startRequest(request);
273262
++filledSlots;
274263
}
264+
requestQueue.remove(processedRequests);
275265

276266
updateStatistics();
277267
};
@@ -344,10 +334,9 @@ define([
344334
return undefined;
345335
}
346336

347-
// Insert into the priority heap and see if a request was bumped off. If this request is the lowest
348-
// priority it will be returned.
337+
// Insert into the priority queue and see if a request was bumped off. If this request is the lowest priority it will be returned.
349338
updatePriority(request);
350-
var removedRequest = requestHeap.insert(request);
339+
var removedRequest = requestQueue.insert(request);
351340

352341
if (defined(removedRequest)) {
353342
if (removedRequest === request) {
@@ -397,12 +386,19 @@ define([
397386
* @private
398387
*/
399388
RequestScheduler.clearForSpecs = function() {
400-
while (requestHeap.length > 0) {
401-
var request = requestHeap.pop();
389+
var request;
390+
var length;
391+
var i;
392+
393+
length = requestQueue.length;
394+
for (i = 0; i < length; ++i) {
395+
request = requestQueue.get(i);
402396
cancelRequest(request);
403397
}
404-
var length = activeRequests.length;
405-
for (var i = 0; i < length; ++i) {
398+
requestQueue.remove(length);
399+
400+
length = activeRequests.length;
401+
for (i = 0; i < length; ++i) {
406402
cancelRequest(activeRequests[i]);
407403
}
408404
activeRequests.length = 0;
@@ -431,7 +427,7 @@ define([
431427
*
432428
* @private
433429
*/
434-
RequestScheduler.requestHeap = requestHeap;
430+
RequestScheduler.requestQueue = requestQueue;
435431

436432
return RequestScheduler;
437433
});

0 commit comments

Comments
 (0)