From 23e71dc57f07e211afcc3a25c5a53e668eb94d73 Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Wed, 4 Jul 2018 20:56:12 -0400 Subject: [PATCH] Remove RequestQueue code --- Source/Core/RequestQueue.js | 186 ---------------------------- Source/Core/RequestScheduler.js | 82 +++++++------ Source/Scene/GlobeSurfaceTile.js | 6 - Specs/Core/RequestQueueSpec.js | 189 ----------------------------- Specs/Core/RequestSchedulerSpec.js | 38 +++--- 5 files changed, 60 insertions(+), 441 deletions(-) delete mode 100644 Source/Core/RequestQueue.js delete mode 100644 Specs/Core/RequestQueueSpec.js diff --git a/Source/Core/RequestQueue.js b/Source/Core/RequestQueue.js deleted file mode 100644 index 169483d3881c..000000000000 --- a/Source/Core/RequestQueue.js +++ /dev/null @@ -1,186 +0,0 @@ -define([ - './Check', - './defineProperties' - ], function( - Check, - defineProperties) { - 'use strict'; - - /** - * Priority queue for the {@link RequestScheduler} implemented as a sorted array. - * The request with the highest priority is placed at index 0 and the request - * with lowest priority is placed at index length - 1. - *

- * A lower request.priority value indicates that the request has higher priority. See {@link Request#priority}. - *

- * - * @alias RequestQueue - * @constructor - * @private - * - * @param {Number} maximumLength The maximum length of the queue. - */ - function RequestQueue(maximumLength) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.number('maximumLength', maximumLength); - //>>includeEnd('debug'); - - this._array = new Array(maximumLength); - this._length = 0; - this._maximumLength = maximumLength; - } - - defineProperties(RequestQueue.prototype, { - /** - * Gets the length of the queue. - * - * @memberof RequestQueue.prototype - * - * @type {Number} - * @readonly - */ - length : { - get : function() { - return this._length; - } - } - }); - - /** - * Get the request at the given index. - * - * @param {Number} index The index of the request. - * - * @return {Request} The request at the given index. - */ - RequestQueue.prototype.get = function(index) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals('index', index, 0); - Check.typeOf.number.lessThan('index', index, this._length); - //>>includeEnd('debug'); - return this._array[index]; - }; - - /** - * Insert a request into the queue. If the length would grow greater than the maximum length - * of the queue, the lowest priority request is removed and returned. - * - * @param {Request} request The request to insert. - * - * @return {Request|undefined} The request that was removed from the queue if the queue is at full capacity. - */ - RequestQueue.prototype.insert = function(request) { - //>>includeStart('debug', pragmas.debug); - Check.defined('request', request); - //>>includeEnd('debug'); - - var array = this._array; - var previousLength = this._length; - var length = this._length; - var maximumLength = this._maximumLength; - - if (length < maximumLength) - { - ++this._length; - } - - if (previousLength === 0) - { - array[0] = request; - return; - } - - var removedRequest; - var lastIndex = previousLength - 1; - - if (previousLength === maximumLength) { - var lastRequest = array[lastIndex]; - if (request.priority >= lastRequest.priority) { - // The array is full and the priority value of this request is too high to be inserted. - return request; - } - // The array is full and the inserted request pushes off the last request - removedRequest = lastRequest; - --lastIndex; - } - - while (lastIndex >= 0 && request.priority < array[lastIndex].priority) { - array[lastIndex + 1] = array[lastIndex]; // Shift element to the right - --lastIndex; - } - array[lastIndex + 1] = request; - - return removedRequest; - }; - - /** - * Call the given function for each request in the queue. - * - * @type {RequestQueue~ForEachCallback} The function to call. - */ - RequestQueue.prototype.forEach = function(callback) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.func('callback', callback); - //>>includeEnd('debug'); - - var array = this._array; - var length = this._length; - for (var i = 0; i < length; ++i) { - callback(array[i]); - } - }; - - /** - * Sorts the queue. - */ - RequestQueue.prototype.sort = function() { - var array = this._array; - var length = this._length; - - // Use insertion sort since our array is small and likely to be mostly sorted already. - // Additionally length may be smaller than the array's actual length, so calling array.sort will lead to incorrect results for uninitialized values. - for (var i = 1; i < length; ++i) { - var j = i; - while ((j > 0) && (array[j - 1].priority > array[j].priority)) { - var temp = array[j - 1]; - array[j - 1] = array[j]; - array[j] = temp; - --j; - } - } - }; - - /** - * Remove length number of requests from the top of the queue. - * - * @param {Number} length The number of requests to remove. - */ - RequestQueue.prototype.remove = function(length) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.number.greaterThanOrEquals('length', length, 0); - Check.typeOf.number.lessThanOrEquals('length', length, this._length); - //>>includeEnd('debug'); - if (length === 0) { - return; - } - if (length === this._length) { - this._length = 0; - return; - } - - // Shift remaining requests back to the left - var array = this._array; - for (var i = length; i < this._length; ++i) { - array[i - length] = array[i]; - } - this._length -= length; - }; - - /** - * The callback to use in forEach. - * @callback RequestQueue~ForEachCallback - * @param {Request} request The request. - */ - - return RequestQueue; -}); diff --git a/Source/Core/RequestScheduler.js b/Source/Core/RequestScheduler.js index 8c307663d3a4..6fd455b58007 100644 --- a/Source/Core/RequestScheduler.js +++ b/Source/Core/RequestScheduler.js @@ -6,9 +6,9 @@ define([ './defined', './defineProperties', './Event', + './Heap', './isBlobUri', './isDataUri', - './RequestQueue', './RequestState' ], function( Uri, @@ -18,12 +18,16 @@ define([ defined, defineProperties, Event, + Heap, isBlobUri, isDataUri, - RequestQueue, RequestState) { 'use strict'; + function sortRequests(a, b) { + return a.priority - b.priority; + } + var statistics = { numberOfAttemptedRequests : 0, numberOfActiveRequests : 0, @@ -33,8 +37,12 @@ define([ numberOfActiveRequestsEver : 0 }; - var requestQueueLength = 20; - var requestQueue = new RequestQueue(requestQueueLength); + var priorityHeapLength = 20; + var requestHeap = new Heap({ + comparator : sortRequests + }); + requestHeap.maximumLength = priorityHeapLength; + requestHeap.reserve(priorityHeapLength); var activeRequests = []; var numberOfActiveRequestsByServer = {}; @@ -115,28 +123,29 @@ define([ }, /** - * The maximum length of the request queue. This limits the number of requests that are sorted by priority. Only applies to requests that are not yet active. + * 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. * * @memberof RequestScheduler * * @type {Number} * @default 20 - * - * @private */ - requestQueueLength : { + priorityHeapLength : { get : function() { - return requestQueueLength; + return priorityHeapLength; }, set : function(value) { - // Cancel all requests and resize the queue - var length = requestQueue.length; - for (var i = 0; i < length; ++i) { - var request = requestQueue.get(i); - cancelRequest(request); + // If the new length shrinks the heap, need to cancel some of the requests. + // Since this value is not intended to be tweaked regularly it is fine to just cancel the high priority requests. + if (value < priorityHeapLength) { + while (requestHeap.length > value) { + var request = requestHeap.pop(); + cancelRequest(request); + } } - requestQueue = new RequestQueue(value); - RequestScheduler.requestQueue = requestQueue; + priorityHeapLength = value; + requestHeap.maximumLength = value; + requestHeap.reserve(value); } } }); @@ -245,19 +254,21 @@ define([ } activeRequests.length -= removeCount; - // Update priority of issued requests and resort the queue - requestQueue.forEach(updatePriority); - requestQueue.sort(); + // Update priority of issued requests and resort the heap + var issuedRequests = requestHeap.internalArray; + var issuedLength = requestHeap.length; + for (i = 0; i < issuedLength; ++i) { + updatePriority(issuedRequests[i]); + } + requestHeap.resort(); // Get the number of open slots and fill with the highest priority requests. // Un-throttled requests are automatically added to activeRequests, so activeRequests.length may exceed maximumRequests var openSlots = Math.max(RequestScheduler.maximumRequests - activeRequests.length, 0); var filledSlots = 0; - var processedRequests = 0; - var totalRequests = requestQueue.length; - while (filledSlots < openSlots && processedRequests < totalRequests) { - // Loop until all open slots are filled or the queue becomes empty - request = requestQueue.get(processedRequests++); + while (filledSlots < openSlots && requestHeap.length > 0) { + // Loop until all open slots are filled or the heap becomes empty + request = requestHeap.pop(); if (request.cancelled) { // Request was explicitly cancelled cancelRequest(request); @@ -273,7 +284,6 @@ define([ startRequest(request); ++filledSlots; } - requestQueue.remove(processedRequests); updateStatistics(); }; @@ -346,9 +356,10 @@ define([ return undefined; } - // Insert into the priority queue and see if a request was bumped off. If this request is the lowest priority it will be returned. + // Insert into the priority heap and see if a request was bumped off. If this request is the lowest + // priority it will be returned. updatePriority(request); - var removedRequest = requestQueue.insert(request); + var removedRequest = requestHeap.insert(request); if (defined(removedRequest)) { if (removedRequest === request) { @@ -398,19 +409,12 @@ define([ * @private */ RequestScheduler.clearForSpecs = function() { - var request; - var length; - var i; - - length = requestQueue.length; - for (i = 0; i < length; ++i) { - request = requestQueue.get(i); + while (requestHeap.length > 0) { + var request = requestHeap.pop(); cancelRequest(request); } - requestQueue.remove(length); - - length = activeRequests.length; - for (i = 0; i < length; ++i) { + var length = activeRequests.length; + for (var i = 0; i < length; ++i) { cancelRequest(activeRequests[i]); } activeRequests.length = 0; @@ -439,7 +443,7 @@ define([ * * @private */ - RequestScheduler.requestQueue = requestQueue; + RequestScheduler.requestHeap = requestHeap; return RequestScheduler; }); diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index efa1ab9f40bd..46bba1be5100 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -7,7 +7,6 @@ define([ '../Core/defineProperties', '../Core/IntersectionTests', '../Core/PixelFormat', - '../Core/Rectangle', '../Renderer/PixelDatatype', '../Renderer/Sampler', '../Renderer/Texture', @@ -29,7 +28,6 @@ define([ defineProperties, IntersectionTests, PixelFormat, - Rectangle, PixelDatatype, Sampler, Texture, @@ -256,10 +254,6 @@ define([ function createPriorityFunction(surfaceTile, frameState) { return function() { - if (Rectangle.contains(surfaceTile.tileBoundingRegion.rectangle, frameState.camera.positionCartographic)) { - // If the camera is inside this tile's bounding region treat it as highest priority - return 0.0; - } return surfaceTile.tileBoundingRegion.distanceToCamera(frameState); }; } diff --git a/Specs/Core/RequestQueueSpec.js b/Specs/Core/RequestQueueSpec.js deleted file mode 100644 index a7bb51107e7e..000000000000 --- a/Specs/Core/RequestQueueSpec.js +++ /dev/null @@ -1,189 +0,0 @@ -defineSuite([ - 'Core/RequestQueue', - 'Core/Math', - 'Core/Request' - ], function( - RequestQueue, - CesiumMath, - Request) { - 'use strict'; - - var length = 20; - - function createRequest(distance) { - return new Request({ - priority : distance - }); - } - - function isSorted(queue) { - var distance = Number.NEGATIVE_INFINITY; - for (var i = 0; i < queue.length; ++i) { - var requestDistance = queue.get(i).priority; - if (requestDistance < distance) { - return false; - } - - distance = requestDistance; - } - return true; - } - - it('sets initial values', function() { - var queue = new RequestQueue(length); - expect(queue._array.length).toBe(length); - expect(queue._length).toBe(0); - expect(queue._maximumLength).toBe(length); - }); - - it('gets length', function() { - var queue = new RequestQueue(length); - expect(queue.length).toBe(0); - queue.insert(createRequest(1.0)); - expect(queue.length).toBe(1); - }); - - it('get', function() { - var queue = new RequestQueue(length); - queue.insert(createRequest(1)); - queue.insert(createRequest(0)); - expect(queue.get(0).priority).toBe(0); - expect(queue.get(1).priority).toBe(1); - }); - - it('insert', function() { - var removedRequest; - var request; - var i; - - CesiumMath.setRandomNumberSeed(0.0); - var distances = new Array(length); - for (i = 0; i < length; ++i) { - distances[i] = CesiumMath.nextRandomNumber(); - } - distances.sort(); - var lowestDistance = distances[0]; - var highestDistance = distances[distances.length - 1]; - - var queue = new RequestQueue(length); - for (i = 0; i < length; ++i) { - removedRequest = queue.insert(createRequest(distances[i])); - expect(removedRequest).toBeUndefined(); // Requests are not removed until the queue is full - } - - expect(isSorted(queue)).toBe(true); - - request = createRequest(highestDistance); - expect(queue.insert(request)).toBe(request); - - request = createRequest(highestDistance + 1.0); - expect(queue.insert(request)).toBe(request); - - request = createRequest(lowestDistance); - expect(queue.insert(request).priority).toBe(highestDistance); - expect(queue.get(0).priority).toBe(lowestDistance); - expect(queue.get(1).priority).toBe(lowestDistance); - expect(queue.get(2).priority).toBeGreaterThan(lowestDistance); - - expect(isSorted(queue)).toBe(true); - }); - - it('forEach', function() { - var total = 0; - var queue = new RequestQueue(length); - for (var i = 0; i < length; ++i) { - queue.insert(createRequest(1)); - } - queue.forEach(function(request) { - total += request.priority; - }); - expect(total).toBe(length); - }); - - it('sort', function() { - var i; - CesiumMath.setRandomNumberSeed(0.0); - var queue = new RequestQueue(length); - for (i = 0; i < length / 2; ++i) { - queue.insert(createRequest(CesiumMath.nextRandomNumber())); - } - queue.forEach(function(request) { - request.priority = CesiumMath.nextRandomNumber(); - }); - expect(isSorted(queue)).toBe(false); - queue.sort(); - expect(isSorted(queue)).toBe(true); - }); - - it('remove', function() { - var queue = new RequestQueue(length); - for (var i = 0; i < length; ++i) { - queue.insert(createRequest(i)); - } - queue.remove(0); - expect(queue.get(0).priority).toBe(0); - expect(queue.get(1).priority).toBe(1); - expect(queue.length).toBe(length); - - queue.remove(1); - expect(queue.get(0).priority).toBe(1); - expect(queue.get(1).priority).toBe(2); - expect(queue.length).toBe(length - 1); - - queue.remove(2); - expect(queue.get(0).priority).toBe(3); - expect(queue.get(1).priority).toBe(4); - expect(queue.length).toBe(length - 3); - - queue.remove(17); - expect(queue.length).toBe(0); - }); - - it('throws if maximumLength is undefined', function() { - expect(function() { - return new RequestQueue(); - }).toThrowDeveloperError(); - }); - - it('throws if get index is out of range', function() { - expect(function() { - var queue = new RequestQueue(length); - queue.get(0); - }).toThrowDeveloperError(); - - expect(function() { - var queue = new RequestQueue(length); - queue.insert(createRequest(0.0)); - queue.get(1); - }).toThrowDeveloperError(); - - expect(function() { - var queue = new RequestQueue(length); - queue.insert(createRequest(0.0)); - queue.get(-1); - }).toThrowDeveloperError(); - }); - - it('throws if forEach callback is not a function', function() { - expect(function() { - var queue = new RequestQueue(length); - queue.forEach(); - }).toThrowDeveloperError(); - expect(function() { - var queue = new RequestQueue(length); - queue.forEach(5); - }).toThrowDeveloperError(); - }); - - it('throws if remove length is out of range', function() { - expect(function() { - var queue = new RequestQueue(length); - queue.remove(1); - }).toThrowDeveloperError(); - - expect(function() { - var queue = new RequestQueue(length); - queue.remove(-1); - }).toThrowDeveloperError(); - }); -}); diff --git a/Specs/Core/RequestSchedulerSpec.js b/Specs/Core/RequestSchedulerSpec.js index b5b3b965cc16..55f49fc1492e 100644 --- a/Specs/Core/RequestSchedulerSpec.js +++ b/Specs/Core/RequestSchedulerSpec.js @@ -12,13 +12,13 @@ defineSuite([ var originalMaximumRequests; var originalMaximumRequestsPerServer; - var originalRequestQueueLength; + var originalPriorityHeapLength; var originalRequestsByServer; beforeAll(function() { originalMaximumRequests = RequestScheduler.maximumRequests; originalMaximumRequestsPerServer = RequestScheduler.maximumRequestsPerServer; - originalRequestQueueLength = RequestScheduler.requestQueueLength; + originalPriorityHeapLength = RequestScheduler.priorityHeapLength; originalRequestsByServer = RequestScheduler.requestsByServer; }); @@ -30,7 +30,7 @@ defineSuite([ afterEach(function() { RequestScheduler.maximumRequests = originalMaximumRequests; RequestScheduler.maximumRequestsPerServer = originalMaximumRequestsPerServer; - RequestScheduler.requestQueueLength = originalRequestQueueLength; + RequestScheduler.priorityHeapLength = originalPriorityHeapLength; RequestScheduler.requestsByServer = originalRequestsByServer; }); @@ -208,7 +208,7 @@ defineSuite([ } }); - it('honors requestQueue length', function() { + it('honors priorityHeapLength', function() { var deferreds = []; var requests = []; @@ -229,23 +229,22 @@ defineSuite([ return request; } - RequestScheduler.requestQueueLength = 1; + RequestScheduler.priorityHeapLength = 1; var firstRequest = createRequest(0.0); var promise = RequestScheduler.request(firstRequest); expect(promise).toBeDefined(); promise = RequestScheduler.request(createRequest(1.0)); expect(promise).toBeUndefined(); - RequestScheduler.requestQueueLength = 3; - promise = RequestScheduler.request(createRequest(1.0)); + RequestScheduler.priorityHeapLength = 3; promise = RequestScheduler.request(createRequest(2.0)); promise = RequestScheduler.request(createRequest(3.0)); expect(promise).toBeDefined(); promise = RequestScheduler.request(createRequest(4.0)); expect(promise).toBeUndefined(); - // The requests are cancelled when the queue length changes - RequestScheduler.requestQueueLength = 2; + // A request is cancelled to accommodate the new heap length + RequestScheduler.priorityHeapLength = 2; expect(firstRequest.state).toBe(RequestState.CANCELLED); var length = deferreds.length; @@ -453,7 +452,7 @@ defineSuite([ }); } - var length = RequestScheduler.requestQueueLength; + var length = RequestScheduler.priorityHeapLength; for (var i = 0; i < length; ++i) { var priority = Math.random(); RequestScheduler.request(createRequest(priority)); @@ -490,7 +489,7 @@ defineSuite([ var i; var request; - var length = RequestScheduler.requestQueueLength; + var length = RequestScheduler.priorityHeapLength; for (i = 0; i < length; ++i) { var priority = i / (length - 1); request = createRequest(priority); @@ -502,31 +501,28 @@ defineSuite([ RequestScheduler.maximumRequests = 0; RequestScheduler.update(); - var requestQueue = RequestScheduler.requestQueue; + var requestHeap = RequestScheduler.requestHeap; var requests = []; var currentTestId = 0; - - for (i = 0; i < length; ++i) { - request = requestQueue.get(i); + while (requestHeap.length > 0) { + request = requestHeap.pop(); requests.push(request); expect(request.testId).toBeGreaterThanOrEqualTo(currentTestId); currentTestId = request.testId; } - requestQueue.remove(length); for (i = 0; i < length; ++i) { - requestQueue.insert(requests[i]); + requestHeap.insert(requests[i]); } invertPriority = true; RequestScheduler.update(); - for (i = 0; i < length; ++i) { - request = requestQueue.get(i); + while (requestHeap.length > 0) { + request = requestHeap.pop(); expect(request.testId).toBeLessThanOrEqualTo(currentTestId); currentTestId = request.testId; } - requestQueue.remove(length); }); it('handles low priority requests', function() { @@ -547,7 +543,7 @@ defineSuite([ var mediumPriority = 0.5; var lowPriority = 1.0; - var length = RequestScheduler.requestQueueLength; + var length = RequestScheduler.priorityHeapLength; for (var i = 0; i < length; ++i) { RequestScheduler.request(createRequest(mediumPriority)); }