Haskell Node.js'ye yanıtı nedir?


217

Erlang topluluğunun yerel olarak engellemeyen G / Ç'yi yaptığı ve konuşlandırmaları birden fazla işlemciye (Node.js'de yerleşik olmayan bir şey bile) kolayca ölçeklendirme yollarına sahip olduğu için Node.js'yi kıskanmamış olduğuna inanıyorum. Http://journal.dedasys.com/2010/04/29/erlang-vs-node-js ve Node.js veya Erlang adresinde daha fazla ayrıntı

Haskell ne olacak? Haskell, Node.js'nin bazı avantajlarını, yani çok iş parçacıklı programlamaya başvurmadan G / Ç'yi engellemekten kaçınmak için temiz bir çözüm sağlayabilir mi?


Node.js ile çekici olan birçok şey var

  1. Olaylar: İş parçacığı manipülasyonu yok, programcı yalnızca geri aramalar sağlar (Snap çerçevesindeki gibi)
  2. Geri aramaların tek bir iş parçacığında çalıştırılması garanti edilir: yarış durumu mümkün değildir.
  3. Güzel ve basit UNIX dostu API. Bonus: Mükemmel HTTP desteği. DNS de mevcuttur.
  4. Her G / Ç varsayılan olarak eşzamansızdır. Bu, kilitleri önlemeyi kolaylaştırır. Ancak, bir geri aramada çok fazla CPU işlemesi diğer bağlantıları etkileyecektir (bu durumda, görev daha küçük alt görevlere bölünmeli ve yeniden programlanmalıdır).
  5. İstemci tarafı ve sunucu tarafı için aynı dil. (Ancak, bu bir çok değer görmüyorum. JQuery ve Node.js olay programlama modelini paylaşmak ama gerisi çok farklı. Sadece sunucu tarafı ve istemci tarafı arasında paylaşım kodu nasıl olabilir göremiyorum pratikte yararlı olabilir.)
  6. Bütün bunlar tek bir üründe paketlenmiştir.

17
Bunun yerine bu soruyu Programcılar'a sormanız gerektiğini düşünüyorum .
Jonas

47
Bir kod parçasının dahil edilmemesi onu öznel bir soru haline getirmez.
gawi

20
Node.js hakkında fazla bir şey bilmiyorum, ancak sorunuz hakkında bana bir şey vurdu: Neden iş parçacığı olasılığını bu kadar tatsız buluyorsunuz? İplikler, çoğullama G / Ç için tam olarak doğru çözüm olmalıdır. Erlang'ın süreçleri de dahil olmak üzere burada iş parçacığı terimini geniş ölçüde kullanıyorum. Belki kilitler ve değişebilir durumdan endişe duyuyorsunuz? İşleri bu şekilde yapmak zorunda değilsiniz - uygulamanız için daha mantıklıysa mesaj geçişi veya işlemleri kullanın.
Simon Marlow

9
@gawi Programlamanın çok kolay olduğunu düşünmüyorum - preemisyon olmadan açlık ve uzun gecikmeler olasılığı ile uğraşmak zorundasınız. Temelde iş parçacıkları bir web sunucusu için doğru soyutlamadır - asenkron I / O ve bununla birlikte gelen tüm zorluklarla uğraşmaya gerek yoktur, sadece bir iş parçacığında yapın. Bu arada, Haskell'de ilginç bulabileceğiniz web sunucuları hakkında bir makale yazdım: haskell.org/~simonmar/papers/web-server-jfp.pdf
Simon Marlow

3
"Geri aramaların tek bir iş parçacığında çalıştırılması garanti edilir: yarış durumu mümkün değil." Yanlış. Node.js'de kolayca yarış koşullarına sahip olabilirsiniz; bir G / Ç işleminin diğerinden önce tamamlanacağını ve BOOM'un olduğunu varsayalım. Ne olduğunu gerçekten imkansız yarış koşullardan biri belirli tür bellekte aynı byte için yani eşzamanlı senkronlaştınlmamış erişimdir.
rightfold

Yanıtlar:


219

