Lecture 4 - Introduction to Plato's Phaedo; Arguments for the Existence of the Soul, Part II [January 25, 2007] 

Chapter 1. Introduction to Plato's Phaedo [00:00:00] 

 이건 좀 듣기 어려움. 아무튼 대충 내용은 플라톤의 파이돈을 다음주까지 읽어와야 토의를 진행할 수 있다는 말과 함께 어떤 개념이 플라톤이 말한것이건 소크라테스가 말한 것이건 각각의 view가 무엇이 있는지가 중요하지 그 개념이 어디서 왔는지는 이 수업에서는 중요하지 않다고 함. 

Phaedon(파이돈)의 주제는 소크라테스의 마지막 날에 맞춰져 있다.  대화의 끝에서  그는 독약을 먹고 죽지만 아마도 죽기직전까지 그가 친구들과 토의했던 것은 영혼의 불멸성일 것이다. 그는 놀랍게도 그의 죽음에 대해서 불안해하지도 걱정하지도 않았었다. 그는 그의 영혼의 불멸성을 믿었기 때문에 그의 죽음을 기꺼이 받아들였다. 

Chapter 2. Creativity and Reason in Machines [00:08:27]

 우리는 어떻게 영혼의 존재를 논의할 수 있을까? 저번에 우리는 몇가지 얘기를 했었다. 사람은 단지 기계가 이나라고, 왜냐하면 기계는 추론할 수 없고 생각할 수 없기 때문에. 하지만 이건 주목할 만한 주장은 아닌것 같다. 채스 게임을 하는 컴퓨터는 추론을 할 수 있는것 처럼 보인다. 그들은 목적에 대한 열망을 가졌고 그것을 이루려고 노력한다. 그리고 유저를 패배시키기위해 추론한다.

여기서 우리는 컴퓨터가 무엇인지 적어도 베스트 채스 플레잉 컴퓨터가 못하는 것이 무엇인지 알 필요가 있다. 여러분은 아마도 컴퓨터는 그냥 모든 게임에 대해 모든 가능한 수를 계산해서 움직인다고 생각할 수 있다. 하지만 채스 플레잉 프로그램은 이렇게 작동하지 않는다. 모든 가능한 채ㅡ 게임의 수는 너무 많이 때문이다. 이걸 다 계산하려면 너무 오래 걸린다. 

그렇다면 채스 플레잉 게임 프로그램은 어떻게 동작할까?. 그것들은 당신이 하는것과 똑같은 방버으로 동작한다. 어떤 것을 움직이는것이 좋을까에 대한 몇몇 가능한 수를 가지고 성공적인 수를 선택한다. 만약 당신이 채스 게임에서 진다면 다음엔 좀더 다른 시도를 할 것이다. 컴퓨터도 당신과 같이 작동한다. 만약 당신이 이 채스 프로그램과 채스 게임을 한다면, 프로그램이 어떻게 움직일지 생각하고 두지 않을 것이다. 이 프로그램을 디자인 하는 사람들 또한 “내가 이렇게 프로그램 했으니 저렇게 움직일꺼야” 하고 두지 않는다. 왜냐하면 채스 게임 프로그램은 지속적으로 상황에 맞게 전략을 바꾸기 때문이다. 따라서, 그냥 실력있는 채스 플레이어로 간주하고 채스를 둘 것이다. 현재 채스 프로그램은 사람 채스 챔피언을 이겼다. 지금은 누구도 채스 프로그램을 이기지 못한다. 

Chapter 3. Feelings in Machines, from Marvin to Hal [00:13:43]

“기계는 추론할 수 있는가?”에 대한 질문은 몇몇 영역에서는 “그렇다" 이다. 따라서 추론능력으로 사람이 영혼이 있다고 말하는 것은 타당하지 않은것 같다. 하지만 영혼이 있다고 믿는 사람들은 이렇게 주장할 수 있다. 느낌, 사랑, 두려움, 기분등은 어떤가? 세상에 어떤 기계도 감정을 가질 수 없기 때문에 사람은 단지 physical object가 아니다.

그렇다면, “혹시 무언가를 느낄수 있고 어떤 감정을 가질 수 있는 기계가 있는가?”를 질문해보자. 예전의 TV show중에 어떤 위험상황이 닥치면 위험하다고 계속 외치는 로봇이 있었다. 이것은 로봇이 걱정을 하는것 처럼 보인다.

또 다른 예로, 2001년 A Space Odyssey라는 영화에서는 어떤 행성을 조사하기 위해서 우주비행사들을 파견하는데, 이때 우주선에는 이 중요한 미션을 도와줄 컴퓨터 프로그램 Hal이 함께 탑재되어있었다. 그런데 이 Hal은 사람이 이 중요한 미션을 망친다는 것을 감지하여 사람들을 모조리 죽이고 미션을 수행하려고 한다. 이를 알아챈 우주비행사 Dave가 Hal을 정지시키려고 하는데, Hal이 이것을 감지하고 Dave를 막으려 한다. 영화내용중 Hal은 Dave한테 말한다. 나는 죽는게 두렵다고. 

물론 이건 픽션이지만, “컴퓨터가 말하고 두려워하다니 말도 안돼!”라고 말하는건 편견이라고 본다. 컴퓨터 프로그램이 감정을 느끼는 것은 자연현상이라고 본다. 하지만 컴퓨터는 단지 기계일 뿐이니까 그 안에 회로 이상의것은 없다는 주장이다. 만약에 이것이 맞다면 우리가 감정을 느끼는 것이 영혼의 존재로 설명할 필요가 없다는 것이다.

(실제로 있지 않는 픽션으로 이끌어내는 결론이 좀 맘에 안들긴 하는데, 고도의 지능에 국한된 기능으로 본다면 그럴수 도 있다고 생각한다.) 

 Chapter 4. Qualia in Emotion and Consciousness: The Dualist's Defense and Its Weakness [00:20:34]

 Dualist의 입장에서 생각해보면, 감정과 느낌은 각각 두가지 관점으로 나눌수 있다. 하나는 행동적인 측면이다. 어떤 환경적 변화나 위험을 감지하고 어떠한 행동을 하는 것. 사람과 로봇 둘다 할 수 있는것이 있는 측면이 있고, 또 다른 하나는 경험의 질적인 측면인데, 행동적인 측면과 동시에 일어나는 내면의 느낌이다. 어떤 행동을 하는것이 아닌 내면에 느껴지는 것. 로봇은 이것이 없다는 주장이다.

만약에 빨간색을 본다면, 사람과 로봇 모두 그것을 보고 빨간색이라고 할 수 있다. 하지만 로봇은 이 경험의 질적인 측면을 가질 수 없다. 만약 태어날 때 부터 장님이 사람이 있다면, 그는 빨간색을 알 수 있을까? 만약 그가 과학자라서 어떤 결과로 부터 그것이 빨간색임을 알아낼 수는 있지만 그는 빨간색을 본것이 아니고 상상할 수도 없다. 따라서 이 질적인 경험을 가질 수 없다. 또한 우리의 삶은 이런 측면들로 채워져있다. 색깔, 소리, 냄새등. 이런것엔 경험의 질적인 측면이 있다.

