From 604aa3f4b3ce30a5f69fa10a783ac403c09c4293 Mon Sep 17 00:00:00 2001 From: vro Date: Thu, 13 May 2021 14:19:00 +0300 Subject: [PATCH] 0031920: Application Framework - speed up methods of getting label by entry and vice versa A table for fast access to the labels by entry is implemented in OCAF document. A method TDF_Data::SetAccessByEntries(true) fills-in a table for fast access to the labels. New labels, created later will be added to the table automatically. The method TDF_Tool::Label() will search the entry in the table and then, if not found, will call the old code. Disabling of usage of the table (by calling of TDF_Data::SetAccessByEntries(false)) cleans the internal table of entries - labels. By default, the table is not used. This improvement is useful for large documents with a lot of labels, and if the application uses entries to get labels. The application should call TDF_Data::SetAccessByEntries(true) for a document and then, the method TDF_Tool::Label() called inside OCAF and XCAF will use the fast access to the labels and speed-up the application. Also, the method TDF_Tool::Entry() is improved (by MPV). Modified files: - TDF_Data.hxx and cxx: the new methods SetAccessByEntries(bool), IsAccessByEntries() and GetLabel(entry) are implemented. No need to use the method GetLabel() directly. It is called in TDF_Tool::Label(). - TDF_Label.cxx: adding of a newly created label to the table of entries - labels. - TDF_Tool.cxx: the method Entry() is accelerated (by MPV) and Label() is improved to call TDF_Data::GetLabel(). - DDF_DataCommands.cxx: a new draw-command is added SetAccessByEntry, which sets or unsets usage of the table for fast access to the labels. Usage of the draw-command is illustrated in a new test "bugs caf bug31920". Tests: - bugs caf bug31920: a new simple test to check TDF_Tool::Label() when fast access to the labels is on. Doc: - dox\upgrade\upgrade.md is extended for new information --- dox/upgrade/upgrade.md | 7 +++ src/DDF/DDF_DataCommands.cxx | 28 ++++++++++-- src/TDF/TDF_Data.cxx | 40 ++++++++++++++++- src/TDF/TDF_Data.hxx | 26 ++++++++++- src/TDF/TDF_Label.cxx | 3 ++ src/TDF/TDF_Tool.cxx | 86 +++++++++++++++++++++++------------- tests/bugs/caf/bug31920 | 22 +++++++++ 7 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 tests/bugs/caf/bug31920 diff --git a/dox/upgrade/upgrade.md b/dox/upgrade/upgrade.md index f809020565..115b36460c 100644 --- a/dox/upgrade/upgrade.md +++ b/dox/upgrade/upgrade.md @@ -2237,3 +2237,10 @@ Both libraries can be now built simultaneously on systems providing both APIs (l Existing applications depending on OpenGL ES (mobile projects first of all) should be adjusted to link against *TKOpenGles*. Note that both *TKOpenGl* and *TKOpenGles* keep exporting classes with the same name, so applications should not attempt to link both libraries simultaneously. + +@subsection upgrade_occt760_fast_access_to_labels Fast access to OCAF label + +Access to an OCAF label via its entry is accelerated. In order to activate it, call *TDF_Data::SetAccessByEntries()*. +The method *TDF_Tool::Label()*, which returns a label by an entry, becomes faster for about 10 .. 20 times. +It has sense for applications, which use an entry as a unique key to access the data in OCAF tree. +Also, the method *TDF_Tool::Entry()*, which returns an entry for a label, is accelerated as well. diff --git a/src/DDF/DDF_DataCommands.cxx b/src/DDF/DDF_DataCommands.cxx index 53117d9387..1b78a6bb73 100644 --- a/src/DDF/DDF_DataCommands.cxx +++ b/src/DDF/DDF_DataCommands.cxx @@ -371,7 +371,27 @@ static Standard_Integer DDF_CheckLabel (Draw_Interpretor& di,Standard_Integer n return 1; } - +//======================================================================= +//function : DDF_SetAccessByEntry +//purpose : SetAccessByEntry DOC 1|0 +//======================================================================= +static Standard_Integer DDF_SetAccessByEntry (Draw_Interpretor& di, Standard_Integer nb, const char** a) +{ + Standard_Integer aRet = 0; + if (nb != 3) { + di << "SetAccessByEntry DOC 1|0\n"; + aRet = 1; + } else { + Handle(TDF_Data) aDF; + if (DDF::GetDF(a[1], aDF)) { + Standard_Boolean aSet = (Draw::Atoi (a[2]) == 1); + aDF->SetAccessByEntries (aSet); + } else { + aRet = 1; + } + } + return aRet; +} // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -415,10 +435,12 @@ void DDF::DataCommands (Draw_Interpretor& theCommands) "CopyLabel (DOC, from, to)", __FILE__, CopyLabel_SCopy, g); - theCommands.Add("CheckAttrs","CheckAttrs DocName Lab1 Lab2 ", + theCommands.Add ("CheckAttrs","CheckAttrs DocName Lab1 Lab2 ", __FILE__, DDF_CheckAttrs, g); - theCommands.Add("CheckLabel","CheckLabel DocName Label ", + theCommands.Add ("CheckLabel","CheckLabel DocName Label ", __FILE__, DDF_CheckLabel, g); + theCommands.Add ("SetAccessByEntry", "SetAccessByEntry DOC 1|0", + __FILE__, DDF_SetAccessByEntry, g); } diff --git a/src/TDF/TDF_Data.cxx b/src/TDF/TDF_Data.cxx index 7cd8877047..76d580d213 100644 --- a/src/TDF/TDF_Data.cxx +++ b/src/TDF/TDF_Data.cxx @@ -94,7 +94,8 @@ myTransaction (0), myNbTouchedAtt (0), myNotUndoMode (Standard_True), myTime (0), -myAllowModification (Standard_True) +myAllowModification (Standard_True), +myAccessByEntries (Standard_False) { const Handle(NCollection_IncAllocator) anIncAllocator= new NCollection_IncAllocator (16000); @@ -118,6 +119,7 @@ void TDF_Data::Destroy() Handle(TDF_Attribute) aFirst = myRoot->FirstAttribute(); myRoot->RemoveAttribute(anEmpty, aFirst); } + myAccessByEntriesTable.Clear(); myRoot->Destroy (myLabelNodeAllocator); myRoot = NULL; } @@ -439,7 +441,43 @@ Handle(TDF_Delta) TDF_Data::Undo(const Handle(TDF_Delta)& aDelta, return newDelta; } +//======================================================================= +//function : SetAccessByEntries +//purpose : +//======================================================================= +void TDF_Data::SetAccessByEntries(const Standard_Boolean aSet) +{ + myAccessByEntries = aSet; + + myAccessByEntriesTable.Clear(); + if (myAccessByEntries) { + // Add root label. + TCollection_AsciiString anEntry; + TDF_Tool::Entry (myRoot, anEntry); + myAccessByEntriesTable.Bind (anEntry, myRoot); + + // Add all other labels. + TDF_ChildIterator itr (myRoot, Standard_True); + for (; itr.More(); itr.Next()) { + const TDF_Label aLabel = itr.Value(); + TDF_Tool::Entry (aLabel, anEntry); + myAccessByEntriesTable.Bind (anEntry, aLabel); + } + } +} + +//======================================================================= +//function : RegisterLabel +//purpose : +//======================================================================= + +void TDF_Data::RegisterLabel(const TDF_Label& aLabel) +{ + TCollection_AsciiString anEntry; + TDF_Tool::Entry (aLabel, anEntry); + myAccessByEntriesTable.Bind (anEntry, aLabel); +} //======================================================================= //function : Dump diff --git a/src/TDF/TDF_Data.hxx b/src/TDF/TDF_Data.hxx index edacaac7bc..990deb2e78 100644 --- a/src/TDF/TDF_Data.hxx +++ b/src/TDF/TDF_Data.hxx @@ -27,6 +27,7 @@ #include #include #include +#include class Standard_NoMoreObject; class TDF_Transaction; class TDF_LabelNode; @@ -97,6 +98,27 @@ Standard_OStream& operator<< (Standard_OStream& anOS) const //! returns modification mode. Standard_Boolean IsModificationAllowed() const; + //! Initializes a mechanism for fast access to the labels by their entries. + //! The fast access is useful for large documents and often access to the labels + //! via entries. Internally, a table of entry - label is created, + //! which allows to obtain a label by its entry in a very fast way. + //! If the mechanism is turned off, the internal table is cleaned. + //! New labels are added to the table, if the mechanism is on + //! (no need to re-initialize the mechanism). + Standard_EXPORT void SetAccessByEntries (const Standard_Boolean aSet); + + //! Returns a status of mechanism for fast access to the labels via entries. + Standard_Boolean IsAccessByEntries() const { return myAccessByEntries; } + + //! Returns a label by an entry. + //! Returns Standard_False, if such a label doesn't exist + //! or mechanism for fast access to the label by entry is not initialized. + Standard_Boolean GetLabel (const TCollection_AsciiString& anEntry, TDF_Label& aLabel) { return myAccessByEntriesTable.Find(anEntry, aLabel); } + + //! An internal method. It is used internally on creation of new labels. + //! It adds a new label into internal table for fast access to the labels by entry. + Standard_EXPORT void RegisterLabel (const TDF_Label& aLabel); + //! Returns TDF_HAllocator, which is an //! incremental allocator used by //! TDF_LabelNode. @@ -195,8 +217,8 @@ private: TColStd_ListOfInteger myTimes; TDF_HAllocator myLabelNodeAllocator; Standard_Boolean myAllowModification; - - + Standard_Boolean myAccessByEntries; + NCollection_DataMap myAccessByEntriesTable; }; diff --git a/src/TDF/TDF_Label.cxx b/src/TDF/TDF_Label.cxx index b610bbb520..97459bbe39 100644 --- a/src/TDF/TDF_Label.cxx +++ b/src/TDF/TDF_Label.cxx @@ -345,6 +345,9 @@ TDF_LabelNode* TDF_Label::FindOrAddChild myLabelNode->myFirstChild = childLabelNode; else // ... somewhere. lastLnp->myBrother = childLabelNode; + // Update table for fast access to the labels. + if (myLabelNode->Data()->IsAccessByEntries()) + myLabelNode->Data()->RegisterLabel (childLabelNode); } if (lastLnp) //agv 14.07.2010 diff --git a/src/TDF/TDF_Tool.cxx b/src/TDF/TDF_Tool.cxx index f587c69603..efa6ae9328 100644 --- a/src/TDF/TDF_Tool.cxx +++ b/src/TDF/TDF_Tool.cxx @@ -372,30 +372,43 @@ void TDF_Tool::RelocateLabel //purpose : Returns the entry as an ascii string. //======================================================================= -void TDF_Tool::Entry -(const TDF_Label& aLabel, - TCollection_AsciiString& anEntry) +void TDF_Tool::Entry (const TDF_Label& aLabel, TCollection_AsciiString& anEntry) { - anEntry.Clear(); if (!aLabel.IsNull()) { - TColStd_ListOfInteger Tags; - TDF_Tool::TagList(aLabel, Tags); - anEntry += TCollection_AsciiString(Tags.First()); - Tags.RemoveFirst(); - if (Tags.IsEmpty()) { - anEntry += TDF_TagSeparator; // It must be the root label case. + int aStrLen = 1; // initial "0" of a root label + TDF_Label aLab = aLabel; + for (; !aLab.IsRoot(); aLab = aLab.Father()) + { + for (int aTag = aLab.Tag(); aTag > 9; aTag /= 10) + ++aStrLen; + aStrLen += 2; // one digit and separator } - else { - while (!Tags.IsEmpty()) { - anEntry += TDF_TagSeparator; - anEntry += TCollection_AsciiString(Tags.First()); - Tags.RemoveFirst(); - } + + if (aStrLen == 1) + { + // an exceptional case for the root label, it ends with separator + static const TCollection_AsciiString THE_ROOT_ENTRY = TCollection_AsciiString ('0') + TDF_TagSeparator; + anEntry = THE_ROOT_ENTRY; + } + else + { + anEntry = TCollection_AsciiString (aStrLen, TDF_TagSeparator); + Standard_Character* aPtr = const_cast(anEntry.ToCString() + aStrLen - 1); + for (aLab = aLabel; !aLab.IsRoot(); aLab = aLab.Father()) + { + int aTag = aLab.Tag(); + for (; aTag > 9; --aPtr, aTag /= 10) + *aPtr = Standard_Character (aTag % 10) + '0'; + *aPtr = Standard_Character (aTag) + '0'; + aPtr -= 2; + } + *aPtr = '0'; } } + else + anEntry.Clear(); } - //======================================================================= //function : TagList //purpose : Returns the entry of a label as a list of integers. @@ -452,12 +465,18 @@ void TDF_Tool::TagList //purpose : Returns the label expressed by . //======================================================================= -void TDF_Tool::Label -(const Handle(TDF_Data)& aDF, - const TCollection_AsciiString& anEntry, - TDF_Label& aLabel, - const Standard_Boolean create) -{ TDF_Tool::Label(aDF,anEntry.ToCString(),aLabel,create); } +void TDF_Tool::Label (const Handle(TDF_Data)& aDF, + const TCollection_AsciiString& anEntry, + TDF_Label& aLabel, + const Standard_Boolean create) +{ + Standard_Boolean isFound = Standard_False; + if (aDF->IsAccessByEntries()) + isFound = aDF->GetLabel (anEntry, aLabel); + + if (!isFound) + TDF_Tool::Label (aDF, anEntry.ToCString(), aLabel, create); +} //======================================================================= @@ -466,15 +485,20 @@ void TDF_Tool::Label // and creates it if is true. //======================================================================= -void TDF_Tool::Label -(const Handle(TDF_Data)& aDF, - const Standard_CString anEntry, - TDF_Label& aLabel, - const Standard_Boolean create) +void TDF_Tool::Label (const Handle(TDF_Data)& aDF, + const Standard_CString anEntry, + TDF_Label& aLabel, + const Standard_Boolean create) { - TColStd_ListOfInteger tagList; - TDF_Tool::TagList(anEntry,tagList); - TDF_Tool::Label(aDF,tagList,aLabel,create); + Standard_Boolean isFound = Standard_False; + if (aDF->IsAccessByEntries()) + isFound = aDF->GetLabel (anEntry, aLabel); + + if (!isFound) { + TColStd_ListOfInteger tagList; + TDF_Tool::TagList (anEntry, tagList); + TDF_Tool::Label (aDF, tagList, aLabel, create); + } } diff --git a/tests/bugs/caf/bug31920 b/tests/bugs/caf/bug31920 new file mode 100644 index 0000000000..9d5a19cfff --- /dev/null +++ b/tests/bugs/caf/bug31920 @@ -0,0 +1,22 @@ +puts "===========" +puts "0031920: Application Framework - speed up methods of getting label by entry and vice versa" +puts "===========" + +NewDocument D +set entry 0:2 +set value 5 +SetInteger D $entry 5 +set checkvalue1 [GetInteger D $entry] +if { $value != $checkvalue1 } { + puts "Set a value of TDataStd_Integer attribute: Error" + return +} + +SetAccessByEntry D 1 +set checkvalue2 [GetInteger D $entry] +if { $value != $checkvalue2 } { + puts "Fast access to label by entry: Error" + return +} + +puts "Fast access to label by entry: OK"