From 599ab54063ee16dc69ae793c2bdc517faf9c0afd Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Tue, 17 Jul 2018 20:28:47 +0200 Subject: [PATCH] queries are now synchronous due to performance reasons --- .../ui/visualization/sequence-view.tsx | 4 +- src/mol-model/structure/query/context.ts | 5 +- .../structure/query/queries/filters.ts | 65 +++++++++---------- .../structure/query/queries/generators.ts | 20 ++---- .../structure/query/queries/modifiers.ts | 29 ++++----- src/mol-model/structure/query/query.ts | 11 +--- src/mol-model/structure/structure/symmetry.ts | 4 +- src/perf-tests/structure.ts | 14 ++-- src/servers/model/server/query.ts | 2 +- 9 files changed, 63 insertions(+), 91 deletions(-) diff --git a/src/mol-app/ui/visualization/sequence-view.tsx b/src/mol-app/ui/visualization/sequence-view.tsx index 69a889502..7d9bf1430 100644 --- a/src/mol-app/ui/visualization/sequence-view.tsx +++ b/src/mol-app/ui/visualization/sequence-view.tsx @@ -34,14 +34,14 @@ function createQuery(entityId: string, label_seq_id: number) { // TODO: this is really ineffective and should be done using a canvas. class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> { - async raiseInteractityEvent(seqId?: number) { + raiseInteractityEvent(seqId?: number) { if (typeof seqId === 'undefined') { InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); return; } const query = createQuery(this.props.seq.entityId, seqId); - const loci = StructureSelection.toLoci(await StructureQuery.run(query, this.props.structure)); + const loci = StructureSelection.toLoci(StructureQuery.run1(query, this.props.structure)); if (loci.elements.length === 0) InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci); else InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, loci); } diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts index 5c94becb6..cefbc8346 100644 --- a/src/mol-model/structure/query/context.ts +++ b/src/mol-model/structure/query/context.ts @@ -4,7 +4,6 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { RuntimeContext } from 'mol-task'; import { Structure, StructureElement } from '../structure'; export interface QueryContextView { @@ -17,7 +16,6 @@ export class QueryContext implements QueryContextView { private currentLocked = false; readonly inputStructure: Structure; - readonly taskCtx: RuntimeContext; /** Current element */ readonly element: StructureElement = StructureElement.create(); @@ -44,9 +42,8 @@ export class QueryContext implements QueryContextView { (this.currentStructure as any) = void 0; } - constructor(structure: Structure, taskCtx: RuntimeContext) { + constructor(structure: Structure) { this.inputStructure = structure; - this.taskCtx = taskCtx; } } diff --git a/src/mol-model/structure/query/queries/filters.ts b/src/mol-model/structure/query/queries/filters.ts index 9bd5a50d2..3bf99a41f 100644 --- a/src/mol-model/structure/query/queries/filters.ts +++ b/src/mol-model/structure/query/queries/filters.ts @@ -4,38 +4,37 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -// import { StructureQuery } from '../query' -// import { StructureSelection } from '../selection' -// import { Unit, StructureProperties as P, Structure } from '../../structure' -// import { Segmentation, SortedArray } from 'mol-data/int' -// import { LinearGroupingBuilder } from '../utils/builders'; -// import { QueryPredicate, QueryFn, QueryContextView } from '../context'; +import { SortedArray } from 'mol-data/int'; +import { Structure } from '../../structure'; +import { QueryPredicate } from '../context'; +import { StructureQuery } from '../query'; +import { StructureSelection } from '../selection'; -// export function pick(query: StructureQuery, pred: QueryPredicate): StructureQuery { -// return async ctx => { -// const sel = await query(ctx); +export function pick(query: StructureQuery, pred: QueryPredicate): StructureQuery { + return ctx => { + const sel = query(ctx); -// if (StructureSelection.isSingleton(sel)) { -// const ret = StructureSelection.LinearBuilder(ctx.inputStructure); -// for (const unit of ctx.inputStructure.units) { -// const { elements } = unit; -// for (let i = 0, _i = elements.length; i < _i; i++) { -// // TODO: optimize this somehow??? -// const s = Structure.create([unit.getChild(SortedArray.ofSingleton(elements[i]))]); -// ctx.lockCurrentStructure(s); -// if (pred(ctx)) ret.add(s); -// ctx.unlockCurrentStructure(); -// } -// } -// return ret.getSelection(); -// } else { -// const ret = StructureSelection.LinearBuilder(ctx.inputStructure); -// for (const s of sel.structures) { -// ctx.lockCurrentStructure(s); -// if (pred(ctx)) ret.add(s); -// ctx.unlockCurrentStructure(); -// } -// return ret.getSelection(); -// } -// }; -// } + if (StructureSelection.isSingleton(sel)) { + const ret = StructureSelection.LinearBuilder(ctx.inputStructure); + for (const unit of ctx.inputStructure.units) { + const { elements } = unit; + for (let i = 0, _i = elements.length; i < _i; i++) { + // TODO: optimize this somehow??? + const s = Structure.create([unit.getChild(SortedArray.ofSingleton(elements[i]))]); + ctx.lockCurrentStructure(s); + if (pred(ctx)) ret.add(s); + ctx.unlockCurrentStructure(); + } + } + return ret.getSelection(); + } else { + const ret = StructureSelection.LinearBuilder(ctx.inputStructure); + for (const s of sel.structures) { + ctx.lockCurrentStructure(s); + if (pred(ctx)) ret.add(s); + ctx.unlockCurrentStructure(); + } + return ret.getSelection(); + } + }; +} diff --git a/src/mol-model/structure/query/queries/generators.ts b/src/mol-model/structure/query/queries/generators.ts index 8454c781f..ef9da1a18 100644 --- a/src/mol-model/structure/query/queries/generators.ts +++ b/src/mol-model/structure/query/queries/generators.ts @@ -11,7 +11,7 @@ import { Segmentation } from 'mol-data/int' import { LinearGroupingBuilder } from '../utils/builders'; import { QueryPredicate, QueryFn, QueryContextView } from '../context'; -export const all: StructureQuery = async (ctx) => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure); +export const all: StructureQuery = ctx => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure); export interface AtomsQueryParams { entityTest: QueryPredicate, @@ -44,13 +44,12 @@ export function atoms(params?: Partial<AtomsQueryParams>): StructureQuery { } function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery { - return async (ctx) => { + return ctx => { const { inputStructure } = ctx; const { units } = inputStructure; const l = ctx.pushCurrentElement(); const builder = inputStructure.subsetBuilder(true); - let progress = 0; for (const unit of units) { l.unit = unit; const elements = unit.elements; @@ -61,9 +60,6 @@ function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery { if (atomTest(ctx)) builder.addElement(l.element); } builder.commitUnit(); - - progress++; - if (ctx.taskCtx.shouldUpdate) await ctx.taskCtx.update({ message: 'Atom Groups', current: progress, max: units.length }); } ctx.popCurrentElement(); return StructureSelection.Singletons(inputStructure, builder.getStructure()); @@ -71,13 +67,12 @@ function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery { } function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomsQueryParams): StructureQuery { - return async (ctx) => { + return ctx => { const { inputStructure } = ctx; const { units } = inputStructure; const l = ctx.pushCurrentElement(); const builder = inputStructure.subsetBuilder(true); - let progress = 0; for (const unit of units) { if (unit.kind !== Unit.Kind.Atomic) continue; @@ -110,9 +105,6 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A } } builder.commitUnit(); - - progress++; - if (ctx.taskCtx.shouldUpdate) await ctx.taskCtx.update({ message: 'Atom Groups', current: progress, max: units.length }); } ctx.popCurrentElement(); return StructureSelection.Singletons(inputStructure, builder.getStructure()); @@ -120,13 +112,12 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A } function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomsQueryParams): StructureQuery { - return async (ctx) => { + return ctx => { const { inputStructure } = ctx; const { units } = inputStructure; const l = ctx.pushCurrentElement(); const builder = new LinearGroupingBuilder(inputStructure); - let progress = 0; for (const unit of units) { if (unit.kind !== Unit.Kind.Atomic) continue; @@ -155,9 +146,6 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group } } } - - progress++; - if (ctx.taskCtx.shouldUpdate) await ctx.taskCtx.update({ message: 'Atom Groups', current: progress, max: units.length }); } ctx.popCurrentElement(); return builder.getSelection(); diff --git a/src/mol-model/structure/query/queries/modifiers.ts b/src/mol-model/structure/query/queries/modifiers.ts index 7943e9e7f..ba3404e34 100644 --- a/src/mol-model/structure/query/queries/modifiers.ts +++ b/src/mol-model/structure/query/queries/modifiers.ts @@ -5,14 +5,13 @@ */ import { Segmentation } from 'mol-data/int'; -import { RuntimeContext } from 'mol-task'; import { Structure, Unit } from '../../structure'; import { StructureQuery } from '../query'; import { StructureSelection } from '../selection'; import { UniqueStructuresBuilder } from '../utils/builders'; import { StructureUniqueSubsetBuilder } from '../../structure/util/unique-subset-builder'; -function getWholeResidues(ctx: RuntimeContext, source: Structure, structure: Structure) { +function getWholeResidues(source: Structure, structure: Structure) { const builder = source.subsetBuilder(true); for (const unit of structure.units) { if (unit.kind !== Unit.Kind.Atomic) { @@ -38,17 +37,14 @@ function getWholeResidues(ctx: RuntimeContext, source: Structure, structure: Str } export function wholeResidues(query: StructureQuery, isFlat: boolean): StructureQuery { - return async (ctx) => { - const inner = await query(ctx); + return ctx => { + const inner = query(ctx); if (StructureSelection.isSingleton(inner)) { - return StructureSelection.Singletons(ctx.inputStructure, getWholeResidues(ctx.taskCtx, ctx.inputStructure, inner.structure)); + return StructureSelection.Singletons(ctx.inputStructure, getWholeResidues(ctx.inputStructure, inner.structure)); } else { const builder = new UniqueStructuresBuilder(ctx.inputStructure); - let progress = 0; for (const s of inner.structures) { - builder.add(getWholeResidues(ctx.taskCtx, ctx.inputStructure, s)); - progress++; - if (ctx.taskCtx.shouldUpdate) await ctx.taskCtx.update({ message: 'Whole Residues', current: progress, max: inner.structures.length }); + builder.add(getWholeResidues(ctx.inputStructure, s)); } return builder.getSelection(); } @@ -64,12 +60,11 @@ export interface IncludeSurroundingsParams { wholeResidues?: boolean } -async function getIncludeSurroundings(ctx: RuntimeContext, source: Structure, structure: Structure, params: IncludeSurroundingsParams) { +function getIncludeSurroundings(source: Structure, structure: Structure, params: IncludeSurroundingsParams) { const builder = new StructureUniqueSubsetBuilder(source); const lookup = source.lookup3d; const r = params.radius; - let progress = 0; for (const unit of structure.units) { const { x, y, z } = unit.conformation; const elements = unit.elements; @@ -77,23 +72,21 @@ async function getIncludeSurroundings(ctx: RuntimeContext, source: Structure, st const e = elements[i]; lookup.findIntoBuilder(x(e), y(e), z(e), r, builder); } - progress++; - if (progress % 2500 === 0 && ctx.shouldUpdate) await ctx.update({ message: 'Include Surroudnings', isIndeterminate: true }); } - return !!params.wholeResidues ? getWholeResidues(ctx, source, builder.getStructure()) : builder.getStructure(); + return !!params.wholeResidues ? getWholeResidues(source, builder.getStructure()) : builder.getStructure(); } export function includeSurroundings(query: StructureQuery, params: IncludeSurroundingsParams): StructureQuery { - return async (ctx) => { - const inner = await query(ctx); + return ctx => { + const inner = query(ctx); if (StructureSelection.isSingleton(inner)) { - const surr = await getIncludeSurroundings(ctx.taskCtx, ctx.inputStructure, inner.structure, params); + const surr = getIncludeSurroundings(ctx.inputStructure, inner.structure, params); const ret = StructureSelection.Singletons(ctx.inputStructure, surr); return ret; } else { const builder = new UniqueStructuresBuilder(ctx.inputStructure); for (const s of inner.structures) { - builder.add(await getIncludeSurroundings(ctx.taskCtx, ctx.inputStructure, s, params)); + builder.add(getIncludeSurroundings(ctx.inputStructure, s, params)); } return builder.getSelection(); } diff --git a/src/mol-model/structure/query/query.ts b/src/mol-model/structure/query/query.ts index a4c87dc50..acbd39ff6 100644 --- a/src/mol-model/structure/query/query.ts +++ b/src/mol-model/structure/query/query.ts @@ -4,19 +4,14 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { RuntimeContext, Task } from 'mol-task' import { Structure } from '../structure' import { StructureSelection } from './selection' import { QueryContext } from './context'; -interface StructureQuery { (ctx: QueryContext): Promise<StructureSelection> } +interface StructureQuery { (ctx: QueryContext): StructureSelection } namespace StructureQuery { - export function run(query: StructureQuery, structure: Structure, ctx?: RuntimeContext) { - return query(new QueryContext(structure, ctx || RuntimeContext.Synchronous)) - } - - export function asTask(query: StructureQuery, structure: Structure) { - return Task.create('Structure Query', ctx => query(new QueryContext(structure, ctx))); + export function run1(query: StructureQuery, structure: Structure) { + return query(new QueryContext(structure)) } } diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index c5f8d2516..227d5c16d 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -25,10 +25,10 @@ namespace StructureSymmetry { const assembler = Structure.Builder(); - const queryCtx = new QueryContext(structure, ctx); + const queryCtx = new QueryContext(structure); for (const g of assembly.operatorGroups) { - const selection = await g.selector(queryCtx); + const selection = g.selector(queryCtx); if (StructureSelection.structureCount(selection) === 0) { continue; } diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index ba9543895..347065088 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -331,7 +331,7 @@ export namespace PropertyAccess { radius: 5, wholeResidues: true }); - const surr = StructureSelection.unionStructure(await StructureQuery.run(q1, a)); + const surr = StructureSelection.unionStructure(StructureQuery.run1(q1, a)); console.timeEnd('symmetry') // for (const u of surr.units) { @@ -372,7 +372,7 @@ export namespace PropertyAccess { // } function query(q: StructureQuery, s: Structure) { - return StructureQuery.run(q, s); + return StructureQuery.run1(q, s); } export async function run() { @@ -444,13 +444,13 @@ export namespace PropertyAccess { //console.log(to_mmCIF('test', Selection.union(q0r))); console.time('q1') - await query(q1, structures[0]); + query(q1, structures[0]); console.timeEnd('q1') console.time('q1') - await query(q1, structures[0]); + query(q1, structures[0]); console.timeEnd('q1') console.time('q2') - const q2r = await query(q2, structures[0]); + const q2r = query(q2, structures[0]); console.timeEnd('q2') console.log(StructureSelection.structureCount(q2r)); //console.log(q1(structures[0])); @@ -461,8 +461,8 @@ export namespace PropertyAccess { //.add('test q', () => q1(structures[0])) //.add('test q', () => q(structures[0])) .add('test int', () => sumProperty(structures[0], l => col(l.element))) - .add('test q1', async () => await query(q1, structures[0])) - .add('test q3', async () => await query(q3, structures[0])) + .add('test q1', async () => query(q1, structures[0])) + .add('test q3', async () => query(q3, structures[0])) // .add('sum residue', () => sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom]))) // .add('baseline', () => baseline(models[0])) diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index 4a1049524..4e6866e94 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -69,7 +69,7 @@ export async function resolveRequest(req: Request, writer: Writer) { ? await req.queryDefinition.structureTransform(req.normalizedParams, wrappedStructure.structure) : wrappedStructure.structure; const query = req.queryDefinition.query(req.normalizedParams, structure); - const result = StructureSelection.unionStructure(await StructureQuery.asTask(query, structure).run(abortingObserver, 250)); + const result = StructureSelection.unionStructure(StructureQuery.run1(query, structure)); perf.end('query'); ConsoleLogger.logId(req.id, 'Query', 'Query finished.'); -- GitLab