새로운 메뉴에 도전 : 커피번 굽기

그동안 바빠서 그다지 빵을 못 구워봤습니다.
맨날 굽는 것만 굽곤 해서 지겨운 마음에 새롭게 커피번에 도전해 봤습니다.
평소에 수서역 로티보이나 잠실역 파파로티를 너무 좋아해서 뱃살의 압박에도 가끔씩 사먹곤 하는데 집에서 만들면 좋겠다는 마음에 도전해봤습니다만 역시 어렵군요. 게다가 버터가 너무 많이 들어가서 죄악감이 느껴집니다.



레시피 :
반죽 - 강력분 200g, 박력분 50g, 버터 25g, 달걀1개, 설탕 20g, 소금5g, 인스턴트 이스트 5g, 우유 65g, 물 35g
토핑 - 버터 50g, 설탕 30g, 계란1개, 진하게 탄 커피 15g
속재료 - 버터 적당히 (...)

버터를 제외한 반죽을 몽땅 섞습니다. 대충대충.


새로 구입한 아이템 전동 반죽기! 손으로 반죽할려니 귀찮아 죽습니다. 그런데 부하가 엄청나게 걸리던데 이 반죽기 모터 안 타나(...) 안그래도 탄내가 나던데...


열심히 반죽됐으면 랩으로 덮어서 40도에서 10분 숙성 시킵니다.


10분후에 꺼내서 4등분해서 속재료를 적당히 집어넣습니다. 구체적으로는 버터를 적당히 잘라다가 만두속 넣듯이 반죽 속에 넣습니다. 다 됐으면 역시 40도에서 40분간 냅둡니다.


그 동안 토핑을 만들어 봅니다. 재료들을 몽땅 넣고 열심히 거품기로 섞습니다. 참 쉽죠?


40분간 발효시킨 반죽은 상상이상으로 엄청나게 크게 부풀었습니다. 충분한 공간을 확보해주지 못해서 다 붙어버렸군요. 왓더헬....


짤주머니에 크림을 담습니다. 두 손만 가지고 크림을 짤주머니에 어떻게 담나 고민했는데 인터넷에 있는 레시피들 보니까 짤주머니를 저런 그릇에 씌워놓고 붓더군요.


적당히 토핑크림을 발라줍니다. 커피가 너무 적거나 연한 듯 색깔이 영 마음에 안 듭니다. 더 진하면 좋을텐데...


200도에서 13분, 근데 제 오븐으로는 온도 좀 낮춰야 될 것 같군요. 여튼 완성.


음.... 빵 맛은 적당히 괜찮은데 속재료를 너무 조금 넣었는지 속재료 없는 그냥 커피빵 맛입니다. (...)



오늘의 소감 :
1. 아악 내 뱃살
2. 가염버터와 무염버터를 둘다 구비해야 하나 (...)
3. 반죽기 만세
4. 발효 재밌다
5. 파파로티 번 먹고 싶다

by netcrawler | 2009/05/12 23:33 | 음식 | 트랙백 | 덧글(5)

[VC/Graphics] SIMD를 사용하여 행렬 곱셈 가속하기

한번 SIMD를 사용해서 행렬곱셈을 해 봤습니다.
있는 코드가 있으면 편하게 긁어다 붙이려 했는데 구글을 아무리 뒤져도 SIMD를 이용해서 행렬*벡터만 보이고 행렬*행렬을 하는 코드가 안 보이길래 만들어버렸군요. 혹시 영어로 검색하는 사람들을 위해서 영어로도 써 봅니다.
Optimized matrix multiplication using SIMD
정도면 되려나요.


* SIMD

여러개의 계산을 병렬처리하는 방법으로 아마 펜티엄 2인가 3에서 128비트 레지스터에 부동소수점 4개를 올려서 한방에 계산하는 뭐 그런게 나왔던것 같습니다. 서로 의존성이 없는 값들을 계산하기에 적합한데 특히 그래픽스에서 행렬과 벡터를 다룰 때 유용하게 사용할수 있습니다.


* 아이디어

기본적으로 SIMD를 이용하면 실수 4개의 덧셈 혹은 곱셈을 한번에 할 수 있습니다.
행렬 계산식을 잘 보면 다음의 두 행렬을 곱한다고 쳤을 때
( a11 a12 a13 a14 )   ( b11 b12 b13 b14 )
( a21 a22 a23 a24 )   ( b21 b22 b23 b24 )
( a31 a32 a33 a34 )   ( b31 b32 b33 b34 )
( a41 a42 a43 a44 )   ( b41 b42 b43 b44 )

두 행렬 A, B를 곱해서 나오는 행렬 C의 첫행은

c11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41
c12 = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42
c13 = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43
c14 = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44

이므로

중간중간에 더하기를 기준으로 잘라보면

(c11, c12, c13, c14) =
(a11 * (b11, b12, b13, b14)) +  
(a12 * (b21, b22, b23, b24)) +  
(a13 * (b31, b32, b33, b34)) +  
(a14 * (b41, b42, b43, b44))

가 됩니다.

여기서 SIMD를 사용하면 a11 * (b11, b12, b13, b14) 를 한 명령어로 처리할 수 있으므로 속도 증가를 기대할 수 있습니다.



* VC 라이브러리

VC에서는 SIMD를 위해서 쓰기 편하게 랩핑된 클래스를 제공합니다. 인라인 어셈이나 알아보기 불편한 함수 셋트가 있긴 한데 편한걸 두고 굳이 힘들게 쓸 이유는 없겠죠;
VC에서 128비트 레지스터에 올라갈 변수는 __m128형입니다. 단순히 float[4]라고 보시면 되겠네요. 한편 SIMD계산을 위한 클래스는 F32vec4입니다. F32vec4는 <fvec.h>를 인클루드해야만 사용가능합니다.



