Browse Source

optimized applyMarkerAction

- extract switch statement out of loop
- use int32 view to handle 4 byte together
- don't check for change (essentially done at a higher level anyway)
Alexander Rose 4 years ago
parent
commit
57da2a7ebb
2 changed files with 78 additions and 38 deletions
  1. 4 1
      src/mol-model/structure/structure/element/loci.ts
  2. 74 37
      src/mol-util/marker-action.ts

+ 4 - 1
src/mol-model/structure/structure/element/loci.ts

@@ -23,6 +23,9 @@ import { StructureProperties } from '../properties';
 import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper';
 import { Boundary } from '../../../../mol-math/geometry/boundary';
 
+// avoiding namespace lookup improved performance in Chrome (Aug 2020)
+const osSize = OrderedSet.size;
+
 /** Represents multiple structure element index locations */
 export interface Loci {
     readonly kind: 'element-loci',
@@ -71,7 +74,7 @@ export namespace Loci {
 
     export function size(loci: Loci) {
         let s = 0;
-        for (const u of loci.elements) s += OrderedSet.size(u.indices);
+        for (const u of loci.elements) s += osSize(u.indices);
         return s;
     }
 

+ 74 - 37
src/mol-util/marker-action.ts

@@ -1,11 +1,12 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { OrderedSet, Interval } from '../mol-data/int';
 import { BitFlags } from './bit-flags';
+import { assertUnreachable } from './type-helpers';
 
 export enum MarkerAction {
     None = 0x0,
@@ -37,50 +38,86 @@ export namespace MarkerActions {
 }
 
 export function applyMarkerActionAtPosition(array: Uint8Array, i: number, action: MarkerAction) {
-    let v = array[i];
     switch (action) {
-        case MarkerAction.Highlight:
-            if (v % 2 === 0) {
-                array[i] = v + 1;
-                return true;
-            }
-            return false;
-        case MarkerAction.RemoveHighlight:
-            if (v % 2 !== 0) {
-                array[i] = v - 1;
-                return true;
-            }
-            return false;
-        case MarkerAction.Select:
-            if (v < 2) {
-                array[i] = v + 2;
-                return true;
-            }
-            return false;
-        case MarkerAction.Deselect:
-            array[i] = v % 2;
-            return array[i] !== v;
-        case MarkerAction.Toggle:
-            if (v >= 2) array[i] = v - 2;
-            else array[i] = v + 2;
-            return true;
-        case MarkerAction.Clear:
-            array[i] = 0;
-            return v !== 0;
+        case MarkerAction.Highlight: array[i] |= 1; break;
+        case MarkerAction.RemoveHighlight: array[i] &= ~1; break;
+        case MarkerAction.Select: array[i] |= 2; break;
+        case MarkerAction.Deselect: array[i] &= ~2; break;
+        case MarkerAction.Toggle: array[i] ^= 2; break;
+        case MarkerAction.Clear: array[i] = 0; break;
     }
-    return false;
 }
 
 export function applyMarkerAction(array: Uint8Array, set: OrderedSet, action: MarkerAction) {
-    let changed = false;
+    if (action === MarkerAction.None) return false;
+
     if (Interval.is(set)) {
-        for (let i = Interval.start(set), _i = Interval.end(set); i < _i; i++) {
-            changed = applyMarkerActionAtPosition(array, i, action) || changed;
+        const start = Interval.start(set);
+        const end = Interval.end(set);
+        const view = new Uint32Array(array.buffer, 0, Math.floor(array.buffer.byteLength / 4));
+        const viewStart = Math.ceil(start / 4);
+        const viewEnd = Math.min(view.length, Math.floor(end / 4));
+
+        const middleStart = viewStart * 4;
+        const middleEnd = viewEnd * 4;
+        const frontStart = start;
+        const frontEnd = frontStart === middleStart ? frontStart : middleStart;
+        const backEnd = end;
+        const backStart = backEnd === middleEnd ? backEnd : middleEnd;
+
+        switch (action) {
+            case MarkerAction.Highlight:
+                for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x01010101;
+                break;
+            case MarkerAction.RemoveHighlight:
+                for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x01010101;
+                break;
+            case MarkerAction.Select:
+                for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x02020202;
+                break;
+            case MarkerAction.Deselect:
+                for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x02020202;
+                break;
+            case MarkerAction.Toggle:
+                for (let i = viewStart; i < viewEnd; ++i) view[i] ^= 0x02020202;
+                break;
+            case MarkerAction.Clear:
+                view.fill(0);
+                break;
+            default:
+                assertUnreachable(action);
+        }
+
+        for (let i = frontStart; i < frontEnd; ++i) {
+            applyMarkerActionAtPosition(array, i, action);
+        }
+
+        for (let i = backStart; i < backEnd; ++i) {
+            applyMarkerActionAtPosition(array, i, action);
         }
     } else {
-        for (let i = 0, _i = set.length; i < _i; i++) {
-            changed = applyMarkerActionAtPosition(array, set[i], action) || changed;
+        switch (action) {
+            case MarkerAction.Highlight:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 1;
+                break;
+            case MarkerAction.RemoveHighlight:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~1;
+                break;
+            case MarkerAction.Select:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 2;
+                break;
+            case MarkerAction.Deselect:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~2;
+                break;
+            case MarkerAction.Toggle:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] ^= 2;
+                break;
+            case MarkerAction.Clear:
+                for (let i = 0, il = set.length; i < il; ++i) array[set[i]] = 0;
+                break;
+            default:
+                assertUnreachable(action);
         }
     }
-    return changed;
+    return true;
 }