Quais são as desvantagens de criar uma implementação de tempo de execução JavaScript multiencadeada? [fechadas]

51

Eu estive trabalhando em uma implementação de tempo de execução JavaScript multiencadeada na semana passada. Eu tenho uma prova de conceito feita em C ++ usando JavaScriptCore e boost.

A arquitetura é simples: quando o tempo de execução termina de avaliar o script principal, ele inicia e ingressa em um conjunto de encadeamentos, que começa a selecionar tarefas de uma fila de prioridade compartilhada, se duas tarefas tentam acessar uma variável simultaneamente, elas são marcadas como atômicas e elas disputam acesso .

Trabalhando em tempo de execução node.js multithread

O problema é que, quando mostro esse design a um programador JavaScript, recebo um feedback extremamente negativo e não faço ideia do porquê. Mesmo em particular, todos dizem que o JavaScript deve ser único, que as bibliotecas existentes teriam que ser reescritas e que os gremlins aparecerão e comerão todos os seres vivos se eu continuar trabalhando nisso.

Originalmente, eu também tinha uma implementação nativa de rotina (usando contextos de impulso), mas tive que abandoná-la (o JavaScriptCore é pedante sobre a pilha) e como não queria arriscar sua ira, decidi não mencioná-la.

O que você acha? O JavaScript é destinado a ser encadeado único e deve ser deixado em paz? Por que todos são contra a ideia de um tempo de execução JavaScript simultâneo?

Edit: O projeto está agora no GitHub , experimente você mesmo e deixe-me saber o que você pensa.

A seguir, é apresentada uma imagem das promessas em execução em todos os núcleos da CPU em paralelo, sem contenção:

A execução promete simultaneamente.

voodooattack
fonte
7
Esta parece ser uma pergunta altamente opinativa. Você perguntou às pessoas que aparentemente não gostaram da sua ideia por que acham que será problemático?
precisa saber é o seguinte
26
Adicionar threads a algo que não deve ser multithread é como converter uma estrada de pista única em uma via expressa sem fornecer informações ao motorista. Funcionará muito bem na maioria das vezes, até que as pessoas comecem a travar aleatoriamente. Com o multithreading, você terá bugs sutis de tempo que não pode reproduzir ou comportamento irregular na maioria das vezes. Você tem que projetar com isso em mente. Você precisa de sincronização de threads. Apenas tornar as variáveis ​​atômicas não elimina as condições de corrida.
mgw854
2
Como você planeja lidar com o acesso multiencadeado ao estado compartilhado? "Marcado como atômico e defende acesso" não explica como você acha que isso realmente funcionaria. Eu acho que as atitudes negativas em relação à ideia são porque as pessoas não têm ideia de como você realmente faria isso funcionar. Ou, se você está colocando toda a carga sobre o desenvolvedor, como em Java ou C ++, para usar mutexes apropriados e coisas do gênero, provavelmente as pessoas estão pensando por que querem esse risco de complicação e programação em um ambiente livre dele.
jfriend00
17
Como coordenar automaticamente o estado aleatório entre os segmentos é considerado um problema quase impossível, então você não tem credibilidade para oferecer qualquer coisa que o faça automaticamente. E, se você vai colocar o fardo de volta no desenvolvedor, como Java ou C ++, a maioria dos programadores do node.js. não quer esse fardo - eles gostam desse node.js. não precisa lidar com isso para a maior parte. Se você quer um ouvido mais compreensivo, terá que explicar / mostrar como e o que você ofereceria a esse respeito e por que seria bom e útil.
jfriend00
3
Por favor, continue seu trabalho. Considero idiomas sem multiencadeamento como idiomas de brinquedo. Acho que a maioria dos desenvolvedores de JavaScript trabalha com o navegador, que possui um modelo de thread único.
Chloe

Respostas:

70

1) A multithreading é extremamente difícil e, infelizmente, a maneira como você apresentou essa ideia até agora implica que você está subestimando seriamente a dificuldade.

No momento, parece que você está simplesmente "adicionando tópicos" ao idioma e se preocupando com como corrigi-lo e ter um desempenho mais tarde. Em particular:

se duas tarefas tentam acessar uma variável simultaneamente, ela é marcada como atômica e elas disputam o acesso.
...
Concordo que as variáveis ​​atômicas não resolverão tudo, mas trabalhar em uma solução para o problema de sincronização é meu próximo objetivo.

