Laufzeitverhalten DBFCDX VO/X#

Deutschsprachiges X#-Forum – German language forum

Moderator: wriedmann

hsg
Posts: 10
Joined: Mon Jul 26, 2021 6:39 am

Laufzeitverhalten DBFCDX VO/X#

Post by hsg »

Moin,

ich bin dabei, eine von Chris und Robert nach X# portierte VO2.8-Anwendung, weiterzuentwickeln und stoße dabei gerade auf ein Problem mit dem Laufzeitverhalten der beiden Apps. An einer Stelle wird aus einer DA83 (spezielles GAEB-Format mit dem Unternehmen Bauleistungen untereinander austauschen können) ein Beleg direkt in eine FoxPro-Datenbank (DBFCDX) eingelesen (in diesem Fall reden wir von 17340 Zeilen). In der VO2.8-Applikation dauert dieses Einlesen ungefähr 2min (eher weniger...) auf der gleichen Maschine braucht die X#-Applikation hingegen über 5min.
Ich vermutete zuerst, dass das Einlesen aus der Datei so dauert, daher habe ich in der X#-Version das Einlesen der einzelnen Zeilen durch reine Speicheroperationen (MemoRead der gesamten Datei vorweg und dann mittels MemoLine die einzelnen Zeilen ausschneiden) optimiert. Leider hat das keine Abhilfe gebracht.
Ist der Datenbanktreiber in X# wirklich so viel langsamer? Oder an welchen Stellschrauben kann ich noch drehen?

Gruß
Jörg
User avatar
Chris
Posts: 4562
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Laufzeitverhalten DBFCDX VO/X#

Post by Chris »

Hi Jörg,

Such a speed difference is probably within the expect able limits, due to the different nature of the two RDDs (managed/Win32). But still, it might be possible to speed your specific case considerably. Can you share the code of this porting routine?
Chris Pyrgas

XSharp Development Team test
chris(at)xsharp.eu
comitas2
Posts: 48
Joined: Thu Jul 18, 2019 9:13 am
Location: Germany

Laufzeitverhalten DBFCDX VO/X#

Post by comitas2 »

