c# - 태그 - 웹접근성 title




속성 및 성능 사용 (4)

Getters / Setters는 몇 가지 특별한 규칙 (setter의 "value"변수, 보이는 매개 변수 목록 없음)이있는 메소드의 구문 설탕입니다.

기사 에 따르면 "메소드의 형식적 인수가 구조체 인 경우 메소드가 인라인되지 않습니다." - ints는 구조체입니다. 따라서이 제한이 적용된다고 생각합니다.

나는 다음 코드에 의해 생성 된 일리노이를 보지 않았지만, 나는 이렇게 생각하는 흥미로운 결과를 얻었다.

using System;
using System.Diagnostics;

public static class Program{
public static void Main()
{
    PropertyTest propertyTest = new PropertyTest();
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    propertyTest.LoopUsingField();
    Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);


    stopwatch.Restart();
    propertyTest.LoopUsingBoxedGetter();
    Console.WriteLine("Using boxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);

    stopwatch.Restart();
    propertyTest.LoopUsingUnboxedGetter();
    Console.WriteLine("Using unboxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);

}

}
public class PropertyTest
{
    public PropertyTest()
    {
        _numRepeat = 1000000000L;
        _field = 1;
        Property = 1;
        IntProperty = 1;
    }

    private long _numRepeat;
    private object _field = null;
    private object Property {get;set;}
    private int IntProperty {get;set;}

    public void LoopUsingBoxedGetter()
    {

        for (long i = 0; i < _numRepeat; i++)
        {
          var f = Property;
        }

    }

    public void LoopUsingUnboxedGetter()
    {
        for (long i = 0; i < _numRepeat; i++)
        {
            var f = IntProperty;
        }
    }

    public void LoopUsingField()
    {
        for (long i = 0; i < _numRepeat; i++)
        {
            var f = _field;
        }
    }
}

이것은 내 컴퓨터, OS X (최근 버전의 모노)에서 다음 결과를 생성합니다 (초 단위).