Adicionar threads ao Javascript sem uma "solução para o problema de sincronização" seria como adicionar números inteiros ao Javascript sem uma "solução para o problema de adição". É tão fundamental para a natureza do problema que basicamente não faz sentido discutir se vale a pena adicionar multithreading sem uma solução específica em mente, não importa o quanto a desejemos.

Além disso, tornar todas as variáveis ​​atômicas é o tipo de coisa que provavelmente fará com que um programa multithread tenha um desempenho pior do que seu equivalente de single-threaded, o que torna ainda mais importante testar o desempenho em programas mais realistas e ver se você está ganhando algo ou não.

Também não está claro para mim se você está tentando manter os threads ocultos do programador node.js.se planeja expô-los em algum momento, criando efetivamente um novo dialeto de Javascript para programação multithread. Ambas as opções são potencialmente interessantes, mas parece que você ainda nem decidiu qual delas está buscando.

Portanto, no momento, você está pedindo aos programadores que pensem em mudar de um ambiente single-threaded para um novo ambiente multithread que não tem solução para o problema de sincronização e nenhuma evidência que melhore o desempenho no mundo real, e aparentemente nenhum plano para resolver esses problemas.

É provavelmente por isso que as pessoas não o levam a sério.

2) A simplicidade e robustez do loop de evento único é uma enorme vantagem.

Os programadores Javascript sabem que a linguagem Javascript é "segura" contra as condições de corrida e outros bugs extremamente insidiosos que afetam toda a programação genuinamente multithread. O fato de que eles precisam de argumentos fortes para convencê-los a desistir de que a segurança não os deixa com a mente fechada, os torna responsáveis.

A menos que você possa, de alguma maneira, manter essa segurança, qualquer pessoa que queira mudar para um node.js multithread pode provavelmente mudar para um idioma como o Go, desenvolvido desde o início para aplicativos multithread.

3) Javascript já suporta "threads de segundo plano" (WebWorkers) e programação assíncrona sem expor diretamente o gerenciamento de threads ao programador.

Esses recursos já resolvem muitos casos de uso comuns que afetam os programadores Javascript no mundo real, sem abrir mão da segurança do loop de evento único.

Você tem algum caso de uso específico em mente que esses recursos não resolvem e que os programadores Javascript desejam uma solução? Nesse caso, seria uma boa ideia apresentar seu node.js multithread no contexto desse caso de uso específico.


PS O que me convenceu a tentar alternar para uma implementação node.js multithread?

Escreva um programa não trivial em Javascript / node.js que, em sua opinião, se beneficiaria de multithreading genuíno. Faça testes de desempenho neste programa de amostra no nó normal e no seu nó multithread. Mostre-me que sua versão melhora significativamente o desempenho em tempo de execução, a capacidade de resposta e o uso de múltiplos núcleos, sem introduzir bugs ou instabilidade.

Depois de fazer isso, acho que você verá pessoas muito mais interessadas nessa idéia.

Ixrec
fonte
11
1) Ok, admito que já adiei a questão da sincronização há um tempo. Quando digo que 'duas tarefas continuarão', esse não é o meu design, mas principalmente uma observação: dl.dropboxusercontent.com/u/27714141/… - Não tenho certeza de que tipo de bruxaria o JavaScriptCore está executando aqui, mas não deveria ' Essa string é corrompida se não for inerentemente atômica? 2) Discordo totalmente. Esta é a razão pela qual JS é encarado como uma linguagem de brinquedo. 3) As promessas do ES6 teriam muito mais desempenho se implementadas usando um agendador de conjuntos de encadeamentos.
precisa saber é o seguinte
13
@voodooattack "Esta é a razão pela qual JS é encarado como uma linguagem de brinquedo." Não, é por isso que você considera uma linguagem de brinquedo. Milhões de pessoas usam JS todos os dias e estão perfeitamente felizes com isso, deficiências e tudo. Certifique-se de resolver um problema que muitas pessoas realmente têm, que não é melhor resolvido apenas mudando de idioma.
Chris Hayes
@ChrisHayes A questão é: por que aturar suas deficiências quando eu posso corrigi-las? A simultaneidade como um recurso melhoraria o JavaScript?
precisa saber é o seguinte
11
@voodooattack Essa é a questão. A simultaneidade como um recurso melhoraria o Javascript? Se você pode obter uma resposta da comunidade para aquilo que não é "não", talvez esteja pensando em algo. No entanto, parece que o loop de eventos e a delegação nativa do Node aos threads de trabalho para bloquear eventos são suficientes para a maior parte do que as pessoas precisam fazer. Eu acho que se as pessoas realmente precisarem de Javascript encadeado, elas usarão trabalhadores de Javascript. Se você puder encontrar uma maneira de fazer com que os trabalhadores trabalhem com funções de primeira classe em vez de arquivos JS, no entanto ... então você pode realmente gostar de algo.
precisa saber é o seguinte
7
@voodooattack As pessoas que chamam o JavaScript de linguagem de brinquedo não sabem do que estão falando. É o idioma preferido para tudo? Claro que não, mas descartá- lo assim é certamente um erro. Se você deseja dissipar a noção de que JS é uma linguagem de brinquedo, faça um aplicativo de produção não trivial nele ou aponte para um existente. Apenas adicionar concorrência não mudará a mente dessas pessoas, de qualquer maneira.
precisa saber é o seguinte
16

