2017-11-17 9 views
1

나는 C의 ++ 응용 프로그램에 루비 2.2를 내장하고 toRubyString 특정 사용 사례에 출력 프로파일 런타임을 지배 밝혀졌다어떻게 나중에 재사용 할 수 있도록 Ruby VALUE 값을 캐시 할 수 있습니까?

VALUE toRubyString(const MyObject &o) 
{ 
    const char *utf8 = get_string_rep_of(o); 
    return rb_enc_str_new(utf8, strlen(utf8), rb_utf8_encoding()); 
} 

형태의 기능을 최적화하기 위해 노력하고 있습니다. 이 경우 함수는 매우 자주 호출되지만 값은 MyObject입니다. 따라서, 내 생각은 내가

std::map<MyObject, VALUE> cache; 

VALUE toRubyString(const MyObject &o) 
{ 
    std::map<MyObject, VALUE>::const_iterator it = cache.find(o); 
    if (it != cache.end()) { 
     return it->second; 
    } 
    const char *utf8 = get_string_rep_of(o); 
    VALUE v = rb_enc_str_new(utf8, strlen(utf8), rb_utf8_encoding()); 
    cache[o] = v; 
    return v; 
} 

아아의 라인을 따라, 다시 사용할 수 있도록 같은 std::map<MyObject, VALUE> 나에 VALUE 값을 캐시하는 것입니다,이 수정과 함께, 루비 인터프리터가 결국 충돌 것을 발견하고, return it->second; 줄을 생략하면 충돌이 사라집니다 (즉, 코드가 캐시 된 항목을 재사용하지 않을 때).

나는 단지 도움이되지 않았다 (캐시에 VALUE를 추가하기 전에) 몇 후 천 함수를 호출하지만, 심지어

rb_gc_mark(v); 

전화를 발생하기 때문에이 가비지 컬렉터 관련이 의심 . 아무도 내가 여기에서 놓칠 수도있는 것에 관해서 어떤 생각을 가지고 있습니까?

+0

중요한 질문은 소유권입니다. 당신이 사용하고있는 리소스를 정리할 책임이있는 사람은'v'와'utf8'입니까? 'v'는이 함수가 끝난 후에도'utf8'에 의존합니까? (다른 두 개의 작은 메모 : 속도에 대해 염려하고 있으므로'std :: map' 대신에'std :: unordered_map'을 사용하는 것을 고려하십시오 - O (1) 대 O (log N) 검색 시간입니다. 맵에 포인터/ref가 아닌 값으로'MyObject'를 넣으면 복사본을 만들 수 있습니다. 괜찮을 지 모르지만 값이 비싸거나 맵을 일치시키지 못하는 경우가 있습니다. std :: map'이 계산됩니다.) – metal

+0

@metal 저는 이것이 소유권 문제라고 생각합니다 (Ruby GC가 여기에 관련되어 있다고 생각하는 이유입니다). 'utf8'에 의해 지시 된 데이터의 소유권은'get_string_rep_of'와 함께 남아 있습니다. 'rb_enc_str_new'는 소유권을 가지지 않고 데이터 복사본을 만듭니다. VALUE 값에 관해서는 Ruby가 소유권을 관리하는 방법을 잘 모르겠지만 어떤 경우에도 명시적인 참조 계산이없는 것 같습니다. –

+0

또 다른 대안은 Ruby에서 값을 캐시하는 것입니다. 즉, toRubyString을 메모 방법으로 만드는 것입니다. 아마 약간 성능이 떨어지지 만 더 안정적으로 보입니다. 그러나 어떤 경우 든 해시 키는 무엇인가 (이것은 C++ 솔루션에도 적용됩니다). 당신이 선호하는 것처럼 객체 ID를 사용한다면, 객체의 문자열 표현이 바뀌는 것을 알 수 없을 것입니다. – user1934428

답변

1

rb_global_variable(&cache[o]) 일 수 있습니다.

Apparently지도에 삽입하면 기존 요소에 대한 포인터가 무효화되지 않아야하므로 기술적으로지도 값의 주소를 사용하는 것이 좋습니다. 그리고 rb_global_variable은 캐시 된 값이 GC에서 완전히 제외되도록합니다.

rb_gc_mark이 작동하지 않는 이유는 다음에 컬렉션에서 개체가 정리되지 않기 때문입니다. 일반적으로 custom class's mark function을 정의 할 때이를 사용하여 GC가 내부 참조 된 객체를 적절하게 표시 할 수 있도록합니다.

+0

포인터 주셔서 감사. 원시 포인터를 사용하면 맵이 메모리를 내부적으로 재 할당 (및 이동)할지 여부를 제어 할 수 없으므로 취약합니다. 미리 할당 된'std :: vector '을 시도한 다음'std :: map :: const_iterator>'를 사용해 보시기 바랍니다. –

+0

그 이유는 그 질문에 연결되어 있습니다.표준에 따르면 맵은 새로운 키를 삽입 할 때 이전 키를 재 할당해서는 안됩니다. 캐시에서 항목을 제거/업데이트하는 경우에만 해당 문제에 대해 걱정할 필요가 있습니다. – Max