На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Concurrency в разных языках и библиотеках
    Я тут на ЛОРе немного участвую в одном обсуждении, не связанном с темой, но этот вопрос был затронут, и я сделал небольшое сравнение "лёгких" тредов Racket и горутин:

    ExpandedWrap disabled
      #lang racket
       
      (define NCHANNELS 100)
      (define NTHREADS 100000)
       
      (define channels (for/vector ((i (in-range NCHANNELS))) (make-channel)))
       
      (define rand-gen (make-pseudo-random-generator))
       
      (define (pick-channel)
        (let ((i (random NCHANNELS rand-gen)))
          (vector-ref channels i)))
       
      (define (mixer)
        (define out (make-channel))
        (define n (- NCHANNELS 1))
        (define (loop)
          (let loop ((i 0))
            (when (channel-try-get (vector-ref channels i))
              (channel-put out #t))
            (loop (if (= i n) 0 (+ i 1)))))
        (thread loop)
        out)
       
      (define (start-thread done)
        (thread
         (lambda ()
           (sleep 0.1)
           (channel-put done #t))))
       
      (define (main)
        (define done (mixer))
        (for ((i (in-range NTHREADS)))
          (start-thread done))
        (for ((i (in-range NTHREADS)))
          (channel-get done))
        (displayln 'done))
       
      (time (main))

    =>
    ExpandedWrap disabled
      c:\Data\Sources\Racket>threads.exe
      done
      cpu time: 28000 real time: 27931 gc time: 12344


    ExpandedWrap disabled
      package main
       
      import (
          "fmt"
          "math/rand"
          "time"
      )
       
      type signal struct{}
       
      const (
          NCHANNELS = 100
          NTHREADS  = 100000
      )
       
      var channels = func() []chan signal {
          c := make([]chan signal, NCHANNELS)
          for i := range c {
              c[i] = make(chan signal)
          }
          return c
      }()
       
      func pickChannel() chan signal {
          return channels[rand.Intn(NCHANNELS)]
      }
       
      func mixer() chan signal {
          return mixall(channels[0], channels[1:]...)
      }
       
      func startThread(done chan signal) {
          time.Sleep(time.Millisecond * 100)
          done <- signal{}
      }
       
      func main() {
          rand.Seed(time.Now().UnixNano())
          start := time.Now()
          realMain()
          fmt.Println(time.Since(start))
      }
       
      func realMain() {
          done := make(chan signal)
          for i := 0; i < NTHREADS; i++ {
              go startThread(done)
          }
          for i := 0; i < NTHREADS; i++ {
              <-done
          }
          fmt.Println("done")
      }
       
      func mixall(ch chan signal, rest ...chan signal) chan signal {
          for _, ch2 := range rest {
              ch = mix(ch, ch2)
          }
          return ch
      }
       
      func mix(ch1 chan signal, ch2 chan signal) chan signal {
          out := make(chan signal)
          go func() {
              for {
                  select {
                  case msg := <-ch1:
                      out <- msg
                  case msg := <-ch2:
                      out <- msg
                  }
              }
          }()
          return out
      }

    =>
    ExpandedWrap disabled
      C:\Data\Sources\Go\src\cmd>go run test.go
      done
      891.3125ms


    Разница почти в 30 раз в пользу Go. А какие результаты даст ваш любимый язык или библиотека?

    (Конечно, тест весьма синтетический, но, я надеюсь он в должной мере нивелирует влияние реализации базовых численных вычислений (Эрланг на числодробилках крайне плохо, насколько я знаю, например), типизации (в Racket --- динамическая, в Go --- статическая), ввода-вывода и акцентирует внимание исключительно на механизме выполнения и взаимодействия независимых "потоков" выполнения)
      korvin, а тебя в Go варианте все функции используются? :blink:
        Цитата MyNameIsIgor @
        korvin, а тебя в Go варианте все функции используются? :blink:

        В смысле? Вроде да. Что тебя смутило? Go-код немного отличается от Racket, т.к. в последнем нет select, но, с другой стороны, в Go-коде создаётся чуть больше каналов и горутин (на микширование). В Racket это тупо циклическим перебором делается, возможно от этого и такая сильная разница, попробую у того знатока Racket, с которым общаюсь на ЛОРе, попросить реализацию select, может более-менее сравняет счёт.
        Сообщение отредактировано: korvin -
          Цитата korvin @
          Что тебя смутило?

          Функция main запускает realMain, которая создаёт один канал и запускает NTHREADS go-routine на основе функции startThread, передав им созданный канал. Потом ждёт NTHREADS сообщений из канала. В свою очередь функция startThread просто засыпает, а проснувшись, отправляет сообщение в канал.
          А остальные функции?
            Цитата MyNameIsIgor @
            Функция main запускает realMain, которая создаёт один канал и запускает NTHREADS go-routine на основе функции startThread, передав им созданный канал. Потом ждёт NTHREADS сообщений из канала. В свою очередь функция startThread просто засыпает, а проснувшись, отправляет сообщение в канал.
            А остальные функции?

            Так все же вызовы прописаны в исходниках остальных =) В Racket нет необходимости в функции main, там можно просто вызывать формы в (top-)module-level, т.е. Go-шная realMain соответствует Racket'овой main.

            функция mixer создаёт канал done, в который горутины startThread "отчитываются" о завершении, при этом она использует mixall (которая в свою очередь использует mix), чтобы собрать сообщения с 100 channels в один. Вообще можно было в startThread передавать...

            Пока писал этот текст, обнаружил косяк, о котором ты, видимо, говорил: горутины шлют ответ в один канал, а не в один из 100 channels, в отличие от. Поправил код:

            ExpandedWrap disabled
              package main
               
              import (
                  "fmt"
                  "math/rand"
                  "time"
              )
               
              type signal struct{}
               
              const (
                  NCHANNELS = 100
                  NTHREADS  = 100000
              )
               
              var channels = makeChannels(NCHANNELS)
               
              func pickChannel() chan signal {
                  return channels[rand.Intn(NCHANNELS)]
              }
               
              func startThread() {
                  time.Sleep(time.Millisecond * 100)
                  pickChannel() <- signal{}
              }
               
              func makeChannels(n int) []chan signal {
                  channels := make([]chan signal, n)
                  for i := range channels {
                      channels[i] = make(chan signal)
                  }
                  return channels
              }
               
              func main() {
                  rand.Seed(time.Now().UnixNano())
                  start := time.Now()
                  realMain()
                  fmt.Println(time.Since(start))
              }
               
              func realMain() {
                  done := mixall(channels[0], channels[1:]...)
                  for i := 0; i < NTHREADS; i++ {
                      go startThread()
                  }
                  for i := 0; i < NTHREADS; i++ {
                      <-done
                  }
                  fmt.Println("done")
              }
               
              func mixall(ch chan signal, rest ...chan signal) chan signal {
                  for _, ch2 := range rest {
                      ch = mix(ch, ch2)
                  }
                  return ch
              }
               
              func mix(ch1 chan signal, ch2 chan signal) chan signal {
                  out := make(chan signal)
                  go func() {
                      for {
                          select {
                          case msg := <-ch1:
                              out <- msg
                          case msg := <-ch2:
                              out <- msg
                          }
                      }
                  }()
                  return out
              }

            =>
            ExpandedWrap disabled
              C:\Data\Sources\Go\src\cmd>go run test.go
              done
              9.3667769s

            Итого разница максимум в 3 раза. Уже не так существенно. =) Впрочем топик этим не закрыт. =)
              Цитата korvin @
              Пока писал этот текст, обнаружил косяк, о котором ты, видимо, говорил: горутины шлют ответ в один канал, а не в один из 100 channels, в отличие от

              Угу, отличие вот в этом
              ExpandedWrap disabled
                done := mixall(channels[0], channels[1:]...)

              Цитата korvin @
              Итого разница максимум в 3 раза.

              Хе-хе :D Подозреваю, что дело тут уже скорее в реализации Racket'а.

              Объясни алгоритм, а то лень курить go. И boost.fibers тоже лень собирать :D Но вон D_KEY собрал - он напишет аналог :lool:
                Переписал Go-код на перебор каналов из channels:

                ExpandedWrap disabled
                  package main
                   
                  import (
                      "fmt"
                      "math/rand"
                      "time"
                  )
                   
                  type signal struct{}
                   
                  const (
                      NCHANNELS = 100
                      NTHREADS  = 100000
                  )
                   
                  var channels = makeChannels(NCHANNELS)
                   
                  func pickChannel() chan signal {
                      return channels[rand.Intn(NCHANNELS)]
                  }
                   
                  func startThread() {
                      time.Sleep(time.Millisecond * 100)
                      pickChannel() <- signal{}
                  }
                   
                  func makeChannels(n int) []chan signal {
                      channels := make([]chan signal, n)
                      for i := range channels {
                          channels[i] = make(chan signal)
                      }
                      return channels
                  }
                   
                  func main() {
                      rand.Seed(time.Now().UnixNano())
                      start := time.Now()
                      realMain()
                      fmt.Println(time.Since(start))
                  }
                   
                  func realMain() {
                      done := mixer()
                      for i := 0; i < NTHREADS; i++ {
                          go startThread()
                      }
                      for i := 0; i < NTHREADS; i++ {
                          <-done
                      }
                      fmt.Println("done")
                  }
                   
                  func mixer() chan signal {
                      out := make(chan signal)
                      go func() {
                          i := 0
                          n := NCHANNELS - 1
                          for {
                              select {
                              case <-channels[i]:
                                  out <- signal{}
                              default: // to make select non-blocking
                              }
                              if i == n {
                                  i = 0
                              } else {
                                  i++
                              }
                          }
                      }()
                      return out
                  }

                =>
                ExpandedWrap disabled
                  C:\Data\Sources\Go\src\cmd>go run test.go
                  done
                  1.2368222s

                Как бы разница вернулась к примерно 30 раз. =)

                Добавлено
                Цитата MyNameIsIgor @
                Объясни алгоритм, а то лень курить go. И boost.fibers тоже лень собирать :D Но вон D_KEY собрал - он напишет аналог :lool:

                Ок, попробую, пример искусственный, потому не могу привести адекватной аналогии из реального мира (по крайней мере прям сейчас навскидку):

                Есть 100(NCHANNELS) точек выдачи товара, каждая в один момент времени может хранить только одну единицу товара, и есть 100000(NTHREADS) поставщиков, каждому выдаётся рандомный адрес точки выдачи из этих 100, соответственно поставщики, получившие одинаковый адрес, соревнуются, кто из них сможет сдать товар. Есть менеджер, который последовательно, по очереди обходит эти 100 точек и на каждой рандомно выбирает одного из поставщиков, которому приехал на этот адрес, и забирает у него товар и отправляет его в одну общую очередь(done), которую обрабатывает realMain. Если не ошибаюсь, это демультиплексинг потоков сообщений. Фактически, можно было вместо сотни каналов channels использовать один, но этой сотней и диспетчером(mixer) так мы слегка распределяем нагрузку. Например каналы --- это Ethernet-кабели, а mixer --- это коммутатор, принимающий данные с разных "входных" Ethernet-портов и посылающий их на один "выходной" порт.
                  Выглядит это примерно так (движение сигналов слева-направо):

                  Прикреплённая картинка
                  Прикреплённая картинка


                  По сути вместо микшера и разных каналов(тред->микшер) можно использовать канал(треды->main), в который будут "писАть" все треды, но так чуть больше нагрузка на диспетчеризацию всего этого дела (что важно для теста) + это может быть вполне реальный, например аудио, микшер.

                  Добавлено
                  Цитата MyNameIsIgor @
                  лень курить go. И boost.fibers тоже лень собирать :D Но вон D_KEY собрал - он напишет аналог :lool:

                  Ну это всяко интересней правил расстановки скобок и ромбонаследования =)
                  Сообщение отредактировано: korvin -
                    Цитата korvin @
                    Ну это всяко интересней правил расстановки скобок и ромбонаследования =)

                    Ну, мне просто лень собирать fibers :)
                      Цитата MyNameIsIgor @
                      Ну, мне просто лень собирать fibers :)

                      "И в этом весь С++"... =))) Комитет разве ещё не решился добавить лёгкие треды в стандарт?
                        Цитата korvin @
                        "И в этом весь С++"... =)))

                        В чём? C++ - единственный язык, в который ещё не добавили легковесные потоки?
                        Цитата korvin @
                        Комитет разве ещё не решился добавить лёгкие треды в стандарт?

                        Вот здесь надо говорить "в этом весь C++". Потому что в любом случае добавят не сами легковесные потоки, а что-то более низкоуровневое.
                          Цитата MyNameIsIgor @
                          В чём? C++ - единственный язык, в который ещё не добавили легковесные потоки?

                          В том, чтобы сделать что-то банальное, нужны существенные телодвижения. =)
                            Цитата korvin @
                            В том, чтобы сделать что-то банальное, нужны существенные телодвижения. =)

                            Ну, давай, без существенных телодвижений создадим банальные легковесные потоки в Java или C#...
                              Цитата MyNameIsIgor @
                              Ну, давай, без существенных телодвижений создадим банальные легковесные потоки в Java или C#...

                              Зачем нам эти быдлоязыки? Давай ConcurrentML, Occam? =))
                                Цитата korvin @
                                Зачем нам эти быдлоязыки?

                                Чтобы работать, например :)
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0803 ]   [ 18 queries used ]   [ Generated: 29.03.24, 02:40 GMT ]