From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-3.6 required=3.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.1 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 02AB21F62D for ; Fri, 6 Jul 2018 07:48:22 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 82B2C120B54; Fri, 6 Jul 2018 16:48:18 +0900 (JST) Received: from o1678948x4.outbound-mail.sendgrid.net (o1678948x4.outbound-mail.sendgrid.net [167.89.48.4]) by neon.ruby-lang.org (Postfix) with ESMTPS id 6F8C1120B05 for ; Fri, 6 Jul 2018 16:48:15 +0900 (JST) Received: by filter0073p3las1.sendgrid.net with SMTP id filter0073p3las1-23258-5B3F1EBC-A 2018-07-06 07:48:12.227353163 +0000 UTC Received: from herokuapp.com (ec2-50-16-3-251.compute-1.amazonaws.com [50.16.3.251]) by ismtpd0039p1mdw1.sendgrid.net (SG) with ESMTP id nL0Ac3ZMT2m4wZ1_oFIFmg Fri, 06 Jul 2018 07:48:11.886 +0000 (UTC) Date: Fri, 06 Jul 2018 07:48:12 +0000 (UTC) From: funny.falcon@gmail.com To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 X-Redmine-MailingListIntegration-Message-Ids: 63222 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 13618 X-Redmine-Issue-Author: normalperson X-Redmine-Issue-Assignee: normalperson X-Redmine-Sender: funny_falcon X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-SG-EID: ync6xU2WACa70kv/Ymy4QrNMhiuLXJG8OTL2vJD1yS7cqqLExRnHUGUwVwKJuk/hog+6AIIRdfVZLe qxRNFeSmpfeuA7NUhk+sJTN7bf4M+mo9FP4PJHlGG2l0iTFaVGAS/MzPt2I2gRNW49k4X9Hu+V7FgV rMi4GrdZMSyXx8z1XEyQ0LdRlF2NbszWpdi4gTU2hCOTmXPv50/dMe6XRQ== X-ML-Name: ruby-core X-Mail-Count: 87835 Subject: [ruby-core:87835] [Ruby trunk Feature#13618] [PATCH] auto fiber schedule for rb_wait_for_single_fd and rb_waitpid X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #13618 has been updated by funny_falcon (Yura Sokolov). > In my experience, threads simply don't scale as as well as fibers, there is too much overhead. Native threads doesn't scale. But we have example of Go: goroutine is a really green thread, and they do really scale. And Ruby 1.8 had green threads. With patches from RubyEE, they were really fast. Word "Thread" should not be red flag. It is just a word. Bunch of hybrid threads scheduled on one native thread will be as fast as Fiber's, they will scale. > The problem with Ruby threads is that the are mutually exclusive when running Ruby code Fibers are also exclusive relative to each other. > There is no need to ever use Mutex, Queue within Thread.scheduler= threads. Excuse me for rude word, but it is bullshit. It is so often repeated, that every one starts to believe. But reality is if there is concurrency (either due to parallelism, or due to asynchronous execution), there is need for synchronization. Especially it is strange to here from you, because socketry/async has Queue, Condition and Semaphore. > There is no need to ever use Mutex, Queue within Thread.scheduler= threads. Again: then why did you then did them for async? If your Queue dosn't use native Mutex, that doesn't make your Queue different beast. It is still synchronization mechanism, that have to be present in standard library. And it should be fully concerned about all possible schedulers. Of course, in the world of proposed hybrid Threads, there always will be a overhead for Mutex, Queue etc for being concerned about hybrid threads. But overhead will be really small if hybrid Threads are scheduled on a same native thread. But what I'm talking about? CRuby already has really fast Mutex and Queue implementation that doesn't use native mutex (beside of GVL). Then which way they differs from Async::Semaphore (with limit=1) and Async::Queue ? Single difference is they signals OS scheduler instead of reactor. But doubtfully it will be too hard to change them to signal hybrid scheduler. ---------------------------------------- Feature #13618: [PATCH] auto fiber schedule for rb_wait_for_single_fd and rb_waitpid https://bugs.ruby-lang.org/issues/13618#change-72854 * Author: normalperson (Eric Wong) * Status: Assigned * Priority: Normal * Assignee: normalperson (Eric Wong) * Target version: ---------------------------------------- ``` auto fiber schedule for rb_wait_for_single_fd and rb_waitpid Implement automatic Fiber yield and resume when running rb_wait_for_single_fd and rb_waitpid. The Ruby API changes for Fiber are named after existing Thread methods. main Ruby API: Fiber#start -> enable auto-scheduling and run Fiber until it automatically yields (due to EAGAIN/EWOULDBLOCK) The following behave like their Thread counterparts: Fiber.start - Fiber.new + Fiber#start (prelude.rb) Fiber#join - run internal scheduler until Fiber is terminated Fiber#value - ditto Fiber#run - like Fiber#start (prelude.rb) Right now, it takes over rb_wait_for_single_fd() and rb_waitpid() function if the running Fiber is auto-enabled (cont.c::rb_fiber_auto_sched_p) Changes to existing functions are minimal. New files (all new structs and relations should be documented): iom.h - internal API for the rest of RubyVM (incomplete?) iom_internal.h - internal header for iom_(select|epoll|kqueue).h iom_epoll.h - epoll-specific pieces iom_kqueue.h - kqueue-specific pieces iom_select.h - select-specific pieces iom_pingable_common.h - common code for iom_(epoll|kqueue).h iom_common.h - common footer for iom_(select|epoll|kqueue).h Changes to existing data structures: rb_thread_t.afrunq - list of fibers to auto-resume rb_vm_t.iom - Ruby I/O Manager (rb_iom_t) :) Besides rb_iom_t, all the new structs are stack-only and relies extensively on ccan/list for branch-less, O(1) insert/delete. As usual, understanding the data structures first should help you understand the code. Right now, I reuse some static functions in thread.c, so thread.c includes iom_(select|epoll|kqueue).h TODO: Hijack other blocking functions (IO.select, ...) I am using "double" for timeout since it is more convenient for arithmetic like parts of thread.c. Most platforms have good FP, I think. Also, all "blocking" functions (rb_iom_wait*) will have timeout support. ./configure gains a new --with-iom=(select|epoll|kqueue) switch libkqueue: libkqueue support is incomplete; corner cases are not handled well: 1) multiple fibers waiting on the same FD 2) waiting for both read and write events on the same FD Bugfixes to libkqueue may be necessary to support all corner cases. Supporting these corner cases for native kqueue was challenging, even. See comments on iom_kqueue.h and iom_epoll.h for nuances. Limitations Test script I used to download a file from my server: ----8<--- require 'net/http' require 'uri' require 'digest/sha1' require 'fiber' url = 'http://80x24.org/git-i-forgot-to-pack/objects/pack/pack-97b25a76c03b489d4cbbd85b12d0e1ad28717e55.idx' uri = URI(url) use_ssl = "https" == uri.scheme fibs = 10.times.map do Fiber.start do cur = Fiber.current.object_id # XXX getaddrinfo() and connect() are blocking # XXX resolv/replace + connect_nonblock Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl) do |http| req = Net::HTTP::Get.new(uri) http.request(req) do |res| dig = Digest::SHA1.new res.read_body do |buf| dig.update(buf) #warn "#{cur} #{buf.bytesize}\n" end warn "#{cur} #{dig.hexdigest}\n" end end warn "done\n" :done end end warn "joining #{Time.now}\n" fibs[-1].join(4) warn "joined #{Time.now}\n" all = fibs.dup warn "1 joined, wait for the rest\n" until fibs.empty? fibs.each(&:join) fibs.keep_if(&:alive?) warn fibs.inspect end p all.map(&:value) Fiber.new do puts 'HI' end.run.join ``` ---Files-------------------------------- 0001-auto-fiber-schedule-for-rb_wait_for_single_fd-and-rb.patch (82.8 KB) -- https://bugs.ruby-lang.org/