Tamam, @ gawi'nin bana işaret ettiği node.js sunumundan biraz izledikten sonra, Haskell'in node.js ile karşılaştırması hakkında biraz daha fazla şey söyleyebilirim. Sunumda Ryan, Green Threads'ın bazı faydalarını açıklıyor, ancak daha sonra bir dezavantaj olarak bir iplik soyutlamasının eksikliğini bulamadığını söylemeye devam ediyor. Özellikle Haskell bağlamında bu konuya katılmıyorum: Bence iş parçacıklarının sağladığı soyutlamalar, sunucu kodunun daha kolay ve daha sağlam olmasını kolaylaştırmak için gerekli. Özellikle:

  • bağlantı başına bir iş parçacığı kullanmak, tüm istemcilerle aynı anda ilgilenen kod yazmak yerine, tek bir istemciyle iletişimi ifade eden kod yazmanıza olanak tanır . Şöyle düşünün: iş parçacıkları olan birden çok istemciyi işleyen bir sunucu neredeyse tek bir istemciyi işleyen sunucuyla aynı görünüyor; temel fark, forkbirincisinde bir yer olması. Uyguladığınız protokol tamamen karmaşıksa, durum makinesini birden çok istemci için aynı anda yönetmek oldukça zorlaşırken, iş parçacıkları tek bir istemciyle iletişimi komut dosyası haline getirmenize izin verir. Kodun düzeltilmesi daha kolay, anlaşılması ve bakımı daha kolaydır.

  • Tek bir OS iş parçacığındaki geri çağrılar, iş parçacığı ile elde ettiğiniz önleyici çoklu görevlerin aksine, ortak çoklu görevdir. İşbirlikçi çoklu görevle ilgili ana dezavantaj, programcının açlık olmadığından emin olmaktan sorumlu olmasıdır. Modülerliği kaybeder: tek bir yerde hata yapın ve tüm sistemi sıkıştırabilir. Bu gerçekten endişelenmek istemediğiniz bir şeydir ve preemption basit bir çözümdür. Dahası, geri aramalar arasında iletişim mümkün değildir (kilitlenecektir).

  • eşzamanlılık Haskell'de zor değildir, çünkü çoğu kod saftır ve inşaat tarafından iş parçacığı için güvenlidir. Basit iletişim ilkelleri vardır. Haskell'deki eşzamanlılık ile kendinizi ayağa vurmak, sınırsız yan etkilere sahip bir dilde olduğundan çok daha zor.


42
Tamam, bu yüzden node.js'nin 2 soruna çözüm olduğunu anladım: 1- eşzamanlılık çoğu dilde zor, 2- OS iş parçacıklarını kullanmak geniş. Node.js çözümü, iş parçacıkları arasındaki iletişimi önlemek ve OS iş parçacıklarının ölçeklenebilirlik sorunlarını önlemek için olay tabanlı eşzamanlılık (w / libev) kullanmaktır. Haskell'in saflık nedeniyle 1 numaralı sorunu yok. # 2 için Haskell, yakın zamanda büyük ölçekli bağlamlar için GHC'de optimize edilen hafif iş parçacıkları + etkinlik yöneticisine sahiptir. Ayrıca, Javascript kullanmak herhangi bir Haskell geliştiricisi için bir artı olarak algılanamaz. Snap Framework kullanan bazı kişiler için Node.js "sadece kötü" dür.
gawi

4
Talep işleme çoğu zaman birbirine bağlı işlem dizisidir. Her engelleme işlemi için geri arama kullanmanın hantal olabileceğini kabul ediyorum. Konular bunun için geri çağrıdan daha uygundur.
gawi

10
Evet! GHC 7'deki yepyeni I / O çoklaması Haskell'deki yazma sunucularını daha da iyi hale getiriyor.
andreypopp

3
İlk noktanız benim için bir anlam ifade etmiyor (yabancı olarak) ... node.js'de bir talebi işlerken geri arama tek bir müşteriyle ilgileniyor. Durumu yönetmek yalnızca birden çok işleme ölçeklenirken endişelenecek bir şey haline gelir ve o zaman bile mevcut kütüphaneleri kullanmak oldukça kolaydır.
Ricardo Tomasi

