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
Laufzeitverhalten DBFCDX VO/X#
Moderator: wriedmann
Laufzeitverhalten DBFCDX VO/X#
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?
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
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Laufzeitverhalten DBFCDX VO/X#
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
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
Laufzeitverhalten DBFCDX VO/X#
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?
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.20, X#2.20.0.3)
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
Laufzeitverhalten DBFCDX VO/X#
Hi Jörg
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.
Just an update on the issue you reported, here is some more info: https://github.com/X-Sharp/XSharpPublic/issues/711comitas2 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.
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
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Laufzeitverhalten DBFCDX VO/X#
Moin Namensvetter,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
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.
Laufzeitverhalten DBFCDX VO/X#
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.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?
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
Laufzeitverhalten DBFCDX VO/X#
Here the relevant parts of the import (dtaBelegExchange.prg in ASBau ExImport)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?
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
Laufzeitverhalten DBFCDX VO/X#
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
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
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Laufzeitverhalten DBFCDX VO/X#
Moin,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
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