우리는 우리자신에게 물어볼 수 있다. “빨간색을 보면 어떤 기분이 들까?, 커피나 파인애플을 먹으면 어떤기분이 들까?” 컴퓨터는 이런 질적 경험을 모르기때문에 우리는 로봇과 다르다고 주장할 수 있다.

그렇다면 이에 대해서 Physicalist는 어떤 생각일까? 기계한테 의식을 넣을 수 있는 방법이 있다면, 기계또한 내면의 질적 경험을 가질 수 있다. 하지만 지금시점에서, 우리는 어떻게 의식을 넣을 수 있는지 모른다. 만약 이 의식을 우리의 정신적 삶의 질적 측면이라고 한다면, phisical term으로는 설명할 수 없다. 바로 이것이 dualist가 "이것을 설명하기 위한건 soul뿐이야” 라고 말하는 것을 그냥 무시할 수 없는 것이다. 하지만 이것이 soul을 믿는다는 것은 아니다. 아직 우리가 의식에 대해서 물리적으로 설명할 수 없을 뿐이라는 것이다.

14세기에 인간의 삶에 대해서 물리적으로 설명할 수 있었을까? 지금은? 완벽히는 아니지만 14세기에 보다는 좀더 삶에대해서 잘 이해할 수 있는 위치에 있을 것이다. 의식도 마찬가지로 아직 그 영역에 대해서 모를뿐이라는 것이다. dualist들에게 그럼 의식은 어떻게 동작하냐고 물어보면 그들도 그것에 대해서 설명할 수 없다. 만약 soul의 구조와 동작원리에 대해서 누군가가 설명할 수있다면 나는 그 주장을 받아들이겠다. 하지만 설명할 수 없다면 이것은 better explanation이 아니다.

감정말고 창의성(Creativity)에 대해서 생각해 보자. 우리는 음악을 만들 수 있고, 시를 지을 수 있다. 기계는 못하지 않는가? 아니다. 기계도 창의적인 행동을 할 수 있다. 채스 챔피온을 이긴 컴퓨터가 한 행동은 세상 그 누구도 하지 않았던 독창적인 행동이다. 그리고 사람이 들었을때 멜로디를 느낄 수 있는 랜덤이 아닌 세상에 없던 음악을 만드는 기계도 있다. 따라서 이것은 soul을 설명하기에 적합하지 않다.

Chapter 5. Free Will as a Defense of the Soul and Conclusion [00:42:20]

 창의성은 적합하지 않다. 하지만 창의적인 컴퓨터 프로그램은 그냥 일련의 코드를 실행할 뿐이다. 로봇은 그냥 자동으로, 필요에의해서, 기계적으로 프로그램을 따른다. 바로 자유의지를 가지고 있지 않다. 

이건 soul에 대한 새로운 논의 거리다.
1. 우리는 자유의지를 가졌다
2. 결정론을 따르지 않는 것들은 자유의지를 가졌다. (컴퓨터 처럼 state와 코드 라인에 따라서 결과가 결정되는 것들을 결정론을 따르는 것이라고 봄)
3. 모든 순수히 물질적인 시스템은 결정론을 따른다.

4. 위 3개의 전제로부터 결론은 우리는 순수 물질적인 시스템이 아니라는 것이다.


블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

원문: http://oyc.yale.edu/philosophy/phil-176#sessions

Lecture 3 - Arguments for the Existence of the Soul, Part I [January 23, 2007] 

Chapter 1. Introduction: The P-Functioning Body [00:00:00] 

Physicalist의 관점에서 바라볼때 사람은 분필이나 종이컵같은 object들과는 달리 여러가지 행동들을 할수있는 body라고 봤었다. 이때 이 여러가지 행동들을 P abilities라고 본다면, 이 행동을 하는 것을 P-functioning이라고 할 수 있고 결국 사람을 P-functioning body라고 정의할 수 있다.

Chapter 2. The Mind According to Physicalists and Dualists [00:03:02]

 먼저 Physicalist 관점에서 mind는 그냥 body가 생각하고 대화하고 계획하고 시를쓰고 사랑에 빠질 수 있다는 사실을 설명하는 하나의 방법이라고 본다. 여기서는 계속 "mind = 설명하는 방법"이다 라고만 나오고 다른말은 안하는데, 오역일지는 모르겠지만 개인적인 느낌으로는 결국 mind는 이러한 P function들을 하기위한 수단, 기능이라고 할수 있을 것 같다. 

"mind는 그냥 이런 기능을 하기위한 수단이야 다른건 없어

The mind is just a way of talking about the fact that our body can think, can communicate, can plan, can deliberate, can be creative, can write poetry, can fall in love. Talk of all of those things is what we mean by the mind, but there's no extra thing, the mind, above and beyond the body.

 다음으로, dualist의 관점으로 볼때, mind는 body와 분리되어있는 soul이라고 본다. immaterial object.


Chapter 3. Inferences to the Best Explanation to Prove the Soul's Existence [00:12:17]

 이 두가지 관점중 우리는 무엇을 믿을 것인가? 다시말해 우리는 soul의 존재를 믿어야 하는가? 보통 우리가 무언가가 존재한다는 것을 믿을때, 예를들어, 의자가 있다는것, 나무가 있다는것, 새들이 있다는것은 우리가 볼수 있고 만질수 있기 때문이다. 그런데 soul은 immaterial object이기 때문에 우리가 가지고있는 오감을 통해 느낄수 없기때문에 이러한 방법으로는 존재한다는 것을 믿을 수 없다. 

보통 이럴때 사용하는 방법이 있는데, 예를들어 우리는 원자가 존재하는것을 믿지만 원자 역시 너무 작기때문에 눈에 보이지 않는다. 그럼 원자의 존재를 어떻게 믿는가? 원자 이론에 의해서 설명할 수 있는것들이 있기 때문이다. 원자들이 모여서 결국 이런저런 물질을 이루게되는것과 결국 physical world의 구성을 설명할수 있는것 등. 우리는 우리가 볼 수 없는 무언가가 존재한다는 것을 추론할 수 있다. 그 무언가의 존재가 다른것으로는 설명할 수 없는 어떤것을 설명할 수 있는 근거가 된다면. 우리는 이러한 패턴을 inferences to the best explanation이라고 한다. 

이러한 패턴의 예로, 왜 우리는 눈에 보이지 않는 세균이 존재한다고 당연히 믿는가? 이것은 사람들이 병에 걸리는 이유를 설명해 주기 때문이다. (세균은 장비를 통해서 눈에 보이지 않나? 뭔가 슬슬 어거지가 느껴지지만 일단 여기서 말하는 바가 이럼)