12
Ayrı bir konu değil. Bu soru Haskell'deki iş için en iyi araçlar için gerçek bir arama veya Haskell'de iş için mükemmel araçların olup olmadığını kontrol ederse, çok iş parçacıklı programlamanın uygun olmayacağı örtük varsayımına meydan okumak gerekir, çünkü Haskell Don Stewart'ın belirttiği gibi, konu başlıkları oldukça farklı. Haskell topluluğunun neden Node.js'yi kıskanmadığını açıklayan cevaplar bu soru için çok fazla konu. gawi'nin yanıtı, sorusuna uygun bir cevap olduğunu gösteriyor.
AndrewC

154

Haskell, Node.js'nin bazı avantajlarını, yani çok iş parçacıklı programlamaya başvurmadan G / Ç'yi engellemekten kaçınmak için temiz bir çözüm sağlayabilir mi?

Evet, aslında Haskell'de olaylar ve evreler birleştirildi.

  • Açık hafif iş parçacıklarıyla (örneğin, tek bir dizüstü bilgisayarda milyonlarca iş parçacığı) programlayabilirsiniz.
  • Veya; ölçeklenebilir olay bildirimini temel alarak zaman uyumsuz bir olayla programlayabilirsiniz.

İş parçacıkları aslında olaylar açısından uygulanır ve sorunsuz iş parçacığı geçişi, belgelenmiş performans ve uygulamalar ile birden fazla çekirdek üzerinde çalışır.

Örneğin

Eşzamanlı koleksiyonlar 32 çekirdekli kimse

alternatif metin

Haskell'de hem olaylar hem de iş parçacıkları var ve tüm başlık altında olduğu gibi.

Uygulamayı açıklayan makaleyi okuyun .


2
Teşekkürler. Tüm bunları sindirmem gerekiyor ... Bu GHC'ye özgü görünüyor. Sanırım sorun değil. Haskell dili, GHC'nin derleyebileceği bir şeydir. Benzer şekilde, Haskell "platformu" az ya da çok GHC çalışma süresidir.
gawi

1
@gawi: Kutudan çıkar çıkmaz kullanışlı olması için paketin içine yerleştirilen diğer tüm paketler. Ve bu, CS kursumda gördüğüm görüntü ile aynı; ve en iyi yanı, Haskell'de kendi programlarınızda benzer harika sonuçlar elde etmenin zor olmamasıdır.
Robert Massaioli

1
Merhaba Don, böyle soruları cevaplarken en iyi performansı gösteren çözgü web sunucusuna bağlanabileceğinizi düşünüyor musunuz? İşte Node.js'ye
Greg Weber

4
Sadece teoride. Haskell "hafif iplikler" düşündüğünüz kadar hafif değil. Bir epoll arabiriminde bir geri çağrıyı kaydetmek, yeşil bir iş parçacığı zamanlamaktan çok daha ucuzdur, elbette OS iş parçacıklarından daha ucuzdurlar, ancak ücretsiz değildirler. Bunların 100.000'ini oluşturmak yakl. 350 MB bellek ve biraz zaman ayırın. Node.js ile 100.000 bağlantıyı deneyin. Hiç sorun değil . Ghc kaputun altında epoll kullandığından daha hızlı olmasa sihir olurdu, bu yüzden doğrudan epoll kullanmaktan daha hızlı olamazlar. Gerçi iş parçacığı arayüzü ile programlama oldukça güzel.
Kr0e

