0031920: Application Framework - speed up methods of getting label by entry and vice... IR-2021-05-21
authorvro <vro@opencascade.com>
Thu, 13 May 2021 11:19:00 +0000 (14:19 +0300)
committerbugmaster <bugmaster@opencascade.com>
Sat, 22 May 2021 07:36:40 +0000 (10:36 +0300)
            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
src/DDF/DDF_DataCommands.cxx
src/TDF/TDF_Data.cxx
src/TDF/TDF_Data.hxx
src/TDF/TDF_Label.cxx
src/TDF/TDF_Tool.cxx
tests/bugs/caf/bug31920 [new file with mode: 0644]

index f809020..115b364 100644 (file)
@@ -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.
index 53117d9..1b78a6b 100644 (file)
@@ -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);
 }
index 7cd8877..76d580d 100644 (file)
@@ -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
index edacaac..990deb2 100644 (file)
@@ -27,6 +27,7 @@
 #include <Standard_Transient.hxx>
 #include <TDF_Label.hxx>
 #include <Standard_OStream.hxx>
+#include <NCollection_DataMap.hxx>
 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<TCollection_AsciiString, TDF_Label> myAccessByEntriesTable;
 };
 
 
index b610bbb..97459bb 100644 (file)
@@ -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
index f587c69..efa6ae9 100644 (file)
@@ -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<Standard_Character*>(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 <anEntry>.
 //=======================================================================
 
-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 <create> 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 (file)
index 0000000..9d5a19c
--- /dev/null
@@ -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"