병에 걸리는 또다른 의견이 있다. 왜 사람이 병에 걸리고 죽는가? 바로 악령이 씌워서이다. 이것 또한 한가지 explanation이다. 하지만 왜 사람이 병에 걸리는가에 대한 best explanation은 세균 감염에 의한 주장이다. 왜냐하면 누가 어떤 종류의 질병에 걸렸는지, 어떻게 질병이 퍼졌는지, 어떻게 치료되었는지, 언제 누가 죽었는지에 대해 더 잘 설명할 수 있는 것이 전자이기 때문이다.


Chapter 4. Can Only the Soul Justify Feature F? [00:19:55]

그렇다면, physicalist는 설명할 수 없는, soul이 존재한다고 가정해야지만 설명이 가능한 어떤 특징들이 존재하는가? 

일단, 이러한 특징이 하나 존재한다고 가정해보자. 이 특징(feature)을 F라고 하자. F는 이런것이 될 수 있을 것이다.

만약 Love를 physicalist perspective로 봤을때 설명이 불가능하고, soul로만 설명이 가능하다면. 이것은 inference to the best explanation의 예가 될 수 있다.

이렇게 physicalist는 설명할 수 없거나 설명할 수 있더라도 dualist view의 설명보다 좋은 설명이 못되는 케이스의 F가 존재한다면, 우리에게 soul이 있다고 믿을 수 있는 근거가 생길 수 있다.


Chapter 5. Abilities, Desires, Emotions – Candidates for Feature F [00:35:07]

 Abilities: to think, to reason

 Belief, Desire 

 Emotions: Love, afraid, hope 

 로봇은 외부로부터 주입되어 프로그램 되어있는 명령이 아닌 그 이상의 것을 하지 못한다. 위 세가지 영역은 사람만이 할 수 있지만 이것은 physicalist view로는 설명이 힘든 부분이다. 



블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

*Lecture 1은 그냥 introduce다. 뭐 대학교 수강신청 기간이라고 보면될듯? 그래서 렉쳐 2부터 시작하고..

현재는 렉쳐5를 쓰다가 말았는데.. 흠 아무튼. 이 강의 내용은 죽음과 죽음 이후에 대한 강의이다. 죽음이후라면 영혼의 존재 유무를 논할 것이다.

원문: http://oyc.yale.edu/philosophy/phil-176#sessions


Lecture 2. The Nature of persons: Dualism vs Physicalism

(Yale University Philosophy class)

(Dualism => 영혼+육체, Physicalism => 육체)

Chapter 1. "Is There Life After Death?" Asking the Right Question [00:00:00]

Is there life after death? 는 death가 end of life를 뜻하기 때문에 영화끝난후에 영화가 있냐? 라는 질문과 같이 앞뒤가 맞지 않는 질문이다. 질문은 Might I still exist after the death of my body?로 부터 시작해야 한다.

Chapter 2. Ways to Conceptualize Self-Identity [00:13:25] 

이 질문에 앞서 우리는 먼저 What am I? What kind of an entity am I? What am I made of? What kind of a thing is a person? 나 자신이 무엇인지, 어떻게 만들어 졌는지, What is a person을 이해할 필요가 있다. What is a person을 두 가지 뷰로 바라본다면,

첫째로, A person is a combination of a body and something else (a soul). 우리몸은 뼈와 살 덩어리, 그리고 이와는 확연히 구분되는 분자와 원자로 이루어져있지 않은 영혼이란 것이 있다.(nonphysical terms, nonmaterial terms) 우리는 이 뷰를 Dualist View라고 부르겠다.

또 다른 뷰는 monist view 다. A person is just a certain kind of material object. 
사람은 그냥 반응하고 관계를 맺을수 있고, 서로 대화하고, 계획하고, 사랑하고, 시를 쓰고 등등의 행위를 할수 있는 body일 뿐이다. 영혼은 없다는 관점.

Chapter 3. Dualists: The Body-Soul Perspective [00:21:18]

 soul은 mind라고 본다. soul는 body를 움직이며 또한 반대로 body가 어떤 반응을 받으면(무언가에 부딪치거나) soul이 고통을 느낀다. soul과 body간에 two-way interaction이 있다고 본다. 

때때로 soul은 우리몸 특정위치 부근에 있다고 여겨지기도 한다. 어쨌거나 중요한 포인트는 body의 생이 끝났을때(물질적으로 썪어 없어져서 완전히 끝났을때) soul은 물질적인 것이 아니기 때문에 계속해서 존재할 수도 있다는 것이다. 이것이 dualist view이다. 

죽음은 무엇인가? 만약 body와 soul이 강하게 연결되어있다면, 죽음은 이 연결을 끊는 것이라고 볼 수 있을 것이다. 이렇게되면 더이상 body는 soul에게 body의 input을 전달하지 못하게 되고, soul또한 body를 더이상 컨트롤 할 수 없게된다. 그리고 soul은 계속해서 존재할지도 모른다. 

만약 사람을 soul과 body의 조합으로 본다면, death로 인해 이 연결이 끊어져서 soul만 남았을때 이것을 사람으로 볼 수 없을 것이다. 하지만 죽음이후의 삶이 있다고 믿고 싶다면, soul자체를 사람으로 보고, body는 하나의 집처럼 생각할 수 있다. 따라서 body가 죽은이후에도 soul은 계속해서 존재할 수 있다. 

우리는 이 관점을 바라보면서 3가지 질문을 던질 수 있다. 첫째로, 정말 soul이 존재하는가? 정말로 body와 soul이 구별되어있는가? 둘째, 만약 soul이 있다면, body가 죽은 후에도 계속 존재할 수 있는가? 마지막으로, body가 생겨나고 끝나는 B1~Bn의 process가 있는것에 비해, Soul도 S1~Sn의 process가 있는가? 있다면 그 기간이 얼마인가? 영원한가?  


Chapter 4. The Physicalists: The Body Is a Body and Conclusion [00:39:00] 

The physicalist view, materialist view. 사람은 그냥 물체다. 분필 또한 물체다. 하지만 분필은 스스로 할 수 있는것이 없다. 스마트폰 또한 물체다 하지만 분필보다는 여러가지 스스로 할수있는 일이 많다. 이 관점에 따르면 사람은 생각할 수 있고, 계획할 수 있고, 추론할 수 있고, 느낄 수 있고, 두려워할 수있고 창조할 수 있고, 꿈꿀수 있고 등등 좀 더 많은 것을 할 수있는 물체일 뿐이다.


블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

[펌] C/C++ volatile 키워드

c++ 2015. 12. 10. 14:35

일단 눈에 들어오는 부분은,

현재 레지스터에 있는 값을 쓰는게 아니라 혹시라도 다른곳에서 바꿨을 수도 있으니 이값은 매번 메모리에서 가져와서 써야한다!

하고 명시해주는 것이 volatile 키워드라는 것인것 같다.


출처: http://skyul.tistory.com/337

================ 퍼온 내용 ================


2006년 9월 마이크로소프트웨어 기고글입니다.


