titulo texto para hago encabezado como centro centrar archivo alinear performance datatable qtp

performance - texto - Operaciones de tabla de datos QTP*extremadamente*lento(mucho mejor bajo el ejecutor de lotes MMDRV)?



como centrar un texto en html (4)

Posiblemente una historia sensacional: QTP parece desperdiciar nuestro tiempo de trabajo sin ninguna razón:

Considere este script, que tiene una tabla de datos de exactamente una fila global con 26 columnas llamadas "A" a "Z" llenas de cualquier valor:

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"

Ejecutar esto bajo QTP 10 toma 15.1 segundos en mi bláster. (La ejecución animada está desactivada, por supuesto).

Ahora ejecuto esto usando mmdrv.exe desde la carpeta bin de QTP, dándole el parámetro "-usr ''''" con el nombre completo que incluye la ruta al archivo .usr de la prueba QTP.

Eso demora 0.07 segundos .

¿Hola? Eso es un aumento de rendimiento de 215 veces, pero una funcionalidad idéntica. ¿Cómo viene?

Estoy cavando por aquí ya que hacemos algunas cosas exóticas con tablas de datos QTP, y enfrentamos serios problemas de rendimiento bajo QTP. Creo que he rastreado la causa hasta las propiedades / métodos DataTable.GetSheet y DTSheet.GetParameter.

Ahora que veo que el MMDRV, que es para ejecutar pruebas QTP desde escenarios de LoadRunner, no tiene esa penalización de rendimiento, me pregunto lo siguiente:

  • ¿Hay una alternativa 1: 1 para acceder a los archivos xls?
  • ¿No debería alguien en Ex-Mercury / HP notar que el acceso a la tabla de datos bajo QTP es muy ineficiente, como lo demuestra MMDRV.EXE, y hacer algo al respecto?
  • Por lo que puedo ver, todas las demás funciones de QTP tienen una velocidad comparable en MMDRV y QTP. ¿Alguien puede reconocer eso? * ¿Alguien más sabe sobre esto?

Gracias por cualquier respuesta, sin importar cuán inquietantes puedan ser.

* ACTUALIZACIÓN * La ejecución con QTP invisible toma 1.54 segundos. Eso es una mejora de 10 veces simplemente ocultando QTP como se indica en una de las respuestas. Suspiro.


Ejecutar con un entorno de desarrollo de GUI completo extrae una penalización de rendimiento. También se puede observar esta diferencia en VUGEN en LoadRunner, corriendo en MDRV proporcionando un aumento sustancial en el rendimiento donde se usa código complejo. También verá gente que con frecuencia se queja de que VUGEN es "más lento que la aplicación real".

Entonces, si esto me sorprende? Realmente no. Lo que es interesante es que no había considerado la existencia de MDRV en la instalación de QTP, pero eso lo hace dado el legado común de QTP con la tecnología TULIP que salió de QUICKTEST for Web. Esa base de tulipán ha sido la base de QuicktestPro en el lado funcional y parte de la tecnología HTTP más nueva en el lado de la carga.


Estamos teniendo los mismos problemas de rendimiento con QTP. Después de las investigaciones, arrinconamos los problemas en 2 áreas.

  • La tabla de datos (rendimiento horrible)
  • QTP es visible / invisible

Descubrimos que QTP funciona 5-6 veces más rápido cuando está oculto

Hicimos una pequeña secuencia de comandos para alternar la visibilidad QTP mientras desarrollamos / depuramos (porque siempre puede forzar que QTP esté oculto en la configuración del agente remoto) ''Este script se usa para mostrar / ocultar la ventana QTP'' QTP se ejecuta mucho más rápido cuando está oculto

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

¿Le gustaría compartir la idea de almacenar en caché el DataTable ya que estamos pensando en desarrollar el mismo mecanismo y nos beneficiaríamos de ver ese ejemplo?

Saludos cordiales, Achraf


La penalización de rendimiento de 200 veces proviene de las operaciones de DataTable. Otras operaciones aún son más lentas bajo QTP que bajo MMDRV, pero no con un factor de horror como ese.

Trabajé en eso al "caché" de todas las llamadas de DataTable en una estructura personalizada (realmente una colección de objetos). Construir eso lleva 5 segundos ya que consulto muchas Hojas y propiedades de parámetros. Procesar mi estructura en lugar de llamar a las propiedades DTSheet y DTParameter es mucho más rápido, de hecho lo suficientemente rápido.

Sospecho que bajo QTP todos los accesos a la tabla de datos se realizan a través del control personalizado de Excel que (HP) tienen licencia de un tercero, mientras que bajo MMDRV usan código que se integra más estrechamente, lo que genera menos sobrecarga por llamada.

Duke diría: "Jajaja, qué desastre".

** Actualización ** A petición, aquí hay un resumen de lo que quiero decir con "caché" de llamadas a DataTable. Es bastante código, así que prepárate ...

Aquí está ese desastre (lo siento, no tengo tiempo para traducir los comentarios en línea de Alemania en este momento) (y lo siento por el formato, obviamente no puedo hacer mucho al respecto, tal vez quieras cortar y pegar esto en el editor de QTP):

Todo comienza con una clase Container genérica en la que puedo almacenar (y acceder por índice) referencias de objetos 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

Utilizo nombres de columna especiales para dar a las columnas significados especiales. DetectColumnKind detecta ese significado según el nombre y escupe una "enumeración". Esto es todo (no mostraré DetectColumnKind aquí):

'' Von MBCollectAllTestData unterstützte Spaltenarten in Datentabellen Private Const ckData = 0 Private Const ckReference = 1 Private Const ckComment = 2

Ahora viene lo verdadero:

Un contenedor con N representaciones de hoja. Recolecto las propiedades de cada hoja y las almaceno en ese contenedor.

'' 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

Aquí están las cosas para construir los contenidos del contenedor:

'' 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

Basado en la información del contenedor, uso las estructuras en TestDataDescr para iterar sobre columnas, etc.

Al igual que en este ejemplo, que recibe un elemento contenedor (SourceSheetDescr), mira cada columna y hace "algo" según el tipo de columna, que es parte de la información incorporada en el elemento contenedor:

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

De esta manera, evito tener que consultar DTSheet.GetParameter (), y con el nombre de coid para llamar a cualquier otro método de DataTable. Esto, por supuesto, es solo un ejemplo del uso de la información que contiene el contenedor.

En nuestros casos de uso típicos, este rendimiento se triplicó en comparación con los métodos de DataTable , y aunque ya habíamos evitado todas las llamadas redundantes, y todas las demás optimizaciones obvias en el código de acceso a la tabla de datos tradicional.


Las operaciones de tabla de datos son muy lentas en QTP. Así que use las fórmulas de Excel en su Datatable para acelerar la operación.