* 코드

struct CMatrix
{
    union
    {
       struct 
       {
            __m128 _L1, _L2, _L3, _L4;
       };
       struct 
       {
            float _11, _12, _13, _14;
            float _21, _22, _23, _24;
            float _31, _32, _33, _34;
            float _41, _42, _43, _44;
        };
    };
    void    MultiplyWith(const CMatrix& mat)
    {
        F32vec4 t1, t2, t3, t4;

        t1  = mat._L1 * F32vec4(_11);
        t1 += mat._L2 * F32vec4(_12);
        t1 += mat._L3 * F32vec4(_13);
        t1 += mat._L4 * F32vec4(_14);

        t2  = mat._L1 * F32vec4(_21);
        t2 += mat._L2 * F32vec4(_22);
        t2 += mat._L3 * F32vec4(_23);
        t2 += mat._L4 * F32vec4(_24);

        t3  = mat._L1 * F32vec4(_31);
        t3 += mat._L2 * F32vec4(_32);
        t3 += mat._L3 * F32vec4(_33);
        t3 += mat._L4 * F32vec4(_34);

        t4  = mat._L1 * F32vec4(_41);
        t4 += mat._L2 * F32vec4(_42);
        t4 += mat._L3 * F32vec4(_43);
        t4 += mat._L4 * F32vec4(_44);


        _L1 = t1; _L2 = t2; _L3 = t3; _L4 = t4;
    }
};




* 주의사항

__m128의 경우 반드시 16byte로 정렬된 메모리에 올라가 있어야만 합니다. 내부적으로 __declspec(align(16)) 을 사용하는데, 이렇게 정렬된 메모리상에 있는 변수의 경우 함수의 인자로 넘길때 값에 의한 (call-by-value) 전달은 허용되지 않습니다. 보통은 const 레퍼런스를 넘기를 방법으로 회피할 수 있죠.
또한 std::vector를 사용할 때 애로사항이 꽃피는데 VC의 std::vector 구현 중 resize() 내부에서 신나게 값에 의한 전달을 하기 때문입니다. 이 현상을 가장 간단하게 회피하는 방법은 std::vector를 대신하는 전용 컨테이너를 만드는것이죠. 이때 주의할 점은 단순히 new로 CMatrix를 할당했을 경우 정렬된 메모리를 얻을 수 없기 때문에 _L1에 엑세스하려는 순간 access violation error가 뜨면서 코드가 죽는 문제가 생깁니다.
이 문제를 회피하기 위해서는 new, new[], delete, delete[]를 모두 오버로드해서 내부적으로 _aligned_malloc()과 _aligned_free()를 사용하도록 신경써야 합니다. 또한 CMatrix를 포함하고 있는 클래스가 있을 경우 그 또한 정렬된 메모리상에 올라가야 합니다. 당연한 얘기죠?



* 퍼포먼스

제 테스트로는 제 CPU(P4 E6300 듀오) 에서 단순히 곱셈/덧셈으로 구현한 행렬곱보다 80-90%정도의 성능향상이 있었습니다.



* 참고자료

http://www.gamasutra.com/features/20000131/barad_03.htm
http://www.gamedev.net/community/forums/topic.asp?topic_id=526505


by netcrawler | 2009/05/04 02:24 | 취미 | 트랙백 | 덧글(7)

오랫만에 까르보나라 만들어보기

요즘 바빠서 뭔가 만들어보지를 못했습니다.
와우 3.1패치가 나왔거든요 'ㅁ'

...

그리고 저번주엔 친구가 놀러와서 밤새 같이 놀았는데 뜬금없이 누구한테 화이트데이 선물로 줄 쿠키를 굽고 싶다 그래서 남자 둘이 새벽 1시에 팬티바람으로 사이좋게 쿠키와 케익을 구웠죠.

..노파심에 덧붙이면 100% 친구의 작품이고 저는 손 안 댔습니다 (...)

여튼 이 때 산 생크림이 또 유통기한이 넘을락 말락 해서 생크림 소모용으로 까르보나라를 다시 만들어봤습니다.

레시피 : 면 250g, 생크림 150ml, 계란3개, 양파반개, 파마산치즈 적당히, 베이컨 적당히.. 참고로 거의 3인분 나옵니다.

오늘도 대충대충 (...)

베이컨을 지집니다. 4장 넣었는데 좀 더 넣을걸 그랬어요. 바짝 지져지면 꺼내둡니다.


양파도 대강썰고


양송이를 넣고싶었지만 없어서 새송이를 넣었는데 그냥 안 넣어야겠습니다. 미묘하게 맛이 다르네요 (...)


면을 9분정도 삶으면서


생크림과 치즈를 준비합니다. 치즈 얼려놓은거 미리 좀 녹여줄걸 그랬나보군요.


기름을 좀 두르고 다진마늘을 좀 넣고 양파와 버섯을 넣고 볶다가 양파가 대충 다 되면 생크림을 넣습니다. 소금과 후추로 간을 합니다.


적당히 졸아들면 베이컨과 면과 치즈를 넣고 불을 끄고 계란노른자도 넣어서 열심히 저은다음에 꺼내먹으면 완성.
오늘의 소감 :
1. 아악 뱃살
2. 생크림 더 많이 넣어야겠다.. 너무 많이 졸여진 듯
3. 베이컨 좀 더 많이 넣어야 비율이 맞겠는데;

by netcrawler | 2009/04/26 22:24 | 음식 | 트랙백 | 덧글(7)

◀ 이전 페이지 다음 페이지 ▶