약 60여개의 C++ 키워드 중에 가장 사용 빈도가 낮은 키워드는 무엇일까? 정답은 volatile이다. 대부분의 C/C++ 참고 서적은 1-2줄 정도로 volatile이 컴파일러의 최적화(optimization) 막아준다고만 설명하고 있다. 또한 Java5 이후로 명확한 메모리 모델이 확립된 자바와는 달리 C/C++의 경우 volatile에 대한 명확한 표준이 없고 컴파일러마다 구현에 차이가 있다는 점도 volatile 키워드의 사용을 어렵게 하고 있다. 하지만 임베디드 시스템이나 멀티쓰레드 프로그래밍이 보편화된 만큼, 이 글에서는 volatile 키워드의 기초부터 다시 살펴보고자 한다.


volatile 소개

volatile로 선언된 변수는 외부적인 요인으로 그 값이 언제든지 바뀔 수 있음을 뜻한다. 따라서 컴파일러는 volatile 선언된 변수에 대해서는 최적화를 수행하지 않는다. volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다. 왜 volatile이라는 키워드가 필요한지 이해하려면 먼저 일반적인 C/C++ 컴파일러가 어떤 종류의 최적화를 수행하는지 알아야 한다. 가상의 하드웨어를 제어하기 위한 다음 코드를 살펴보자.


*(unsigned int *)0x8C0F = 0x8001

*(unsigned int *)0x8C0F = 0x8002;

*(unsigned int *)0x8C0F = 0x8003;

*(unsigned int *)0x8C0F = 0x8004;

*(unsigned int *)0x8C0F = 0x8005;

잘못된 하드웨어 제어 코드


이 코드를 보면 5번의 메모리 쓰기가 모두 같은 주소인 0x8C0F에 이루어짐을 알 수 있다. 따라서 이 코드를 수행하고 나면 마지막으로 쓴 값인 0x8005만 해당 주소에 남아있을 것이다. 영리한 컴파일러라면 속도를 향상시키기 위해서 최종적으로 불필요한 메모리 쓰기를 제거하고 마지막 쓰기만 수행할 것이다. 일반적인 코드라면 이런 최적화를 통해 수행 속도 면에서 이득을 보게 된다.

하지만 이 코드가 MMIO(Memmory-mapped I/O)처럼 메모리 주소에 연결된 하드웨어 레지스터에 값을 쓰는 프로그램이라면 이야기가 달라진다. 각각의 쓰기가 하드웨어에 특정 명령을 전달하는 것이므로, 주소가 같다는 이유만으로 중복되는 쓰기 명령을 없애버리면 하드웨어가 오동작하게 될 것이다. 이런 경우 유용하게 사용할 수 있는 키워드가 volatile이다. 변수를 volatile 타입으로 지정하면 앞서 설명한 최적화를 수행하지 않고 모든 메모리 쓰기를 지정한 대로 수행한다.


*(volatile unsigned int *)0x8C0F = 0x8001

*(volatile unsigned int *)0x8C0F = 0x8002;

*(volatile unsigned int *)0x8C0F = 0x8003;

*(volatile unsigned int *)0x8C0F = 0x8004;

*(volatile unsigned int *)0x8C0F = 0x8005;

올바른 하드웨어 제어 코드


특정 메모리 주소에서 하드웨어 레지스터 값을 읽어오는 프로그램의 경우도 마찬가지다. 아래 코드의 경우 같은 주소에서 반복적으로 메모리를 읽으므로, 최적화 컴파일러라면 buf[i] = *p;에서 *p를 한 번만 읽어온 후에 그 값을 반복해 사용할 것이다. 하지만 volatile 키워드가 있다면 *p를 참조할 때마다 직접 메모리에서 새 값을 가져와야 한다. 이 경우는 하드웨어가 메모리 0x8C0F 번지 값을 새롭게 변경해 주는 경우이다.


void foo(char *buf, int size)

{

     int i;

     volatile char *p = (volatile char *)0x8C0F;

 

     for (i = 0 ; i < size; i++)

     {

         buf[i] = *p;

         ...

     }

}

하드웨어 레지스터 읽기


가시성


volatile 키워드는 앞서 살펴본 하드웨어 제어를 포함하여 크게 3가지 경우에 흔히 사용된다.

 

(1) MMIO(Memory-mapped I/O)

(2) 인터럽트 서비스 루틴(Interrupt Service Routine)의 사용

(3) 멀티 쓰레드 환경

 

세 가지 모두 공통점은 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다. 인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용하면 된다.


int done = FALSE;

 

void main()

{

     ...

     while (!done)

     {

         // Wait

     }

     ...

}

 

interrupt void serial_isr(void)

