Search

'2012/02/13'에 해당되는 글 3건

  1. 2012.02.13 Welcome to Parallel C#(14.1) - 긴급 패치.
  2. 2012.02.13 Welcome to Parallel C#(14) - 거기까지. 6
  3. 2012.02.13 Inertia Tensor(1)

Welcome to Parallel C#(14.1) - 긴급 패치.

C# Parallel Programming 2012. 2. 13. 20:23 Posted by 알 수 없는 사용자

신석현님께서 아주 좋은 지적을 해주셨습니다. 제가 예제로 사용한 코드를 돌리면 매번 prime count가 다르게 나온다는 것이었습니다. 참고를 위해서~~ 다시 그 문제의 코드를 붙여 보겠습니당. :)

using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApplication1

{

    class Program

    {

        static IEnumerable<long> GetPrimeNumber(long num)

        {

            List<long> primeList = new List<long>();

 

            Parallel.For(0, num + 1, (i) =>

            {

                bool isPrime = true;

                Parallel.For(2, i, (j, loopState) =>

                {

                    if (i % j == 0)

                    {

                        isPrime = false;

                        loopState.Break();

                    }

                });

 

                if (isPrime)

                {

                    primeList.Add(i);

                }

            });

 

            return primeList;

        }

 

        static void Main(string[] args)

        {

            Stopwatch sw = new Stopwatch();

            sw.Start();

            IEnumerable<long> primeList = GetPrimeNumber(99999);

            sw.Stop();

 

            Console.WriteLine("Elapsed : {0}, Found prime counts : {1}",

                sw.Elapsed.ToString(),

                primeList.Count());

            //뭔가 한다.

        }

    }

}


이 코드에서 문제가 되는 곳은 바로~!!!!! List<T>를 사용했다는 점입니다. Parallel.For를 돌리면, 여러 스레드가 구역을 나눠서 동시에 Parallel.For안의 코드를 돌리게 되는데요. 이 과정에서 스레드 간의 충돌이 생겨서 그렇습니다. 문제를 찾아보기 위해서 리플렉터로 List<T>.Add(T)의 코드를 찾아보니...

public void Add(T item)

{

    if (this._size == this._items.Length)

    {

        this.EnsureCapacity(this._size + 1);

    }

    this._items[this._size++] = item;

    this._version++;

}


이렇게 되어 있습니다. 여기서 7번째 줄의 'this._size++'부분이 문제가 되는 것 같습니다. 제가 11번째 포스팅에서 적은 내용 처럼 말이죠. 그래서 size가 1증가 되기 전에 같은 같은 인덱스에 값이 두번 쓰여지는 것이 아닌가 하는 추측이 가네욤!!! :)

그래서 가설을 확인해보기 위해서 List<T>를 여러스레드가 동시에 사용가능한 ConcurrentBag<T>로 바꿔서 테스트를 진행해봤습니다. 그랬더니!! 매번 같은 prime count가 나오는 것을 볼 수 있었습니다. 하하하하하 ㅠ_ㅠ;; 참고로 ConcurrentBag<T>.Add<T>의 코드를 봤더니 내부적으로...

private void AddInternal(ThreadLocalList<T> list, T item)

{

    bool lockTaken = false;

    try

    {

        Interlocked.Exchange(ref list.m_currentOp, 1);

        if ((list.Count < 2) || this.m_needSync)

        {

            list.m_currentOp = 0;

            Monitor.Enter(list, ref lockTaken);

        }

        list.Add(item, lockTaken);

    }

    finally

    {

        list.m_currentOp = 0;

        if (lockTaken)

        {

            Monitor.Exit(list);

        }

    }

}


위와 같은 코드를 사용하더군요. Monitor를 사용해서 동기화를 시키고 있죠~? 하하하 :) 날카로운 질문을 해주신 신석현님께 감사드립니다 :)

Welcome to Parallel C#(14) - 거기까지.

C# Parallel Programming 2012. 2. 13. 09:00 Posted by 알 수 없는 사용자

자~ 그럼 이제 부터는 다시 병렬 프로그래밍으로 돌아가서 조금 이야기를 해보도록 할 까욤~? 오래전에 써놓은 자료를 보다보니..... 제가 쓴 내용이지만 무슨 내용인지 이해가 안되는 글이 많아서 다시 정리를 하느라 조금 애를 먹고 있습니다. 하하하하 :) 뭐 제 수준이 딱 거기 까지니까 말이죵 호호호호.


- 병렬 작업을 중단할 때능?

기존의 for루프를 중단할 때는 그냥 간단하게 break하나 추가해넣으면 되었습니다. for루프는 정해진 순서에 따라서 순차적으로 실행되기 때문에 특정 조건에서 break를 만나서 루프가 중단 된다고 하더라도 항상 취소되기 이전까지의 내용은 모두 실행이 되었음을 보장할 수 있었죠. 즉,