Basta adivinhar aqui para demonstrar um problema em sua abordagem. Não posso testá-lo com a implementação real, pois não há link em nenhum lugar ...

Eu diria que é porque os invariantes nem sempre são expressos pelo valor de uma variável e 'uma variável' não é suficiente para ser o escopo de um bloqueio no caso geral. Por exemplo, imagine que temos um invariante a+b = 0(saldo de um banco com duas contas). As duas funções abaixo garantem que a invariante seja sempre mantida no final de cada função (a unidade de execução em JS de thread único).

function withdraw(v) {
  a -= v;
  b += v;
}
function deposit(v) {
  b -= v;
  a += v;
}

Agora, no seu mundo multithread, o que acontece quando dois threads são executados withdrawe depositao mesmo tempo? Obrigado, Murphy ...

(Você pode ter um código que trata + = e - = especialmente, mas isso não ajuda. Em algum momento, você terá um estado local em uma função e sem nenhuma maneira de 'bloquear' duas variáveis ​​ao mesmo tempo que seu invariante irá ser violado.)


EDIT: Se o seu código é semanticamente equivalente ao código Go em https://gist.github.com/thriqon/f94c10a45b7e0bf656781b0f4a07292a , meu comentário é preciso ;-)

thriqon
fonte
Esse comentário é irrelevante, mas os filósofos são capazes de bloquear vários objetos, mas passam fome porque os travam em uma ordem inconsistente.
Dietrich Epp
3
@voodooattack "Acabei de testar ..." Você nunca encontrou ordem de execução inconsistente com agendamento de threads? Varia de corrida para corrida, de máquina para máquina. Sentar e executar um único teste (ou até cem testes!) Sem identificar o mecanismo de como as operações são agendadas é inútil.
jpmc26
4
@voodooattack O problema é que simplesmente testar a simultaneidade não é útil. Você precisa provar que a invariante se manterá ou o mecanismo nunca poderá ser confiável na produção.
sapi
11
Mas você não forneceu ao usuário nenhuma ferramenta para "usar o sistema com responsabilidade", porque não há absolutamente nenhum mecanismo de bloqueio. Tornar tudo atômico dá uma ilusão de segurança de encadeamento (e você recebe o desempenho de tudo ser atômico, mesmo quando não precisa de acesso atômico), mas na verdade não resolve a maioria dos problemas de simultaneidade, como o que o thriqon oferece aqui. Para outro exemplo, tente iterar sobre uma matriz em um segmento, enquanto outro segmento adiciona ou remove elementos da matriz. Na verdade, o que faz você pensar que a implementação do Array do mecanismo é segura para threads?
precisa saber é o seguinte
2
@voodooattack Portanto, se os usuários puderem usar seus threads apenas para funções sem dados compartilhados ou efeitos colaterais (e é melhor não usá-los para mais nada, porque, como vimos aqui, não há como garantir a segurança do thread), então, qual o valor que você está fornecendo aos Web Workers (ou a uma das muitas bibliotecas que fornecem uma API mais utilizável para os Web Workers)? E os Web Workers são muito mais seguros, porque tornam impossível para o usuário usar o sistema "de forma irresponsável".
precisa saber é o seguinte
15