{

     ...

     if (ETX == rxChar)

     {

         done = TRUE;

     }

     ...


serial.c


위 시리얼 통신 예제는 전역 변수로 done을 선언해서 시리얼 통신 종료를 알리는 ETX 문자를 받으면 main 프로그램을 종료시킨다. 문제는 done이 volatile이 아니므로 main 프로그램은 while(!done)을 수행할 때 매번 메모리에서 done을 새로 읽어오지 않는다는 점이다. 따라서 serial_isr() 루틴이 done 플래그를 수정하더라도 main은 이를 모른 채 계속 루프를 돌고 있을 수 있다. done을 volatile로 선언해주면 매번 메모리에서 변수 값을 새로 읽어오므로 이 문제가 해결된다.

인터럽트의 경우와 마찬가지로 멀티 쓰레드 프로그램도 수행 도중에 다른 쓰레드가 전역 변수 값을 임의로 변경할 수 있다. 하지만 컴파일러가 코드를 생성할 때는 다른 쓰레드의 존재 여부를 모르므로 변수 값이 변경되지 않았다면 매번 새롭게 메모리에서 값을 읽어오지 않는다. 따라서 여러 쓰레드가 공유하는 전역 변수라면 volatile로 선언해주거나 명시적으로 락(lock)을 잡아야 한다.

이처럼 레지스터를 재사용하지 않고 반드시 메모리를 참조할 경우 가시성(visibility) 이 보장된다고 말한다. 멀티쓰레드 프로그램이라면 한 쓰레드가 메모리에 쓴 내용이 다른 쓰레드에 보인다는 것을 의미한다.


문법과 타입


C/C++의 volatile은 상수(constant)를 선언하는 const와 마찬가지로 타입에 특정 속성을 더해주는 타입 한정자(type qualifier)이다. const int a = 5; 라고 선언했을 경우 a라는 변수는 정수 타입의 변수이면서 동시에 상수의 속성을 가짐을 의미한다. 같은 방식으로 volatile int a; 라고 선언해주면 정수 변수 a는 volatile 속성을 가지게 된다.

조심해야 할 것은 포인터 타입에 volatile을 선언하는 경우이다. int *를 volatile로 선언하는 몇 가지 방법을 비교해보자.


volatile int* foo;

int volatile *foo;

int * volatile foo;

int volatile * volatile foo;

volatile의 선언


복잡하게 보이지만 원리는 const 타입 한정자와 동일하다. *를 기준으로 왼쪽에 volatile 키워드가 올 경우 포인터가 가리키는 대상이 volatile함을 의미하고, * 오른쪽에 volatile 키워드가 올 경우에는 포인터 값 자체가 volatile임을 의미한다. volatile이 *의 양쪽에 다 올 경우는 포인터와 포인터가 지시하는 대상이 모두 volatile함을 의미한다. 하드웨어 제어의 예처럼 일반적으로 포인터가 가리키는 대상이 volatile 해야 할 경우가 많으므로 volatile int * 형태가 가장 많이 사용된다. volatile int*와 int volatile*은 동일한 의미이다.


01: int foo(int& a)

02: {

03: }

04:

05: int bar(volatile int& b)

06: {

07: }

08:

09: int main()

10: {

11:     volatile int a = 0;

12:     int b = 0;

13:

14:     foo(a);

15:     bar(b);

16: }

 

$ g++ a.cc

a.cc: In function `int main()':

a.cc:14: error: invalid initialization of reference of type 'int&' from expression of type 'volatile int'

a.cc:2: error: in passing argument 1 of `int foo(int&)'


또한 int는 volatile int의 서브타입에 해당한다. 서브 타입을 쉽게 설명하면 A를 요구하는 곳에 언제든지 B를 사용할 수 있다면 B는 A의 서브 타입이다. 쉬운 예로 Class의 경우 Derived 클래스는 Base 클래스의 서브 타입이다. volatile int를 요구하는 곳에 언제든지 int를 넘길 수 있으므로 int는 volatile int의 서브 타입이라고 말할 수 있는 것이다. 그 예로 위 C++ 프로그램은 컴파일 에러가 난다. volatile int를 받는 bar() 함수에 int 인자로 호출하는 것은 아무 문제없지만, int를 요구하는 foo() 함수에 volatile int 타입인 a를 넘기면 컴파일 에러가 나는 것이다.


01: int foo(int& a)

02: {

03: }

04:

05: int bar(const int& b)

06: {

07: }

08:

09: int main()

10: {

11:     const int a = 0;

12:      int b = 0;

13:

14:     foo(a);

15:      bar(b);

16: }

foobar2.cc


이 관계가 쉽게 이해되지 않는다면 위의 예처럼 volatile 키워드를 같은 타입 한정자인 const로 대체해보자. 변수 a는 const int 타입이므로 이미 상수로 선언되었다. const int인 a를 int를 요구하는 foO() 함수에 넘기면 const라는 가정이 깨어지므로 컴파일 에러가 된다. 반대로 b의 경우 원래 const가 아니었지만 bar로 넘기면서 const 속성을 새롭게 부여받게 된다. const 속성을 부여받는다고 말하면 무엇인가 기능이 추가되는 것 같지만, 이는 바꿔 말해서 원래 int의 2가지 기능인 읽기, 쓰기에서 쓰기 기능이 사라지는 것으로 볼 수도 있다. 이를 클래스로 표현해 보면 다음과 같을 것이다.


class ConstInteger {

public:

     ConstInteger(int v) : value(v) {}

     int get() { return value; }

 

protected:

     int value;

};

 

class Integer : public ConstInteger {

public:

     Integer(int v) : ConstInteger(v) {}

     void set(int v) { value = v; }

};

ConstInteger.cc


위 클래스를 두고 보면 volatile/const int와 int의 관계가 명확해진다. Integer는 ConstInteger을 상속한 클래스이므로 ConstInteger를 요구하는 곳 어디에나 쓸 수 있는 것이다. 반대로 Integer가 필요한 곳에 ConstIntger를 넘기면 set() 메쏘드가 없으므로 문제가 된다. 따라서 컴파일러는 이를 금지하는 것이다.

volatile의 const와 같은 맥락에서 생각할 수 있다. volatile 속성을 부여받는 다는 것은 바꿔 말하면 컴파일러가 최적화를 할 자유를 잃는다고 말할 수 있다. ConstInteger의 경우만큼 명확하지는 않지만 이 관계를 클래스로 생각해 본다면 아마 다음과 같을 것이다.


class VolatileInteger {

public:

     VolatileInteger(int v) : value(v) {}

     int get() { return value; }

     void set(int v) { value = v; }

 

protected:

     int value;

};

 

class Integer : public VolatileInteger {

public:

     Integer(int v) : VolatileInteger(v) {}

     void optimize();

};

VolatileInteger.cc


재배치(reordering)


지금까지 volatile 키워드의 일반적인 기능과 문법에 대해서 살펴보았다. C 표준은 volatile 키워드와 메모리 모델에 대한 명확한 정의를 내리지 않고 있기 때문에 컴파일러마다 그 구현에 다소 차이가 있다. C++ 표준은 volatile 대해 별도의 정의하지 않고 가능한 한 C 표준을 따르라고만 하고 있다.

마이크로소프트의 Visual C++를 예로 들어보면, volatile 키워드에 앞서 살펴본 가시성(visibility) 뿐만 아니라 재배치(reordering) 문제에 대한 해결책도 추가하였다. Visual C++의 volatile 변수는 다음과 같은 기능을 추가로 한다.

 

(1) volatile write: volatile 변수에 쓰기를 수행할 경우, 프로그램 바이너리 상 해당 쓰기보다 앞선 메모리 접근은 모두 먼저 처리되어야 한다.

(2) volatile read: volatile 변수에 읽기를 수행할 경우, 프로그램 바이너리 상 해당 읽기보다 나중에 오는 메모리 접근은 모두 이후에 처리되어야 한다.

 

재배치(reordering)는 컴파일러가 메모리 접근 속도 향상, 파이프라인(pipeline) 활용 등 최적화를 목적으로 제한된 범위 내에서 프로그램 명령의 위치를 바꾸는 것을 말한다. 우리가 프로그램에 a = 1; b = 1; c = 1; 이라고 지정했다고 해서 컴파일된 바이너리가 반드시 a, b, c 순서로 메모리를 쓰지 않을 수 있다는 뜻이다. 만약 a, c가 같은 캐시(cache)에 있거나 인접해 있어서 같이 쓸 경우 속도 향상을 볼 수 있다면 a = 1; c = 1; b = 1; 로 순서가 바뀔 수 있는 것이다.

Visual C++의 경우 volatile을 사용하면 컴파일러가 수행하는 이러한 재배치에 제약을 주게 된다. a = 1; b = 1; c = 1;에서 c가 volatile로 선언된 변수였다면 a = 1;과 b=1;은 반드시 c에 1을 대입하기 전에 일어나야 한다. 물론 a와 b 사이에는 순서가 없으므로 b = 1; a = 1; c = 1; 과 같은 형태로 재배치가 일어날 수는 있다. 재배치가 일어나지 않도록 보장하는 문제가 왜 중요한지는 MSDN에서 발췌한 다음 예를 통해 살펴보자.


#include <iostream>

#include <windows.h>

using namespace std;

 

volatile bool Sentinel = true;

int CriticalData = 0;

 

unsigned ThreadFunc1( void* pArguments ) {

while (Sentinel)

Sleep(0); // volatile spin lock

 

// CriticalData load guaranteed after every load of Sentinel

cout << "Critical Data = " << CriticalData << endl;

return 0;

}

 

unsigned ThreadFunc2( void* pArguments ) {

Sleep(2000);

CriticalData++; // guaranteed to occur before write to Sentinel

Sentinel = false; // exit critical section

return 0;

}

 

int main() {

HANDLE hThread1, hThread2;

DWORD retCode;

 

hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc1,

NULL, 0, NULL);

hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc2,

NULL, 0, NULL);

 

