Schnellere Alternative zu StrD()?

Für allgemeine Fragen zur Programmierung mit PureBasic.
DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

@NicTheQuick
Ja es sind eine Million Zeilen.

Code: Alles auswählen

...
999996,0.01399989987845,2.1249999921
999997,0.01399991987845,2.1416666587
999998,0.01399993987845,2.1583333254
999999,0.01399995987845,2.1583333254
1000000,0.01399997987845,2.1749999921
@STARGÅTE
#iSaveDataNbDecima ist 14, und die Werte sind eher klein, stammen von einem Oszilloskop.
Wenn ich die eine Zeile kopiere, dauert es 21 Sekunden.

Mit der Idee von sibru habe ich nur schnell probiert, da spare ich 4 Sekunden ein. Aber das mit dem Dezimalpunkt und negativen Werten hat noch nicht funktioniert, aber als Test reicht es.

Peter
matbal
Beiträge: 246
Registriert: 30.03.2011 20:53

Re: Schnellere Alternative zu StrD()?

Beitrag von matbal »

Ich habe gerade printf() im englischen Forum entdeckt.
Damit ließe sich eine ganze Ausgabezeile in einem Rutsch zusammenbauen. Vielleicht bringt das was?

https://www.purebasic.fr/english/viewto ... 13&t=71083
Michael Vogel
Beiträge: 71
Registriert: 16.03.2006 11:20

Re: Schnellere Alternative zu StrD()?

Beitrag von Michael Vogel »

matbal hat geschrieben:Ich habe gerade printf() im englischen Forum entdeckt.
Damit ließe sich eine ganze Ausgabezeile in einem Rutsch zusammenbauen. Vielleicht bringt das was?

https://www.purebasic.fr/english/viewto ... 13&t=71083
Also hier bringt printf doch einiges....

Code: Alles auswählen

Global _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8

Threaded *__Result
*__Result = AllocateMemory(4096)

