Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance QTP数据表操作*非常*慢(在MMDRV批处理执行器下更好)?_Performance_Datatable_Qtp - Fatal编程技术网

Performance QTP数据表操作*非常*慢(在MMDRV批处理执行器下更好)?

Performance QTP数据表操作*非常*慢(在MMDRV批处理执行器下更好)?,performance,datatable,qtp,Performance,Datatable,Qtp,可能是一个扣人心弦的故事——QTP似乎无缘无故地浪费了我们的工作时间: 考虑这个脚本,它的datatable正好包含一个全局行,其中26列名为“a”到“Z”,其中填充了任意值: Print "Started" Services.StartTransaction "Simpletest" Set G=DataTable.GetSheet ("Global") For J=1 to 26 For I=1 to 100 Set P=G.GetParameter (Chr

可能是一个扣人心弦的故事——QTP似乎无缘无故地浪费了我们的工作时间:

考虑这个脚本,它的datatable正好包含一个全局行,其中26列名为“a”到“Z”,其中填充了任意值:

Print "Started"
Services.StartTransaction "Simpletest"
Set G=DataTable.GetSheet ("Global")
For J=1 to 26   
    For I=1 to 100
        Set P=G.GetParameter (Chr (J+64))
        If P.Value = "Hi" Then
        End If
    Next
Next
Services.EndTransaction "Simpletest"
Print "Ended"
在QTP 10下执行此操作需要在我的爆炸机上花费15.1秒。(当然,动画跑步已关闭。)

现在,我从QTP的bin文件夹中使用mmdrv.exe执行此操作,并为其指定参数“-usr”“”,该参数是包括QTP test.usr文件路径在内的全名

这需要0.07秒

你好?这是性能提升的215倍,但功能相同。怎么回事?

我在这里挖掘,因为我们对QTP数据表做了一些异国情调的工作,并且在QTP下面临严重的性能问题。我相信已经找到了DataTable.GetSheet和DTSheet.GetParameter属性/方法的原因

现在,我看到用于从LoadRunner场景中执行QTP测试的MMDRV没有性能损失,我想知道以下几点:

  • 访问xls文件是否有1:1的替代方案
  • Ex Mercury/HP的人员是否应该注意到,正如MMDRV.EXE所演示的那样,QTP下的数据表访问效率非常低,并对此采取一些措施
  • 就我所见,所有其他QTP功能在MMDRV和QTP下的速度相当。有人能承认吗? *还有谁知道这件事吗
感谢您的回复,无论这些回复有多令人不安


*更新*使用QTP隐形执行需要1.54秒。这是一个10倍的改进,只需隐藏QTP,如其中一个答案所述。唉。

在全GUI开发环境下运行会导致性能下降。您也可以在LoadRunner中观察到VUGEN中的这种差异,在使用复杂代码的情况下,在MDRV上运行可以显著提高性能。您还会看到人们经常抱怨VUGEN“比实际应用程序慢”


那么,如果这让我感到惊讶?不是真的。有趣的是,我没有考虑在QTP安装上是否存在MDRV,但这使得QTP与Web QUICKTEST中的TULIP技术有着共同的传统。tulip base在功能方面是QuicktestPro的基础,在负载方面是一些较新的web HTTP技术的基础。

数据表操作造成了200倍的性能损失。QTP下的其他操作仍然比MMDRV下的慢,但没有这样的恐怖因素

我通过在自定义结构(实际上是一个对象集合)中“缓存”所有DataTable调用来解决这个问题。因为我查询了大量的图纸和参数属性,所以构建这个需要5秒钟。处理我的结构而不是调用DTSheet和DTParameter属性要快得多,确实足够快了

我怀疑在QTP下,所有数据表访问都是通过他们(HP)从第三方获得许可的自定义Excel控件完成的,而在MMDRV下,他们使用的代码集成得更紧密,从而减少了每次调用的开销

公爵会说:“哈哈哈,真是一团糟。”

**更新**根据请求,这里概述了我所说的“缓存”数据表调用的含义。这是相当多的代码,所以要做好准备

这是一团乱麻(抱歉,现在没有时间翻译德语内联评论)(对于格式,我显然无能为力,也许你想在QTP的编辑器中剪切并粘贴此内容):

这一切都从一个通用容器类开始,我可以在其中存储(并通过索引访问)N个对象引用:

' Container-Klasse, die N Objektreferenzen aufnehmen kann. 
Class TContainer
  Public iItems() ' Array, das die Objektreferenzen aufnimmt
  Private iItemsHaveUBound ' True, wenn das Array mindestens ein Element hat 

  ' Konstruktor
  Private Sub Class_Initialize 
    iItemsHaveUBound=false ' Kein Element in iItems vorhanden 
  End Sub

  ' Anzahl der enthaltenen Objektreferenzen?
  Public Property Get Count
    If iItemsHaveUBound Then ' Nur wenn > 0 Elemente enthalten sind (also mindestens einmal ReDim Preserve für iItems gelaufen ist),
      ' können wir UBound aufrufen. Macht keinen Sinn, ist aber so, ein UBound (E) liefert für ein frisches Private E() einen Subscript error...
      Count=UBound (iItems)+1 ' Grösstmöglicher Index+1, da Zählung bei 0 beginnt, und 0-basierender Index+1 = Abzahl
    else
      Count=0 ' Jungfräuliches iItems(), direkt 0 liefern
    End If
  End Property

  ' Getter für indizierte Referenz (Index ist 1-basierend!)
  Public Default Property Get Item (ByVal Index) 
    Set Item=iItems(Index-1)
  End Property

  ' Setter für indizierte Zuweisung (Index ist 1-basierend!)
  Public Property Set Item (ByVal Index, ByVal Val)
    ' MBLogDebugComment "SetItem","Index=" & Index 
    If Count <= (Index-1) Then
      ReDim Preserve iItems (Index-1)
      iItemsHaveUBound=true
    End If
    Set iItems(Index-1)=Val
  End Property

  Public Property Get AddItem (ByVal Val)
     Item(Count+1)=Val
     Set AddItem=Val
  End Property

End Class
现在,真正的东西来了:

容纳N张纸的容器。我收集每张纸的属性并将它们存储在该容器中

' Klassen, die die Tabellenbkattstrukturen repräsentieren. Hintergrund ist ein ganz abgefahrener: Der Kollektor muss sich die Spaltenstrukturen aller
' intensiv anschauen, um seinen Job zu machen (Verweise verstehen, Klassencode generieren, Zuweisungscode generieren). Dafür greift er wieder und wieder
' auf DTSheet- und DTParameter-Instanzen zu. Das ist performancemässig aber sehr, sehr teuer (warum auch immer!). Um erträgliche Laufzeiten zu erhalten,
' enumeriert der Kollektor im helper BuildTestDataDescr die Sheets und deren Spalten und merkt sich in eigenen Datenstrukturen alles, was er später
' über die Spalten so wissen muss. Anschliessend macht der Kollektor seinen Job anhand dieser Repräsentationen, nicht mehr anhand der 
' DataTable-Eigenschaften. Das ergibt funktional das gleiche, macht aber performancemässig einen Riesen-Unterschied.

' Klasse, die eine Tabellenblattspalte repräsentiert
Class TestDataColumnDescr 
    Public Column ' as DTParameter; Referenz auf die Original-Spalte
    Public ColumnName ' as String; der Name der Spalte
    Public ColumnKind ' fertig ausgerechnete Spaltenart in Sachen Kollektor
    Public ColumnRefdSheet ' as DTSheet; bei Verweisspalte: das verwiesene Sheet
    Public ColumnRefdSheetName ' as String; bei Verweisspalte: der Name des verwiesenen Sheets
    Public ColumnRefdSheetDescr ' as TestDataSheetDescr; bei Verweisspalte: Referenz auf den TestDataSheetDescr-Descriptor des verwiesenen Sheets
    Public ColumnRefdSheetIDColumn ' as DTParameter; bei Verweisspalte: Referenz auf die Original-ID-Spalte des verwiesenen Sheets
    Public ColumnRefdSheetPosColumn ' as DTParameter; bei Verweisspalte: Referenz auf die Original-Pos-Spalte des verwiesenen Sheets (Nothing, wenn 1:1)
    ' Konstruktor
  Private Sub Class_Initialize 
  End Sub
End Class

' Klasse, die ein Tabellenblatt repräsentiert
Class TestDataSheetDescr 
    Public Sheet ' as DTSheet; Referenz auf das Original-Sheet
    Public SheetName ' as String; Name des Sheets
    Public SheetRowCount ' as Integer; Anzahl Zeilen im Original-Sheet
    Public SheetColumnCount ' as Integer; Anzahl Spalten im Original-Sheet
    Public SheetColumn ' as TContainer; Container aller Spaltendescriptoren (TestDataColumnDescr)
  ' Konstruktor
  Private Sub Class_Initialize 
        Set SheetColumn=New TContainer
  End Sub
End Class
以下是用于构建容器内容物的材料:

' Container aller Tabellenblattrepräsentationen
Dim TestDataDescr ' wird in BuildTestDataDescr instanziiert

' Aufbau von Tabellenblattrepräsentationen, damit Kollektor nicht dauernd DataSheet-Funktionen aufrufen muss. TestDataDescr instanziieren, aufbauen.
Public Sub BuildTestDataDescr
    ' Build N Sheet Descriptors
    Dim SheetIndex
    Dim ColumnIndex
    Dim S
  Dim S1
  Dim S2
    Dim Index
    dim SheetDescr, ColumnDescr
    ' Zunächst die N Sheet-Descriptoren mit ihren Spaltendescriptoren anlegen

    'Services.StartTransaction "BuildTestDataDescr"
    Set TestDataDescr = New TContainer
    For SheetIndex=1 to DataTable.GetSheetCount
        set SheetDescr = New TestDataSheetDescr
        With TestDataDescr.AddItem (SheetDescr)
            Set .Sheet=DataTable.GetSheet (SheetIndex)
            .SheetName=.Sheet.Name
            .SheetRowCount=.Sheet.GetRowCount
            .SheetColumnCount=.Sheet.GetParameterCount 
            Set S=.Sheet ' .Sheet ist im folgenden With nicht erreichbar, keine Ahnung, warum (nested Withes funken nicht anscheinend)
            For ColumnIndex=1 to .SheetColumnCount
                set ColumnDescr = New TestDataColumnDescr
                With .SheetColumn.AddItem (ColumnDescr)
                    Set .Column=S.GetParameter (ColumnIndex)
                    .ColumnName=.Column.Name
                End With
            Next
        End With
    Next

    ' Jetzt etwaige Verweisspalten mit zugehöriger Info anreichern (wir machen das in einem zweiten Schritt, damit wir garantiert zu allen
    ' verwiesenen Blättern einen Descriptor finden -- ohne Rekursion und komplizierten Abbruchbedingungen bei zyklischen Verweisen...); ferner
    ' müssen die Namen von auswahltabellenbasierten Spalten angepasst werden:

    For SheetIndex=1 to TestDataDescr.Count
        With TestDataDescr(SheetIndex)
            For ColumnIndex=1 to .SheetColumnCount
                Set S=.Sheet ' .Sheet ist im folgenden With nicht erreichbar, keine Ahnung, warum (nested Withes funken nicht anscheinend)
                With .SheetColumn(ColumnIndex)
                    .ColumnKind=DetectColumnKind (.ColumnName,S1,S2)
                    Select Case .ColumnKind
                        Case ckComment
                            ' Nuttin', weil: Ist ja eine Gruppier- oder Kommentarspalte -- ignorieren
                        Case ckData
                            ' Datenspalte -- hier nichts weiter zu tun
                            .ColumnName=S1 ' ausser: Namen bereinigen (hat nur Folgen für auswahllistenbasierte Spalten)
                        Case ckReference 
                            ' Verweisspalte -- merken, was später immer wieder an info benötigt wird
                            .ColumnName=S1
                            Set .ColumnRefdSheet=MBFindSheet (S2)
                            If .ColumnRefdSheet is Nothing Then
                                MBErrorAbort "MBUtil.MBCollectAllTestData", _
                                    "Fehler beim Definieren von Klassen;" & vbNewline _
                                    & "Spalte '" & .ColumnName & "' definiert einen Verweis auf Datentabellenblatt '" & S2 & "', welches nicht existiert." & vbNewline _
                                    & "Bitte überprüfen Sie die entsprechenden Datentabellenblätter" 
                            End If
                            .ColumnRefdSheetName=.ColumnRefdSheet.Name
                            Set .ColumnRefdSheetIDColumn=.ColumnRefdSheet.GetParameter ("ID")
                            Set .ColumnRefdSheetPosColumn=MBFindColumn (.ColumnRefdSheet,"Pos")
                            For Index=1 to TestDataDescr.Count
                                If TestDataDescr(Index).SheetName = .ColumnRefdSheetName then
                                    Exit For
                                End If
                            Next
                            Set .ColumnRefdSheetDescr=TestDataDescr(Index)
                    End Select
                End With
            Next
        End With
    Next
    'Services.EndTransaction "BuildTestDataDescr"
End Sub
基于容器中的信息,我使用TestDataDescr中的结构来迭代列等

如本例中所示,它接收一个容器元素(SourceSheetDescr),查看每一列,并根据列类型执行“操作”,列类型是容器元素中内置信息的一部分:

  For ParamIndex=1 to SourceSheetDescr.SheetColumnCount
        With SourceSheetDescr.SheetColumn(ParamIndex)
            Select Case .ColumnKind
                Case ckComment
                    ' Do something
                Case ckData
                    ' Do something else             Case ckReference 
                    ' Do other stuff                            End Select
        End With
  Next
这样我就不必查询DTSheet.GetParameter()和acoid来调用任何其他DataTable方法。 当然,这只是使用容器所保存信息的一个示例


在我们的典型用例中,与调用DataTable方法相比,这将性能提高了三倍,而且即使我们已经避免了所有冗余调用,并且在传统的数据表访问代码中进行了所有其他明显的优化。

我们在QTP中也遇到了同样的性能问题。经过调查,我们在两个方面解决了问题

  • 数据表(糟糕的性能)
  • QTP可见/不可见
我们发现QTP隐藏时运行速度快5-6倍

我们制作了一个小脚本,用于在开发/调试时切换QTP可见性(因为您总是可以强制将QTP隐藏在远程代理设置中) '此脚本用于显示/隐藏QTP窗口 “QTP在隐藏时运行得更快

Dim qtApp
Set qtApp = CreateObject("QuickTest.Application")
qtApp.Launch            ' Start QuickTest
If qtApp.Visible = False Then  ' Make the QuickTest application invisible/visible
    qtApp.Visible = True
Else
    qtApp.Visible = False
End If
在我们考虑开发相同的机制时,您是否愿意分享缓存DataTable的想法,并且看到这样一个示例会很有好处

亲切问候,,
Achraf在QTP中的数据表操作非常缓慢。因此,在数据表中使用Excel公式可以加快运算速度

那又怎样?在霍法尔,看到200倍以上的性能损失难道不奇怪吗?它来自哪里?(你知道2.6 GHz PC上的系数200意味着什么吗?)我在VUGEN中观察到了类似的加速,复杂代码使用一些相当复杂的规则动态创建和销毁数据,这就是我缺乏震惊的原因,通常与
Dim qtApp
Set qtApp = CreateObject("QuickTest.Application")
qtApp.Launch            ' Start QuickTest
If qtApp.Visible = False Then  ' Make the QuickTest application invisible/visible
    qtApp.Visible = True
Else
    qtApp.Visible = False
End If