retCode = WaitForSingleObject(hThread1,3000);

 

CloseHandle(hThread1);

CloseHandle(hThread2);

 

if (retCode == WAIT_OBJECT_0 && CriticalData == 1 )

cout << "Success" << endl;

else

cout << "Failure" << endl;

}

volatile.cpp


프로그램 수행은 간단하다. 이 프로그램은 쓰레드를 2개 생성하는데 ThreadFunc1은 Sentinel 플래그가 true인 동안 루프를 돌고, ThreadFunc2는 잠시 기다렸다 Sentinel 플래그를 false로 만들어준다. ThreadFunc2는 Sentinel을 false로 만들기 전에 전역 변수인 CriticalData을 1만큼 증가시킨다..

이 프로그램에서 만약 Sentinel이 volatile로 선언되지 않았다면 ThreadFunc1은 가시성을 보장받지 못하므로, ThreadFunc2가 Sentinel의 값을 바꾸더라도, 레지스터에 든 값을 사용해 영원히 루프를 돌 수 있음은 이미 살펴보았다. 그럼 이번에는 volatile로 선언해서 Sentinel의 가시성이 보장된다면 이 프로그램의 수행 결과는 어떻게 될까?

간단히 생각하면 CriticalData를 1증가 시킨 이후에 Sentinel을 false로 바꾸므로 ThreadFunc1은 1이라는 값을 찍을 것이라고 생각할 것이다. 하지만 CriticalData는 volatile이 아니므로 여전히 메모리가 아닌 ThreadFunc2의 레지스터에만 남아있을 확률이 있다. 이 경우 ThreadFunc1은 변경된 CriticalData가 아닌 0이라는 값이 나올 수 있다. 수행 타이밍에 따라서 0이 되기도 1이 되기도 하는 것이다.

가정을 바꿔서 CriticalData 또한 volatile이라고 해보자. 모든 문제가 해결된 것 같지만, 결과는 여전히 0 혹은 1이 나온다. CriticalData가 volatile이면 레지스터가 아닌 메모리에 직접 쓰므로 가시성은 확보되지만, 재배치의 문제가 남아있다. 컴파일러가 보기에 ThreadFunc2의 CriticalData++과 Sentinel = false는 전혀 관계없는 변수이다. 따라서 최적화를 이유로 이 순서를 뒤집어 Sentinel = false를 먼저 수행하고 CiriticalData=+을 수행할 수 있다. 이 경우 ThreadFunc2에서 Sentinel = false만 수행하고 컨텍스트 스위치(context switch)가 일어난 경우 ThreadFunc1은 아직 CriticalData++이 수행되기 전에 CriticalData 값인 0을 읽을 수 있다.

여기서 Visual C++가 추가한 시멘틱(semantic)을 적용해보자. ThreadFunc2에서 Sentinel = false는 volatile write이므로 프로그램 바이너리에서 그 이전에 수행되어야 할 명령은 모두 volatile write 이전에 수행되게 된다. 따라서 CriticalData++;은 반드시 Sentinel = false; 이전에 수행된다. ThreadFunc1은 Sentinel을 volatile read하므로 그 이후에 실행되는 CriticalData 읽기는 반드시 Sentinel을 읽은 후에 수행된다. 따라서 위 프로그램은 정확히 1을 출력하는 올바른 프로그램이 된다.

또한 재배치가 일어나지 않음을 보장할 경우 Sentinel이 volatile이기만 하면 CriticalData는 volatile이 아니더라도 가시성(visibility)이 보장되는 효과도 있다. 이렇게 다른 volatile 변수로 인해 공짜로 가시성을 얻는 경우 피기백킹(piggybacking, 돼지 등을 타고 공짜로 달린다는 의미)이라고 부른다.

 

좋은 코딩 습관으로 생각되었다가 재배치 문제로 안전하지 않음이 밝혀진 예로 더블 체크 이디엄(double check idiom)이 있다. 아래 코드처럼 initialized == false로 초기화 여부를 확인하고 객체를 생성해 얻어올 경우 반드시 락을 잡아줘야 한다. read-test-write는 원자적(atomic)이지 않기 때문에 여러 쓰레드가 동시에 초기화를 시작할 수 있기 때문이다. 문제는 한 번 초기화가 된 이후에도 매번 객체를 얻어갈 때마다 락을 잡고 풀어야 한다는 점이다.


Foo* Foo::getInstance()

{

mutex.lock();

 

if (instance == 0) {

     instance = new Foo();

}

 

mutex.unlock();

 

return instance;

}

check.cc


이러한 오버헤드를 피하기 위해 다음과 같은 일단 초기화 여부를 확인한 이후에 실제로 락을 잡아서 다시 한 번 정말 초기화되지 않았는지 확인하는 패턴이 널리 사용되었다. 이 경우는 앞선 예와는 달리 한 번 초기화가 이루어지고 나면 더 이상 락을 잡지 않고 객체를 얻어올 수 있다.

이 코드는 언뜻 보기에 무척 효율적으로 보이지만 재배치와 관련해 큰 문제가 있다. 특히 instance = new Foo(); 의 수행 순서가 문제가 된다. 메모리를 할당 받아 생성자를 호출한 후에 메모리 주소를 instance를 대입한다고 하면 별 문제가 없겠지만, 일부 필드의 초기화 과정과 instance의 포인터 대입의 순서가 컴파일러 재배치로 인해 바뀔 수 있다. 이 경우 아직 일부 필드가 초기화되지 않은 상태에서 instance가 0이 아니게 되므로, 다른 쓰레드가 객체를 얻을 수 있다.


Foo* Foo::getInstance()

{

if (instance == 0) {

     mutex.lock();

 

     if (instance == 0) {

         instance = new Foo();

}

 

mutex.unlock();

}

 

return instance;

}

double_check.cc


정리


지금까지 C/C++의 volatile 키워드의 기본적인 기능과 관련된 문제들을 살펴보았다. volatile은 미묘한 키워드라 잘 알고 쓰면 큰 도움이 될 수 있지만, 또한 여러 가지 문제를 일으키는 근원이 되기도 한다. 특히 명확한 표준이 있는 게 아니므로, 사용하는 자신이 사용하는 C/C++ 컴파일러의 매뉴얼을 꼼꼼히 읽고 volatile을 어떻게 지원하는지 파악하는 게 중요하다.