Há uma década, Brendan Eich (o inventor do JavaScript) escreveu um ensaio chamado Threads Suck , que é definitivamente um dos poucos documentos canônicos da mitologia do design do JavaScript.

Se está correto é outra questão, mas acho que teve uma grande influência sobre como a comunidade JavaScript pensa em concorrência.

Erik Pukinskis
fonte
31
A última pessoa de quem gostaria de receber conselhos é Brendan Eich. Toda a sua carreira é baseada na criação de JavaScript, que é tão ruim que temos um monte de ferramentas criadas para tentar contornar sua porcaria inata. Pense em quantas horas de desenvolvedor foram desperdiçadas no mundo por causa dele.
Phil Wright
12
Embora eu entenda por que você desconsideraria a opinião dele em geral e até o seu desprezo pelo JavaScript, não entendo como sua perspectiva permitiria desconsiderar a experiência dele no domínio.
Billy Cravens
4
@BillyCravens haha, até o livro canônico sobre Javascript: "Javascript the good parts " de Crockford revela o jogo no título. Trate as atitudes de Eich Da mesma forma, se ater apenas ao material que ele diz que é bom :-)
gbjbaanb
13
@ PhilWright: Sua primeira implementação de Javascript foi uma variante Lisp. O que para mim lhe rende um enorme respeito. A decisão de seu chefe de forçá-lo a substituir a sintaxe Lisp pela sintaxe do tipo C não é culpa dele. Javascript em sua essência ainda é um tempo de execução Lisp.
slebetman
7
@ PhilWright: Você não deve culpar Eich pela porcaria do idioma. São erros nas implementações iniciais e a necessidade de compatibilidade com versões anteriores de uma linguagem da Web que impedia o JS de amadurecer. Os conceitos principais ainda formam uma linguagem maravilhosa.
Bergi
8

O acesso atômico não se traduz em comportamento seguro de thread.

Um exemplo é quando uma estrutura de dados global precisa ser inválida durante uma atualização, como reformular um hashmap (ao adicionar uma propriedade a um objeto, por exemplo) ou classificar uma matriz global. Durante esse período, você não pode permitir que nenhum outro thread acesse a variável. Isso basicamente significa que você precisa detectar todos os ciclos de leitura, atualização e gravação e travar sobre isso. Se a atualização não for trivial, ela terminará em interromper o território problemático.

O Javascript foi encadeado e com sandbox único desde o início e todo o código é escrito com essas suposições em mente.

Isso tem uma grande vantagem em relação a contextos isolados e permite que dois contextos separados sejam executados em threads diferentes. Também quero dizer que as pessoas que escrevem javascript não precisam saber como lidar com as condições de corrida e vários outros pitfals multi-thread.

catraca arrepiante
fonte
É por isso que acho que a API do agendador deve ser mais avançada e usada internamente para promessas e funções que não têm efeitos colaterais.
precisa saber é o seguinte
6

Sua abordagem vai melhorar significativamente o desempenho?

Duvidoso. Você realmente precisa provar isso.

Sua abordagem tornará mais fácil / rápido escrever código?

Definitivamente não, o código multithread é muitas vezes mais difícil de acertar do que o código thread único.

Sua abordagem será mais robusta?

Impasses, condições de corrida, etc., são um pesadelo para corrigir.

Phil Wright
fonte
2
Tente criar um raytracer usando o node.js, agora tente novamente com threads. Multi-processo não é tudo.
precisa saber é o seguinte
8
@voodooattack, ninguém em sã consciência escreveria um traçador de raios usando javascript, com ou sem threading, porque é um algoritmo relativamente simples, mas intensivo em computação, que é melhor escrito em uma linguagem totalmente compilada, preferencialmente com suporte SIMD . Para os tipos de problemas para os quais o javascript é usado, o multiprocesso é mais que suficiente.
Jan Hudec
@JanHudec: Heh, JS vai obter apoio SIMD bem :-) hacks.mozilla.org/2014/10/introducing-simd-js
Bergi
2
@voodooattack Se você não os conhece, dê uma olhada no SharedArrayBuffers . O JavaScript está obtendo mais construções de simultaneidade, mas elas são adicionadas com extrema cautela, abordando pontos problemáticos específicos, tentando minimizar a tomada de decisões ruins de design com as quais teremos que conviver por anos.
REINSTATE MONICA -Jeremy Banks
2