3
Ek olarak: Yeni IO yöneticisi (ghc), (m log n) karmaşıklığına sahip bir programlama algoritması kullanır (burada m, çalıştırılabilir iş parçacığı sayısı ve n toplam iş parçacığı sayısıdır). Epoll karmaşıklığı k (k okunabilir / yazılabilir fd sayısı = = Bu nedenle ghc, yüksek trafik bağlantılarıyla karşılaşırsanız çok iyi olmayan tüm karmaşıklık üzerinde O (k * m log n) değerine sahiptir. Windows performansı hakkında konuşmayalım ... Node.js çok daha hızlı çünkü IOCP kullanıyor.
Kr0e

20

Öncelikle, node.js'nin tüm bu geri çağrıları ortaya çıkaran doğru şeyi yaptığını düşünmüyorum. Sonunda programınızı CPS (devam geçiş tarzı) olarak yazıyorsunuz ve bence bu dönüşümü yapmak derleyicinin işi olmalı.

Olaylar: İş parçacığı manipülasyonu yok, programcı yalnızca geri aramalar sağlar (Snap çerçevesindeki gibi)

Bunu göz önünde bulundurarak, isterseniz eşzamansız bir stil kullanarak yazabilirsiniz, ancak bunu yaparak, her bir istek için bir iş parçacığı ile verimli bir eşzamanlı tarzda yazmayı kaçırırsınız. Haskell, özellikle diğer dillerle karşılaştırıldığında senkron kodda gülünç derecede etkilidir. Tüm olayların altında.

Geri aramaların tek bir iş parçacığında çalıştırılması garanti edilir: yarış durumu mümkün değildir.

Hala node.js'de bir yarış durumunuz olabilir, ancak daha zor.

Her istek kendi iş parçacığında. Diğer iş parçacıklarıyla iletişim kurması gereken kod yazdığınızda, haskell'in eşzamanlılık ilkelleri sayesinde iş parçacığı güvenli hale getirmek çok basittir.

Güzel ve basit UNIX dostu API. Bonus: Mükemmel HTTP desteği. DNS de mevcuttur.

Hacking'e bir göz atın ve kendiniz görün.

Her G / Ç varsayılan olarak eşzamansızdır (bu bazen sinir bozucu olabilir). Bu, kilitleri önlemeyi kolaylaştırır. Ancak, bir geri aramada çok fazla CPU işlemesi diğer bağlantıları etkileyecektir (bu durumda, görev daha küçük alt görevlere bölünmeli ve yeniden programlanmalıdır).

Böyle bir sorununuz yok, ghc çalışmanızı gerçek işletim sistemi iş parçacıkları arasında dağıtacaktır.

İstemci tarafı ve sunucu tarafı için aynı dil. (Ancak, bu bir çok değer görmüyorum. JQuery ve Node.js olay programlama modelini paylaşmak ama gerisi çok farklı. Sadece sunucu tarafı ve istemci tarafı arasında paylaşım kodu nasıl olabilir göremiyorum pratikte yararlı olabilir.)

Haskell burada kazanamaz ... değil mi? Tekrar düşünün, http://www.haskell.org/haskellwiki/Haskell_in_web_browser .

Bütün bunlar tek bir üründe paketlenmiştir.

İndir ghc, cabal ateş. Her ihtiyaç için bir paket var.


Sadece şeytanın avukatı oynuyordum. Yani, evet puanlarına katılıyorum. İstemci tarafı ve sunucu tarafı dil birleştirmesi hariç. Teknik olarak mümkün olduğunu düşünüyorum, ancak sonunda yerdeki tüm Javascript ekosisteminin yerini alabileceğini sanmıyorum (JQuery ve arkadaşları). Node.js destekçileri tarafından öne sürülen bir argüman olsa da, bunun çok önemli olduğunu düşünmüyorum. Sunum katmanınız ve arka ucunuz arasında bu kadar kod paylaşmanız gerekiyor mu? Gerçekten programcıların sadece bir dili bilmesini hedefliyor muyuz?
gawi

Gerçek kazanç, hem sunucu hem de istemci tarafında sayfaları gerçek zamanlı sayfaların oluşturulmasını kolaylaştırabilmenizdir.
dan_waterworth

@dan_waterworth tam olarak, bkz meteor veya derby.js
mb21

1
@gawi Kodun% 85'inin istemci ve sunucu arasında paylaşıldığı üretim hizmetlerimiz var. Bu, toplulukta evrensel JavaScript olarak bilinir. React'i, istemcide ilk faydalı oluşturma süresini azaltmak için sunucudaki içeriği dinamik olarak oluşturmak için kullanıyoruz. Haskell'i tarayıcıda çalıştırabileceğinizi bilsem de, aynı kod tabanını kullanarak sunucu tarafı ve istemci tarafı oluşturmaya izin veren herhangi bir "evrensel Haskell" en iyi uygulamalarından haberdar değilim.
Eric Elliott

8

Ben şahsen Node.js'yi ve geri çağrılarla programlamayı gereksiz yere düşük seviyeli ve biraz doğal olmayan bir şey olarak görüyorum. GHC'de bulunan gibi iyi bir çalışma zamanı sizin için geri aramaları işleyebiliyorsa ve bunu oldukça verimli bir şekilde gerçekleştirebiliyorsa neden geri aramalarla programlanmalı?

Bu arada, GHC çalışma zamanı büyük ölçüde iyileşti: şimdi MIO adı verilen "M" nin çok çekirdekli olduğuna inanan "yeni bir yeni IO yöneticisi" bulunuyor . Mevcut IO yöneticisinin temeli üzerine kuruludur ve asıl amacı 4+ çekirdek performans düşüşünün nedeninin üstesinden gelmektir. Bu makalede verilen performans numaraları oldukça etkileyicidir. Kendini gör:

Mio ile Haskell'deki gerçekçi HTTP sunucuları, 20 CPU çekirdeğine kadar ölçeklendirilir ve önceki GHC sürümlerini kullanan aynı sunuculara kıyasla 6,5 ​​kata kadar daha yüksek performans elde eder. Haskell sunucularının gecikme süresi de artırıldı: [...] ılımlı bir yük altında, önceki GHC sürümleriyle karşılaştırıldığında beklenen yanıt süresini 5,7x azaltır

Ve:

Ayrıca Mio ile McNettle'ın (Haskell'de yazılmış bir SDN denetleyicisi) 40'tan fazla çekirdeğe etkili bir şekilde ölçeklenebileceğini, tek bir makinede saniyede 20 milyondan fazla yeni talebe ulaştığını ve dolayısıyla mevcut tüm SDN denetleyicilerinin en hızlısı haline geldiğini gösteriyoruz. .