volatile은 단일 CPU 환경에서 컴파일러 재배치 문제는 해결해주지만, MMU나 멀티CPU에 의한 재배치에 대해서는 완전한 대안을 제공하지 못한다. 또한 변수를 읽은 후에 값을 수정하고 다시 쓰는 read-modify-write를 원자적으로 수행할 수 있게 해주지도 않는다. a += 5; 같은 단순한 명령도 실제로는 a를 메모리에서 읽고 5를 더한 후에 다시 메모리 쓰는 복잡한 연산이므로 a를 volatile로 선언하는 것만으로는 이 코드를 멀티쓰레드에서 안전하게 수행할 수는 없다는 뜻이다. 유용성과 한계를 충분히 인지하고 필요에 따라 적절히 volatile을 사용하자.

 

참고문헌


[1] The "Double-Checked Locking is Broken" Declarationhttp://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html



블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

redis sentinel

redis 2015. 11. 30. 17:56

Redis sentinel은 redis failover를 위한 중개자.

http://redis.io/topics/sentinel

https://www.youtube.com/watch?v=fBDRO-d6cZQ

이 두개의 싸이트만 참조하면 대충 이해감.

Sentinel Config

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

레디스 홈피에서도 말하듯 이게 mymaster 레디스 셋을 위한 미니멈 셋팅. slave정보는 sentinel config에 없어도됨. 알아서 마스터를 통해 가져옴


Master

마스터는 원래 띄우던 config로 띄우고


Slave

slave녀석은 마스터 녀석 설정에 다음을 추가한다. (마스터 config에 별다른 특이사항이 없다면 그냥 아래만 추가해서 써도될듯)

port 6380

slaveof 127.0.0.1 6379

(여기서 센티넬이 다른머신에 있다면 127 말고 머신 ip 써야 failover 됨)


'redis' 카테고리의 다른 글

zscore list copy  (0) 2016.10.12
[Redis] HSET vs SET  (0) 2015.01.09
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

sockaddr vs sockaddr_in

TCP/IP 2015. 8. 1. 14:35

sockaddr 은 소켓 operation에서 쓰이는 일반적인 주소 struct type이다.
sockaddr_in 은 IP 기반 internet 커뮤니케이션을 위한 주소 struct type이다.

struct sockaddr{
    unsigned short   sa_family;
    char             sa_data[14];
};

sockaddr_in은 sockaddr로 convert되도록 마지막 8byte의 char 패딩을 둠으로써 scokaddr과 동일사이즈로 정의되어있다.

struct sockaddr_in {
    short int            sin_family;
    unsigned short int   sin_port;
    struct in_addr       sin_addr;
    unsigned char        sin_zero[8];
};

struct in_addr {
    unsigned long s_addr;
};

'TCP/IP' 카테고리의 다른 글

Socket broken pipe  (0) 2019.02.20
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

Docker overview

서버 교양 2015. 6. 13. 13:54

Docker

  1. DockerFile
    1. 이 파일로 부터 instance image를 만든다.
    2. 어떤 파일을 복사해둘지, 어떤 패키지를 인스톨 할것인지, 어떤 명령어로 서버를 실행할지 설정을 해둔다.
    3. 여기서 expose와 run할때 expose는 그 의미가 좀 다르다.
    4. 여기서 expose는 이 instance의 특정 ip를 내부 컨테이너에 한정해서 열어두겠다. 이고 run할때 expose는 host로 어느 포트를 열어줄 것인가를 정의하는 것이다.
    5. 기본적으로 exposed되지 않은 포트로는 host에서 접속할 수 없다.
    6. volume도 마찬가지

  2. Image
    1. Container instance로 띄울수 있는 Image다. (아마존 AMI같은거라고 볼수 있다.)
  3. Container
    1. Image로 실행된 instance
    2. 각 프로세스라고 볼 수 있다.
    3. container안에 접속해서 새로운 패키지를 설치했을때 이 변경사항을 commit하여 새로운 이미지로 만들수도있다. 하지만 이걸 다시 docfile로 빼는것은 없는것 같다.

  4. Run
    1. expose
      1. run할 인스턴스에 특정 포트를 여는의미 -p 이용 (publish)
    2. link
      1. link는 container간에 container 이름이나 특정 ailias이름으로 접근하기 위해서 사용. 딱히 link를 안해주어도 하나의 host안에 떠있는 container들은 서로의 ip만 알면 접근가능하다. 하지만 ip는 설치할때마다 동적이므로 코드레벨에서 이름으로 지정해놓고 쓰려면 link를 이용하는게 좋다.
    3. volume
      1. expose와 비슷한 개념으로 폴더를 공유할 수있게 해준다. 좀더 특이한점은 여러 Container가 하나의 폴더를 공유할 수 있게도 해준다.
  5. Ambassador
    1. docker를 분산환경에 셋팅 할 수 도 있는데, 이때 서로 exposed된 port, ip에 name으로만 붙고싶은데 이때 붙었던 instance의 ip가 변경되면(스케일업을 한다던지) 여기 붙고있던 instance들의 hosts파일을 변경해준다던지 하는 추가 작업을 해줘야한다. 이때 Ambassador를 이용하면 mongos처럼 docker앞쪽에 network잡을 대신 처리해줄 수 있다.


  • Docker의 장점은 일단 local환경이나 개발환경에서 OS에 국한되지 않는 여러가지 셋팅이 가능하게 해준다는 점과 이를 Image로 관리해서 쉽게 개발환경을 구축할수있다는점.

====== 유용한 docker command =========
- docker file로 image 만들기: docker build -t name . (docker file 이 있는곳에서)
- 실행중 & 스탑된 container 목록: docker ps -a
- 실행중 & 스탑된 container 제거: docker rm container_id
- container image 목록: docker images
- container image 제거: docker rmi image_id
 
- docker command 실행이 끝나도 프로세스가 남아있어서 attach가능하도록
: docker run -dit container_name /bin/sh (그냥 docker run -it container_name 이라고 하고 써도 된다. 바로 접속해서 쓸꺼면.)
- host port mapping: docker run -p 8080:8080 (-P 대문자옵션은 dockerfile에서 미리 exposed된 것들을 맵핑하겠다는것이고 아니면 -p를 써야한다)
- host directory mount: -v host절대경로:docker절대경로
- docker container process로 shell 접속: docker attach contrainer_id (detatch 는 ctl p ctl q)
- docker container save: docker commit container_id image_name
(docker commit 2c08332 arena)
- docker container stop: docker stop container_id
- 각 docker file line당 disk증가량 보려면: docker history image_name

* 아 그리고 이게 명령어 마다 layer라서 실행하고 다른 라인으로 RUN rm 해봤자 용량이 줄지않는다. && 으로 연결해서 rm 해야 image 용량이 늘어나지 않는다!

* 참고로 dockerfile RUN하나당 disk layer하나다 무슨말인고 하니,

RUN wget something (200MB)

RUN rm something 

이렇게 하면 이미지 사이즈에 200메가는 반영되지 않기를 바라지만 200메가는 고스란히 반영된다.

RUN wget something && rm something 이라고 해야된다.

'서버 교양' 카테고리의 다른 글

