سؤال

في VB.NET، وهو أسرع في الاستخدام لوسيطات الطريقة، ByVal أو ByRef?

أيضًا، ما الذي يستهلك المزيد من الموارد في وقت التشغيل (RAM)؟


قرأت من خلال هذا السؤال, ، ولكن الإجابات ليست قابلة للتطبيق أو محددة بما فيه الكفاية.

هل كانت مفيدة؟

المحلول

يجب استخدام وسيطتي Byval وByRef بناءً على المتطلبات ومعرفة كيفية عملها لا على السرعة.

http://www.developer.com/net/vb/article.php/3669066

رداً على تعليق سلاو..

ما الذي يستهلك المزيد من الموارد في وقت التشغيل؟

يتم تمرير المعلمات على المكدس.المكدس سريع للغاية ، لأن تخصيص الذاكرة الخاص به هو ببساطة زيادة مؤشر لحجز "إطار" جديد أو "سجل تخصيص". لا تتجاوز معظم معلمات .NET حجم تسجيل الجهاز قليلاً إذا تم استخدام مساحة "المكدس" لتمرير المعلمات.في الواقع، يتم تخصيص الأنواع والمؤشرات الأساسية على المكدس.يقتصر حجم المكدس في .NET على 1 ميغابايت.من المفترض أن يمنحك هذا فكرة عن مدى قلة الموارد التي يتم استهلاكها من خلال تمرير المعلمات.

قد تجد هذه السلسلة من المقالات مثيرة للاهتمام:

تحسين الأداء من خلال تخصيص المكدس (إدارة ذاكرة .NET:الجزء 2)

وهو أسرع؟ByVal أو ByRef.

من الصعب في أحسن الأحوال القياس بدقة وخيالية - اعتمادًا على سياق القياس الخاص بك، ولكن المعيار الذي كتبته لوصف الطريقة 100 مليون مرة توصل إلى ما يلي:

  • نوع المرجع - تم اجتيازه بواسطةRef:420 مللي ثانية
  • نوع المرجع - مرت ByVal:382 مللي ثانية
  • نوع القيمة - تم تمريره بواسطةRef:421 مللي ثانية
  • نوع القيمة - تم تمريره ByVal:416 مللي ثانية
Public Sub Method1(ByRef s As String)
    Dim c As String = s
End Sub

Public Sub Method2(ByVal s As String)
    Dim c As String = s
End Sub

Public Sub Method3(ByRef i As Integer)
    Dim x As Integer = i
End Sub

Public Sub Method4(ByVal i As Integer)
    Dim x As Integer = i
End Sub