Macro Printf(Result, sFormat, _arg1, _arg2=_arg2, _arg3=_arg3, _arg4=_arg4, _arg5=_arg5, _arg6=_arg6, _arg7=_arg7, _arg8=_arg8)
	CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		ImportC ""
			CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
				__Printf_#MacroExpandedCount(*pResult, nBytes, sFMT.p-UTF8, sBugfix.s, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8) As "_snprintf"
			CompilerElse
				__Printf_#MacroExpandedCount(*pResult, nBytes, sFMT.p-UTF8, sBugfix.s, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8) As "__snprintf"
			CompilerEndIf
		EndImport
	CompilerCase #PB_OS_MacOS
		ImportC ""
			__Printf_#MacroExpandedCount(*pResult, nBytes, sFMT.p-UTF8, sBugfix.s, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8) As "_snprintf"
		EndImport
	CompilerCase #PB_OS_Linux
		ImportC ""
			__Printf_#MacroExpandedCount(*pResult, nBytes, sFMT.p-UTF8, sBugfix.s, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8) As "snprintf"
		EndImport
	CompilerEndSelect
	__Printf_#MacroExpandedCount(*__Result, MemorySize(*__Result) - 1, "%s" + sFormat, #Empty$, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8)
	Result = PeekS(*__Result, -1, #PB_UTF8)
EndMacro


text.s
t1.s
t2.s
sComma.s=","
#iSaveDataNbDecimal=14
#sChar0="0"
EndOfLine.s=#CRLF$

iCount=1
rPoint1=2
rPointTime1.d=1/7


t-ElapsedMilliseconds()
For i=0 To 999999
	;Text = StrD(rPointTime1, #iSaveDataNbDecimal)
	;Text = RTrim(StrD(rPointTime1, #iSaveDataNbDecimal),#sChar0)
	;Text = Str(iCount) + sComma + RTrim(StrD(rPointTime1, #iSaveDataNbDecimal), #sChar0) + sComma + StrD(rPoint1) + EndOfLine

	;t1= Str(rPointTime1)
	;t2=sComma + StrD(rPoint1) + EndOfLine
	;Text = Str(iCount) + sComma + t1 + t2

	;Printf(Text,"%i,%.16f,%i",rPoint1,rPointTime1.d,iCount.i)

	Printf(Text,"%.16f",rPointTime1.d)
	Text=	Str(rPointTime1)+sComma + RTrim(Text,#sChar0) +sComma+ Str(rPointTime1)+EndOfLine

	;Text = Str(iCount) + sComma + RTrim(Str(rPointTime1), #sChar0) + sComma + StrD(rPoint1) + EndOfLine
Next i
t+ElapsedMilliseconds()

MessageRequester(Text,Str(t)+"ms")

DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

Ich schaffe es nicht, das Makro Printf() in einem Modul zu verwenden. Für einen Test habe ich auch versucht es aufzuteilen, wegen dem ImportC. Aber entweder wird das Makro nicht gefunden, oder __Printf_.

Peter
DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

Mit dem Beispiel von Michael ist Printf() immer langsamer, wenn ich die Formatierung so verwende wie in meinem Code. Ich habe natürlich kompiliert getestet.

Peter
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Hast du mal versucht jede Zeile einzeln in die Datei zu schreiben anstatt sie vorher zusammenzubauen und dann reinzuschreiben?
Sprich mach aus dem

Code: Alles auswählen

sText = Str(iCount) + sComma + RTrim(StrD(rPointTime1, #iSaveDataNbDecimal), #sChar0) + sComma + StrD(rPoint1) + EndOfLine
iCount + 1
sText + Str(iCount) + sComma + RTrim(StrD(rPointTime2, #iSaveDataNbDecimal), #sChar0) + sComma + StrD(rPoint2) + EndOfLine
rPointNumber + 1

WriteString(iFileNumber, sText)
das hier

Code: Alles auswählen

WriteString(iFileNumber, Str(iCount) + sComma + RTrim(StrD(rPointTime1, #iSaveDataNbDecimal), #sChar0) + sComma + StrD(rPoint1))
iCount + 1
WriteString(iFileNumber, Str(iCount) + sComma + RTrim(StrD(rPointTime2, #iSaveDataNbDecimal), #sChar0) + sComma + StrD(rPoint2))
rPointNumber + 1
Da wäre ich jetzt neugierig inwiefern das hilft.
Bild
DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

@NicTheQuick
Das ist um ~ 0,8 Sekunden schneller.

Peter
DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

Ich kann auch die Daten binär speichern, also die berechneten Werte als Float, da dauert die gleiche Prozedur 0,3 Sekunden. Nur so als Vergleich.

Peter
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von NicTheQuick »

Ich habe mal zum Spaß eine eigene StrD-Funktion geschrieben.
Ein wichtiger Hinweis dazu: Sie kann aktuell nur 50 Stellen vor und 49 Stellen nach dem Komma. Möchte man mehr, muss man StrD2_Buffer mit mehr Leerzeichen füllen und in der ersten Zeile in der Procedure die 50 durch eine größere Zahl ersetzen. Am besten die Hälfte der Anzahl von Leerzeichen.

In einem kleinen Test braucht die Funktion nur 25% der Zeit des originalen StrD und man kann sich sogar das RTrim sparen, weil die Funktion alle Nullen am Ende automatisch entfernt.

Code: Alles auswählen

Threaded StrD2_Buffer.s = "                                                                                                    "
Procedure.s StrD2(value.d, nbDecimals.i = 10)
	Protected *c.Character = @StrD2_Buffer + SizeOf(Character) * 50
	Protected.Character *c1 = *c, *c2 = *c, *e = *c - SizeOf(Character)
	Protected d.i, neg.i = Bool(value < 0.0)
	value = Abs(value)
	Protected.d int2, int = Round(value, #PB_Round_Down)
	value - int
	
	; First the part before the digit
	If int > 0.0
		While int >= 1.0
			int2 = Round(int / 10, #PB_Round_Down)
			*c1 - SizeOf(Character)
			*c1\c = '0' + (10 + Int(int - 10.0 * int2) % 10) % 10
			int = int2
		Wend
	Else
		*c1 - SizeOf(Character)
		*c1\c = '0'
	EndIf
	*c1 - SizeOf(Character)
	*c1\c = ' '
	*c1 + SizeOf(Character)
	
	value * 10.0
	
	; Now for the fractional part
	For d = 2 To nbDecimals
		int = Round(value, #PB_Round_Down)
		*c2 + SizeOf(Character)
		*c2\c = '0' + Int(int)
		If *c2\c > '0'
			*e = *c2
		EndIf
		value - int
		value * 10.0
	Next
	
	; Now the complicated rounding part
	int = Round(value, #PB_Round_Nearest)
	If int < 10.0 ; no carry after rounding
		*c2 + SizeOf(Character)
		*c2\c = '0' + Int(int)
		If *c2\c > '0'
			*e = *c2
		EndIf
	
	Else ;carry
		; set every 9 at the end to 0
		While *c2\c = '9' And *c2 > *c
			*c2\c = '0'
			*c2 - SizeOf(Character)
		Wend
		; increase the non-9 digit by 1
		If *c2 > *c ; if we are still in the fractional part
			*c2\c + 1
			*e = *c2
		
		Else ; if we need to round up the integer part
			*e = *c - SizeOf(Character)
			*c2 = *e
			; again turn the 9 to a 0
			While *c2\c = '9'
				*c2\c = '0'
				*c2 - SizeOf(Character)
			Wend
			; and increase the last remaining non-9 digit by 1
			If *c2\c = ' '
				*c2\c = '1'
				*c1 = *c2
			Else
				*c2\c + 1
			EndIf
		EndIf
	EndIf
	
	; Add a sign if necessary
	If neg
		*c1 - SizeOf(Character)
		*c1\c = '-'
	EndIf
	*c\c = '.'
	
	ProcedureReturn PeekS(*c1, 1 + (*e - *c1) / SizeOf(Character))
EndProcedure

Debug StrD2(-0.0099890007, 2)
Debug StrD2(-123.4569, 3)
Debug StrD2(9.999, 2)
Debug StrD2(79.999, 2)
Debug "10^23 with StrD2:"
Debug StrD2(Pow(11,23), 14)
Debug "10^23 with StrD:"
Debug StrD(Pow(11,23), 14)
Interessanterweise scheinen große Zahlen wie 10^23 nach meiner Methode auch näher an der Wahrheit zu sein als das, was StrD liefert (siehe Beispiel im Code).

Edit:
13:49 Bugfix
Bild
DePe
Beiträge: 156
Registriert: 26.11.2017 16:17
Wohnort: Wien
Kontaktdaten:

Re: Schnellere Alternative zu StrD()?

Beitrag von DePe »

Hallo NicTheQuick,

das Speichern ist um 1/3 schneller, also 10 Sekunden, anstatt 15 Sekunden.
Die gespeicherten Werte stimmen überein, unterscheiden sich zwar, aber sie sind richtig.

Jetzt bin ich ziemlich erstaunt, dass das möglich ist.

Danke, ich werde versuchen die Prozedur zu verstehen.

Peter

Edit: Die Zeitwerte sind sogar 'genauer', mit keinen oder weniger Rundungsfehlern bei den letzten Nachkommastellen.

Code: Alles auswählen

StrD():
24621,-0.005507600028,3.0083333254
24622,-0.005507580005,3.0083333254
24623,-0.005507559981,3.0083333254
24624,-0.005507539958,3.0083333254
24625,-0.005507519934,3.0083333254
24626,-0.005507499911,3.0249999921

StrD2():
24621,-0.005507600003,3.0083333254
24622,-0.005507580003,3.0083333254
24623,-0.005507560003,3.0083333254
24624,-0.005507540003,3.0083333254
24625,-0.005507520003,3.0083333254
24626,-0.005507500003,3.0249999921
Zuletzt geändert von DePe am 04.11.2020 13:43, insgesamt 2-mal geändert.
Antworten