for (int i = 0; i < 100; i++)

{

    if (i == 50)

    {

        break;

    }

}


위와 같은 코드가 있다고 할 때, i가 50이되어서 for루프가 중단된다고 할 때, i가 0-49일때는 이미 다 진행된 상태라는 것이죠. 그런데 이런 루프를 빠르게 처리하기 위해서 여러 스레드를 동시에 사용하는 Parallel.For를 사용했다고 한다면... 각 스레드가 나눠서 진행중인 작업을 어떻게 중단 시켜야 할까요?


- 브레이크를 걸자.

일단은 기존의 for루프와 가장 유사한 형태를 보이는 것 부터 살펴봅시당. 일단 비교를 위해서 다음과 같은 예제를 먼저 살펴보겠습니다.

using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApplication1

{

    class Program

    {

        static IEnumerable<long> GetPrimeNumber(long num)

        {

            List<long> primeList = new List<long>();

 

            for (long i = 0; i <= num; i++)

            {

                bool isPrime = true;

                for (long j = 2; j < i; j++)

                {

                    if (i % j == 0)

                    {

                        isPrime = false;

                        break;

                    }

                }

                if (isPrime)

                {

                    primeList.Add(i);

                }

            }

 

            return primeList;

        }

       

        static void Main(string[] args)

        {

            Stopwatch sw = new Stopwatch();

            sw.Start();

            IEnumerable<long> primeList = GetPrimeNumber(99999);

            sw.Stop();

 

            Console.WriteLine("Elapsed : {0}, Found prime counts : {1}",

                sw.Elapsed.ToString(),

                primeList.Count());

            //뭔가 한다.

        }

    }

}


아주 간단한 코드인데요, 1부터 사용자가 입력한 숫자 중에서 소수를 구해서 리스트에 추가하는 코드입니다. 그리고 각 수가 소수인지 검사하는 부분에서 1이 아닌 수로 나눠지는 케이스가 발견 되면 그 즉시 break를 이용해서 중단을 시키고 있죠. 위 코드를 실행하면 CPU점유율은 아래와 같이 단일 스레드를 이용하는 것을 볼 수 있습니다.


그리고 위 코드를 3번 실행해서 얻은 평균 실행 시간은 6.28초 입니다. 그렇다면 위 코드의 Parallel.For버전을 살펴 볼까욤?

using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApplication1

{

    class Program

    {

        static IEnumerable<long> GetPrimeNumber(long num)

        {

            List<long> primeList = new List<long>();

 

            Parallel.For(0, num + 1, (i) =>

            {

                bool isPrime = true;

                Parallel.For(2, i, (j, loopState) =>

                {

                    if (i % j == 0)

                    {

                        isPrime = false;

                        loopState.Break();

                    }

                });

 

                if (isPrime)

                {

                    primeList.Add(i);

                }

            });

 

            return primeList;

        }

       

        static void Main(string[] args)

        {

            Stopwatch sw = new Stopwatch();

            sw.Start();

            IEnumerable<long> primeList = GetPrimeNumber(99999);

            sw.Stop();

 

            Console.WriteLine("Elapsed : {0}, Found prime counts : {1}",

                sw.Elapsed.ToString(),

                primeList.Count());

            //뭔가 한다.

        }

    }

}


for를 Parallel.For로 바꿨고, break역시 Parallel.For의 인자로 넘어오는 loopState변수에 대해서 Break메서드를 호출하는 것으로 변경되었습니다. 이 코드를 실행해보면, 아래와 같이 CPU의 4개의 코어가 모두 동작하는 것을 볼 수 있습니다.




그리고 역시 위 코드를 3번 실행해서 얻은 평균 실행 시간은 5.32 초입니다. 전에 비해서 약 15%의 성능향상이 있었습니다. 물론 소수를 구하는 알고리즘 자체를 더 개선할 수도 있지만, 단순히 싱글 스레드를 이용한 코드와 작업을 병렬화 시켜서 멀티 코어를 이용한 코드간의 차이가 이정도라면 의미가 있지 않을까요? 그리고 좀더 CPU를 많이 사용하는 코드일 수록 그 차이는 더 벌어지겠죠 :)


- 그런데 사실은...

위 코드에서 몇자를 바꾸고 나면, 코드의 평균 실행 시간이 5.29초로 조금 더 떨어지게 됩니다. 그 코드를 올려드릴 테니 어디를 수정한 건지 한번 찾아보시죵 :)

using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Diagnostics;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace ConsoleApplication1