Sub Main()

    Dim s As String = "Hello World!"
    Dim k As Integer = 5

    Dim t As New Stopwatch

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method1(s)
    Next
    t.Stop()

    Console.WriteLine("Reference Type - ByRef " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method2(s)
    Next
    t.Stop()

    Console.WriteLine("Reference Type - ByVal " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method3(i)
    Next
    t.Stop()

    Console.WriteLine("Value Type - ByRef " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method4(i)
    Next
    t.Stop()

    Console.WriteLine("Value Type - ByVal " & t.ElapsedMilliseconds)

    Console.ReadKey()

End Sub

التعليق على المتغير والتخصيص في كل طريقة -

  • نوع المرجع - تم اجتيازه بواسطةRef:389 مللي ثانية
  • نوع المرجع - مرت ByVal:349 مللي ثانية
  • نوع القيمة - تم تمريره بواسطةRef:416 مللي ثانية
  • نوع القيمة - تم تمريره ByVal:385 مللي ثانية

يمكن للمرء أن يستنتج أن تمرير أنواع المراجع (سلاسل، فئات) ByVal سيوفر بعض الوقت.قد تقول أيضًا أن تمرير أنواع القيم (عدد صحيح، بايت) - ByVal سيوفر بعض الوقت.

مرة أخرى، الوقت لا يكاد يذكر في المخطط الكبير للأشياء.الأهم من ذلك هو استخدام Byval و Byref بشكل صحيح وفهم ما يجري "وراء الكواليس". ستؤثر الخوارزميات التي تم تنفيذها في روتينك على وقت تشغيل البرنامج عدة مرات.

نصائح أخرى

إذا كنت تستخدم نوع قيمة كبيرة جدا (ارشد هو كبير جدا، على سبيل المثال) قد يكون أسرع قليلا جدا لتمرير معلمة حسب المرجع. وفي حالات أخرى، قد يكون هناك <م> أكثر نسخ الخ عند تمرير بالرجوع من حيث القيمة - على سبيل المثال، إذا كنت قد حصلت على المعلمة بايت، ثم بايت واحد هو أقل وضوحا من بايت أربعة أو ثمانية أن المؤشر سوف تتخذ إذا مرت عليه بالرجوع.

في الواقع، يجب أن لا تقلق تقريبا حول هذا الموضوع. كتابة أكثر <م> قراءة كود ممكن، وهو ما يعني دائما تقريبا يمر المعلمات من حيث القيمة بدلا من المرجعية. يمكنني استخدام الأساسية ByRef نادرا جدا.

إذا كنت ترغب في تحسين أداء واعتقد ان الأساسية ByRef سوف تساعدك، <م> الرجاء قياس بعناية (في الوضع الدقيق) قبل الالتزام به.

وتحرير: ألاحظ في تصريحات لآخر (سبق قبولها أو حذفها الآن) الإجابة أن هناك قدرا كبيرا من سوء الفهم حول ما الأساسية ByRef مقابل الأساسية ByVal يعني عندما يتعلق الأمر أنواع القيمة. لدي مقال عن المعلمة التي تمر الذي أثبت شعبية على مدى سنوات - انها في C # تنطبق المصطلحات، ولكن نفس المفاهيم إلى VB.NET.

وهذا يعتمد. إذا كان يتم تمرير كائن، فإنه يمر بالفعل مؤشر. هذا هو السبب إذا كنت تمر في ArrayList (على سبيل المثال) ويضيف طريقتك سومثينغ إلى ArrayList، ثم رمز الدعوة أيضا لديه نفس الكائن في انها ArrayList، الذي تم تمريره في، لأنه هو نفسه ArrayList. الوقت الوحيد الذي لا يمر مؤشر، هو عند تمرير متغير مع نوع البيانات الجوهرية، مثل عدد صحيح، أو ضعف، إلى وظيفة. عند هذه النقطة، فإنه يقوم بإنشاء نسخة. ومع ذلك، فإن حجم البيانات من هذه الكائنات صغيرة جدا، وأنه سيكون من الصعب إحداث تغيير في اي من الاتجاهين، من حيث استخدام الذاكرة أو سرعة التنفيذ.

إذا كنت تمر في نوع مرجع الأساسية ByRef أبطأ.

وذلك لأن ما يتم تمريره في مؤشر إلى مؤشر. أي الوصول إلى الحقول على الكائن يتطلب dereferencing مؤشر اضافية، والتي سوف يستغرق بضعة دورات ساعة إضافية لإكمال.

إذا كنت تمرير نوع قيمة، ثم الأساسية ByRef قد يكون أسرع إذا كان الهيكل لديه العديد من الأعضاء، لأنه يمر سوى مؤشر واحد بدلا من نسخ القيم على المكدس. من حيث أعضاء الوصول، الأساسية ByRef سيكون أبطأ لأنه يحتاج للقيام dereference مؤشر إضافي (SP-> pValueType-> عضوا مقابل SP-> الأعضاء).

ومعظم الوقت في VB يجب أن لا داعي للقلق حول هذا الموضوع.

في. NET أنه من النادر أن يكون أنواع القيمة مع عدد كبير من الأعضاء. وهي عادة ما تكون صغيرة. في هذه الحالة، ويمر في نوع قيمة لا يختلف عن تمرير في حجج متعددة لهذا الإجراء. على سبيل المثال، إذا كان لديك التعليمات البرمجية التي مرت في كائن نقطة من حيث القيمة، انها الأداء الإقتصادي الأداء سيكون نفس الأسلوب الذي تولى قيم X و Y كمعلمات. رؤية تنفيذ DoSomething (س عدد صحيح، ص عدد صحيح) ربما لن يسبب قلق الأداء. في الواقع، كنت ربما لم تفكر مرتين حول هذا الموضوع.

إذا كنت تقوم بتعريف أنواع قيمة كبيرة نفسك، ثم ربما يجب عليك إعادة النظر في تحويلها إلى أنواع المراجع.

والفرق الوحيد الآخر هو الزيادة في عدد المراوغات مؤشر اللازمة لتنفيذ التعليمات البرمجية. ومن النادر أن تحتاج من أي وقت مضى لتحسين على هذا المستوى. في معظم الوقت، وهناك إما القضايا الحسابية التي يمكن أن تعالج، أو عنق الزجاجة الأداء الإقتصادي الأداء الخاص بك هو IO ذات الصلة، مثل انتظار قاعدة بيانات أو الكتابة إلى ملف، في هذه الحالة القضاء على مراوغات المؤشر لن يساعدك كثيرا.

وهكذا، بدلا من التركيز على هيتر BYVAL أو الأساسية ByRef أسرع، أود أن أوصي أنه يجب عليك حقا أن نركز على ما يمنحك دلالات التي تحتاج إليها. بشكل عام، انها فكرة جيدة لاستخدام BYVAL إلا إذا كنت بحاجة على وجه التحديد الأساسية ByRef. يجعل هذا البرنامج أسهل بكثير لفهم.

على الرغم من أنني لا أعرف الكثير عن الأجزاء الداخلية لـ .NET، إلا أنني سأناقش ما أعرفه عن اللغات المجمعة.هذا لا ينطبق على أنواع المراجع, ، وقد لا تكون دقيقة تمامًا فيما يتعلق بأنواع القيم.إذا كنت لا تعرف الفرق بين أنواع القيمة وأنواع المراجع، فلا ينبغي عليك قراءة هذا.سأفترض 32 بت x86 (مع مؤشرات 32 بت).

  • لا يزال تمرير القيم الأصغر من 32 بت يستخدم كائن 32 بت في المكدس.سيكون جزء من هذا الكائن "غير مستخدم" أو "محشو".لا يؤدي تمرير مثل هذه القيم إلى استخدام ذاكرة أقل من تمرير قيم 32 بت.
  • سيؤدي تمرير القيم الأكبر من 32 بت إلى استخدام مساحة مكدس أكبر من المؤشر، وربما وقت نسخ أطول.
  • إذا تم تمرير كائن حسب القيمة، فيمكن للمستدعي جلب الكائن من المكدس.إذا تم تمرير كائن حسب المرجع، فيجب على المستدعي أولاً جلب عنوان الكائن من المكدس، ثم جلب قيمة الكائن من مكان آخر.بالقيمة تعني جلب أقل، أليس كذلك؟حسنًا، في الواقع يجب أن يتم الجلب بواسطة المتصل - ولكن ربما كان المتصل قد اضطر بالفعل إلى الجلب لأسباب مختلفة وفي هذه الحالة يتم حفظ الجلب.
  • من الواضح أن أي تغييرات يتم إجراؤها على القيمة المرجعية يجب حفظها مرة أخرى في ذاكرة الوصول العشوائي (RAM)، بينما يمكن تجاهل معلمة القيمة الثانوية.
  • من الأفضل التمرير حسب القيمة بدلاً من التمرير حسب المرجع فقط لنسخ المعلمة إلى متغير محلي وعدم لمسها مرة أخرى.

الحكم:

من المهم جدًا فهم ما يفعله ByVal وByRef لك، وفهم الفرق بين أنواع القيمة والمراجع، بدلاً من التفكير في الأداء.القاعدة رقم واحد هي أن استخدم الطريقة الأكثر ملاءمة للتعليمات البرمجية الخاصة بك.

بالنسبة لأنواع القيم الكبيرة (أكثر من 64 بت)، قم بالتمرير حسب المرجع ما لم تكن هناك ميزة للتمرير حسب القيمة (مثل التعليمات البرمجية الأبسط، أو "إنه أمر منطقي"، أو تناسق الواجهة).

بالنسبة لأنواع القيم الأصغر، لا تحدث آلية التمرير فرقًا كبيرًا في الأداء، وعلى أي حال من الصعب التنبؤ بأي طريقة ستكون أسرع، لأنها تعتمد على حجم الكائن، وكيفية استخدام المتصل والمستدعى للكائن، وحتى اعتبارات التخزين المؤقت .فقط افعل كل ما هو منطقي بالنسبة للتعليمات البرمجية الخاصة بك.

وByVal إنشاء نسخة من المتغير، في حين ByRef بتمرير مؤشر. لذا أود أن أقول إن ByVal أبطأ (بسبب الوقت الذي يستغرقه لنسخ) ويستخدم المزيد من الذاكرة.

وكان فضولي للتحقق من سلوكيات مختلفة تبعا الكائن والذاكرة الأعراف

وكانت النتيجة يبدو أن demostrate BYVAL يفوز دائما، يعتمد على الموارد إذا كانت ذاكرة جمع أو أقل (4.5.1 فقط)

Public Structure rStruct
    Public v1 As Integer
    Public v2 As String
End Structure

Public Class tClass
    Public v1 As Integer
    Public v2 As String
End Class



Public Sub Method1(ByRef s As String)
    Dim c As String = s
End Sub

Public Sub Method2(ByVal s As String)
    Dim c As String = s
End Sub

Public Sub Method3(ByRef i As Integer)
    Dim x As Integer = i
End Sub

Public Sub Method4(ByVal i As Integer)
    Dim x As Integer = i
End Sub

Public Sub Method5(ByVal st As rStruct)
    Dim x As rStruct = st
End Sub

Public Sub Method6(ByRef st As rStruct)
    Dim x As rStruct = st
End Sub


Public Sub Method7(ByVal cs As tClass)
    Dim x As tClass = cs
End Sub

Public Sub Method8(ByRef cs As tClass)
    Dim x As tClass = cs
End Sub
Sub DoTest()

    Dim s As String = "Hello World!"
    Dim cs As New tClass
    cs.v1 = 1
    cs.v2 = s
    Dim rt As New rStruct
    rt.v1 = 1
    rt.v2 = s
    Dim k As Integer = 5




    ListBox1.Items.Add("BEGIN")

    Dim t As New Stopwatch
    Dim gt As New Stopwatch

    If CheckBox1.Checked Then
        ListBox1.Items.Add("Using Garbage Collection")
        System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce
        GC.Collect()
        GC.WaitForPendingFinalizers()
        GC.Collect()
        GC.GetTotalMemory(False)
    End If

    Dim d As Double = GC.GetTotalMemory(False)

    ListBox1.Items.Add("Free Memory:   " & d)

    gt.Start()
    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method1(s)
    Next
    t.Stop()

    ListBox1.Items.Add("Reference Type - ByRef " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method2(s)
    Next
    t.Stop()

    ListBox1.Items.Add("Reference Type - ByVal " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method3(i)
    Next
    t.Stop()

    ListBox1.Items.Add("Value Type - ByRef " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()
    For i As Integer = 0 To 100000000
        Method4(i)
    Next
    t.Stop()

    ListBox1.Items.Add("Value Type - ByVal " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()

    For i As Integer = 0 To 100000000
        Method5(rt)
    Next
    t.Stop()

    ListBox1.Items.Add("Structure Type - ByVal " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()

    For i As Integer = 0 To 100000000
        Method6(rt)
    Next
    t.Stop()

    ListBox1.Items.Add("Structure Type - ByRef " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()

    For i As Integer = 0 To 100000000
        Method7(cs)
    Next
    t.Stop()

    ListBox1.Items.Add("Class Type - ByVal " & t.ElapsedMilliseconds)

    t.Reset()
    t.Start()

    For i As Integer = 0 To 100000000
        Method8(cs)
    Next
    t.Stop()
    gt.Stop()

    ListBox1.Items.Add("Class Type - ByRef " & t.ElapsedMilliseconds)
    ListBox1.Items.Add("Total time " & gt.ElapsedMilliseconds)
    d = GC.GetTotalMemory(True) - d
    ListBox1.Items.Add("Total Memory Heap consuming (bytes)" & d)


    ListBox1.Items.Add("END")

End Sub


Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click


    DoTest()

End Sub
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top