Mio GHC 7.8.1 sürümüne girdi. Şahsen bunu Haskell performansında atılmış önemli bir adım olarak görüyorum. Önceki GHC sürümü ve 7.8.1 tarafından derlenen mevcut web uygulamaları performansını karşılaştırmak çok ilginç olurdu.


6

IMHO olayları iyidir, ancak geri aramalar yoluyla programlama iyi değildir.

Web uygulamalarının kodlanmasını ve hata ayıklamasını özel kılan sorunların çoğu, bunları ölçeklenebilir ve esnek yapan şeylerden gelir. En önemlisi, HTTP'nin durumsuz doğası. Bu gezinilebilirliği artırır, ancak bu, G / Ç öğesinin (bu durumda web sunucusu) uygulama kodundaki farklı işleyicileri çağırdığı bir denetimin tersine çevrilmesini sağlar. Bu olay modeli -veya daha doğru bir şekilde söylenen- geri arama modeli bir kabustur, çünkü geri aramalar değişken kapsamları paylaşmadığından ve navigasyonun sezgisel bir görünümü kaybolur. Kullanıcı diğer sorunların yanı sıra ileri geri hareket ettiğinde olası tüm durum değişikliklerini önlemek çok zordur.

Sorunların olay modelinin iyi çalıştığı GUI programlamasına benzediği söylenebilir, ancak GUI'lerde gezinme ve geri düğmesi yoktur. Bu, web uygulamalarındaki olası geçişleri çoğaltır. Bu sorunu çözme girişiminin sonucu, karmaşık yapılandırmalara sahip ağır çerçevelerdir ve sorunun kökünü sorgulamadan birçok yaygın sihirli tanımlayıcı vardır: geri arama modeli ve değişken kapsamların paylaşılmasının doğal eksikliği ve sıralama yok. tanımlayıcılar bağlanarak oluşturulur.

Ocsigen (ocaml) deniz kenarı (smalltalk) WASH (üretilmiyor, Haskell) ve mflow (Haskell) gibi navigasyon ve REST dolgunluğunu korurken durum yönetimi sorununu çözen ardışık tabanlı çerçeveler vardır. bu çerçeveler içinde, programcı navigasyonu, programın sayfaları gönderdiği ve tek bir iş parçacığında yanıt beklediği, değişkenlerin kapsamda olduğu ve geri düğmesinin otomatik olarak çalıştığı zorunlu bir sıra olarak ifade edebilir. Bu, doğal olarak, navigasyonun programcı tarafından açıkça görülebildiği daha kısa, daha güvenli, daha okunabilir bir kod üretir. (adil uyarı: mflow'un geliştiricisiyim)


Node.js'de geri çağrılar, örneğin veritabanları gibi zaman uyumsuz G / Ç işlemek için kullanılır. İlginç olsa da soruyu cevaplamayan farklı bir şeyden bahsediyorsunuz.
Robin Green

Haklısın. Umarım itirazlarınızı karşılamak üç yıl sürdü: github.com/transient-haskell
agocorona

Düğüm artık eşzamansız işlevleri destekliyor, yani aslında eşzamansız olan zorunlu stil kodu yazabilirsiniz. Kaputun altında vaat ediyor.
Eric Elliott

5

Soru oldukça saçma çünkü 1) Haskell bu konuyu çok daha iyi bir şekilde çözdü ve 2) kabaca Erlang'ın yaptığı gibi. Düğüme karşı kıyaslama: http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks

Haskell'e 4 çekirdek verin ve tek bir uygulamada saniyede 100k (basit) istekte bulunabilir. Düğüm çok fazla iş yapamaz ve tek bir uygulamayı çekirdekler arasında ölçeklendiremez. Haskell çalışma zamanı engellenmediği için bunu elde etmek için hiçbir şey yapmanız gerekmez. Çalışma zamanında yerleşik olmayan ve engelleme olmayan IO'ya sahip diğer tek (nispeten yaygın) dil Erlang'dır.


14
Saçma? Soru "Haskell'in bir yanıtı var mı" değil, "Haskell yanıtı nedir" dir. Soru sorulduğunda, GHC 7 bile serbest bırakılmadı, bu yüzden Haskell henüz "oyunda" değildi (belki Snap gibi libev kullanan çerçeveler hariç). Bunun dışında katılıyorum.
gawi

1
Bu yanıtı yayınladığınızda bunun doğru olup olmadığını bilmiyorum, ancak şimdi, düğüm uygulamalarının çekirdekler arasında kolayca ölçeklenmesine izin veren düğüm modülleri var. Ayrıca, bu bağlantı tek bir çekirdek üzerinde çalışan node.js'yi 4 çekirdek üzerinde çalışan haskell ile karşılaştırıyor. Daha adil bir konfigürasyonda tekrar çalıştığını görmek istiyorum, ama ne yazık ki, github repo gitti.
Tim Gautier

2
4'ten fazla çekirdek kullanan Haskell, uygulamanın performansını düşürür. Bu konuda bir makale vardı, aktif olarak çalıştı, ancak yine de bir sorun. Bu nedenle, 16 çekirdek sunucuda 16 Node.js örneğinin çalıştırılması, büyük olasılıkla bu çalışma zamanı hatası nedeniyle + RTS-N1'den daha yavaş olacak + RTS-N16 kullanan tek bir ghc uygulamasından çok daha iyi olacaktır. Bunun nedeni, birçok işletim sistemi iş parçacığıyla kullanıldığında yavaşlayacak olan tek bir IOManager kullanmalarıdır. Umarım bu hatayı düzeltirler ama o zamandan beri var, bu yüzden çok fazla umudum olmaz ...
Kr0e

Bu cevaba bakan herkes, Düğüm'ün 100k basit istekleri tek bir çekirdek üzerinde kolayca işleyebileceğini ve vatansız bir Düğüm uygulamasını birçok çekirdek arasında ölçeklendirmenin çok kolay olduğunu bilmelidir. pm2 -i max path/to/app.jsmevcut çekirdeklere göre otomatik olarak optimum örnek sayısına ölçeklenir. Ayrıca, Düğüm de varsayılan olarak engellemez.
Eric Elliott

1

1
Bu soruya nasıl cevap veriyor?
dfeuer

1
@dfeuer Bağlantı şöyle olmalıdır: Snap Haskell Web Framework libev'i düşürdü, biçimlendirmenin neden başarısız olduğunu bilmiyorum. Düğüm sunucusu çalışma zamanı başladığında Linux libev ile ilgiliydi ve Snap Web FrameWork de öyle. Snap ile Haskell, nodejs içeren ECMAscript'e benzer, bu nedenle Snap, nodejs ile birlikte nasıl gelişir, Haskell'den daha alakalı, bu bağlamda ECMAscript ile daha doğru bir şekilde karşılaştırılabilir.
Chawathe Vipul S
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.