Logstash install  (0) 2017.03.17
[펌] 인증 암호화와 해쉬  (0) 2016.10.29
SSL 인증서 발급  (0) 2015.03.04
글로벌 푸시 시스템 구성하기  (0) 2015.01.16
글로벌 푸시 보내기  (0) 2014.11.27
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

보통 celery.py와 tasks.py를 두고 worker를 실행시키고

애플리케이션 서버에서는 tasks.py를 import해서 사용한다.


근데, 여기서 사용자의 눈에는 안보이는 것이있다.

워커를 실행할때는 당연히 celery.py를 이용해서 실행하기 때문에 별 이상해 보일것이 없는데,

잡을 줄때는 어떻게 이녀석이 redis에 커넥션이 되서 일을 전달 할 수 있는 것일까?


tasks.py를 보면 우리가 사용한 app은 결국 celery.py에서 가져온 것이기 때문에 일을 주는 애들도 각각 이 celery.py를 실행하여 celery app instance를 만들기 때문에 저런 상황이 가능한 것이다. 즉, 워커들도 각각 celery.py를 실행하듯, 사용하는 쪽에서도 celery.py를 각각 실행해서 app을 가지고 있다는 것!


'celery' 카테고리의 다른 글

[celery] How to run different multiple workers using different queue  (0) 2017.12.05
celery 오해와 진실  (0) 2015.03.13
Revoking task  (0) 2014.11.24
예약 푸시  (0) 2014.11.20
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

http://docs.mongodb.org/manual/administration/monitoring/

프로파일링 하고자하는 DB를 선택해서

db.setProfilingLevel(1)

셋팅을 해주고

db.system.profile.find( { millis : { $gt : 100 } } )

이런식으로 찾으라고 되어있다.

하지만 millis는 그냥 자신의 쿼리가 얼마나 오래 걸린지만 나타낼뿐, 자신이 기다린 db read/write lock time이 얼마나 되며 자신이 수행되는동안 db read/write lock time을 얼마나 잡았는지는 포함되지 않는다.

profile doc 내부에 보면 아래와 같은 lock에 대한 정보가 추가로 있는데

system.profile.lockStats.timeLockedMicros

system.profile.lockStats.timeAcquiringMicros

timeLockedMicros는 자신이 lock을 얼마나 잡았는지, timeAcquiringMicros는 자신이 lock을 잡기위해 기다린 시간을 뜻한다.

따라서 client측에서의 총 기다린 시간은 millis + 자기가 기다린 lock시간이 되겠다. 그리고 자기가 잡은 lock은 그다음 쿼리에 영향을 미칠거고..

만약 자기가 lock을 잡기위해 기다린 시간이 엄청 길다면 앞에 있는 쿼리가 lock을 걸고 기다렸거나 아니면 다른 리소스에서 디스크자원을 써서 기다리는 시간이 길어졌다고 볼수 있다.

보통은 millis로만 검색해서 실제로 lock이 얼마나 있었는지 못보고 지나갈 수도 있는데 이 lock타임으로 검색하거나, 특정 시간때에 몽고디비가 이상 현상이 있었다면 그시간때 ts로 profile doc을 검색해보는 것이 좋을 듯하다.


* 참고로 아마존을 사용할때 EBS Volumes에 Read/Write Bandwidth 그래프를 보고 언제 갑자기 read/write량이 몰렸었는가를 볼 수 있으니 위 내용과 관련해서 같이 보면 좋다. 이런걸 보고 몇 IOPS가 필요한지 판단할 수 있을 듯하고, 그리고 가끔 보면 튀는게 있는데 아마도 클라우드 서비스라 그런지 다른 곳과 총 bandwidth를 나눠쓸거 같다는 생각이 들고 그러다보면 순간적으로 우리쪽에 Disk I/O 기다림이 생길수 있어서 이때 쌓였던 I/O들이 한번에 몰리면서 튀는현상을 만들어 내지 않았나 가정을 해본다.

env:200KDAU500GB1500IOPS

'mongoDB' 카테고리의 다른 글

mongodb "convert to capped collection" effect  (0) 2018.02.23
How to mount mongoDB disk to /data folder  (0) 2018.02.23
mongos setup on centos  (0) 2018.01.12
Shading  (0) 2014.04.23
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,

functools.wraps에 대해

python 2015. 4. 15. 16:52

functools.wraps는 보통 python fucntion을 wrapping할 때 원래 function의 정보들을 유지 시키기 위해서 사용된다.

간단한 wrapper 코드를 보자.


import functools


def print_iam_wrapper(method):

    @functools.wraps(method)        ----- 1

    def wrapper(*args, **kwargs):    ----- 2

        print "I'm wrapper"

        return method(*args, **kwargs)


    return wrapper


@print_iam_wrapper

def iam_func():

    print 'iam_func'


print iam_func.__name__                  ----- 3 

iam_func()                                       ----- 4


여기서 1번을 제거해도 4번의 결과는 같다. 하지만 3번의 결과는 다르다.

wraps가 하는 일은 wraps, update_wrapper, partial 코드를 살펴보면 이해가 간다.

(3개의 function에 대한 코드는 아래에 두겠다.)

1번 decorator가 실행될때 method가 넘어가서 넘어오는 것은 결국 wraps의 결과인 partial() 리턴값인데 이것은 newfunc 이다. 그렇다면 1번은 @newfunc가 되었다고 볼 수 있다.


그리고 @newfunc에 파라미터로 넘어가는 것은 아래있는 wrapper function이 된다. 이때 newfunc은 update_wrapper를 실행하고 있으므로 이는 또한 @update_wrapper와 같다. 다만 이때 wrapped로 처음에 전달된 method 즉 iam_func이 넘어가 있을 것이다. 결국 의미는 wrapped에 설정되어있는 '__module__', '__name__', '__doc__', '__dict__' attritbute를 wrapper에 셋팅하여 던져주는 것이다.



* 참고) update_wrapper, wraps, partial

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')

WRAPPER_UPDATES = ('__dict__',)

def update_wrapper(wrapper,

                   wrapped,

                   assigned = WRAPPER_ASSIGNMENTS,

                   updated = WRAPPER_UPDATES):

    for attr in assigned:

        setattr(wrapper, attr, getattr(wrapped, attr))

    for attr in updated:

        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

    # Return the wrapper so this can be used as a decorator via partial()

    return wrapper


def wraps(wrapped,

          assigned = WRAPPER_ASSIGNMENTS,

          updated = WRAPPER_UPDATES):


    return partial(update_wrapper, wrapped=wrapped,

                   assigned=assigned, updated=updated) 


def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc























'python' 카테고리의 다른 글

gunicorn vs uwsgi  (0) 2017.01.20
flask async response  (0) 2017.01.04
Apple Push Notification Service(APNs) python modules  (0) 2015.04.07
Getting specific timezone timestamp from time string  (0) 2015.01.20
Shallow copy VS Deep copy  (0) 2014.08.27
블로그 이미지

시간을 거스르는자

ytkang86@gmail.com

,