  • 필드 사용 : 2.606
  • 박스형 getter 사용 : 2.585
  • unboxed getter 사용 : 2.71

코드를 최적화하고 속성 (심지어 자동 속성)을 사용하면 실행 시간에 큰 영향을 미친다는 것을 알았습니다. 아래 예를 참조하십시오.

[Test]
public void GetterVsField()
{
    PropertyTest propertyTest = new PropertyTest();
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    propertyTest.LoopUsingCopy();
    Console.WriteLine("Using copy: " + stopwatch.ElapsedMilliseconds / 1000.0);

    stopwatch.Restart();
    propertyTest.LoopUsingGetter();
    Console.WriteLine("Using getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
    stopwatch.Restart();
    propertyTest.LoopUsingField();
    Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
}

public class PropertyTest
{
    public PropertyTest()
    {
        NumRepet = 100000000;
        _numRepet = NumRepet;
    }

    int NumRepet { get; set; }
    private int _numRepet;
    public int LoopUsingGetter()
    {
        int dummy = 314;
        for (int i = 0; i < NumRepet; i++)
        {
            dummy++;
        }
        return dummy;
    }

    public int LoopUsingCopy()
    {
        int numRepetCopy = NumRepet;
        int dummy = 314;
        for (int i = 0; i < numRepetCopy; i++)
        {
            dummy++;
        }
        return dummy;
    }

    public int LoopUsingField()
    {
        int dummy = 314;
        for (int i = 0; i < _numRepet; i++)
        {
            dummy++;
        }
        return dummy;
    }
}

내 컴퓨터의 Release 모드에서 나는 다음을 얻는다.

Using copy: 0.029
Using getter: 0.054
Using field: 0.026 

내 경우에는 재앙입니다. 가장 중요한 루프는 최대한의 성능을 얻고 자 할 때 어떤 속성도 사용할 수 없습니다.

여기서 내가 뭘 잘못하고 있니? JIT optimizer 의해 inlined 될 것이라고 생각했습니다.


당신은 코드를 최적화한다고 말하지만, 어떻게 기능성이 있어야하는지, 그리고 소스 데이터가이 코드로 들어가는 것이 무엇인지, 그리고 이것이 "진짜"코드가 아닌지 궁금하다. BinarySearch 기능을 활용할 때 많은 양의 데이터를 구문 분석하는 경우를 고려하십시오. 이것은 매우 큰 데이터 세트를 가진 .Contains () 함수보다 훨씬 빠르다.

List<int> myList = GetOrderedList();
if (myList.BinarySearch(someValue) < 0)
// List does not contain data

아마도 당신은 단순히 데이터를 순환하고 있습니다. 데이터를 반복하면서 값을 반환하는 경우 yield 키워드를 사용하는 것이 좋습니다. 또한 자체 스레드 관리를 사용하거나 사용할 수있는 경우 병렬 라이브러리의 잠재적 인 사용을 고려하십시오.

이것은 게시 된 출처에 의한 판단으로는 보이지 않지만 매우 일반적이어서 언급할만한 가치가 있다고 생각했습니다.

public IEnumerable<int> LoopUsingGetter()
{
    int dummy = 314;

    for (int i = 0; i < NumRepet; i++)
    {
        dummy++;
        yield return dummy;
    }
}

[ThreadStatic]
private static int dummy = 314;

public static int Dummy
{
    get
    {
        if (dummy != 314) // or whatever your condition
        {
            return dummy;
        }

        Parallel.ForEach (LoopUsingGetter(), (i)
        {
            //DoWork(), not ideal for given example, but due to the generic context this may help
            dummy += i;
        });
    }

    return dummy;
}

마이크로 최적화 대신 80/20 성능 규칙을 따르십시오. 성능 대신 유지 보수성을위한 코드를 작성하십시오. 아마 어셈블리 언어가 가장 빠르지 만 그것이 모든 목적을 위해 어셈블리 언어를 사용해야한다는 것을 의미하지는 않습니다.

루프를 1 억 번 실행하고 그 차이는 0.02 밀리 초 또는 20 마이크로 초입니다. 함수를 호출하면 약간의 오버 헤드가 발생하지만 대부분의 경우에는 문제가되지 않습니다. 컴파일러가 인라인 화하거나 고급 기능을 수행 할 수 있다고 믿을 수 있습니다.

필드에 직접 액세스하는 것은 99 %의 경우에 문제가됩니다. 모든 변수가 참조되는 위치를 제어 할 수 없으며 잘못된 것이 발견되면 너무 많은 위치에서 수정합니다.


코드 최적화 확인란이 선택되어 있는지 확인해야합니다.

  1. 선택되어 있지 않으면 속성에 대한 액세스가 여전히 메서드 호출입니다.
  2. JITed 코드가 동일하기 때문에 속성이 선형으로되어 있고 성능이 직접 필드 액세스와 같습니다.

X64 JIT 컴파일러에는 inlinig에 대한 더 많은 제한이 있습니다. JIT64 인라인 최적화에 대한 자세한 내용은 http://blogs.msdn.com/b/davbr/archive/2007/06/20/tail-call-jit-conditions.aspx를 참조하십시오.

포인트 #3 The caller or callee return a value type 참조하십시오 #3 The caller or callee return a value type . 속성이 참조 유형을 반환하는 경우 속성 가져 오기 도구가 배치됩니다. 이것은 속성 int NumRepet { get; set; } int NumRepet { get; set; } int NumRepet { get; set; } 는 인라인되지 않지만 object NumRepet { get; set; } object NumRepet { get; set; } object NumRepet { get; set; } 다른 제한을 위반하지 않으면 인라인됩니다.

X64 JIT의 최적화가 좋지 않기 때문에 새로운 언급이 John 언급으로 소개됩니다





optimization