Skip to content

Commit

Permalink
Showing 6 changed files with 234 additions and 441 deletions.
186 changes: 0 additions & 186 deletions Source/Core/RequestQueue.js

This file was deleted.

82 changes: 43 additions & 39 deletions Source/Core/RequestScheduler.js
Original file line number Diff line number Diff line change
@@ -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;
});
6 changes: 0 additions & 6 deletions Source/Scene/GlobeSurfaceTile.js
Original file line number Diff line number Diff line change
@@ -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);
};
}
174 changes: 174 additions & 0 deletions Specs/Core/HeapSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
defineSuite([
'Core/Heap'
], function(
Heap) {
'use strict';

var length = 100;

function checkHeap(heap, comparator) {
var array = heap.internalArray;
var pass = true;
var length = heap.length;
for (var i = 0; i < length; ++i) {
var left = 2 * (i + 1) - 1;
var right = 2 * (i + 1);
if (left < heap.length) {
pass = pass && (comparator(array[i], array[left]) <= 0);
}
if (right < heap.length) {
pass = pass && (comparator(array[i], array[right]) <= 0);
}
}
return pass;
}

// min heap
function comparator(a, b) {
return a - b;
}

it('maintains heap property on insert', function() {
var heap = new Heap({
comparator : comparator
});
var pass = true;
for (var i = 0; i < length; ++i) {
heap.insert(Math.random());
pass = pass && checkHeap(heap, comparator);
}

expect(pass).toBe(true);
});

it('maintains heap property on pop', function() {
var heap = new Heap({
comparator : comparator
});
var i;
for (i = 0; i < length; ++i) {
heap.insert(Math.random());
}
var pass = true;
for (i = 0; i < length; ++i) {
heap.pop();
pass = pass && checkHeap(heap, comparator);
}
expect(pass).toBe(true);
});

it('limited by maximum length', function() {
var heap = new Heap({
comparator : comparator
});
heap.maximumLength = length / 2;
var pass = true;
for (var i = 0; i < length; ++i) {
heap.insert(Math.random());
pass = pass && checkHeap(heap, comparator);
}
expect(pass).toBe(true);
expect(heap.length <= heap.maximumLength).toBe(true);
// allowed one extra slot for swapping
expect(heap.internalArray.length).toBeLessThanOrEqualTo(heap.maximumLength + 1);
});

it('pops in sorted order', function() {
var heap = new Heap({
comparator : comparator
});
var i;
for (i = 0; i < length; ++i) {
heap.insert(Math.random());
}
var curr = heap.pop();
var pass = true;
for (i = 0; i < length - 1; ++i) {
var next = heap.pop();
pass = pass && (comparator(curr, next) <= 0);
curr = next;
}
expect(pass).toBe(true);
});

it('insert returns the removed element when maximumLength is set', function() {
var heap = new Heap({
comparator : comparator
});
heap.maximumLength = length;

var i;
var max = 0.0;
var min = 1.0;
var values = new Array(length);
for (i = 0; i < length; ++i) {
var value = Math.random();
max = Math.max(max, value);
min = Math.min(min, value);
values[i] = value;
}

// Push 99 values
for (i = 0; i < length - 1; ++i) {
heap.insert(values[i]);
}

// Push 100th, nothing is removed so it returns undefined
var removed = heap.insert(values[length - 1]);
expect(removed).toBeUndefined();

// Insert value, an element is removed
removed = heap.insert(max - 0.1);
expect(removed).toBeDefined();

// If this value is the least priority it will be returned
removed = heap.insert(max + 0.1);
expect(removed).toBe(max + 0.1);
});

it('resort', function() {
function comparator(a, b) {
return a.distance - b.distance;
}

var i;
var heap = new Heap({
comparator : comparator
});
for (i = 0; i < length; ++i) {
heap.insert({
distance : i / (length - 1),
id : i
});
}

// Check that elements are initially sorted
var element;
var elements = [];
var currentId = 0;
while (heap.length > 0) {
element = heap.pop();
elements.push(element);
expect(element.id).toBeGreaterThanOrEqualTo(currentId);
currentId = element.id;
}

// Add back into heap
for (i = 0; i < length; ++i) {
heap.insert(elements[i]);
}

// Invert priority
for (i = 0; i < length; ++i) {
elements[i].distance = 1.0 - elements[i].distance;
}

// Resort and check the the elements are popped in the opposite order now
heap.resort();
while (heap.length > 0) {
element = heap.pop();
expect(element.id).toBeLessThanOrEqualTo(currentId);
currentId = element.id;
}
});
});
189 changes: 0 additions & 189 deletions Specs/Core/RequestQueueSpec.js

This file was deleted.

38 changes: 17 additions & 21 deletions Specs/Core/RequestSchedulerSpec.js
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 2705996

Please sign in to comment.