2009-08-17 1 views
1

id2key_value라는 Dictionary<string, Dictionary<string, string>>에 해시 (ID)로 매핑 된 키/값 쌍이 있습니다. 이것을 행과 함께 데이터베이스와 같은 테이블을 표현하는 방법으로 생각할 수 있습니다. 캐스팅 비용?

는 내가의 아이디어를 내놓았다 때 나는 영리한되는 줄 알았는데, 잘

public int GetInt(string id, string key) 
{ 
    int value = 0; 
    bool success = int.TryParse(map[id][key], out value); 

    if (success) 
     return value; 
    else 
     throw new InvalidOperationException(
      "Trying to obtain a non-integer value with GetInt()."); 
} 

처럼 캐스트를 수행하여 몇 가지 기본적인 데이터 유형의 사용을 용이하게하기 위해 몇 가지 도우미 기능을 추가 된 "cast- 캐시 ", 기본적으로 이미 구문 분석 된 개체를 보유하고 있으므로 int, bool, DateTime 등의 문자열 구문 분석을 건너 뛸 수 있으며 캐시에서 해당 데이터 형식으로 간단하게 캐스팅 할 수 있습니다. 마찬가지로,

public int GetInt(string id, string key) 
{ 
    if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key)) 
     return (int) cast_cache[id][key]; 

    int value = 0; 
    bool success = int.TryParse(map[id][key], out value); 

    if (success) 
    { 
     this.AddToCache(id, key, value); 

     return value; 
    } 
    else 
     throw new InvalidOperationException(
      "Trying to obtain a non-integer value with GetInt()."); 
} 

"캐스트 캐시"는 간단히 Dictionary<string, Dictionary<string, object>>입니다.

따라서지도에 10000 개의 정수가 추가 된 성능 테스트를 수행했습니다. 그런 다음 "캐스팅 캐싱"의 유무에 관계없이 무작위로 1 백만 건의 검색을 수행했습니다.

캐싱없이 495 (ms) 및 캐싱을 사용하여 490 (ms) 걸렸습니다. 나는 또한 DateTime을 사용하여 테스트를 실시했다. 차이점은 더 크지 만 예상하지 못한 것 (~ 750 (ms) 비 캐시 된 캐시 ~ 500 (ms) 캐시).

캐스트의 원리를 이해하지 못했지만이 작업이 얼마나 비싸고 성능이 문자열에서 "비 직렬화 된"것과 너무 가깝습니까?

+0

당신이 제네릭을 사용할 수 없습니다 어떻게되는지? – Dykam

+0

@Dyamam, 어떻게? id2key_value는 구현 된 공급자 (파일, 데이터베이스 등)에 지속적으로 저장할 수있는 문자열로 역 직렬화되는 모든 유형을 포함 할 수 있습니다. 제네릭을 사용하는 비전은 어디에 있습니까? –

+1

파싱이 캐스팅하지 않습니다 ... –

답변

11

캐스팅은 개체 자체를 만지기 때문에 (대부분 해당 개체를 가리키는 참조를 변경하는 것이므로) 대부분의 사람들이 생각하는 것보다 훨씬 빠릅니다.

캐스팅을 피해야하는 주된 이유 중 하나는 캐스팅 할 때 유형 안전을 피하고 잠재적 인 실행 시간 오류를 응용 프로그램에 도입하는 것입니다. 캐스팅이 성능 문제가되는 경우는 드뭅니다.

부작용으로 모든 참조 유형과 값 유형을 캐시에서 테스트하여 모든 값 유형의 복싱 및 언 박싱으로 인해 성능상의 불이익을받지 않도록해야합니다. 권투의

성능 저하는 객체 에 값 유형을 주조하는이 값 유형은 힙에 복사해야합니다 같은 기준을 변경 이상을 포함 않는다는 사실에서 비롯됩니다. 또한 박스형 값 유형을 사용하면 참조 유형을 unbox 한 다음 해당 값을 힙에서 스택으로 다시 복사합니다.

+1

).이 경우 실제로는 다른 것보다 언 박싱 중입니다. ...하지만 일반적으로 동의합니다. –

+1

Nice - 나는 당신이 논평하기 전에 편집했습니다 : –

+0

질문에있는 문제는 boxing/unboxing이 아니며, 문자열에서 데이터를 다시 파싱합니다. boxing/unboxing은 string.TryParse()에 비해 매우 빠릅니다. –

-2

시간과 공간의 관계 때문에 예제가 더 빠르게 작동합니다. 모든 해시 유형을 계속 캐싱 할 경우 프로그램 용량이 얼마나 필요합니까?

2

잠시만 요, 코드에서 "캐스트"라고하는 것은 캐스트가 아닙니다.

당신이를하고있는 경우 : a 변환하지 캐스트의

bool success = int.TryParse(map[id][key], out value); 

합니다.캐스트은 다음과 같습니다

value = (int) map[id][key]; 

또는 객체가 정말 문자열이 아닌 INT 때문에이 경우 실패

value = map[id][key] as int; 

.

