Index: TODO =================================================================== diff -u -r2488a3699c78579fb1f4f1b4f2a8366946287fe5 -rb3b35e7780dcdaf8c2d07aaba4b01128f9dd2683 --- TODO (.../TODO) (revision 2488a3699c78579fb1f4f1b4f2a8366946287fe5) +++ TODO (.../TODO) (revision b3b35e7780dcdaf8c2d07aaba4b01128f9dd2683) @@ -5331,6 +5331,13 @@ - updated migration guide and tutorial to reflect recent changes +nsf.c: +- cleanup of NsfParameterInvalidateClassCacheCmd(): performance improvements. + After the fixing of indirect mixin validation, performance of invalidation + went up by a factor of 5. At least, in some important cases (invalidation of + rootclasses like nx::Object / xotcl::Object), we are again on the same level + as before (actually slightly faster). + ======================================================================== TODO: Index: generic/nsf.c =================================================================== diff -u -r97b643b1f8f0d1cb74d8bf1508b29d4b7fdb05c1 -rb3b35e7780dcdaf8c2d07aaba4b01128f9dd2683 --- generic/nsf.c (.../nsf.c) (revision 97b643b1f8f0d1cb74d8bf1508b29d4b7fdb05c1) +++ generic/nsf.c (.../nsf.c) (revision b3b35e7780dcdaf8c2d07aaba4b01128f9dd2683) @@ -25457,91 +25457,103 @@ */ static int NsfParameterInvalidateClassCacheCmd(Tcl_Interp *interp, NsfClass *cl) { + NsfClasses *subClasses; + NsfClasses *clPtr; + int isMixinOf = 0, nrSubClasses = 0; assert(interp); assert(cl); /* - * First, invalidate the cached parameters on this class (if available) and - * bump the epoch counter. Even when the current class does not have parsed - * parameters (yet), e.g. a subclass might have cached this information - * already. + * First, increment the epoch in case we have a parsedParam. The + * classParamPtrEpoch is just used for PER_OBJECT_PARAMETER_CACHING */ - if (cl->parsedParamPtr) { +#if defined(PER_OBJECT_PARAMETER_CACHING) + if (unlikely(cl->parsedParamPtr != NULL)) { NsfClassParamPtrEpochIncr("NsfParameterInvalidateClassCacheCmd"); - /*fprintf(stderr, ".... %s invalidate %p\n", ClassName(cl), cl->parsedParamPtr);*/ - ParsedParamFree(cl->parsedParamPtr); - cl->parsedParamPtr = NULL; } +#endif /* - * During lifetime, invalidations are propagated to subclasses and/or to - * classes extended by the given mixin class. During shutdown, we avoid the - * storm of invalidations. + * Clear the cached parsedParam of the class and all its subclasses (the + * result of TransitiveSubClasses contains the starting class). Furthermore, + * make a quick check, if any of the subclasses is a class mixin of some + * other class. */ - if (likely(RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF)) { - /*fprintf(stderr, ".... the current class %s has clopt %p\n", ClassName(cl), cl->opt);*/ + subClasses = TransitiveSubClasses(cl); - if ( - 1 - /* unlikely(cl->opt != NULL) && unlikely(cl->opt->isClassMixinOf != NULL) */ - // TODO cleanup; base case above is included here as well. - ) { - Tcl_HashTable objTable, *commandTable = &objTable; - Tcl_HashSearch hSrch; - Tcl_HashEntry *hPtr; + for (clPtr = subClasses; clPtr; nrSubClasses++, clPtr = clPtr->nextPtr) { + NsfClass *subClass = clPtr->cl; - /* - * The current class is mixed into some other class. - */ - /*fprintf(stderr, ".... the current class %s is mixed into some other class\n", - ClassName(cl));*/ + //fprintf(stderr, "startCl %s subcl %s\n", ClassName(cl), ClassName(subClass)); - Tcl_InitHashTable(commandTable, TCL_ONE_WORD_KEYS); - MEM_COUNT_ALLOC("Tcl_InitHashTable", commandTable); + if (subClass->parsedParamPtr) { + ParsedParamFree(subClass->parsedParamPtr); + subClass->parsedParamPtr = NULL; + } + if ((subClass->opt != NULL) && unlikely(subClass->opt->isClassMixinOf != NULL)) { + isMixinOf = 1; + } + } - GetAllClassMixinsOf(interp, commandTable, Tcl_GetObjResult(interp), - cl, 1, 0, NULL, NULL); +#if 0 + if (likely(RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF)) { + fprintf(stderr, "startCl %s has %d subclasses, a subclass mixed into something else: %d\n", + ClassName(cl), nrSubClasses, isMixinOf); + } +#endif - for (hPtr = Tcl_FirstHashEntry(commandTable, &hSrch); hPtr; - hPtr = Tcl_NextHashEntry(&hSrch)) { - NsfClass *mixinOfClass = (NsfClass *)Tcl_GetHashKey(commandTable, hPtr); + /* + * During lifetime, invalidations are propagated to subclasses and/or to + * classes extended by the given mixin class. During shutdown, we avoid the + * storm of invalidations. + * + * Furthermore: of the class is the root class of the object system + * (e.g. nx::Object), then all potentially involved classes are already + * included in the subclasses, there is no need for the expensive mixin-of + * computation. cross-object-system invalidation are another story, + * invalidation currently not supported. + */ + if (likely(RUNTIME_STATE(interp)->exitHandlerDestroyRound == NSF_EXITHANDLER_OFF) + && isMixinOf + && (cl->object.flags & NSF_IS_ROOT_CLASS) == 0) { - if (mixinOfClass) { - /*fprintf(stderr, "... invalidate mixinOfClass %s\n", ClassName(mixinOfClass));*/ - if (mixinOfClass->parsedParamPtr) { - ParsedParamFree(mixinOfClass->parsedParamPtr); - mixinOfClass->parsedParamPtr = NULL; - } - } - } - Tcl_DeleteHashTable(commandTable); - MEM_COUNT_FREE("Tcl_InitHashTable", commandTable); + Tcl_HashTable objTable, *commandTable = &objTable; + Tcl_HashSearch hSrch; + Tcl_HashEntry *hPtr; - } else { - NsfClasses *subClasses = TransitiveSubClasses(cl); + /* + * The current class (or one of its subclasses) is mixed into some other class. + */ + /*fprintf(stderr, ".... the current class %s is mixed into some other class\n", + ClassName(cl));*/ - if (subClasses != NULL) { - NsfClasses *clPtr; - /* - * The current class is NOT mixed into some other class and is just a - * potential superclass of some other classes. In this case, we have to - * invalidate cached parameters in subclasses. - */ - for (clPtr = subClasses; clPtr; clPtr = clPtr->nextPtr) { - NsfClass *subClass = clPtr->cl; + Tcl_InitHashTable(commandTable, TCL_ONE_WORD_KEYS); + MEM_COUNT_ALLOC("Tcl_InitHashTable", commandTable); - if (subClass->parsedParamPtr) { - ParsedParamFree(subClass->parsedParamPtr); - subClass->parsedParamPtr = NULL; - } + GetAllClassMixinsOf(interp, commandTable, Tcl_GetObjResult(interp), + cl, 1, 0, NULL, NULL); + + for (hPtr = Tcl_FirstHashEntry(commandTable, &hSrch); hPtr; + hPtr = Tcl_NextHashEntry(&hSrch)) { + NsfClass *mixinOfClass = (NsfClass *)Tcl_GetHashKey(commandTable, hPtr); + + if (mixinOfClass) { + /*fprintf(stderr, "... invalidate mixinOfClass %s\n", ClassName(mixinOfClass));*/ + if (mixinOfClass->parsedParamPtr) { + ParsedParamFree(mixinOfClass->parsedParamPtr); + mixinOfClass->parsedParamPtr = NULL; } - NsfClassListFree(subClasses); } } + Tcl_DeleteHashTable(commandTable); + MEM_COUNT_FREE("Tcl_InitHashTable", commandTable); + } + NsfClassListFree(subClasses); + return TCL_OK; } Index: tests/properties.test =================================================================== diff -u -r2f793442bb2a7860acc5620811dcafddc43074d3 -rb3b35e7780dcdaf8c2d07aaba4b01128f9dd2683 --- tests/properties.test (.../properties.test) (revision 2f793442bb2a7860acc5620811dcafddc43074d3) +++ tests/properties.test (.../properties.test) (revision b3b35e7780dcdaf8c2d07aaba4b01128f9dd2683) @@ -1054,7 +1054,8 @@ ? {M0 info mixinof} "" ? {M0 info mixinof -closure} "::C ::D" - ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} "objectName x object-mixin class object-filter __initblock" + ? {lmap p [C info lookup parameters create] {nsf::parameter::info name $p}} \ + "objectName x object-mixin class object-filter __initblock" ? {llength [C info lookup parameters create]} [expr {$base + 1}] ? {llength [D info lookup parameters create]} [expr {$base + 1}]