1 // sort object 2 3 var manual = 0; // single stepping 4 var interval_time = 0; // setTimeout interval 5 var inner_loop_enabled = 1; // should the stepper iterate a little during each step 6 7 // number of elements 8 var size = 300; 9 var query = window.location.search.substring(1); 10 var params = query.split('&'); 11 for (var i = 0; i < params.length; i++) { 12 var pos = params[i].indexOf('='); 13 var key = params[i].substring(0, pos); 14 var val = params[i].substring(pos+1); 15 if (key == "size") { 16 var sz = parseInt(val); 17 size = Math.max(sz, 3); 18 size = Math.min(1000, size); 19 } 20 } 21 22 var log; 23 function log(msg) { 24 if (window.console != undefined) { 25 window.console.log(msg); 26 } 27 } 28 29 function Sort(name, func) { 30 this.name = name; 31 this.func = func; 32 this.size = size; 33 this.compare_x = null; 34 this.compare_y = null; 35 this.compares = 0; 36 this.swap_x = null; 37 this.swap_y = null; 38 this.swaps = 0; 39 this.start_time = 0; 40 this.stop_time = 0; 41 this.work_queue = new Array(); 42 this.timer = 0; 43 this.last_time = 0; 44 this.num_iterations = 0; 45 this.num_jobs = 0; 46 this.overhead_total = 0; 47 this.overhead_min = 1000000; 48 this.overhead_max = 0; 49 this.processing_total = 0; 50 this.processing_min = 1000000; 51 this.processing_max = 0; 52 this.step_min = 1000000; 53 this.step_max = 0; 54 55 this.setup(); 56 } 57 58 Sort.prototype.setup = function() { 59 this.size = size; 60 this.bars = new Array(this.size); 61 this.numbers = new Array(this.size); 62 for (i = 0; i < this.size; i++) { 63 this.numbers[i] = i + 1; 64 } 65 for (i = 0; i < this.size; i++) { 66 var r = Math.floor(Math.random() * this.numbers.length); 67 if (i != r) { 68 var tmp = this.numbers[i]; 69 this.numbers[i] = this.numbers[r]; 70 this.numbers[r] = tmp; 71 } 72 } 73 } 74 75 Sort.prototype.status = function(str) { 76 var label = document.getElementById(this.name + "_label"); 77 label.innerHTML = "<b>" + this.name + " Sort</b><br />" + str; 78 } 79 80 Sort.prototype.stepper = function() { 81 if (!manual) { 82 var sort = this; 83 this.timer = setTimeout(function(){sort.stepper();},interval_time); 84 } 85 var t = new Date(); 86 var overhead = t - this.last_time; 87 this.overhead_total += overhead; 88 this.overhead_min = Math.min(this.overhead_min, overhead); 89 this.overhead_max = Math.max(this.overhead_max, overhead); 90 this.last_time = t; 91 92 var elapsed = t - this.start_time; 93 var avg = 94 Math.floor((elapsed - this.processing_total) / this.num_iterations); 95 this.status("Overhead: " + avg + "ms"); 96 97 var ops = 0; 98 for (;;) { 99 var count = this.work_queue.length; 100 if (count > 0) { 101 var func = this.work_queue.pop(); 102 if (func.status != undefined) { 103 //this.status(func.status); 104 } 105 ops++; 106 this.num_jobs++; 107 func(); 108 } else { 109 break; 110 } 111 if (manual || inner_loop_enabled == 0) { 112 break; 113 } 114 t = new Date(); 115 // If any measurable time has passed, we're good. 116 // Since the Date has a resolution of 15ms on Windows 117 // there's no way to differentiate accurately for anything 118 // less than that. We don't want to process for longer than 119 // the timer interval anyway (which is about 10ms), so this 120 // is fine. 121 // NOTE: on non-windows platforms, this actually does matter since 122 // their timer resolution is higher 123 // TODO(erikkay): make this a parameter 124 if (t - this.last_time > 10) { 125 break; 126 } 127 } 128 var processing = t - this.last_time; 129 this.processing_min = Math.min(this.processing_min, processing); 130 this.processing_max = Math.max(this.processing_max, processing); 131 this.processing_total += processing; 132 var step_time = processing + overhead; 133 this.step_min = Math.min(this.step_min, step_time); 134 this.step_max = Math.max(this.processing_max, step_time); 135 this.num_iterations++; 136 this.last_time = new Date(); 137 138 if (ops == 0) { 139 this.finished(); 140 } 141 } 142 143 Sort.prototype.add_work = function(work, name) { 144 if (name != undefined) { 145 work.status = name; 146 } 147 this.work_queue.push(work); 148 } 149 150 Sort.prototype.init = function() { 151 this.print(); 152 this.status(""); 153 } 154 155 Sort.prototype.reset = function() { 156 this.stop(); 157 this.start_time = 0; 158 this.stop_time = 0; 159 this.setup(); 160 this.print(); 161 } 162 163 Sort.prototype.start = function() { 164 if (this.start_time > 0) { 165 if (this.stop_time > 0) { 166 this.shuffle(); 167 this.start_time = 0; 168 this.stop_time = 0; 169 this.status(""); 170 return; 171 } else if (manual) { 172 this.stepper(); 173 return; 174 } else { 175 this.finished(); 176 return; 177 } 178 } 179 if (!manual) { 180 var t = this; 181 this.timer = setTimeout(function(){t.stepper();},interval_time); 182 } 183 this.compares = 0; 184 this.swaps = 0; 185 this.start_time = (new Date()).getTime(); 186 this.last_time = this.start_time; 187 this.num_jobs = 0; 188 this.stop_time = 0; 189 this.overhead_total = 0; 190 this.overhead_min = 1000000; 191 this.overhead_max = 0; 192 this.processing_total = 0; 193 this.processing_min = 1000000; 194 this.processing_max = 0; 195 this.num_iterations = 0; 196 this.func(this); 197 } 198 199 Sort.prototype.cleanup = function() { 200 if (this.compare_x) { 201 this.compare_x.style.borderColor = "black"; 202 this.compare_y.style.borderColor = "black"; 203 } 204 if (this.swap_x) { 205 this.swap_x.style.backgroundColor = "green"; 206 this.swap_y.style.backgroundColor = "green"; 207 } 208 this.work_queue = new Array(); 209 } 210 211 Sort.prototype.stop = function() { 212 if (this.timer != 0) { 213 clearTimeout(this.timer); 214 this.timer = 0; 215 } 216 this.cleanup(); 217 } 218 219 Sort.prototype.finished = function(err) { 220 this.stop(); 221 222 this.stop_time = (new Date()).getTime(); 223 224 var total = (this.stop_time - this.start_time); 225 if (err == null) { 226 var step_avg = Math.floor(total / this.num_iterations); 227 var overhead = total - this.processing_total; 228 var overhead_avg = Math.floor(overhead / this.num_iterations); 229 var processing_avg = Math.floor(this.processing_total / this.num_iterations); 230 var table = "<table><tr><td>Times(ms)</td><td>Total</td><td>Avg</td><td>Max</td></tr>" 231 + "<tr><td>Total</td><td>" + total + "</td><td>" + step_avg + "</td><td>" + this.step_max + "</tr>" 232 + "<tr><td>Work</td><td>" + this.processing_total + "</td><td>" + processing_avg + "</td><td>" + this.processing_max + "</tr>" 233 + "<tr><td>Overhead</td><td>" + overhead + "</td><td>" + overhead_avg + "</td><td>" + this.overhead_max + "</tr>" 234 + "</table>"; 235 this.status(table); 236 } else { 237 this.status(err); 238 log("error: " + err); 239 } 240 log("finished in: " + total); 241 } 242 243 Sort.prototype.shuffle = function() { 244 for (i = 0; i < this.size; i++) { 245 var r = Math.floor(Math.random() * this.size); 246 if (i != r) { 247 this.swap(i,r); 248 } 249 } 250 this.cleanup(); 251 } 252 253 Sort.prototype.print = function() { 254 var graph = document.getElementById(this.name); 255 if (graph == undefined) { 256 alert("can't find " + this.name); 257 } 258 var text = "<div id='" + this.name + "_label' class='label'>" + this.name + " Sort</div>"; 259 var len = this.numbers.length; 260 var height_multiple = (graph.clientHeight-20) / len; 261 var width = Math.max(1,Math.floor((graph.clientWidth-10) / len)); 262 if (width < 3) { 263 border = 0; 264 } else { 265 border = 1; 266 } 267 var left_offset = Math.round((graph.clientWidth - (width*len))/2); 268 for (i = 0; i < len; i++) { 269 var val = this.numbers[i]; 270 var height = Math.max(1, Math.floor(val * height_multiple)); 271 var left = left_offset + i * width; 272 text += "<li class='bar' style='border: " + border + "px solid black; height:" + height + "px; left:" + left + "; width:" + width + "' id='" + this.name + val + "' value='" + val + "'></li>"; 273 } 274 graph.innerHTML = text; 275 var nodes = document.getElementsByTagName("li"); 276 var j = 0; 277 for (i = 0; i < nodes.length; i++) { 278 var name = nodes[i].id; 279 if (name.indexOf(this.name) == 0) { 280 this.bars[j] = nodes[i]; 281 j++; 282 } 283 } 284 } 285 286 Sort.prototype.compare = function(x, y) { 287 var bx = this.bars[x]; 288 var by = this.bars[y]; 289 //log("compare " + x + "(" + bx.value + ")," + y + "(" + by.value + ")"); 290 if (this.compare_x != bx) { 291 if (this.compare_x) { 292 this.compare_x.style.borderColor="black"; 293 } 294 bx.style.borderColor="yellow"; 295 this.compare_x = bx; 296 } 297 if (this.compare_y != by) { 298 if (this.compare_y) { 299 this.compare_y.style.borderColor="black"; 300 } 301 by.style.borderColor="white"; 302 this.compare_y = by; 303 } 304 this.compares++; 305 return bx.value - by.value; 306 } 307 308 Sort.prototype.swap = function(x, y) { 309 var bx = this.bars[x]; 310 var by = this.bars[y]; 311 //log("swap " + x + "(" + bx.value + ")," + y + "(" + by.value + ")"); 312 if (this.swap_x != x) { 313 if (this.swap_x) { 314 this.swap_x.style.backgroundColor="green"; 315 } 316 bx.style.backgroundColor="blue"; 317 this.swap_x = bx; 318 } 319 if (this.swap_y != y) { 320 if (this.swap_y) { 321 this.swap_y.style.backgroundColor="green"; 322 } 323 by.style.backgroundColor="red"; 324 this.swap_y = by; 325 } 326 var tmp = bx.style.left; 327 bx.style.left = by.style.left; 328 by.style.left = tmp; 329 this.bars[x] = by; 330 this.bars[y] = bx; 331 this.swaps++; 332 } 333 334 Sort.prototype.insert = function(from, to) { 335 var bf = this.bars[from]; 336 if (from > to) { 337 for (i = from; i > to; i--) { 338 var b1 = this.bars[i]; 339 var b2 = this.bars[i-1]; 340 b2.style.left = b1.style.left; 341 this.bars[i] = b2; 342 } 343 bf.style.left = this.bars[to].style.left; 344 this.bars[to] = bf; 345 } else { 346 // TODO 347 } 348 } 349