Dictionary<string, string>이 있지만 실제로 임의의 개체를 저장하는 데 관심이 있다면 Dictionary<string, object>이됩니다. 결과적으로 변환 대신에 캐스트를 사용할 수있게됩니다. 앤드류 헤어가 지적한 것처럼 훨씬 빠릅니다.

+0

"음, 기본적으로 이미 파싱 된 객체를 보유하고있는"캐스트 캐시 "에 대한 아이디어를 생각해 냈을 때 영리하다고 생각했기 때문에 int, bool, DateTime 등의 문자열 구문 분석을 건너 뛸 수있었습니다. 캐시에서 적절한 데이터 유형으로 캐스팅하기 만하면됩니다. " 분명히 캐시의 불행한 이름을 이해합니다. 이 솔루션은 객체의 직렬화를 ToString() 또는 반사 (reflection)로 제한합니다. 이는 적절하지 않습니다.이렇게하려면 저장 공간에 개체 유형을 저장해야하며 더 많은 공간이 필요하며 피하려고하는 대상이 필요합니다. –

0

사전 조회 수를 줄이면 코드가 캐시에서 더 빨리 수행 될 수 있습니다. 또한 캐시를 Dictionary<string, int> 대신 Dictionary<string, object>으로 만들면 값 비싼 복싱 및 언 박싱을 피할 수 있습니다.

public int GetInt(string id, string key) 
{ 
    int value; 
    Dictionary<string, int> cache; 

    if (cast_cache.TryGetValue(id, out cache) 
      && cache.TryGetValue(key, out value)) 
    { 
     return value; 
    } 

    if (int.TryParse(map[id][key], out value)) 
    { 
     this.AddToCache(id, key, value); 
     return value; 
    } 

    throw new InvalidOperationException("Trying to obtain a non-integer value with GetInt()."); 
} 
+0

나는 모든 다른 유형에 대해 사전을 사용할 수는 있지만, 여기에서는 내가하는 일의 목적에 어긋납니다. 'Dictionary '로 바꾸면 50 %의 속도 향상을 얻을 수 있습니다. 감사합니다! 나는 HounShell의 제안이 훨씬 더 빠른 속도로 향상 될 것이라고 믿는다. –

0

거기에 몇 가지 사항을 고려해야합니다. 첫째, 올바른 용어를 알고 있으므로 실제로는 언 박싱입니다 (값 유형을 가져 와서 참조 유형으로 저장했거나 박스로 저장했습니다. 값 유형으로 되돌리기는 언 박싱 임). 나는 5 사전이 거기에 통과 계산

if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key))    
    return (int)cast_cache[id][key] 

:

둘째, 내가 대신 캐시 사전에 여러 통화에있어, 코드의 대부분이 언 박싱에없는 것을 내기 것 cast_cache을 .ContainsKey (id), cast_cache [id], .ContainstKey (key), cast_cache [id] 및 [key].

꽤 가혹합니다. 집계 된 키를 사용하여 많은 것을 줄일 수 있습니다. [id] [key]를 찾는 대신 단일 객체로 결합하십시오. 사전의 개수를 기하 급수적으로 줄이고 ContainsKey()를 try/catch (건너 뛰는 속도 검사)로 건너 뛰면 해당 조회를 2, 1로 줄일 수 있습니다.

여기에 당신이 사람들을 결합 할 수 있도록 해주는 클래스는 다음과 같습니다

public class Vector 
{ 
    private object[] _Data; 

    public object this[int index] 
    { 
     get 
     { 
      return _Data[index]; 
     } 
    } 

    public Vector(params object[] data) 
    { 
     _Data = (object[])data.Clone(); 
    } 

    public override bool Equals(object obj) 
    { 
     Vector OtherVector = obj as Vector; 

     if (OtherVector == null) 
      return false; 

     if (OtherVector._Data.Length != _Data.Length) 
      return false; 

     for (int I = 0; I < _Data.Length; I++) 
      if (!_Data[I].Equals(OtherVector._Data[I])) 
       return false; 

     return true; 
    } 

    public override int GetHashCode() 
    { 
     int Result = 0; 
     for (int I = 0; I < _Data.Length; I++) 
      Result = Result^(_Data[I].GetHashCode() * I); 

     return Result; 
    } 
} 

가 밖으로 시도하고 속도가

+0

이것은 내 직감이 말하고있는 것입니다. 나는 밖으로 시험해보고, 감사 할 것이다! –

+0

나는 이것을 어떻게 사용하는지 알아 내려고하고있다. 캐시가 'Dictionary '으로 변경되면 벡터에 의해 생성 된 충돌이 어떻게 처리됩니까? int hash = (새 Vector (id, key)). GetHashCode(); –

+0

Dictionary , object>이어야합니다. Dictionary는 키에 대해 GetHashCode() 및 Equals()를 사용하여 사물을 봅니다. Vector 클래스는 두 호출의 프록시 역할을 수행합니다. 해시 코드가 상대적으로 고유하고 해시 코드가 Equal() (GetHashCode() 오버로드에 대한 두 가지 요구 사항) 및 해당 구성 요소 각각에 대해 Equals()를 호출하는 두 클래스에서 동일하다는 것을 확인합니다. 그것은 본질적으로 n 값 키에 대한 프록시이지만 직접 사용할 수 있습니다. – Hounshell