كيف ملحوظ هو الفرق في الأداء بين تليست, توبجيكتليست, ومجموعة عادي, إذا كان يمكن تقدير?
-
27-10-2019 - |
سؤال
*تلخيص:
يرجى التحقق من التعليقات المطلعة من خبراء دلفي.على وجه التحديد بالنسبة لي ، وأود أن محاولة استخدام تيست القديمة/توبجيكتليست كما اقترح ديفيد ، واستخدام الصلب الزهر و توبجيكتليست.قائمة الممتلكات كما اقترح بوشيز.سأحاول تديناراي عند إعادة بيعها في المستقبل.
=====================================================================
أقول أن لدي TAtom
فئة كما هو محدد في التعليمات البرمجية التالية.هناك حوالي hundreds
يصل إلى thousands
من حالات تاتوم في وقت التشغيل, stored in a dynamic array
الى الان.في وقت التشغيل ، ولست بحاجة للقيام الرياضيات تعويم بسيطة على TAtom.X/Y/Z
من بين جميع مثيلات تاتوم الموجودة أكثر من 30
مرات في الثانية.
الآن ، ولست بحاجة لإضافة قدرة adding
, inserting
, deleting
من حالات تاتوم في وقت التشغيل.يبدو أن اختياراتي هي (1) طلب مجموعة كبيرة;(2) التمسك مجموعة ديناميكية و سيتلنغث يدويا;(3) قم بالتبديل إلى القائمة العادية;(4) التبديل إلى توبجيكتليست العادية.
أريد تجنب (1) ما لم يكن ذلك ضروريا ، لأنني بعد ذلك يجب أن أغير الكثير من التوقيعات الوظيفية.(2) تبدو ليست جيدة سواء ، لأن تليست/توبجيكتليست يبدو ولد لهذه المهمة.ومع ذلك, لأن هناك حاجة إلى نوع الصب باستخدام تليست العادية/توبجيكتليست, يمكن لبعض تعليق واحد على أداء ممكن ضرب?أعني ، سيكون من الأفضل إذا كان يمكن تقدير عبء الأداء قبل إعادة كتابة التعليمات البرمجية.إذا كان الأداء سينخفض بشكل ملحوظ, هل هناك تقنيات أخرى يمكنني استخدامها?
علاوة على ذلك, أنا أتساءل عما إذا كان هناك فرق في الأداء بين استخدام تليست و توبجكتليست?
TAtom = class
public
ElementZ: Integer;
X, Y, Z: Extended;
other variables: other types;
end;
TAAtom = array of TAtom;
المحلول
إذا كنت تستخدم Generics.Collections.TObjectList<TAtom>
وليس هناك حاجة للصب.
يجب أن يكون الأداء على ما يرام للاستخدام الذي تصفه.إدراج هو أكثر تطلبا من إضافة إلى نهاية لأنك تحتاج إلى تحويل العناصر بعد نقطة الإدراج حتى القائمة.
طالما أنك تتجنب SetLength(A, Length(A)+1)
واختيار استراتيجية تخصيص أكثر منطقية صفائف ديناميكية تعادل كل من TList
مثل الطبقات.
في بعض الأحيان ، واجهت مشاكل في الأداء وتجزئة الذاكرة عند محاولة الاحتفاظ بقوائم كبيرة ككتل متجاورة من الذاكرة.ثم لجأت إلى مخطط تخصيص فرعي.ولكن نظرا لأن قوائمك تحتوي على مراجع كائن هي في الأساس مؤشرات ، فلديك بالفعل تخصيص فرعي ضمني.
كل شيء إلى حد ما المضاربة وكنت حقا بحاجة لقياس-وإلا يمكننا تخمين فقط.
نصائح أخرى
هل يمكنني إضافة خيار آخر إلى قائمتك?
إذا كنت لا تستخدم أي ميزة الميراث للبيانات في TAtom
, ، يمكنك استخدام record
بدلا من class
.سيتطلب كل مثيل فئة ليتم تخصيصها في الذاكرة ، مليئة الصفر وتهيئتها بشكل فردي. Getmem/Freemem
دائما التكلفة ، وسوف تجزئة الذاكرة زيادة.
ديناميكية مخصصة مسبقا array of record
سيكون أسرع من مثيلات الفئة الفردية للإضافة.وستكون البيانات مناسبة بشكل أفضل لذاكرة التخزين المؤقت لوحدة المعالجة المركزية إل 1/إل 2.
لإدراج وحذف ، ستكون مجموعة من هذه السجلات أبطأ من TList
إذا كان لديك عدد كبير من العناصر ، لأنه سيكون هناك المزيد من البيانات لحذف / إدراج (TList/TObjectList
كلاهما يحتفظ بقائمة من المؤشرات فقط).لإدخال / حذف أسرع ، يجب عليك استخدام قائمة مرتبطة بشكل أفضل.
هناك بعض النفقات العامة في TList/TObjectList
آلية بسبب الإخطار الداخلي.آلية و GetItem()
يمكن أن تكون الخاصية أبطأ قليلا (بسبب فحص النطاق) من استخدام صفيف ديناميكي مباشرة.
ولكن مع شركائنا مجمع تديناراي, ، يمكنك التمسك بصفيف ديناميكي ، ولا يزال لديك أداء جيد ، وميزات التخصيص المسبق ، و TList
- مثل الأساليب.وحتى المزيد من الطرق المتاحة ، مثل SaveToStream, Slice, Reverse
, ، الفرز مع الفهارس الخارجية وما إلى ذلك...
type
TAtom = record // could be 'packed record' to save memory (but loose perf)
ElementZ: Integer;
X, Y, Z: Extended;
other variables: other types;
// TDynArray also handle complex (e.g. string) types here
end;
TAtoms = array of TAtom;
var Atom: TAtom;
AtomArray: TAtoms;
AtomCount: integer;
Atoms: TDynArray;
begin
Atoms.Init(TypeInfo(TAtoms),AtomArray,@AtomCount);
Atoms.Capacity := 10000; // pre-allocate array = same as SetLength(AtomArray,10000)
for i := 1 to 10000 do begin
A.ElementZ := Random(1000);
A.X := Random;
A.Y := Ramdom;
A.Z := Random;
// set other fields
Atoms.Add(A); // fast adding of A properties
end;
// you have TList-like methods for your dynamic array
Atoms.Delete(500); // delete 500th item
A.ElementZ := 5000;
Atoms.Insert(500,A); // insert A values at 500th index
assert(Atoms.Count=10000);
assert(AtomCount=10000); // same as Atoms.Count
Atoms.Compare := SortDynArrayInteger;
Atoms.Sort; // will sort by 1st Integer value = ElementZ
for i := 1 to Atoms.Count-1 do // or AtomCount-1
// you have still direct access to AtomArray[]
// -> this is even the fastest access to the data
assert(AtomArray[i].ElementZ >=AtomArray[i-1].ElementZ )
Atoms.SaveToStream(aStream); // will also save any string content
Atoms.Reverse; // reverse all items order
Atoms.Clear;
// faster adding will be done with direct access to the dynamic array
Atom.Count := 10000; // allocate memory for 10000 items
for i := 0 to 10000-1 do
with AtomArray[i] do
begin
ElementZ := Random(2000);
X := Random;
Y := Random;
Z := Random;
end;
Atoms.Sort; // TDynArray knows about the data just created
end; // no need to have any try...finally ..Free block
يعمل مع دلفي 6 تصل إلى ز.
مع الإصدار الأحدث من دلفي دعم الأدوية ، يجب أن تذهب بشكل أفضل في هذا الاتجاه.
ومع ذلك ، لأن نوع الصب هو مطلوب باستخدام العادية تيست / توبجيكتليست ، يمكن لبعض واحد التعليق على الأداء المحتمل ضرب?
إذا قمت بكتابة المصبوب باستخدام النموذج
List[I] as TAtom
كما سيتم إضافة النفقات العامة الصغيرة ، والتي يمكن أن تضيف حقا في وضعك.ومع ذلك ، إذا كنت من الصعب التلبيس
TAtom(List[I])
بقدر ما أعرف ، لا النفقات العامة يحدث في الواقع كما يتم تنفيذ التلبيس دون الشيكات وأعتقد أيضا يتم ذلك في وقت الترجمة.
أما بالنسبة للجانب الآخر من سؤالك ، وأعتقد أنها قد تم تغطيتها بشكل صحيح بالفعل...
كما توبجيكتليست هو سليل مباشر من تيليست العروض ستكون قريبة جدا.
السؤال الأول:هل نتحدث عن Classes.TList
و Contnrs.TObjectList
أم أننا نتحدث عن Generics.Collections.TList
على التوالي Generics.Collections.TObjectList
?
إذا كنا نتحدث عن الأدوية الجنيسة ، يتم تنفيذ كل من تليست و توبجكتليست باستخدام المصفوفات الديناميكية.إذا كان هناك أي فرق في الأداء بين استخدام صفيف ديناميكي مباشرة أو استخدام واجهة أجمل للحاوية العامة ، فسيكون ذلك ضئيلا.
إذا كنا نتحدث عن "كبار السن" TList
و TObjectList
, ، ثم نحن بحاجة للمقارنة فقط TList
مع المصفوفة الديناميكية المكافئة ، منذ ذلك الحين TObjectList
هو سليل TList
لذلك يرث كل خصائص الأداء. TList
يستخدم كتلة من الذاكرة المخصصة باستخدام ReallocMem
.الصفيف الديناميكي يفعل نفس الشيء داخليا ، لذلك لا ينبغي أن يكون هناك فرق كبير!
خاتمة
إذا كان هناك أي فرق في الأداء بين الاثنين ، فمن المحتمل أن يكون ذلك بسبب الاستخدام الساذج للمصفوفة الديناميكية يستخدم اللعين SetLength(A, Length(A)+1)
, ، في حين أن التنفيذ الأفضل في جميع الحاويات المقدمة دلفي قبل تخصيص الذاكرة في أجزاء أكبر.مع التعليمات البرمجية المناسبة لا ينبغي أن يكون هناك أي فرق كبير بين تلك البديل!
قم بعمل مشروع اختبار وقياس وقت إضافة وإدراج وحذف الآلاف من مثيلات تاتوم باستخدام الطرق الأربعة.ثم تقرر أي واحد لاستخدام.
تيست و توبجيكتليست ربما أسرع من مجموعة ديناميكية عند إضافة ، إدراج وحذف ، لأن مجموعة ديناميكية باستمرار يجب إعادة تخصيصها.تنفيذ تليست و توبجكتليست لا تفعل ذلك.
TList
الخ.هل بالضبط ما رمز العمل على أجزاء من الذاكرة أو ديناريز يجب أن تفعل ، ولكن تنفيذها هو الأمثل بالفعل للحالة المشتركة ، ويتضمن اعتبارات حول كيفية سلوك مدير الذاكرة.
يمكن أن يكون أحد المعايير هو نسبة القراءات / التحديثات إلى التسلسل.إذا تم تحديث التسلسل بشكل غير منتظم بعد إنشائه ، فيجب أن تكون هناك سرعة أفضل يمكن إدراكها باستخدام الدينارات ، لأن الوصول إلى العناصر باستخدام TList
ويتطلب الإعجابات استدعاء طريقة واحدة بالإضافة إلى التحقق من الحدود ، بالإضافة إلى التحقق من النوع إذا كنت تستخدم as
المشغل.
في النهاية ، فإن تكلفة الحساب القيام به على TAtom
يجب أن تهيمن على وقت التشغيل ، مما يجعل اختيار ديناراي أو TListXXX
غير ذي صلة.
سيكون للمصفوفات الديناميكية للسجلات أقل تأثير عند الوصول إلى العناصر ، إذا كانت ذراتك كائنات ، فستكون جميع القوائم مكافئة إلى حد ما من حيث سرعة الوصول.
ومع ذلك ، إذا قمت بإجراء العديد منها ، فإن المشكلة الرئيسية الخاصة بك تكون إدراج وحذف ، والتي جميع القوائم والمصفوفات سوف تؤدي بشكل سيئ ، ولكن هذا هو ما التنميط سوف اقول لكم.إذا كان هذا هو الحال ، فقد ترغب في التفكير:
- قائمة مرتبطة إذا لم تكن بحاجة إلى الوصول إلى الذرات حسب الفهرس
- شجرة ، يجب أن يكون لديك استخدام لتقسيم الفضاء من الذرات الخاصة بك ، قد تستخدم كذلك هذا القسم لعقد الذرات الخاصة بك بدلا من مجموعة
- السماح بعناصر غير محددة / لا شيء في صفيفك / قائمتك ، والحفاظ على مجموعة من العناصر غير المحددة/لا شيء ، وفهرس إذا كنت بحاجة إلى قائمة مرتبة (من المحتمل أن تكون الحل الأعلى أداء ، ولكن من المحتمل أيضا أن تكون الأكثر تعقيدا من حيث الكفاءة)