{

    class Program

    {

        static IEnumerable<long> GetPrimeNumber(long num)

        {

            List<long> primeList = new List<long>();

 

            Parallel.For(0, num + 1, (i) =>

            {

                bool isPrime = true;

                Parallel.For(2, i, (j, loopState) =>

                {

                    if (i % j == 0)

                    {

                        isPrime = false;

                        loopState.Stop();

                    }

                });

 

                if (isPrime)

                {

                    primeList.Add(i);

                }

            });

 

            return primeList;

        }

        

        static void Main(string[] args)

        {

            Stopwatch sw = new Stopwatch();

            sw.Start();

            IEnumerable<long> primeList = GetPrimeNumber(99999);

            sw.Stop();

 

            Console.WriteLine("Elapsed : {0}, Found prime counts : {1}",

                sw.Elapsed.ToString(),

                primeList.Count());

            //뭔가 한다.

        }

    }

}


어딘지 찾으셨나요? 바로 loopState변수에 대해서 Break를 호출하던 것을 Stop으로 바꿔준 것입니다. 그래서 조금 더 효율적인 코드가 되는 것이죠. 왜 그럴까요? 그거에 대해서는!!!! 다음 포스팅에서 ㅋㅋㅋㅋㅋ


- 참고자료
http://stackoverflow.com/questions/1510124/program-to-find-prime-numbers

Inertia Tensor(1)

물리 2012. 2. 13. 00:09 Posted by 알 수 없는 사용자
이번에는 Inertia Tensor에 대해서 설명 드리겠습니다.
Inertia Tensor를 이해하기 위해서는,  운동량(Linear Momentum), 각운동량(Angulra Momentum), 질량중심(Center of Mass)에 대해서 알고 있어야 합니다. 그래서 이 3요소에 대해서 먼저 언급을 한 후에,  Inerta Tensor에 대해 설명 드리겠습니다.


1) 운동량(Linear Momentum)

운동량은 말 그대로 물체가 현재 운동하고 있는 정도를 나타냅니다.
아래의 그림처럼, 질량이 m1, 위치가 p1, 속도가 v1이라 물체가 있을때 이 물체의 운동량은 아래 식과 같습니다.




여기서 ML1은 1번 물체의 운동량(Linear Momentum)을 나타냅니다.
이 운동량을 시간에 대해서 미분하면(시간에 따른 변화를 측정) 아래와 같은 식이 나옵니다.


이 식을 보면, 물체의 운동량을 변화시키기 위해서는 힘이 필요하고, 외부의 힘이 없으면 물체의 운동량은 일정하게 유지 됩니다.
이게 바로 운동량 보존의 법칙입니다.


2) 각운동량(Angular Momentum)

각운동량은 임의의 기준점에 대한, 물체의 회전 운동량을 나타냅니다.
아래의 그림처럼, 질량이 m1, 위치가 p1, 속도가 v1이라 물체가 있을때, p점에서 측정한 이 물체의 각운동량은 아래의 식과 같습니다.




여기서 MA1은 1번 물체의 각운동량(Angular Momentum)을 나타냅니다.
이 각운동량을 시간에 대해서 미분하면(시간에 따른 변화를 측정) 아래와 같은 식이 나옵니다.

 



이 식을 보면, 물체의 각운동량을 변화시키기 위해서는 힘이 필요하고, 외부의 힘이 없으면 물체의 각운동량은 일정하게 유지 됩니다. 이게 바로 각운동량 보존의 법칙입니다.
그리고  각운동량은 운동량과 달리, 아래의 그림처럼 측정 지점에 따라 다르게 나타납니다.




3) 질량중심(Center of mass)
물체(RigidBody)는 아래의 그림처럼 미소질량들의 모임으로 해석할 수 있습니다. 


질량중심은 두가지 의미를 가지고 있습니다.
(1) 물체의 각 미소질량들이 동일한 힘을 받을때,  질량중심은 아래 그림처럼 그 물리량을 대표할 수 있는 하나의 지점을 의미합니다. 미소질량들이 받는 동일한 힘의 대표적인 예가 중력입니다. 질량중심에 반대의 힘을 가하면 물체의 운동량은 정지되게 됩니다. (중심을 잡게 됩니다.)


식으로 나타내면 아래와 같습니다.
여기서 Pc가 구하고자 하는 질량 중심이고, Pxyz는 미소질량의 위치, P는 측정 위치를 나타냅니다.



이식을 풀면, 아래과 같은 식이 나옵니다.


(2) 질량중심에서는 아래의 그림처럼 상대운동(속도, 가속도)이 모두 0이 됩니다.


식으로 나타내면 아래와 같습니다.
아래의 식을 풀어도 (1)과 같은 결과를 얻습니다.


이상으로 운동량, 각운도량, 질량중심에 대한 설명을 마치겠습니다.

 


 

'물리' 카테고리의 다른 글

Inertia Tensor(2)  (0) 2012.02.19
RigidBody의 Restitution, Friction, Damping  (2) 2012.02.06
[Bullet Physics] RigidBody 만들기  (2) 2012.01.05
[Bullet Physics] Bullet 물리엔진의 설치  (2) 2011.12.22