Hallo Jörg,
wahrscheinlich nutzen nur "Jörg's" DBFCDX. Alle Anderen nutzen SQL oder ADS.
Ich habe wegen meinen Laufzeitproblem (siehe https://www.xsharp.eu/forum/german/2575 ... iggestellt ) meine Portierung nach X-Sharp erstmal auf Eis gelegt.
Gruß Jörg
FFF
Posts: 1522
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Laufzeitverhalten DBFCDX VO/X#

Post by FFF »

Jörg,
wenn man mir anbietet, für lau meinen Code zu checken, ob's vielleicht einen Dreh gibt, mein Problem zu lösen, finde ich es eher seltsam, darauf mit "ich habe das Projekt auf Eis gelegt" zu reagieren...

Wir wissen nichts, WIE Dein Import läuft - 17k Appends? Was wäre, wenn Du den Index vor dem Import killst, importierst und dann neu schreibst?
Regards
Karl
(on Win8.1/64, Xide32 2.19, X#2.19.0.2.)
User avatar
Chris
Posts: 4562
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Laufzeitverhalten DBFCDX VO/X#

Post by Chris »

Hi Jörg
comitas2 wrote: wahrscheinlich nutzen nur "Jörg's" DBFCDX. Alle Anderen nutzen SQL oder ADS.
Ich habe wegen meinen Laufzeitproblem (siehe https://www.xsharp.eu/forum/german/2575 ... iggestellt ) meine Portierung nach X-Sharp erstmal auf Eis gelegt.
Just an update on the issue you reported, here is some more info: https://github.com/X-Sharp/XSharpPublic/issues/711

So performance problem is confirmed and Robert already knows what is probably causing it, will just require some time for a fix to be implemented.
Chris Pyrgas

XSharp Development Team test
chris(at)xsharp.eu
hsg
Posts: 10
Joined: Mon Jul 26, 2021 6:39 am

Laufzeitverhalten DBFCDX VO/X#

Post by hsg »

comitas2 wrote: Hallo Jörg,
wahrscheinlich nutzen nur "Jörg's" DBFCDX. Alle Anderen nutzen SQL oder ADS.
Ich habe wegen meinen Laufzeitproblem (siehe www.xsharp.eu/forum/german/2575-ist-der ... iggestellt ) meine Portierung nach X-Sharp erstmal auf Eis gelegt.
Gruß Jörg
Moin Namensvetter,

scheinbar :( Habe deinen Artikel erst gesehen, als ich meinen geschrieben habe. Das Index-Problem habe ich aber bei mir zumindest nicht feststellen können. Aber die Applikation ist so alt und groß, da wird eine Umstellung auf SQL wohl nicht so ganz einfach.
hsg
Posts: 10
Joined: Mon Jul 26, 2021 6:39 am

Laufzeitverhalten DBFCDX VO/X#

Post by hsg »

FFF wrote:Jörg,
wenn man mir anbietet, für lau meinen Code zu checken, ob's vielleicht einen Dreh gibt, mein Problem zu lösen, finde ich es eher seltsam, darauf mit "ich habe das Projekt auf Eis gelegt" zu reagieren...

Wir wissen nichts, WIE Dein Import läuft - 17k Appends? Was wäre, wenn Du den Index vor dem Import killst, importierst und dann neu schreibst?
Achtung, du scheinst hier gerade zwei Leute zu verwechseln ;) Hier schreiben gerade zwei unterschiedliche Jörgs! Mein Projekt ist noch nicht auf Eis gelegt, das liegt zudem auch gar nicht in meiner Hand.

Mit dem Index killen wird nicht gehen, da auch zwischendurch Summen über Teilabschnitte berechnet werden. An der Stelle brauche ich die Indizes.

Gruß
Jörg
hsg
Posts: 10
Joined: Mon Jul 26, 2021 6:39 am

Laufzeitverhalten DBFCDX VO/X#

Post by hsg »

Chris wrote:Hi Jörg,

Such a speed difference is probably within the expect able limits, due to the different nature of the two RDDs (managed/Win32). But still, it might be possible to speed your specific case considerably. Can you share the code of this porting routine?
Here the relevant parts of the import (dtaBelegExchange.prg in ASBau ExImport)

Code: Select all

METHOD pshImp83( )  
   LOCAL oScr			AS dlgProgress
   LOCAL oDialog		AS OpenDialog
   LOCAL nLen			AS DWORD
   LOCAL nZeichen		AS DWORD
   LOCAL cZeile			AS STRING  
   LOCAL cInh			AS STRING   
   LOCAL nLines			AS INT 
   LOCAL i				AS INT
      
   	SELF:lFehler				:= FALSE
   	SELF:OZ_Maske				:= ""
   	SELF:nStruktur				:= 0 
   	SELF:lEinlesPreis			:= FALSE 
   	SELF:lBelInBel				:= FALSE 		// Beleg in Gewerke aufteilen
   
   	// Auswahlfenster Hidden
   
   	// Belegkopf erzeugen
   	IF Empty( SELF:cBeleg )
   		SELF:Belegkopf( )
   	ELSE
   		IF YesNo( "Soll der einzulesende Beleg als Gewerk eingefügt werden.", TRUE)
   			SELF:lBelInBel		:= TRUE
   		ENDIF
   	ENDIF
   	SELF:oDbf:SetOrder( "Beleg" )
   	IF SELF:oDbf:Seek( SELF:cBeleg )
   		SELF:nRec				:= SELF:oDbf:RecNo
   		SELF:oDbf:RLOCK( SELF:nRec )
   	ELSE
   		Warning( "Konnte Beleg "+ SELF:cBeleg +"nicht finden. Abbruch!")
   		RETURN SELF
   	ENDIF
   	
   	// Preisübernahme aus LV?
   	IF YesNo( "Preise und Kalkulationen aus dem LV "+AllTrim( SELF:cBlankText )+" übernehmen?", TRUE )
   		SELF:lEinlesPreis		:= TRUE
   	ENDIF
   
   	// Dateiauswahl
   	oDialog						:= OpenDialog{ SELF, "*.D83" } 
   	oDialog:Setstyle( OFN_NOCHANGEDIR, TRUE )   
   	oDialog:InitialDirectory	:= "C:"
   	oDialog:Show( ) 
   	SELF:cFile					:= oDialog:filename
   	oDialog						:= NULL_OBJECT
   
   	IF .NOT. Empty( SELF:cFile )  .AND. .NOT. Empty( SELF:cBeleg )  
   		SELF:nHandle 			:= FOpen2(SELF:cFile, FO_READ)
   		IF SELF:nHandle = F_ERROR
   			Warning( DosErrString(FError()) )
   		ENDIF
   		cInh					:= MemoRead( SELF:cFile)
   		nLines					:= MLCount(cInh, 80)
   		oScr					:= dlgProgress{ AppWnd( ) , FALSE  }
   		oScr:Max				:= nLines  
   		oScr:Caption			:= "Einlesen in Beleg: " +SELF:cBeleg
   		oScr:Message			:= "Importstatus - Einlesen der Angebotsaufforderung"
   		CenterDialog( oScr )
   		oScr:Show( )
   
        // Einlesen der Datei
   		SELF:lInsLos			:= FALSE
   		SELF:nInsLos			:= 0
   
        FOR i := 1 UPTO nLines
            cZeile				:= MemoLine(cInh, 80, i)
           	oScr:Inc( ) 
           	nZeichen 			:= Len(cZeile)
   	        SELF:Import( cZeile, "83" )
   	        IF Left( cZeile, 2 ) == "99"
   	        	EXIT
   	        ENDIF
   	        IF nZeichen != 80  .OR. SELF:lFehler == TRUE 
   	        	Warning("Einlesevorgang abgebrochen!")
   	        	EXIT
   	        ENDIF
        NEXT
   		oScr:EndDialog( 1 )
   		// Datei wieder schließen.
   		FClose( SELF:nHandle ) 
   	ENDIF
RETURN SELF
   
METHOD Import( cZeile, cArt)  
   LOCAL 	cTemp		AS STRING 
   LOCAL 	cNr			AS STRING
   //LOCAL	cText		AS STRING
   LOCAL 	i			AS DWORD
   LOCAL	nPreis		AS FLOAT
   LOCAL	nAnz		AS DWORD
   LOCAL	nEPreis		AS FLOAT
   LOCAL	nRecno		AS DWORD
    
   	// Einleseschleife GAEB 
   	DO CASE
   		// Beginn der vertraglichen Regelungen
   		CASE Left( cZeile, 2 ) == "T0"
   //			SELF:cVertTxt			:= ""
   
   		// Texte einer vertraglichen Regelung
   		CASE Left( cZeile, 2 ) == "T1"
   			SELF:cVertTxt 			+= SubStr( cZeile, 3, 72 ) + UMBRUCH
   
   		// Ende der vertraglichen Regelungen
   		CASE Left( cZeile, 2 ) == "T9"
   			SELF:oDbf:Beschr		:= Oem2Ansi( SELF:cVertTxt )
   			SELF:cVertTxt			:= ""
   
   		// Eröffnungssatz Leistungsverzeichnis
   		CASE Left( cZeile, 2 ) == "00"
   			// Falls Zeilenart "T9" nicht kam
   			IF .NOT. Empty( SELF:cVertTxt ) 
   				SELF:oDbf:Beschr		+= Oem2Ansi( SELF:cVertTxt )
   				SELF:cVertTxt			:= ""				
   			ENDIF		
   			// Positionsnummernmaske (OZMaske einlesen)
   			SELF:OZ_Maske				:= SubStr( cZeile, 63, 9)              
   			SELF:lLos					:= IIF( SubStr( cZeile, 74, 1 ) == "X", TRUE, FALSE ) 
   			IF .NOT. (SubStr( cZeile, 11, 2 ) == "81"  .OR. ;
   				SubStr( cZeile, 11, 2 ) == "83"  .OR. ;
   				SubStr( cZeile, 11, 2 ) == "84"  .OR. ; 
   				SubStr( cZeile, 11, 2 ) == "85"  .OR. ;  
   				SubStr( cZeile, 11, 2 ) == "86")
   				Warning("Diese Datei entspricht nicht der Datenart "+cArt+"!")
   				SELF:lFehler			:= TRUE
   			ELSE
   				SELF:oDbf:OZMaske		:= SELF:OZ_Maske
  			ENDIF
   
   		//Information Leistungsverzeichnis
   		CASE Left( cZeile, 2 ) == "01"
   			SELF:lPreise				:= IIF(.NOT. Empty(SubStr( cZeile, 72, 1 )), TRUE, FALSE )
   
   		// Information Projekt
   		CASE Left( cZeile, 2 ) == "02"
   			// Belegname
   			IF .NOT. Empty( Oem2Ansi( SubStr( cZeile, 3, 60 ))) 
   				SELF:oDbf:BauText		:= Oem2Ansi( SubStr( cZeile, 3, 60 ))
   			ENDIF
   
   		// Information Auftraggeber
   		CASE Left( cZeile, 2 ) == "03"
   			SELF:oDbf:K_Name1				:= Oem2Ansi( SubStr( cZeile, 3, 60 ))
   
   		// Information Bieter/Auftragnehmer
   		CASE Left( cZeile, 2 ) == "04"
   			// Stammdaten
   
   		// Bezeichnung der Einheitspreisaufgliederung
   		CASE Left( cZeile, 2 ) == "06"
   
   		// Zuordnung Leistungsverzeichnis
   		CASE Left( cZeile, 2 ) == "07"
   			// ??
   
   		// Kennzeichen der Währung
   		CASE Left( cZeile, 2 ) == "08"
   			SELF:oDbf:WaeKu				:= SubStr( cZeile, 3,  6 )
   			SELF:cWhrg					:= AllTrim( Left( SubStr( cZeile, 3,  6 ), 3 ))
   			// Muß gerechnet werden?
   			SELF:lEuro					:= FALSE
   			SELF:lDM					:= FALSE 
   				DO CASE
   					CASE AllTrim( SELF:oOwner:cWhrg ) == "EUR"  .AND. SELF:cWhrg != "EUR"
   						SELF:lEuro				:= TRUE 
   					CASE AllTrim( SELF:oOwner:cWhrg ) == "EUR" .AND. SELF:cWhrg == "EUR"
   						SELF:lEuro				:= FALSE 
   					CASE AllTrim( SELF:oOwner:cWhrg ) == "DM"  .AND. SELF:cWhrg != "DM"
   						SELF:lDM				:= TRUE 
   					CASE AllTrim( SELF:oOwner:cWhrg ) == "DM"  .AND. SELF:cWhrg == "DM"
   						SELF:lDM				:= FALSE 
   				ENDCASE				
 			    SELF:oDbf:WaeBez			:= Oem2Ansi( SubStr( cZeile, 9, 50 ))
   
   		// Beginn des Loses
   		CASE Left( cZeile, 2 ) == "10"
   			IF cArt == "83"
   				IF SELF:nStruktur	!= 0
   					Warning("Strukturfehler in der GAEB Datei aufgetreten.")
   					SELF:lFehler				:= TRUE
   				ELSE
   					SELF:lInsLos				:= TRUE
   					SELF:cLosNr 				:= SubStr( cZeile, 3,  2 )
   					IF .NOT. SELF:oSumm:Seek( SELF:cBeleg + SELF:cLosNr + Space( 12 ))
   						IF SELF:AppendSum( )
   							SELF:oSumm:Los		:= SELF:cLosNr
   							SELF:oSumm:Bezeich	:= Oem2Ansi( SubStr( cZeile, 5, 44 ))
   						ENDIF
   			            SELF:AppendLine( )
   						SELF:oRech:Los			:= SELF:cLosNr
   						SELF:oRech:Gewerk		:= SELF:cGewerk
   						SELF:oRech:Titel		:= SELF:cTit
   						SELF:oRech:UTitel		:= SELF:cUTit
   						SELF:oRech:PosText		:= Oem2Ansi( SubStr( cZeile, 5, 44 )) 
   						SELF:oRech:ZeilenArt	:= "L "
   					ELSE
   						Warning("Losdefinition bereits vorhanden.")
   						SELF:oSumm:Bezeich		:= Oem2Ansi( SubStr( cZeile, 5, 44 ))
   					ENDIF
   				ENDIF
   			ELSE
   				SELF:cLosNr 					:= SubStr( cZeile, 3,  2 )				
   			ENDIF
   
   		// Beginn der LV-Gruppe
   		CASE Left( cZeile, 2 ) == "11"
   			// Einfügen des Loses
   //			IF SELF:nInsLos == 1
   //				SELF:lInsLos				:= FALSE
   //				SELF:nInsLos				:= 0
   //			ENDIF
   //			IF SELF:lInsLos
   //				SELF:nInsLos				:= 1
   //			ELSE
   				// einfügen von Titeln und Gewerk
   				SELF:nStruktur				+= 1
   				SELF:Splitten( SubStr( cZeile, 3, 9 ), "11"	) // OZ zerlegen
   				IF .NOT. SELF:oSumm:Seek( SELF:cBeleg + SELF:cLosNr + SELF:cGewerk + SELF:cTit + SELF:cUTit)
   					SELF:AppendSum( )
   					SELF:oSumm:Blankett		:= SELF:cBlank
   					SELF:oSumm:Los			:= SELF:cLosNr
   					SELF:oSumm:Gewerk		:= SELF:cGewerk
   					SELF:oSumm:Titel		:= SELF:cTit
   					SELF:oSumm:UTitel		:= SELF:cUTit
   	                SELF:AppendLine( )
   					SELF:oRech:Los			:= SELF:cLosNr
   					SELF:oRech:Gewerk		:= AllTrim( SELF:cGewerk )
   					SELF:oRech:Titel		:= AllTrim( SELF:cTit )
   					SELF:oRech:UTitel		:= AllTrim( SELF:cUTit )
   					// Zeilentyp ermitteln
   					DO CASE
   						CASE SELF:nStruktur == 1
   							DO CASE 
   								CASE SELF:nMax == 3
   									SELF:oRech:ZeilenArt	:= "G "
   								CASE SELF:nMax == 2  .AND. Empty( SELF:cUTit )
   									SELF:oRech:ZeilenArt	:= "T "
   								CASE SELF:nMax == 2  .AND. .NOT. Empty( SELF:cUTit )
   									SELF:oRech:ZeilenArt	:= "U " 
   								CASE SELF:nMax == 1
   									SELF:oRech:ZeilenArt	:= "T "
   							ENDCASE
   						CASE SELF:nStruktur == 2
   							DO CASE 
   								CASE SELF:nMax == 3
   									SELF:oRech:ZeilenArt	:= "T "
   								CASE SELF:nMax == 2
   									SELF:oRech:ZeilenArt	:= "U "
   							ENDCASE
    
   						CASE SELF:nStruktur == 3
   							SELF:oRech:ZeilenArt			:= "U "
   					ENDCASE 
   //					SELF:oRech:Skip( 0 )
   //				ELSE
   //					Warning("Beleg-Strukturdefinition bereits vorhanden.")
   //				ENDIF
   			ENDIF
   
   		// Bezeichnung der LV-Gruppe
   		CASE Left( cZeile, 2 ) == "12"
   			cTemp 						:= AllTrim( SELF:oSumm:Bezeich ) 
   			SELF:oSumm:Bezeich			:= IIF( Empty( cTemp ), Oem2Ansi( SubStr( cZeile, 3, 40)), cTemp + " " + Oem2Ansi( SubStr( cZeile, 3, 40)) )
   			SELF:oRech:PosText			:= IIF( Empty( cTemp ), Oem2Ansi( SubStr( cZeile, 3, 40)), cTemp + " " + Oem2Ansi( SubStr( cZeile, 3, 40)) )
   
   		// Beginn eines Hinweistextes
   		CASE Left(cZeile,2) == "20"
   			// ?? wohin damit???
   
   		// Beginn einer Teilleistung (Position)
   		CASE Left(cZeile,2) == "21"
   			IF cArt	== "83"
   				SELF:lFirstPos				:= TRUE
   				SELF:Splitte
User avatar
wriedmann
Posts: 3644
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Laufzeitverhalten DBFCDX VO/X#

Post by wriedmann »

Hallo Jörg & Jörg,
das DBFCDX RDD wird nicht nur von Euch beiden verwendet, sondern ist unter anderem auch für mich wesentlich. Dasselbe gilt für viele VFP-Kollegen.
Sicher mache ich neue Sachen nur mehr mit SQL (wenn es überhaupt geht). Das hat aber überhaupt nichts mit der Performance von DBFCDX zu tun, sondern mit der technischen Überlegenheit der diversen SQL-Engines. Nur habe ich in ca. 90% aller Fälle gar nicht mal die Wahl, bzw. in vielen neuen Applikationen läuft es auf einen Mix aus SQL und DBFCDX raus (in manchen sogar mit mehreren SQL-Engines), weil einfach auf die Daten bestehender und teils sehr alter Applikationen zugegriffen werden muss.

Was jetzt die Performance und die Stabilität des DBFCDX-RDDs betrifft: die fehlende Performance ist, wie bereits Chris geschrieben hat, ein bekanntes Problem, und es wurde auch schon sehr viel dran gemacht.

Daher nochmal der Aufruf: liefert bitte mit möglichst wenig Aufwand Testcases, damit das Problem nachvollzogen und behoben werden kann!

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
hsg
Posts: 10
Joined: Mon Jul 26, 2021 6:39 am

Laufzeitverhalten DBFCDX VO/X#

Post by hsg »

wriedmann wrote:Hallo Jörg & Jörg,
das DBFCDX RDD wird nicht nur von Euch beiden verwendet, sondern ist unter anderem auch für mich wesentlich. Dasselbe gilt für viele VFP-Kollegen.
....
Daher nochmal der Aufruf: liefert bitte mit möglichst wenig Aufwand Testcases, damit das Problem nachvollzogen und behoben werden kann!

Wolfgang
Moin,
beruhigend zu hören, dass es noch andere gibt! Allerdings ist ein Testcase hier nicht ganz so einfach, da ein ganzer Haufen von Datenbanken involviert sind. Aber ich versuche es die Tage mal.
Mir ging es erst mal vordringlich darum zu wissen, ob es tatsächlich an dem Datenbanktreiber liegt oder ich in eine andere Richtung suchen muss.

Gruß
Jörg
Post Reply