AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Liste der Anhänge anzeigen (Anzahl: 1)
okay, war ne doofe Frage ...
hätte ich auch gleich drauf kommen können ... siehe Anhang (Bild) ... |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Vielen Dank für die Infos und Hilfen!
Ich habe mir mal angehen, wie TSystemTime realisiert ist. Nur mal eine kurze Übersicht, falls es jemanden direkt interessiert:
Delphi-Quellcode:
Das braucht natürlich einiges an Speicherplatz. Ich habe mich jetzt für GetTickCount entschieden, das die Systemlaufzeit als Cardinalwert zurück liefert.
PSystemTime = ^TSystemTime;
_SYSTEMTIME = record wYear: Word; wMonth: Word; wDayOfWeek: Word; wDay: Word; wHour: Word; wMinute: Word; wSecond: Word; wMilliseconds: Word; end; {$EXTERNALSYM _SYSTEMTIME} TSystemTime = _SYSTEMTIME; SYSTEMTIME = _SYSTEMTIME; {$EXTERNALSYM SYSTEMTIME} Als Korrektur will ich die neue Unit nochmal hier einbinden. Vielleich hilf es ja mal jemandem.
Delphi-Quellcode:
unit myGuid;
interface uses System.Classes; type PmyGUID = ^TmyGUID; TmyGUID = record //: 12 Byte / 96 Bit C : Cardinal; //: 4 Byte / 32 Bit TS1: Cardinal; //: 4 Byte / 32 Bit TS2: Cardinal; //: 4 Byte / 32 Bit class function Create(const aGuid: PmyGUID): TmyGUID; overload; static; class function Create(const aGuid: String): TmyGUID; overload; static; class function Create(const aGuid: TmyGUID): TmyGUID; overload; static; class function CreateEmpty: TmyGUID; static; class function CreateNew: TmyGUID; static; class function StringIsValidGuid(const aGuid: String): Boolean; static; class operator Equal(const Left, Right: TmyGUID): Boolean; class operator NotEqual(const Left, Right: TmyGUID): Boolean; inline; function GetHashCode: Integer; function IsEmpty: Boolean; function ToString: String; procedure DoEmpty; procedure DoNew; procedure FromString(const aGuid: String); procedure ReadFromStream(const aStream: TMemoryStream); procedure WriteToStream(const aStream: TMemoryStream); end; implementation uses System.SysUtils, System.Hash, Winapi.Windows; const MaxCardinal = 4294967295; var customTS1: Cardinal = 0; customTS2: Cardinal = 0; customC : Cardinal = 0; {: **************************************** TmyGUID *************************************** :} {: ---------------------------------------- default --------------------------------------- :} //: 2023-10-30 class function TmyGUID.Create(const aGuid: String): TmyGUID; begin if (aGuid <> '') then begin Result := TmyGUID.CreateEmpty; Result.FromString(aGuid) end else begin Result := TmyGUID.CreateNew; end; end; //: 2023-10-30 class function TmyGUID.Create(const aGuid: TmyGUID): TmyGUID; begin Result := aGuid; end; //: 2023-10-30 class function TmyGUID.Create(const aGuid: PmyGUID): TmyGUID; begin Result := aGuid^; end; //: 2023-10-30 class function TmyGUID.CreateEmpty: TmyGUID; begin Result.TS1 := 0; Result.TS2 := 0; Result.C := 0; end; //: 2023-10-30 class function TmyGUID.CreateNew: TmyGUID; begin customTS2 := GetTickCount; Result.TS1 := customTS1; Result.TS2 := customTS2; Result.C := customC; if (customC = MaxCardinal) then customC := 0 else Inc(customC); end; //: 2023-10-30 class function TmyGUID.StringIsValidGuid(const aGuid: String): Boolean; var I: Integer; C: Char; begin //: #FFFFFFFF-FFFFFFFF-FFFFFFFF //: 123456789012345678901234567 - > Length = 27 if (Length(aGuid) = 27) then begin Result := True; for I := 1 to Length(aGuid) do begin C := aGuid[I]; case I of 1: if C <> '#' then begin Result := False; Break; end; 10, 19: if C <> '-' then begin Result := False; Break; end; else if not CharInSet(C, ['0'..'9', 'A'..'F']) then begin Result := False; Break; end; end; end; end else Result := False; end; //: 2023-10-30 class operator TmyGUID.Equal(const Left, Right: TmyGUID): Boolean; begin Result := ((Left.TS1 = Right.TS1) and (Left.TS2 = Right.TS2) and (Left.C = Right.C)); end; //: 2023-10-30 class operator TmyGUID.NotEqual(const Left, Right: TmyGUID): Boolean; begin Result := ((Left.TS1 <> Right.TS1) or (Left.TS2 <> Right.TS2) or (Left.C <> Right.C)); end; //: 2023-10-30 function TmyGuid.GetHashCode: Integer; begin Result := 17; Result := Result * 397 + Integer(C); Result := Result * 397 + THashBobJenkins.GetHashValue(TS1, sizeOf(Cardinal), 5); Result := Result * 397 + THashBobJenkins.GetHashValue(TS2, sizeOf(Cardinal), 7); end; //: 2023-10-30 function TmyGUID.IsEmpty: Boolean; begin Result := ((TS1 = 0) and (Ts2 = 0) and (C = 0)); end; function TmyGUID.ToString: String; begin //: #FFFFFFFF-FFFFFFFF-FFFFFFFF //: 123456789012345678901234567 - > Length = 27 Result := '#' + TS1.ToHexString + '-' + TS2.ToHexString + '-' + C.ToHexString; end; //: 2023-10-30 procedure TmyGUID.DoEmpty; begin TS1 := 0; TS2 := 0; C := 0; end; //: 2023-10-30 procedure TmyGUID.DoNew; var lGuid: TmyGUID; begin lGuid := CreateNew; TS1 := lGuid.TS1; TS2 := lGuid.TS2; C := lGuid.C; end; //: 2023-10-30 procedure TmyGUID.FromString(const aGuid: String); begin //: #FFFFFFFF-FFFFFFFF-FFFFFFFF //: 123456789012345678901234567 - > Length = 27 if (StringIsValidGuid(aGuid)) then begin TS1 := StrToInt('$' + Copy(aGuid, 2, 8)); TS2 := StrToInt('$' + Copy(aGuid, 11, 8)); C := StrToInt('$' + Copy(aGuid, 20, 8)); end else begin TS1 := 0; Ts2 := 0; C := 0; end; end; //: 2023-10-30 procedure TmyGUID.ReadFromStream(const aStream: TMemoryStream); begin aStream.ReadData(C); aStream.ReadData(TS1); aStream.ReadData(Ts2); end; //: 2023-10-30 procedure TmyGUID.WriteToStream(const aStream: TMemoryStream); begin aStream.WriteData(C); aStream.WriteData(TS1); aStream.WriteData(Ts2); end; initialization //: 2023-10-30 customTS1 := GetTickCount; customTS2 := GetTickCount; customC := 0; end. |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Vielen Dank für Deinen Code.
Ich würde da noch eine Copyright oder PD Notiz machen ... Nur mal so als Idee: vielleicht könntest Du ja den Code auch dafür verwenden, wenn Du mit RPC Gedanken spielst ? weil, für MIDL und Co. ja viele solcher GUID's verwendet werden. vielleicht könntest Du ja noch statt einen MemoryStream, ein FileStream einbauen, damit man dann ein Template für die MIDL RPC Dingends hat. vielleicht könntest Du dann ja auch noch TRY EXCEPT Blöcke einbauen, die dann Fehler abfangen, wenn zum Beispiel Daten geschrieben werden sollen, auf denen man dann keinen Zugriff hat ... nur mal so gedacht... |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Zitat:
Darauf bin ich vor Jahren mal reingefallen ... |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Auch die Auflösung von GetTickCount ist eher überschaubar. Eventuell wäre TTimeStamp eine Alternative.
|
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Die erzeugten Werte sollten GLOBAL (GUID) möglichst kollisionsfrei sein. CreateGUID(NewGUID); guid := GUIDToString(NewGUID); schneidet wohl in den meisten Anwendungsfällen besser ab als eine "Lösung" via TDateTime oder GetTickCount. Ich nenne mal deinen Vorschlag LUID (L für lokal) und den üblichen 128 Bit Wert GUID.
TDateTime. TDateTime ist ein double. 8 Byte. Wenn du deine Funktion zwischen Jahr 1 und Jahr 9999 anbieten willst, dann speicherst du für Zeitspempel1 9999*365.25*24*60*60*1000 voneinander verschiedene Werte ab, dazu benötigst du eigentlich nur 48.5 Bits (und nicht 64). Da dein zweiter Zeitstempel z2 zeitlich nach dem ersten liegt, ist der Informationsgehalt von z2 noch tiefer. Nehmen wir mal an, z2 liege maximal 100 Jahre nach z1; dann sind das weitere 41.5 Bit Infos. Zusammen mit deinem 2 Byte Zähler, speicherst du 92 Bit Infos in deinen 144Bits, verschwendest also 52 Bits. Eine GUID, wie du sie dir einfach via Delphi unter Windows abholen kannst nutzt global (über alle Geräte) 128 Bits. LUID ist also 128-92=36 Bit schwächer; erzeugt 2^36 Mal weniger Werte als GUID. Das wirkt sich natürlich sehr schlecht auf die Kollisionswahrscheinlichkeit aus. Wie viele LUIDs kannst du ausgeben, bis die Wahrscheinlichkeit mindestens einer Kollision auf >50% steigt? Wären deine LUIDs gleichverteilt (wäre optimal; sie sind es nicht, da du zum Beispiel im Jahr 2023 kein Zeitstempel1 2045 ausgegeben wird), dann wären es k=1/2+sqrt(1/4+2*N*ln(2))*, wobei N=2^92 die Anzahl möglicher LUIDs. Für GUIDs gilt N=2^128. 92 Bit-Werte kollidieren gemäss * bezogen auf den Fall "mind. 1 Kollision" 2^18 mal schneller als die 128 Bit GUIDs. Effektiv tun es deine LUIDs noch viel rascher, weil deine LUIDs "aufsteigend" und damit weniger "zufällig/gleichverteilt" als die GUIDs ausgegeben werden. Rollo63 hat Infos zum Thema Kollisionen verlinkt. Es gibt dort ein Beispiel, in welchem die Ausgabe von 1 Milliarde GUIDs/sec erst nach rund 86 Jahren mit p=0.5 zu mindestens einer Kollision führt. Selbst bei optimalen 92 Bit Werten dauerte es bei gleicher Ausgabegeschwindigkeit (1 Mio 92 Bit Werte/Millisekunde(!)) nur ungefähr 3 Stunden bis es in 50% aller Fälle zu mindestens einer Kollision kommt. Der GetTickCount Vorschlag schneidet auch nicht gut ab; da verwendest du insgesamt 4+4+4 Bytes=96 Bits.(Nebenbei: Es gibt auch GetTickCount64). GetTickCount/64 hat laut microsoft immer noch eine Auflösung von nur 10 bis 16ms (wirkt sich negativ auf die Kollisionswahrscheinlichkeit p=1-N^k/((N-k)!*N^k) aus). Bei 16ms Auflösung verlierst du zum Beispiel 2*4=8 Bits an Infos, hast also noch 88 Bytes Informationsgehalt. Sinspin erwähnte hochauflösende Timer. Diese hätten immerhin eine bessere Auflösung als TDateTime, gettickcount, gettickcount64. Die GUID, welche du in Delphi via CreateGUID(NewGUID); abrufen kannst sind wohl in den meisten Anwendungsfällen genügend kollisionsfrei. Wenn du kollisionsfreie IDs benötigst, dann musst du Eigenschaften der Geräte nutzen, welche jedes Gerät eindeutig identifizieren oder aber du verwendest wie weiter oben erwähnt wurde eine zentrale Vergabestelle (da würde dann auch ein einfacher Zähler genügen). |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
ist schon interessant der Link zu Microsoft.
Allerdings weiß ich gerade nicht, wie die Testdaten zustande gekommen sind - habe das nur so flüchtig überflogen. Aber die Tabelle mit "1 Woche Messunsicherheit von 6,08 Sekunden" ist schon nah am Limit. Ich nehme mal an, das die modernen Computer mittels NTP Server angepasst werden, sofern Internet verfügbar ist. Geht wohl nicht tiefer auf Comsumer PC... |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Zitat:
Ich verwende daher seit Jahren GetTickCount64. Wenn dann der Zähler in ca. 585 Millionen Jahren wieder auf 0 geht interessiert mich nicht. |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Zitat:
Mein Rechner lief 24/7 unter Vollast. Erst für SETI@Home dann für Rosetta@Home. Ich brauchte im Winter jedenfalls keine Heizung. Wenn ich mich recht erinnere wurde Windows anfangs instabil wenn man über 49 Tage kam. Das hat sich irgendwann geändert. Nur das Problem dass die Netzwerkkarte nach einigen GB nicht mehr wollte, das blieb über ein paar Windows Versionen hinweg ein Problem. Spätestens dann war es Zeit den Rechner mal neu zu starten. |
AW: TGUID - einzigartige ID auf andere Computer Systeme ?
Zitat:
/AlterMannGebrabbelEnde |
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:45 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz