@@ -163,26 +163,52 @@ function addMap(depMap, id, prop, dependency) {
163163 callbacks . push ( dependency ) ;
164164}
165165
166- function addPattern ( depMap , idSpec , prop , dependency ) {
166+ // Patterns are stored in a nested Map structure to avoid the overhead of
167+ // stringifying ids for every callback.
168+ function addPattern ( patterns , idSpec , prop , dependency ) {
167169 const keys = Object . keys ( idSpec ) . sort ( ) ;
168170 const keyStr = keys . join ( ',' ) ;
169171 const values = props ( keys , idSpec ) ;
170- const keyCallbacks = ( depMap [ keyStr ] = depMap [ keyStr ] || { } ) ;
171- const propCallbacks = ( keyCallbacks [ prop ] = keyCallbacks [ prop ] || [ ] ) ;
172- let valMatch = false ;
173- for ( let i = 0 ; i < propCallbacks . length ; i ++ ) {
174- if ( equals ( values , propCallbacks [ i ] . values ) ) {
175- valMatch = propCallbacks [ i ] ;
176- break ;
177- }
172+ const valuesKey = values
173+ . map ( v =>
174+ typeof v === 'object' && v !== null
175+ ? v . wild
176+ ? v . wild
177+ : JSON . stringify ( v )
178+ : String ( v )
179+ )
180+ . join ( '|' ) ;
181+
182+ if ( ! patterns . has ( keyStr ) ) {
183+ patterns . set ( keyStr , new Map ( ) ) ;
184+ }
185+ const propMap = patterns . get ( keyStr ) ;
186+ if ( ! propMap . has ( prop ) ) {
187+ propMap . set ( prop , new Map ( ) ) ;
178188 }
189+ const valueMap = propMap . get ( prop ) ;
190+
191+ let valMatch = valueMap . get ( valuesKey ) ;
179192 if ( ! valMatch ) {
180193 valMatch = { keys, values, callbacks : [ ] } ;
181- propCallbacks . push ( valMatch ) ;
194+ valueMap . set ( valuesKey , valMatch ) ;
182195 }
183196 valMatch . callbacks . push ( dependency ) ;
184197}
185198
199+ // Convert the nested Map structure of patterns into the plain nested object structure
200+ // expected by the rest of the code, with stringified id keys.
201+ // This is only done once per pattern, at the end of graph construction,
202+ // to minimize the overhead of stringifying ids.
203+ function offloadPatterns ( patternsMap , targetMap ) {
204+ for ( const [ keyStr , propMap ] of patternsMap . entries ( ) ) {
205+ targetMap [ keyStr ] = { } ;
206+ for ( const [ prop , valueMap ] of propMap . entries ( ) ) {
207+ targetMap [ keyStr ] [ prop ] = Array . from ( valueMap . values ( ) ) ;
208+ }
209+ }
210+ }
211+
186212function validateDependencies ( parsedDependencies , dispatchError ) {
187213 const outStrs = { } ;
188214 const outObjs = [ ] ;
@@ -626,9 +652,10 @@ export function validateCallbacksToLayout(state_, dispatchError) {
626652 validatePatterns ( inputPatterns , 'Input' ) ;
627653}
628654
629- export function computeGraphs ( dependencies , dispatchError ) {
655+ export function computeGraphs ( dependencies , dispatchError , config ) {
630656 // multiGraph is just for finding circular deps
631657 const multiGraph = new DepGraph ( ) ;
658+ const start = performance . now ( ) ;
632659
633660 const wildcardPlaceholders = { } ;
634661
@@ -657,7 +684,9 @@ export function computeGraphs(dependencies, dispatchError) {
657684 hasError = true ;
658685 dispatchError ( message , lines ) ;
659686 } ;
660- validateDependencies ( parsedDependencies , wrappedDE ) ;
687+ if ( config . validate_callbacks ) {
688+ validateDependencies ( parsedDependencies , wrappedDE ) ;
689+ }
661690
662691 /*
663692 * For regular ids, outputMap and inputMap are:
@@ -683,8 +712,10 @@ export function computeGraphs(dependencies, dispatchError) {
683712 */
684713 const outputMap = { } ;
685714 const inputMap = { } ;
686- const outputPatterns = { } ;
687- const inputPatterns = { } ;
715+ const outputPatternMap = new Map ( ) ;
716+ const inputPatternMap = new Map ( ) ;
717+ let outputPatterns = { } ;
718+ let inputPatterns = { } ;
688719
689720 const finalGraphs = {
690721 MultiGraph : multiGraph ,
@@ -701,12 +732,14 @@ export function computeGraphs(dependencies, dispatchError) {
701732 return finalGraphs ;
702733 }
703734
735+ // builds up wildcardPlaceholders with all the wildcard keys and values used in the callbacks, so we can generate the full list of ids that each callback depends on.
704736 parsedDependencies . forEach ( dependency => {
705737 const { outputs, inputs} = dependency ;
706738
707- outputs . concat ( inputs ) . forEach ( item => {
708- const { id} = item ;
709- if ( typeof id === 'object' ) {
739+ outputs
740+ . concat ( inputs )
741+ . filter ( item => typeof item . id === 'object' )
742+ . forEach ( item => {
710743 forEachObjIndexed ( ( val , key ) => {
711744 if ( ! wildcardPlaceholders [ key ] ) {
712745 wildcardPlaceholders [ key ] = {
@@ -722,11 +755,11 @@ export function computeGraphs(dependencies, dispatchError) {
722755 } else if ( keyPlaceholders . exact . indexOf ( val ) === - 1 ) {
723756 keyPlaceholders . exact . push ( val ) ;
724757 }
725- } , id ) ;
726- }
727- } ) ;
758+ } , item . id ) ;
759+ } ) ;
728760 } ) ;
729761
762+ // Efficiently build wildcardPlaceholders.vals arrays
730763 forEachObjIndexed ( keyPlaceholders => {
731764 const { exact, expand} = keyPlaceholders ;
732765 const vals = exact . slice ( ) . sort ( idValSort ) ;
@@ -808,6 +841,7 @@ export function computeGraphs(dependencies, dispatchError) {
808841 const cbOut = [ ] ;
809842
810843 function addInputToMulti ( inIdProp , outIdProp , firstPass = true ) {
844+ if ( ! config . validate_callbacks ) return ;
811845 multiGraph . addNode ( inIdProp ) ;
812846 multiGraph . addDependency ( inIdProp , outIdProp ) ;
813847 // only store callback inputs and outputs during the first pass
@@ -825,6 +859,7 @@ export function computeGraphs(dependencies, dispatchError) {
825859 cbOut . push ( [ ] ) ;
826860
827861 function addOutputToMulti ( outIdFinal , outIdProp ) {
862+ if ( ! config . validate_callbacks ) return ;
828863 multiGraph . addNode ( outIdProp ) ;
829864 inputs . forEach ( inObj => {
830865 const { id : inId , property} = inObj ;
@@ -859,41 +894,50 @@ export function computeGraphs(dependencies, dispatchError) {
859894 outputs . forEach ( outIdProp => {
860895 const { id : outId , property} = outIdProp ;
861896 // check if this output is also an input to the same callback
862- const alsoInput = checkInOutOverlap ( outIdProp , inputs ) ;
897+ let alsoInput ;
898+ if ( config . validate_callbacks ) {
899+ alsoInput = checkInOutOverlap ( outIdProp , inputs ) ;
900+ }
863901 if ( typeof outId === 'object' ) {
864- const outIdList = makeAllIds ( outId , { } ) ;
865- outIdList . forEach ( id => {
866- const tempOutIdProp = { id, property} ;
867- let outIdName = combineIdAndProp ( tempOutIdProp ) ;
902+ if ( config . validate_callbacks ) {
903+ const outIdList = makeAllIds ( outId , { } ) ;
904+ outIdList . forEach ( id => {
905+ const tempOutIdProp = { id, property} ;
906+ let outIdName = combineIdAndProp ( tempOutIdProp ) ;
907+ // if this output is also an input, add `outputTag` to the name
908+ if ( alsoInput ) {
909+ duplicateOutputs . push ( tempOutIdProp ) ;
910+ outIdName += outputTag ;
911+ }
912+ addOutputToMulti ( id , outIdName ) ;
913+ } ) ;
914+ }
915+ addPattern ( outputPatternMap , outId , property , finalDependency ) ;
916+ } else {
917+ if ( config . validate_callbacks ) {
918+ let outIdName = combineIdAndProp ( outIdProp ) ;
868919 // if this output is also an input, add `outputTag` to the name
869920 if ( alsoInput ) {
870- duplicateOutputs . push ( tempOutIdProp ) ;
921+ duplicateOutputs . push ( outIdProp ) ;
871922 outIdName += outputTag ;
872923 }
873- addOutputToMulti ( id , outIdName ) ;
874- } ) ;
875- addPattern ( outputPatterns , outId , property , finalDependency ) ;
876- } else {
877- let outIdName = combineIdAndProp ( outIdProp ) ;
878- // if this output is also an input, add `outputTag` to the name
879- if ( alsoInput ) {
880- duplicateOutputs . push ( outIdProp ) ;
881- outIdName += outputTag ;
924+ addOutputToMulti ( { } , outIdName ) ;
882925 }
883- addOutputToMulti ( { } , outIdName ) ;
884926 addMap ( outputMap , outId , property , finalDependency ) ;
885927 }
886928 } ) ;
887929
888930 inputs . forEach ( inputObject => {
889931 const { id : inId , property : inProp } = inputObject ;
890932 if ( typeof inId === 'object' ) {
891- addPattern ( inputPatterns , inId , inProp , finalDependency ) ;
933+ addPattern ( inputPatternMap , inId , inProp , finalDependency ) ;
892934 } else {
893935 addMap ( inputMap , inId , inProp , finalDependency ) ;
894936 }
895937 } ) ;
896938 } ) ;
939+ outputPatterns = offloadPatterns ( outputPatternMap , outputPatterns ) ;
940+ inputPatterns = offloadPatterns ( inputPatternMap , inputPatterns ) ;
897941
898942 // second pass for adding new output nodes as dependencies where needed
899943 duplicateOutputs . forEach ( dupeOutIdProp => {
@@ -913,6 +957,11 @@ export function computeGraphs(dependencies, dispatchError) {
913957 }
914958 }
915959 } ) ;
960+ const end = performance . now ( ) ;
961+ if ( ! window . dash_component_api ) {
962+ window . dash_component_api = { } ;
963+ }
964+ window . dash_component_api . callbackGraphTime = ( end - start ) . toFixed ( 2 ) ;
916965
917966 return finalGraphs ;
918967}
0 commit comments