Sua implementação não consiste apenas em introduzir simultaneidade, mas em introduzir uma maneira específica de implementar simultaneidade, ou seja, simultaneidade por estado mutável compartilhado. Ao longo da história, as pessoas usaram esse tipo de simultaneidade e isso levou a muitos tipos de problemas. É claro que você pode criar programas simples que funcionam perfeitamente com o uso de simultaneidade de estado mutável compartilhada, mas o teste real de qualquer mecanismo não é o que ele pode fazer, mas pode ser dimensionado conforme o programa se torna complexo e mais e mais recursos são adicionados ao programa. Lembre-se de que o software não é algo estático que você constrói uma vez e acaba com ele, mas continua evoluindo ao longo do tempo e se algum mecanismo ou conceito puder '

Você pode observar outros modelos de concorrência (por exemplo, passagem de mensagens) para ajudá-lo a descobrir que tipo de benefícios esses modelos oferecem.

Ankur
fonte
11
Eu acho que as promessas do ES6 se beneficiariam do modelo da minha implementação, uma vez que não disputam acesso.
precisa saber é o seguinte
Dê uma olhada na especificação dos WebWorkers. Há pacotes NPM que fornecem a sua execução, mas você pode implementá-las como o núcleo de seu motor em vez como um pacote
Ankur
JavaScriptCore (a implementação de JS da Web que estou usando) já os implementa, é apenas um sinalizador de compilação.
precisa saber é o seguinte
Está bem. Os WebWorkers são simultâneos com a passagem de mensagens. Você pode tentar o seu exemplo do raytracer com eles e compará-lo com a abordagem de estado mutável.
Ankur #
4
A passagem de mensagens é sempre implementada em cima do estado mutável, o que significa que seria mais lento. Não vejo seu ponto aqui. : - /
voodooattack 12/04
1

Isso é necessário. A falta de um mecanismo de simultaneidade de baixo nível no nó js limita suas aplicações em campos como matemática e bioinformática, etc ... Além disso, a simultaneidade com encadeamentos não necessariamente entra em conflito com o modelo de concordância padrão usado no nó. Existem semânticas conhecidas para threading em um ambiente com um loop de evento principal, como estruturas de interface do usuário (e nodejs), e elas são definitivamente excessivamente complexas para a maioria das situações em que ainda têm usos válidos.

Certamente, seu aplicativo Web médio não exigirá threads, mas tente fazer algo um pouco menos convencional, e a falta de uma primitiva de simultaneidade de baixo nível de som rapidamente o levará a algo que o ofereça.

dryajov
fonte
4
Mas "matemática e bioinformática" seria melhor escrito em C #, Java ou C ++
Ian
Na verdade, Python e Perl são os idiomas dominantes nessas áreas.
dryajov
11
Na verdade, a principal razão pela qual estou implementando isso é por causa dos aplicativos de aprendizado de máquina. Então você tem razão.
precisa saber é o seguinte
@dryajov Seja feliz que você não sabe o quão grande Fortran está em algumas das áreas de ciências computacionais ...
cmaster
@dryajov Por serem mais acessíveis aos humanos que talvez não sejam programadores em período integral, não porque sejam inerentemente melhores no sequenciamento de genoma - temos linguagens criadas para fins específicos como R e compilamos linguagens científicas como Fortran para isso.
gato
0

Eu realmente acredito que é porque é uma ideia diferente e poderosa. Você está indo contra os sistemas de crenças. As coisas se tornam aceitas ou populares por meio da rede e não com base no mérito. Além disso, ninguém quer se adaptar a uma nova pilha. As pessoas automaticamente rejeitam coisas muito diferentes.

Se você conseguir criar uma maneira de transformá-lo em um módulo npm regular que parece improvável, talvez algumas pessoas o usem.

Jason Livesay
fonte
Você quer dizer que os programadores JS estão bloqueados pelo fornecedor para npm?
precisa saber é o seguinte
3
A questão é sobre um novo sistema de tempo de execução, e não algum módulo npm e também simultaneidade mutável de estado compartilhado, é realmente uma idéia antiga.
Ankur #
11
@voodooattack, quero dizer que eles estão travados no cérebro com a abordagem que já é popular e será quase impossível superar o viés do status quo.
Jason Livesay