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.8 required=3.0 tests=AWL,BAYES_00, 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 72E621F403 for ; Wed, 13 Jun 2018 00:58:43 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 3C3D61217F7; Wed, 13 Jun 2018 09:58:41 +0900 (JST) Received: from dcvr.yhbt.net (dcvr.yhbt.net [64.71.152.64]) by neon.ruby-lang.org (Postfix) with ESMTPS id C1F851217F7 for ; Wed, 13 Jun 2018 09:58:30 +0900 (JST) Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id D70131F403; Wed, 13 Jun 2018 00:58:26 +0000 (UTC) Date: Wed, 13 Jun 2018 00:58:26 +0000 From: Eric Wong To: ruby-core@ruby-lang.org Message-ID: <20180613005826.x3xu7ugngdgxqrui@dcvr> References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="mn6bdu7ux2h7wxzq" Content-Disposition: inline In-Reply-To: X-ML-Name: ruby-core X-Mail-Count: 87483 Subject: [ruby-core:87483] Re: [Ruby trunk Feature#14736] Thread selector for flexible cooperative fiber based concurrency 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: , Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Content-Transfer-Encoding: 7bit --mn6bdu7ux2h7wxzq Content-Type: text/plain; charset=utf-8 Content-Disposition: inline samuel@oriontransfer.net wrote: > I've been playing around with port scanners. Implemented in Go > (goroutines), Python (asyncio) and Ruby (async). > > I wrote up the results here: > https://github.com/socketry/async-await/tree/master/examples/port_scanner Attached is the implementation for Threadlet/auto-fiber/wachamacallit rebased against ruby trunk r63641: https://80x24.org/spew/20180613003524.9256-1-e@80x24.org/raw On a busy Linux VM, Threadlet was close to your Go implementation in speed (timing results were unstable, however) and Ruby async was around 3x slower behind (even with timing instabilities). I kept on getting errors with the Python3 version ("Event loop is closed") so I never let it finish I needed to deal with EPIPE because the system I tested on had RDS (16385) enabled in the kernel which was triggering EPIPE (I don't know Go or Python): ``` diff --git a/examples/port_scanner/port_scanner.go b/examples/port_scanner/port_scanner.go index 45f2d1c..ad0f049 100755 --- a/examples/port_scanner/port_scanner.go +++ b/examples/port_scanner/port_scanner.go @@ -55,7 +55,7 @@ func checkPortOpen(ip string, port int, timeout time.Duration) { } else if strings.Contains(err.Error(), "refused") { // fmt.Println(port, "closed", err.Error()) } else { - panic(err) + fmt.Println(port, "err", err.Error()) } return } diff --git a/examples/port_scanner/port_scanner.py b/examples/port_scanner/port_scanner.py index 372f0b3..ca9d41a 100755 --- a/examples/port_scanner/port_scanner.py +++ b/examples/port_scanner/port_scanner.py @@ -22,6 +22,8 @@ class PortScanner: # print("{} closed".format(port)) except asyncio.TimeoutError: print("{} timeout".format(port)) + except SystemError: + print("{} error".format(port)) def start(self, timeout=1.0): self.loop.run_until_complete(asyncio.gather( diff --git a/examples/port_scanner/port_scanner.rb b/examples/port_scanner/port_scanner.rb index 0e4160e..3ac0109 100755 --- a/examples/port_scanner/port_scanner.rb +++ b/examples/port_scanner/port_scanner.rb @@ -25,6 +25,8 @@ class PortScanner # puts "#{port} closed" rescue Async::TimeoutError puts "#{port} timeout" + rescue SystemCallError => e + puts "#{port} #{e.message}" end async def start(timeout = 1.0) ``` --mn6bdu7ux2h7wxzq Content-Type: application/x-ruby Content-Disposition: attachment; filename="port_scanner_threadlet.rb" Content-Transfer-Encoding: quoted-printable #!/usr/bin/env ruby=0Arequire 'socket'=0A=0Aclass PortScanner=0A def initi= alize(host: '0.0.0.0', ports:, batch_size: 1024)=0A @host =3D host= =0A @ports =3D ports.to_a=0A @batch_size =3D batch_size=0A end= =0A=0A def scan_port(port, timeout)=0A peer =3D Socket.tcp(@host, port,= connect_timeout: timeout)=0A puts "#{port} #{peer.wait_writable(timeout= ) ? 'open' : 'timeout'}"=0A peer.close=0A rescue Errno::ECONNREFUSED=0A= # puts "#{port} closed"=0A rescue SystemCallError =3D> e=0A puts "#= {port} #{e.message}"=0A end=0A=0A def start(timeout =3D 1.0)=0A @batch= _size.times.map do=0A Threadlet.start do=0A while port =3D @po= rts.shift=0A scan_port(port, timeout)=0A end=0A end= =0A end.each(&:join)=0A end=0Aend=0A=0Alimits =3D Process.getrlimit(Pro= cess::RLIMIT_NOFILE)=0Abatch_size =3D [512, limits.first].min=0Ahost =3D "1= 27.0.0.1"=0Ascanner =3D PortScanner.new(host: host, ports: Range.new(1, 655= 35), batch_size: batch_size)=0A=0Ascanner.start=0A --mn6bdu7ux2h7wxzq Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline --mn6bdu7ux2h7wxzq--