How can I run iterators in parallel?

Tags:

루비에서 2개의 iterator가 있을 때 이를 병렬로 처리하기 위한 Matz의 방법이라고 합니다.

  require 'thread'

  def combine(*args)
    queues = []
    threads = []
    for it in args
      queue = SizedQueue.new(1)
      th = Thread.start(it, queue) do |i,q|
        self.send(it) do |x|
          q.push x
        end
      end
      queues.push queue
      threads.push th
    end
    loop do
      ary = []
      for q in queues
        ary.push q.pop
      end
      yield ary
      for th in threads
        return unless th.status
      end
    end
  end
  public :combine

  def it1 ()
    yield 1; yield 2; yield 3
  end

  def it2 ()
    yield 4; yield 5; yield 6
  end

  combine('it1','it2') do |x|
    # x is (1, 4), then (2, 5), then (3, 6)
  end

하지만 아쉽게도 이걸 그대로 입력하고 돌리면 답이 제대로 나오지가 않습니다.

가장 큰 이유는 루비에서 thread가 기본적으로 daemon 으로 돌아간다는 것이죠. 즉, 쓰레드가 돌고 있어도 메인 쓰레드를 실행중인 쓰레드가 종료되면 프로그램이 종료되죠. 세상에 어떤 언어가 그렇게 쓰레드/프로세스를 돌린답니까;; 이해가 안가요. 거기다가 thread.kill 인스턴스 메소드가 있질 않나(자바에선 deprecated된게 백만년전. 네. C#에선 obsolte되지 않았습니다. 하지만 이 부분은 자바의 손을 들어주렵니다.)

이건 단순히 쓰레드를 정지하는 간단한 메소드인 stop이 있고 없고의 문제가 아니라, 언어의 디자인으로 인해서 그 언어를 사용하는 사람이 프로그래밍을 제대로 할 수 있게 도와줄 수 있는가 아닌가의 문제라고 생각이 됩니다. stop이 있으면 좋죠. 하지만 문제는 제대로 stop 하도록 하기위해서는 알아야할 것들이 있는데, 그것들을 너무 쉽게 뛰어넘어가버리는 것이 아닌가 우려되는군요.

아무래도 루비에서의 쓰레딩은 너무 naive 합니다. 기본적으로 native thread도 아닌데다가, 이건 뭐 sleep 하면 타임 퀀텀을 잡아먹으면서 sleep하더군요. (아니 그럴거면 쓰레드 왜씁니까;;)

#!/usr/local/bin/ruby -w

require 'thread'

Thread.critical = true

t1 = Thread.new do
    for i in (1..100)
        if i % 2 == 0
            puts i
        end

        sleep(0.1)
    end
end


t2 = Thread.new do
    for i in (1..100)
        if i % 2 == 1
            puts i
        end

        sleep(0.1)
    end
end

Thread.critical = false

t1.join
t2.join

이 코드를 실행해보면 짝수가 다 나온뒤에 홀수가 나오는 것을 볼 수 있습니다. 이거 왠지 윈도우즈95를 떠올리지 않나요;; 해결방법은 t1내에서 pass를 해줘야한다는 것입니다. (크흑..) 몇가지 테스트를 계속 해보고 있지만 쓰레딩 구현이 도대체 왜 이모냥인건지 제가 잘 모르는건지 지금으로선 알 수가 없군요. 심지어 thread pool 도 없는데, 풀을 만들자면 간단한 Barrier 라던가 Latch 라던가 하는게 있어야 쓰레드들을 다 띄워놓고 걔들을 다 정지시켜 놓을텐데 그게 있는것도 아니고. ConditionVariable 이란 것이 있긴하지만 이건 항상 Mutex 얻은 상태에서만 할 수 있고;

자바엔 심지어 CopyOnWriteArrayList와 같이 값이 쓰일때에만 복제본이 만들어지는 고속의 쓰레드 안전한 자료구조도 있단 말입니다. 심지어 ConcurrentHashMap은 put/get 단위로 syncrhonize 한 게 아니라, 내부 구현상 최적화 되도록 (아마 동시에 put 할 수 있는 쓰레드가 거의 10개였던 듯) synchronize 되었죠. 아직 ruby는 이쪽으로는 상당히 먼 듯.

기회가 되면 루비용 thread pool이라도 만들어보려고 하는데 – 제가 찾아본 결과로는 없음 – thread 자체가 마음에 안드니 할게 너무 많네요.

p.s. 자바 찬양글 아닙니다. 안그래도 조금전에 -Xmx1G 로 프로그램 하나 돌렸는데 한시간돌고 out of memory 로 죽어버려서 아니 뭐 이딴언어가 다있어 하며 열받는중;

Comments

2 responses to “How can I run iterators in parallel?”

  1. MKSeo Avatar
    MKSeo

    헉. -Xmx1500M 를 하니 16분 돌고 끝나는군요. 그럼 1시간동안 GC만 하고 있었단 말인가;;;;;;; ㅠ.ㅜ

  2. 지나가다 Avatar
    지나가다

    문제점 다 고쳐졌삼.
    fiber 라는 경량 스레드도 새로 생겼삼.

Leave a Reply

Your email address will not be published. Required fields are marked *