Libelektra: comentários sobre o trabalho de API de alto nível e LCDproc

Criado em 10 jun. 2019  ·  65Comentários  ·  Fonte: ElektraInitiative/libelektra

Oi,

Acabei de ver alguns exemplos do código LCDproc que você escreveu e, em extensão, a API de alto nível pela primeira vez. E com "olhado" quero dizer apenas olhei: ainda não tive tempo de configurar um container onde eu possa construir com segurança o mestre elektra e tentar algumas coisas de verdade ou verificar os benchmarks que definimos no início.

A primeira coisa a notar é que o código é muito detalhado. Embora tokens como ELEKTRA_TAG_LCDEXEC_ADDRESS sejam fáceis de entender à primeira vista, acho que eles dificultam a leitura do código se você tiver muitas linhas disso.

Em seguida, você parece adorar tipos de dados opacos. Estou ciente dos benefícios de manter a ABI estável enquanto as coisas mudam sob a superfície. No entanto, a desvantagem é que isso torna o código novamente mais difícil de ler em grandes quantidades e também sobrecarrega muito os binários resultantes. (Cada chamada para um getter externo normalmente leva pelo menos 3 instruções e invalida o conteúdo de todos os registradores. De um POV de programação verde, isso é bastante lamentável.) O último problema pode ser "consertado" tornando o getter inlineável, mas é claro no custo de todos os benefícios. Eu reconsideraria quais tipos de dados realmente valem a pena serem opacos.

Vejo que você está fornecendo suas próprias versões de tipos de dados básicos como long. Qual problema você está tentando resolver com isso? IMO torna principalmente o código mais difícil de ler.

Em particular, não gosto que você também tenha usado tipos de dados elektra em estruturas de dados LCDproc não diretamente relacionadas à configuração.

Que os identificadores de erro sejam estruturas de dados malloc() é bastante surpreendente. Especialmente porque lembro que tivemos uma discussão há cerca de um ano, onde defendi tratar malloc() como à prova de falhas e você discordou. Agora você está usando malloc em um contexto em que claramente precisa tratá-lo como à prova de falhas. Existem apenas algumas funções, que realmente podem causar erros, então este é um tópico sem importância, mas acho a escolha pouco ortodoxa. A maioria das pessoas parece tentar fazer tratamento de erros na pilha.

Talvez isso já esteja respondido em algum documento, mas não encontrei rapidamente: Quando elektraGet() retorna uma string (ou outro ponteiro para uma estrutura de dados), qual o escopo onde o objeto é válido? Meu palpite é até que elektraClose() seja chamado, mas não é óbvio. Além disso, qual é uma boa política para manter o controle da Elektra em relação ao uso de recursos (e outras considerações)?

Vejo que você ficou bem próximo da estrutura original do código. Eu acho que para alguns de seus objetivos isso faz sentido, mas OTOH eu me pergunto se você pode realmente exibir elektra enquanto adere à estrutura ditada por uma API de configuração estrangeira. Talvez você possa compartilhar seus pensamentos sobre o assunto. Também talvez seja interessante para a tese comparar para um driver a diferença entre uma tradução direta e uma implementação elektra idiomática, se houver.

Desculpe se isso soa principalmente negativo. Suponho que seja natural que, ao olhar para algo muito rapidamente, principalmente as coisas negativas se destaquem. Em breve, tentarei encontrar tempo para explorar seu código com mais detalhes.

HTH,
Harald

Todos 65 comentários

Muito obrigado para sua revisão!

A primeira coisa a notar é que o código é muito detalhado. Embora tokens como ELEKTRA_TAG_LCDEXEC_ADDRESS sejam fáceis de entender à primeira vista, acho que eles dificultam a leitura do código se você tiver muitas linhas disso.

Eu acho que é um bom padrão ter nomes seguros contra colisões. Você quer para LCDproc LCDEXEC_ADDRESS em vez disso?

Em seguida, você parece adorar tipos de dados opacos.

Sim, eu os amo porque queremos fornecer APIs com uma experiência de atualização suave. No caso concreto dos getters poderíamos abrir uma exceção (já fazemos isso na geração de código C++). Mas vejo isso como uma otimização que só deve ser feita se percebermos que é relevante para o LCDproc. ( @kodebach fará o benchmark disso.) Pela minha experiência, só é relevante se os getters forem chamados com muita frequência em loops apertados. Se os getters forem usados ​​apenas algumas vezes no procedimento de inicialização, isso não deve importar.

Vejo que você está fornecendo suas próprias versões de tipos de dados básicos, como long. Qual problema você está tentando resolver com isso?

Que tipos como "int" têm tamanhos diferentes em plataformas diferentes, mas precisamos saber o que permitimos no arquivo de configuração.

Que os identificadores de erro são estruturas de dados malloc() é bastante surpreendente.

Sim, tivemos longas discussões sobre isso. @domhof estava muito convencido de que é muito melhor para a usabilidade da API. Se você não passar o objeto de erro, a chamada da API será encerrada, o que dificulta muito o uso incorreto. Não tenho certeza se isso está totalmente implementado agora, no entanto. (@kodebach?)

Agora você está usando malloc em um contexto em que claramente precisa tratá-lo como à prova de falhas.

É também que chamamos outros mallocs quando ocorrem erros. O conceito da API seria à prova de falhas: se o malloc para o objeto de erro falhar, um ponteiro nulo será passado para a API e, em seguida, o Elektra sairá em caso de falha. Mas isso não é testado, no entanto. É importante que isso funcione?

Meu palpite é até que elektraClose() seja chamado, mas não é óbvio.

Bom ponto, o documento da API deve ser muito claro sobre isso, criei o #2774.

Eu acho que é um bom padrão ter nomes seguros contra colisões.

Claro, mas desde que o compilador os detecte, nada realmente ruim pode acontecer.

Você quer para LCDproc LCDEXEC_ADDRESS em vez disso?

LCDEXEC_ADDRESS ou CONF_ADDRESS ambos parecem razoáveis ​​para mim.

Mas vejo isso como uma otimização que só deve ser feita se percebermos que é relevante para o LCDproc.

Esta foi principalmente uma observação geral. Eu não acho que o lcdproc seja afetado pior do que os outros.

Pela minha experiência, só é relevante se os getters forem chamados com muita frequência em loops apertados. Se os getters forem usados ​​apenas algumas vezes no procedimento de inicialização, isso não deverá importar.

Minha experiência é exatamente o oposto: em um loop apertado, quando tudo está em cache, algumas instruções extras dificilmente importam - mesmo nas CPUs ARM que estou usando principalmente. Está carregando código no cache em primeiro lugar, essa é a operação realmente cara. Claro que para o código de inicialização, isso também não é um problema. Nesse contexto, estou preocupado apenas com o tamanho dos binários resultantes: a otimização do compilador pode fazer muitas coisas, mas não pode contornar uma API.

O uso de estruturas de dados opacas custa cerca de 20 bytes por acesso de membro sobre o caso transparente. Um custo de RAM que deve ser pago mesmo que o acesso esteja em algum caminho de erro raramente usado e um custo de flash que deve ser pago mesmo que o código não esteja sendo executado. Multiplique isso pelo número de acessos em um aplicativo típico e o número de sistemas em que o aplicativo está sendo executado e você obterá facilmente um número bastante grande. Certamente há casos em que é razoável gastar esses recursos para obter flexibilidade extra. Mas em muitos outros casos não é e só acontece por hábito.

Vejo que você está fornecendo suas próprias versões de tipos de dados básicos como long. Qual problema você está tentando resolver com isso?
Que tipos como "int" têm tamanhos diferentes em plataformas diferentes, mas precisamos saber o que permitimos no arquivo de configuração.

Sim, "int" é ruim para dados armazenados. Mas AFAIK os outros tipos como "longo" não têm o mesmo problema. E o que é mais importante, já existem os tipos de "stdint.h" com tamanho explícito, que são amplamente conhecidos. Eu recomendo que você use esses. Inventar o seu só torna o código mais difícil de ler para todos.

Se o malloc para o objeto de erro falhar, um ponteiro nulo será passado para a API e, em seguida, o Elektra será encerrado em caso de falha. Mas isso não é testado, no entanto. É importante que isso funcione?

Claro que não, eu ainda defendo tratar malloc() de pequenas estruturas de dados à prova de falhas, afinal. Eu não teria comentado sobre isso, se não fosse pela discussão do ano passado.

Claro, mas desde que o compilador os detecte, nada realmente ruim pode acontecer.

Sim, agora você recebe mensagens de erro muito boas, mesmo com macros. Alguns anos atrás, o erro do compilador não indicava como as macros manipulavam algo: Então, às vezes, era necessário muito tempo para descobrir que as macros estavam envolvidas.

Esta foi principalmente uma observação geral. Eu não acho que o lcdproc seja afetado pior do que os outros.

Há uma grande diferença se a API for usada para cada acesso de configuração em comparação com se um aplicativo copiar os dados de configuração em structs. Afetados serão principalmente aplicativos que não copiam. E não copiar é realmente recomendado se você quiser atualizações rápidas de configuração.

A otimização do compilador pode fazer muitas coisas, mas não pode contornar uma API.

Exatamente, então eu me pergunto por que sua experiência é contrária? Se nosso getter retornasse algum membro de uma struct e pudesse ser embutido, não haveria custo de API. Se você pagar apenas o custo da API em um número constante de casos, é muito diferente dos aplicativos em que um número ilimitado de chamadas de API pode ser feito (que não copia).

Sim, "int" é ruim para dados armazenados. Mas AFAIK os outros tipos como "longo" não têm o mesmo problema. E o que é mais importante, já existem os tipos de "stdint.h" com tamanho explícito, que são amplamente conhecidos. Eu recomendo que você use esses. Inventar o seu só torna o código mais difícil de ler para todos.

Para C99 ou se stdint.h estiver disponível, nós digitamos def para eles. Portanto, poderíamos usar esses tipos de tamanho fixo se você suporta apenas sistemas que possuem C99 ou stdint.h.

espero ter resolvido tudo...

Coisas que realmente podem ser mudadas

(sem redesenhar grandes partes da API)

Embora tokens como ELEKTRA_TAG_LCDEXEC_ADDRESS sejam fáceis de entender à primeira vista, acho que eles dificultam a leitura do código se você tiver muitas linhas disso.

Eu acho que é um bom padrão ter nomes seguros contra colisões.

Claro, mas desde que o compilador os detecte, nada realmente ruim pode acontecer.

Concordo, as tags atuais são muito longas. Infelizmente, como C não suporta nenhum tipo de namespace, é difícil evitar nomes longos e, ao mesmo tempo, evitar colisões de nomes em geral. Vou adicionar algumas opções para manipular as tags geradas. Atualmente eles são criados usando o prefixo ELEKTRA_TAG_ e então uma versão adaptada do nome da chave. Então ELEKTRA_TAG_LCDEXEC_ADDRESS representa a chave lcdexec/address .

Você também não precisa usar as tags. Se você der uma olhada em como as tags são resolvidas, você encontrará várias alternativas que resolvem para a mesma função inline. Algumas dessas alternativas podem ser mais curtas. Também deve ser bastante seguro usar diretamente as funções inline (por exemplo elektraGetLcdexecAddress ).

Em particular, não gosto que você também tenha usado tipos de dados elektra em estruturas de dados LCDproc não diretamente relacionadas à configuração.

Provavelmente poderíamos usar os tipos stdint.h equivalentes. Embora não seja necessariamente 100% compatível, se você usar um compilador que não seja compatível com C99. (veja abaixo os detalhes)

Talvez isso já esteja respondido em algum documento, mas não encontrei rapidamente: Quando elektraGet() retorna uma string (ou outro ponteiro para uma estrutura de dados), qual o escopo onde o objeto é válido? Meu palpite é até que elektraClose() seja chamado, mas não é óbvio. Além disso, qual é uma boa política para manter o controle da Elektra em relação ao uso de recursos (e outras considerações)?

Isso é realmente bem complicado. Veja minha resposta em #2774.

Sobre o design da API

Em seguida, você parece adorar tipos de dados opacos.

Também não sou fã de como a Elektra usa structs. No entanto, não tive qualquer influência nesta decisão. Tentei minimizar o impacto no desempenho (e no tamanho binário) da API de geração de código. É por isso que todas as funções de acesso geradas são static inline . AFAIK dessa forma, o compilador deve inline sempre que possível e, como são estáticos, todos os não utilizados devem ser removidos.

No caso concreto dos getters poderíamos abrir uma exceção (já fazemos isso na geração de código C++). Mas vejo isso como uma otimização que só deve ser feita se percebermos que é relevante para o LCDproc. ( @kodebach fará o benchmark disso.)

O que exatamente você quer dizer? Por favor, esclareça o que exatamente você quer dizer com "fazer uma exceção".

Vejo que você está fornecendo suas próprias versões de tipos de dados básicos como long. Qual problema você está tentando resolver com isso? IMO torna principalmente o código mais difícil de ler.

Eu absolutamente concordo. AFAIK o problema que estamos tentando resolver é que C não define tamanhos fixos para seus tipos de dados. O padrão C define apenas limites inferiores.

Por que esses nomes detalhados baseados nos tipos CORBA são usados, eu não sei. Esta decisão foi antes do meu tempo e eu não tenho absolutamente nenhuma ideia de como essa conclusão foi alcançada. Eu entendo que, usando nossos próprios tipos, mantemos alguma compatibilidade com C89 (embora eu esteja bastante certo de que não somos estritamente compatíveis com C89, a API gerada por código certamente não é), mas isso não é um argumento bom o suficiente contra o uso de stdint.h IMHO.

No entanto: Como dito acima, se C99+ for usado, todos os tipos kdb_ retornam para as versões stdint.h equivalentes.

se você suporta apenas sistemas que possuem C99

A API de geração de código requer mais ou menos C99. Usamos coisas como for (int i = 0; ...) { ... } então definitivamente não somos compatíveis com C89 (o GNU89 pode funcionar).

Que os identificadores de erro são estruturas de dados malloc() é bastante surpreendente.

O tratamento de erros também foi decidido antes do meu tempo. Eu também não faria assim. No entanto, sou definitivamente da opinião de que na maioria dos casos podemos tratar malloc() como prova de falhas. Além disso, lidar com erros na pilha é difícil ou impossível aqui, pois pode haver muitos avisos. Para lidar com tudo na pilha, precisaríamos alocar espaço para o número máximo de avisos (1000 eu acho) na pilha.

Atualmente, também há uma grande refatoração do tratamento de erros de baixo nível em andamento, portanto, alguns dos tratamentos de erro de alto nível serão alterados. É também por isso que atualmente a única propriedade de erros publicamente acessível é a descrição.

Equívocos

Se você não passar o objeto de erro, a chamada da API será encerrada, o que dificulta muito o uso incorreto. Não tenho certeza se isso está totalmente implementado agora, no entanto. (@kodebach?)

Você não precisa passar um objeto de erro. Você só passa um ElektraError ** . Causamos um erro fatal, se este ponteiro for NULL. Erros fatais por padrão chamam exit() . Em C puro, provavelmente também é a única maneira de lidar com erros fatais. No entanto, se a API for usada, por exemplo, em C++, o manipulador de erro fatal também pode lançar uma exceção.

Há uma grande diferença se a API for usada para cada acesso de configuração em comparação com se um aplicativo copiar os dados de configuração em structs.

Sim, não copiar é muito mais desperdício. (ver abaixo)

E não copiar é realmente recomendado se você quiser atualizações rápidas de configuração.

Atualizações instantâneas (como na API de notificação) não são suportadas de forma alguma pela API de alto nível. Atualmente, apenas atualizamos o KeySet interno em chamadas elektraOpen , elektraEnsure (será mesclado em elektraOpen ) e elektraSet* (o que torna elektraSet bastante caro). Podemos oferecer suporte a atualizações instantâneas no futuro, mas eu aconselho fortemente a não chamar elektraGet* toda vez que você precisar do seu valor de configuração.

Na verdade, recomendo chamar elektraGet* apenas uma vez para cada valor de configuração, a menos que você tenha certeza de que retornará um novo valor. Isso se deve ao modo como o sistema do tipo Elektras funciona. Cada chamada elektraGet* converte o valor de configuração de uma string.

Problemas de otimização

Infelizmente, minha experiência é que o Elektra está muito mal otimizado e que, se você realmente se importa com o desempenho, não deve usá-lo (ainda). IMO Elektra usa malloc() muito e muitas operações que parecem ser baratas na verdade não são (°). Entendo que kdb* são operações caras ( kdbOpen ainda é mais caro do que você imagina), mas também muito do material de manipulação de chaves é caro. Um bom exemplo é keySetMeta . Parece que deve ser bastante simples. Se você sabe que os metadados são armazenados em um KeySet interno (°°), você pensaria que keySetMeta é mais ou menos o mesmo que keyNew + ksAppendKey . No entanto, há um ksLookup e às vezes um elektraStrDup (°°°) lá.

O uso de RAM também não é um ponto forte da Elektra. Por exemplo, cada Key armazena o nome completo da chave duas vezes. Uma vez na forma escapada e uma vez na forma não escapada. Isso também torna os arquivos mmap-cache muito maiores do que o esperado.


(°) Isso provavelmente se deve ao fato de muitas pessoas terem trabalhado na Elektra. A maioria dos quais provavelmente não estava muito preocupada com o desempenho.

(°°) Ter um KeySet escondido dentro de cada Key é outra coisa que eu não gosto. A maior parte da funcionalidade KeySet e Key não é necessária para armazenar metadados.

(°°°) Acho que temos que ter elektraStrDup e elektraStrNDup para garantir que eles usem elektraMalloc (também não sou fã disso), mas tenho certeza por não usando strdup ou strndup quebramos algumas otimizações do compilador. Também porque elektraStrLen é uma coisa, eu nunca vou entender. Ele diz que é seguro unicode e multibyte, mas não vejo como. Claramente, não relataria o comprimento correto para ASCII codificado em UTF-16.

Parece que o github comeu minha resposta por e-mail ontem. Colando aqui novamente:

Obrigado por suas instruções. Na verdade eu compilei elektra e lcdproc
esta noite. Aqui estão meus resultados, responderei as conversas amanhã.

Depois de compilar, comparei tamanhos binários com e sem suas modificações
(commits f0cb7bb1 vs. 7973efc3) e notei que todos os binários aumentaram
muito. Por exemplo:

harald<strong i="11">@debian</strong>:~$ ls -l lcdproc*/clients/lcdexec/lcdexec
33852 Jun 11 17:39 lcdproc-0.5base/clients/lcdexec/lcdexec
73520 Jun 11 17:56 lcdproc/clients/lcdexec/lcdexec

De certa forma, um resultado decepcionante. Especialmente considerando que a
versão base tem um analisador de configuração vinculado estaticamente enquanto o
nova versão é dinamicamente vinculada ao libelektra.

A causa principal parece ser o código dentro do elektragen.c, mas a maioria dos outros
os arquivos de objeto também aumentaram de tamanho.

Eu também tentei tomar alguns timings, mas qualquer cliente segfaults imediatamente.
Isso é no buster armhf. ( kdb ls / funciona bem.)

Eu não olhei para isso ainda, mas acho que o segfault será bastante
fácil de corrigir. A questão do tamanho OTOH me incomoda muito.

Aqui está o que o gdb diz sobre o segfault:

Reading symbols from lcdproc/clients/lcdproc/lcdproc...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/harald/lcdproc/clients/lcdproc/lcdproc
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0xb6745ed0 in getPluginPlacementList (plugin=0x0)
    at /home/harald/libelektra/src/plugins/list/list.c:496
496             Key * pluginInfo = keyNew ("system/elektra/modules/", KEY_END);

Parece que o github comeu minha resposta por e-mail ontem.

Acabou aqui https://github.com/ElektraInitiative/libelektra/issues/2748#issuecomment -501032010.

todos os binários aumentaram muito.

Uma parte disso é a especificação que é compilada no binário. No entanto, ainda existem algumas redundâncias. Vou dar uma olhada.

Mas se você quiser manter o tamanho do binário pequeno, sempre será melhor com sua própria solução especializada. Na verdade, não faz sentido comparar apenas os tamanhos dos binários, pois você também precisa ter as bibliotecas Elektra instaladas. Esses sozinhos são maiores que os antigos clientes LCDproc. Você só precisa tomar uma decisão aqui: você quer todos os recursos do Elektra ou quer pequenos binários?

prefixo ELEKTRA_TAG

Na verdade, só precisamos de um prefixo escolhido pelo usuário, e no LCDproc escolheríamos não ter um prefixo.

O que exatamente você quer dizer? Por favor, esclareça o que exatamente você quer dizer com "fazer uma exceção".

Poderíamos abrir uma exceção que elektraGet não faz uma chamada de API.

Por que esses nomes detalhados baseados nos tipos CORBA são usados, eu não sei. Esta decisão foi antes do meu tempo e eu não tenho absolutamente nenhuma ideia de como essa conclusão foi alcançada.

Está documentado em #1324.

Isso se deve ao modo como o sistema do tipo Elektras funciona. Cada chamada elektraGet* converte o valor de configuração de uma string.

Isso se deve ao modo como a implementação funciona, não ao sistema de tipos. O código gerado em C++ já mostra que poderia ser diferente. Mas temo que isso seja uma reimplementação do elektraGet e algumas partes da geração de código.

A otimização não é tão trivial (especialmente em C, onde você precisaria gerar structs).

Uma parte disso é a especificação que é compilada no binário. No entanto, ainda existem algumas redundâncias. Vou dar uma olhada.

Talvez devêssemos reconsiderar a compilação da especificação se o tamanho do binário é tão importante. (E simplesmente falhar se não houver especificação)?

Ou tem um switch de compilação para compilá-lo?

@haraldg O que você acha?

Você só precisa tomar uma decisão aqui: você quer todos os recursos do Elektra ou quer pequenos binários?

A maioria dos recursos do Elektra não deve afetar o tamanho binário dos aplicativos. A especificação compilada, que fornece o recurso de que os aplicativos também iniciam sem serem instalados, é uma exceção e não a regra.

Isso se deve ao modo como o sistema do tipo Elektras funciona. Cada chamada elektraGet* converte o valor de configuração de uma string.

Isso se deve ao modo como a implementação funciona, não ao sistema de tipos.

O sistema de tipos (como em quais tipos estão disponíveis) é irrelevante sim. Mas "a maneira como o sistema do tipo Elektras funciona" não é. O que quero dizer é que o núcleo do Elektra não possui um sistema de tipos. Tudo é uma corda. Por que ter verificação de tipo através do plugin type . Mas armazenamos apenas strings, porque uma Key não tem noção de um tipo.

A otimização não é tão trivial (especialmente em C, onde você precisaria gerar structs).

Nós apenas precisaríamos armazenar o valor convertido dentro da estrutura Key após a verificação de tipo. Mas não há como fazer isso agora. Nem mesmo por metadados, que IMO seria a solução errada de qualquer maneira.

Talvez devêssemos reconsiderar a compilação da especificação se o tamanho do binário é tão importante. (E simplesmente falhar se não houver especificação)? Ou tem um switch de compilação para compilá-lo?

Vou adicionar uma opção à geração de código para desativá-lo. Mas em algum momento temos que definir qual é o nosso objetivo. A Elektra simplesmente não pode atender a todos os casos de uso. Tamanho binário, uso de RAM e desempenho são todas métricas em que uma solução especializada limitada às necessidades exatas de um aplicativo sempre superará uma solução geral como a Elektra.

Outra coisa que pode aumentar o tamanho do binário é o fato de gerarmos setters para tudo também. Vou adicionar uma opção para desabilitar setters, caso seu compilador não retire essas funções não utilizadas.

O sistema de tipos (como em quais tipos estão disponíveis) é irrelevante sim. Mas "a maneira como o sistema do tipo Elektras funciona" não é. O que quero dizer é que o núcleo do Elektra não possui um sistema de tipos. Tudo é uma corda. Por que ter verificação de tipo através do plugin de tipo. Mas armazenamos apenas strings, porque uma Key não tem noção de um tipo.

Sim, é assim que sua implementação funciona. Mas você já poderia transformar antes e só retornar o valor em elektraGet.

Nós apenas precisaríamos armazenar o valor convertido dentro da estrutura da chave após a verificação de tipo. Mas não há como fazer isso agora. Nem mesmo por metadados, que IMO seria a solução errada de qualquer maneira.

Se você precisa de um ksLookup, você é muito lento de qualquer maneira. Se você quer muito rápido, você precisa gerar um struct, preenchê-lo em elektraOpen, e em elektraGet é uma função inline estática que retorna o membro do struct. (Então você trivialmente também tem todas as garantias.)

Vou adicionar uma opção à geração de código para desativá-lo.

@kodebach Melhor esperar pela resposta do Harald. E primeiro precisamos descobrir quais são as causas do grande binário, talvez não seja apenas a especificação.

Eu realmente gosto muito da sua ideia de especificação compilada com specload. Assim, eu também estava pensando que poderíamos compactar a especificação, mas isso seria demais para sua tese agora.

Outra coisa que pode aumentar o tamanho do binário é o fato de gerarmos setters para tudo também. Vou adicionar uma opção para desabilitar setters, caso seu compilador não retire essas funções não utilizadas.

@kodebach Você conseguiu reproduzir o tamanho binário grande? Sempre tente primeiro encontrar as causas antes de otimizar.

Oi, como estou atrasado para a festa e vocês já discutiram muito, não vou responder tudo de novo. Se você sentir que perdeu uma resposta minha, por favor, levante a questão novamente.

De onde vem o aumento de tamanho:

Sim, as cordas são uma grande parte. No entanto, a seção de texto é muito maior do que a seção ro-data. Eu acho que empurrar todos os argumentos para as muitas chamadas de keyNew() na pilha, na verdade, ocupa mais espaço do que os próprios argumentos.

Acabei de usar o CFLAGS padrão do lcdproc, não tenho certeza de qual otimização eles escolhem - talvez nenhuma. No entanto, não acho que setters ou outro código não usado sejam um problema: os tamanhos da maioria dos objetos crescem apenas moderadamente - provavelmente apenas devido a argumentos extras de função e outras pequenas alterações.

Eu compilei manualmente elektragen.c com otimização -O e o arquivo de objeto resultante é um pouco menor, mas nada dramático. Acho que mais experimentos podem ser feitos, mas isso não parece ser uma solução por si só.

Sobre métricas e otimização

Na verdade, não otimizamos o lcdproc para nada, mas tentamos não usar recursos inutilmente. Definitivamente, não há um limite de tamanho forte. No entanto eu me preocupo em suportar sistemas embarcados, afinal sou o mantenedor do pacote OpenWRT do elektra.

É verdade que o aumento de tamanho ainda é pequeno comparado ao tamanho da própria elektra. Provavelmente seria insano usar elektra no lcdproc, se for o único usuário no sistema típico onde o lcdproc está instalado. Estou apostando no elektra eventualmente decolando e sendo instalado amplamente de qualquer maneira, então o tamanho do elektra não conta diretamente. No entanto, se a tendência de quase dobrar o tamanho instalado dos aplicativos continuar e o elektra decolar, precisarei de um novo laptop apenas por causa disso. Eu não gosto disso.

Também sinto que o tamanho do elektra se justifica, porque fornece muitos recursos interessantes. Mas um grande aumento de tamanho em binários de aplicativos é menos justificado: agora precisamos do dobro do tamanho em lcdproc para fazer praticamente o que fazíamos antes. (Sim, não nos importamos muito com a parte de especificação do elektra.)

As métricas que eu uso são: tamanho instalado, tamanho binário, espaço de memória, (linhas de cache preenchidas periodicamente), (instruções executadas). As duas últimas não são relevantes aqui, porque o código relacionado ao elektra não será executado repetidamente.

Dos outros três, o tamanho do binário é o menos importante: seções binárias mapeadas não usadas regularmente serão removidas da RAM e não usarão recursos permanentemente. tamanho binário é principalmente útil porque é muito fácil de medir e comparar. o tamanho instalado obviamente ocupa um espaço no chip flash e qualquer memória que não seja mapeada do sistema de arquivos (ou seja, estruturas de dados malloc()) deve ser mantida na RAM (a menos que você tenha espaço de troca, o que a maioria dos sistemas não possui) para todo sempre.

Pessoalmente, eu gasto principalmente esforços de otimização em problemas relacionados ao cache. Mas, além das considerações gerais da API, isso não é relevante para o elektra.

Possíveis melhorias e soluções alternativas

Falta-me muito do contexto das decisões de design por trás do elektra, então provavelmente não posso contribuir muito para esta parte da discussão. Algumas idéias, que flutuam na minha mente:

Algum tempo atrás, Markus e eu discutimos a possibilidade de usar a geração de código com algum tipo de modo embarcado: Em sistemas embarcados normalmente ninguém está fazendo nenhuma mudança de configuração. Todas as strings (de ajuda) direcionadas aos usuários e muitas das verificações não são úteis. Talvez isso vá na mesma direção que o "compile switch" sugerido acima?

Talvez a especificação possa ser armazenada em um formato mais eficiente do que construí-la programaticamente. (Ouvi dizer que você já reclama dos problemas de estabilidade da ABI, mas vale a pena considerar pelo menos.)

Na verdade, todo esse negócio de armazenar a especificação no binário (qual binário mesmo no caso de aplicativos modulares?) e depois imprimi-lo em stdout sob demanda parece bastante indireto para mim. Você pode armazená-lo em uma biblioteca elf e vinculá-lo dinamicamente (e dl_open() do kdb) ou até mesmo armazená-lo em algum outro formato adequado e eficiente e carregá-lo no construtor elf. (Supondo que realmente haja uma razão para não apenas carregá-lo durante elektraOpen(), que provavelmente é o que eu teria feito.)

Em última análise, não tenho uma visão clara das compensações, então não posso fazer sugestões sérias. É algo que só pode ser desenvolvido durante as discussões.

Respostas para @markus2330

Mas você já poderia transformar antes e só retornar o valor em elektraGet.

E onde o valor seria armazenado entre a transformação e elektraGet ? Além disso, quando a transformação aconteceria? Você transformaria todas as chaves em elektraOpen ? Isso seria um desperdício, se apenas uma pequena quantidade de chaves fosse acessada. É claro que poderíamos transformar durante o primeiro get e depois retornar apenas o valor. Mas então ainda há a questão de como armazenar os valores transformados. Se modificarmos as Keys no KeySet interno, teremos que "desmodificá-las" para cada elektraSet caso contrário kdbSet não funcionará corretamente. Portanto, teríamos que ter um tipo separado de KeySet de cache, mas precisamos de várias pesquisas durante elektraGet e desperdiçamos RAM. Então, no final, a solução atual é provavelmente a melhor que podemos fazer, sem modificar a estrutura Key .

Se você precisa de um ksLookup, você é muito lento de qualquer maneira.

Se ksLookup estiver muito lento, toda a Elektra está muito lenta. ksLookup é uma das operações mais otimizadas da Elektra.

Se você quer muito rápido, você precisa gerar um struct, preenchê-lo em elektraOpen, e em elektraGet é uma função inline estática que retorna o membro do struct. (Então você trivialmente também tem todas as garantias.)

Você já pode gerar um struct com a API de geração de código. Na verdade, isso já é amplamente usado no LCDproc. (É um pouco diferente, porque há elektraGet / elektraFillStruct especial que retorna toda a estrutura.) Poderíamos ocultar tal estrutura internamente, mas isso significaria que a API de alto nível não não funcionam mais sem geração de código.

Você conseguiu reproduzir o tamanho binário grande? Sempre tente primeiro encontrar as causas antes de otimizar.

Uma opção para desabilitar a geração de setters também pode ser útil para outras coisas, por exemplo, se você quiser proibir intencionalmente o aplicativo de configurar chaves. É claro que apenas desabilitar a geração não proíbe o aplicativo de definir valores (existem outras maneiras), mas reduz a probabilidade de erros.

Desabilitar a geração do material specload também pode fazer sentido, se usado corretamente. Veja abaixo um exemplo.

Respostas para @haraldg

Acabei de usar o CFLAGS padrão do lcdproc, não tenho certeza de qual otimização eles escolhem

AFAIK se a depuração não estiver habilitada, -O3 será usado. O que de acordo com este artigo pode realmente aumentar o uso de memória em favor da velocidade de execução. Para otimizar o tamanho do código, -Os deve ser usado.

Eu compilei manualmente o elektragen.c com otimização -O e o arquivo de objeto resultante é um pouco menor, mas nada dramático.

Certamente não sou especialista em compilador ou otimização, mas os arquivos de objeto AFAIK sempre conterão todo o código. Somente ao vincular as funções executáveis ​​não utilizadas podem ser removidas.

Talvez a especificação possa ser armazenada em um formato mais eficiente do que construí-la programaticamente.

Formatos de armazenamento mais eficientes resultarão em perda de velocidade de execução.

Na verdade, todo esse negócio de armazenar a especificação no binário e depois imprimi-la no stdout sob demanda parece bastante indireto para mim.

A ideia era que a especificação fosse embutida no executável que a utilizasse. Dessa forma, a especificação é sempre exatamente o que o executável espera. Ele não pode ser modificado sem também recompilar o executável (ou algum hacking intenso).

Você pode armazená-lo em uma biblioteca elf e vinculá-lo dinamicamente

Eu pensei sobre isso, mas há duas desvantagens:
1) todos os aplicativos precisam enviar um executável e uma biblioteca de objetos compartilhados
2) o aplicativo e a biblioteca podem ser modificados separadamente e ficar fora de sincronia

O Elektra também tem uma versão estática, caso dl_open não esteja disponível ou seja desencorajado por algum outro motivo. Usar um .so não seria compatível com esta versão.

qual binário mesmo no caso de aplicações modulares?

Atualmente a API de geração de código espera exatamente um executável por especificação, então a questão é trivial. No entanto, não há nada que o impeça de não usar a função gerada doSpecloadCheck (pode ser renomeada). Você tem que montar a especificação usando specload manualmente. Em teoria, você pode facilmente montar um aplicativo diferente ou até mesmo um simples script bash que imprime a especificação em stdout. Se você armazenar a especificação como um arquivo quickdump Elektra gzipado /somewhere/spec.eqd.gz você pode até montá-lo via

sudo kdb mount -R noresolver spec.eqd "spec/sw/lcdproc/lcdproc/#0/current" specload "app=/usr/bin/zcat" "app/args=#0" "app/args/#0=/somewhere/spec.eqd.gz"

ou até mesmo armazená-lo em algum outro formato adequado e eficiente e carregá-lo no construtor elf

Não tenho certeza de como todo o material do construtor ELF funciona. Isso está além do meu conhecimento de C.

Assumindo que realmente há uma razão para não apenas carregá-lo durante o elektraOpen(), o que provavelmente é o que eu teria feito.

Uma coisa que você precisa levar em conta é que a especificação deve ser acessível para kdb , assim como para a aplicação do usuário. Caso contrário kdb set não pode executar a validação.

Minhas Observações

Também fiz alguns testes e, embora não tenha usado um chip ARM, consegui reproduzir aumentos de tamanho semelhantes em x86-64. No meu caso, os aumentos não foram tão extremos, mas ainda ~ 1,8x para lcdexec.

Eu então simplesmente comentei todas as linhas keyNew (obviamente o executável não será executado corretamente, mas o tamanho pode ser comparado), e o aumento de tamanho caiu para apenas ~ 1,3x. Ambas as seções .text e .rodata são significativamente afetadas por essas linhas. Vou pensar em maneiras mais eficientes de armazenar a especificação.

Se usarmos -flto e -fuse-linker-plugin para habilitar a eliminação de código morto durante a vinculação, o aumento de tamanho será reduzido para apenas ~ 1,02x. (Nota: a versão antiga basicamente não é afetada por essas opções; diferença de tamanho < 0,5%). Portanto, provavelmente devemos habilitar essas opções por padrão (para compilações sem depuração).

Todos esses testes foram feitos com lcdexec . Por lcdvc vamos de ~1,18x para ~1,02x. Para lcdproc isso só reduz a diferença de ~ 1,6x para ~ 1,25x (aqui -flto -fuse-linker-plugin teve mais efeito na versão antiga, além de algumas coisas de ponteiro de prevenção inline).

TL;DR Acho que o aumento de tamanho pode ser atenuado por meio de opções do compilador e algumas alterações na forma como a especificação é armazenada. Infelizmente, parece que o tamanho do binário ainda aumentará, apesar de não usar um analisador de configuração vinculado estaticamente.

@haraldg escreveu:

por favor, levante a questão novamente.

Se for necessário que o binário seja capaz de iniciar sem instalação ainda não foi respondido. Eu comecei um problema para priorização de recursos em #2779.

No entanto, se a tendência de quase dobrar o tamanho instalado dos aplicativos continuar e o elektra decolar, precisarei de um novo laptop apenas por causa disso. Eu não gosto disso.

Eu também não gosto. Foi surpreendente para mim que isso acontecesse. @kodebach Estou muito interessado em seus resultados como a API de baixo nível se compara à API de alto nível.

Talvez isso vá na mesma direção que o "compile switch" sugerido acima?

Sim, vai nessa direção (principalmente a sugestão do @kodebach de não gerar setters). Se você não é contra essas "compilações de compilação", podemos discutir outras possibilidades para reduzir o tamanho do binário.

Na verdade, todo esse negócio de armazenar a especificação no binário (qual binário mesmo no caso de aplicativos modulares?) e depois imprimi-lo em stdout sob demanda parece bastante indireto para mim. Você pode armazená-lo em uma biblioteca elf e vinculá-lo dinamicamente (e dl_open() do kdb) ou até mesmo armazená-lo em algum outro formato adequado e eficiente e carregá-lo no construtor elf. (Supondo que realmente haja uma razão para não apenas carregá-lo durante elektraOpen(), que provavelmente é o que eu teria feito.)

Claro que também suportamos carregá-lo em elektraOpen(), mas só funciona se o aplicativo já estiver instalado (a especificação precisa ser instalada e montada). Fazê-lo parte do binário sempre funciona, o que é muito útil para o desenvolvimento (você pode executar o binário a partir do diretório de compilação). Em um futuro mais distante, algo como "strip" pode remover a especificação durante a instalação (como para binários instalados, pode não ser necessário). No entanto, escrever tal utilitário "strip", também estará fora do escopo do @kodebach.

@kodebach escreveu:

temos que "desmodificá-los" para cada elektraSet, caso contrário, o kdbSet não funcionará corretamente.

Claro, elektraSet seria mais caro. Ele sempre precisa atualizar o KeySet e o cache (struct).

Se o ksLookup estiver muito lento, todo o Elektra estará muito lento. ksLookup é uma das operações mais otimizadas da Elektra.

Se você considerar que as APIs geradas por código também fazem parte do Elektra, essas APIs não são muito lentas. Eles podem ser tão rápidos quanto o acesso a variáveis.

elektraFillStruct que retorna toda a estrutura

Ahh, ok, agora entendi melhor o que você queria fazer. Então, basicamente, nossas idéias são as mesmas: eu apenas esconderia as estruturas dos usuários para que eles não pudessem fazer alterações na estrutura (o que destruiria a sincronização com o KeySet).

Uma coisa que você precisa levar em consideração é que a especificação deve ser acessível ao kdb, assim como ao aplicativo do usuário. Caso contrário, o conjunto kdb não pode executar a validação.

E também perderíamos a introspecção, o acesso aos defaults, ... e tudo o mais que facilita a vida dos administradores. A validação é apenas uma pequena parte disso.

Claro que também suportamos carregá-lo em elektraOpen(), mas só funciona se o aplicativo já estiver instalado (a especificação precisa ser instalada e montada). Fazê-lo parte do binário sempre funciona, o que é muito útil para o desenvolvimento (você pode executar o binário a partir do diretório de compilação).

Parece haver aqui um grande equívoco. No momento, aplicativos que usam a API de geração de código absolutamente e sem exceção exigem a execução de uma chamada kdb mount antes de executar o aplicativo. Caso contrário, o aplicativo deve funcionar até certo ponto, mas não terá todas as funcionalidades. A razão para isso é simples. Não temos como "injetar" coisas em kdbGet de uma forma que seja passada para plugins.

Certamente não sou especialista em compilador ou otimização, mas os arquivos de objeto AFAIK sempre conterão todo o código. Somente ao vincular as funções executáveis ​​não utilizadas podem ser removidas.

IIRC ao usar pelo menos -O o compilador descarta símbolos estáticos não utilizados. O vinculador pode descartar símbolos externos dependendo dos sinalizadores.

Formatos de armazenamento mais eficientes resultarão em perda de velocidade de execução.

Claro que existem trocas. No entanto, isso não é verdade em geral.

Assumindo que realmente há uma razão para não apenas carregá-lo durante o elektraOpen(), o que provavelmente é o que eu teria feito.

Eu tenho sido desleixado. O que eu quis dizer foi algo como: Leia durante loadConfiguration() de um arquivo externo.

Uma coisa que você precisa levar em consideração é que a especificação deve ser acessível ao kdb, bem como ao aplicativo do usuário.

Sim, que é uma das razões pelas quais essa coisa de especificação está no binário é indireta. Ter a especificação em um arquivo separado tornaria as coisas muito mais simples. E provavelmente também suporta bem aplicativos modulares.

o aplicativo e o [spec] podem ser modificados separadamente e ficar fora de sincronia

Deve ser fácil evitar acidentes gerando nomes de arquivos exclusivos. Você pode até usar somas de verificação para se proteger levemente contra temperagem, mas eu não recomendo.

Se usarmos o plug-in -flto e -fuse-linker-plugin para habilitar a eliminação de código morto durante a vinculação, o aumento de tamanho será reduzido para apenas ~ 1,02x. (Nota: a versão antiga basicamente não é afetada por essas opções; diferença de tamanho < 0,5%). Portanto, provavelmente devemos habilitar essas opções por padrão (para compilações sem depuração).

Isso é interessante - eu deveria verificar o tamanho do analisador de configuração antigo, para ter uma ideia melhor de onde estamos. No entanto, lembre-se de que esses são recursos de otimização específicos do gcc e também (não é uma preocupação para o lcdproc) usar essas otimizações pode não ser viável para vincular aplicativos grandes. (Eu não sei nada sobre este tópico, mas fazer otimizações globalmente em vez de por arquivo parece algo que pode escalar mal.)

Se for necessário que o binário seja capaz de iniciar sem instalação ainda não foi respondido.

Em última análise, isso será bom ter. Mas sugiro que primeiro nos concentremos em como as coisas deveriam ser nos sistemas de produção e na ABI geral para acesso às especificações. Os recursos de desenvolvimento e as especificidades do lcdproc devem vir mais tarde.

Se você não é contra essas "compilações de compilação", podemos discutir outras possibilidades para reduzir o tamanho do binário.

Sim, idealmente selecionaríamos com configure se esta for uma compilação para depuração, produção, alvos incorporados, etc. configure configuraria as coisas para o gerador de código fazer a coisa certa. Eu não tenho uma opinião forte sobre os detalhes. Acho que concordamos com a ideia geral?

Claro que também suportamos carregá-lo em elektraOpen(), mas só funciona se o aplicativo já estiver instalado (a especificação precisa ser instalada e montada). Fazê-lo parte do binário sempre funciona, o que é muito útil para o desenvolvimento (você pode executar o binário a partir do diretório de compilação).

Você também pode incorporar um nome de arquivo de fallback em vez de toda a especificação dentro do binário.

Em um futuro mais distante, algo como "strip" pode remover a especificação durante a instalação (como para binários instalados, pode não ser necessário).

Isso parece impraticável. Muito mais fácil fazer a coisa certa durante a geração de código.

Para explorar um pouco as possíveis abordagens e no interesse de obter uma imagem clara sobre as compensações: entendi corretamente, que, se não tivermos a especificação disponível, o aplicativo ainda será (ou pelo menos poderá) ser executado, mas irá travar (via callback de erro fatal) quando acessarmos um valor que está faltando na configuração? Esse comportamento provavelmente é bom para sistemas embarcados autônomos. kdb seria usado para mesclar a configuração padrão e a configuração personalizada durante a construção do firmware e validar tudo.

Outros sistemas provavelmente não se importam muito com validação, mas ainda gostariam de ter padrões para itens de configuração ausentes. Isso pode ser facilmente suportado por uma variante da API elektraGet: Algo como elektraGetLongDefault("key", default_value) , em muitos casos, levaria apenas uma instrução extra para passar default_value - ou seja, apenas o espaço mínimo necessário para armazenar o valor de qualquer maneira . Não sei se isso retornaria o valor padrão ou travaria em erros de conversa, mas não vamos nos distrair com detalhes ainda. No momento da geração do código, selecionaríamos entre este e o modo full-spec. Isso é provavelmente o que eu usaria para pacotes OpenWRT LCDproc.

Para sistemas não incorporados, queremos claramente a especificação completa. Se analisar a especificação de um formato eficiente de espaço for considerado muito caro, você poderá armazenar em cache uma versão validada e revalidar apenas se os carimbos de data/hora forem alterados. Mas isso não é relevante no momento, a não ser apontar que não _temos_ que assumir compromissos ruins entre tamanho e velocidade.

Eu entendi corretamente, que, se não tivermos a especificação disponível, o aplicativo ainda será (ou pelo menos poderá) funcionar, mas falhará (via retorno de chamada de erro fatal) quando acessarmos um valor que está faltando na configuração ?

Existem duas partes para isso. O KeySet com os valores padrão passados ​​para elektraOpen e a especificação completa que deve ser montada de alguma forma. Os padrões passados ​​para elektraOpen evitam que chamadas para elektraGet falhem se uma chave estiver faltando. No entanto, eles não evitam falhas devido a problemas de conversão de tipo. Para evitar essas falhas, a especificação deve ser montada. Com uma especificação montada elektraGet as chamadas nunca devem falhar. Se houver problemas elektraOpen falhará.

Algo como elektraGetLongDefault("key", default_value) em muitos casos levaria apenas uma instrução extra para passar default_value - ou seja, apenas o espaço mínimo necessário para armazenar o valor de qualquer maneira.

Como dito acima, você pode passar valores padrão para elektraOpen . Ao usar a geração de código, este KeySet é gerado e seus padrões sempre corresponderão à sua especificação. Pode ter havido outras razões pelas quais essa API não é usada, mas foi decidida antes do meu tempo.


Se eu entendi todas as preocupações corretamente, tudo deve ser mais ou menos coberto por esses três modos:

  1. Especificações e padrões incorporados. Semelhante ao que temos agora. A incorporação da especificação garante que ela não fique fora de sincronia com o aplicativo e permite aplicativos de arquivo único.
  2. Especificação em arquivo externo, sem padrões. Nenhum KeySets em binário garante o tamanho binário mínimo. O aplicativo só será executado se a especificação estiver montada. loadConfiguration verifica se a especificação está montada. O arquivo externo também pode armazenar o KeySet com mais eficiência. kdb gen produzirá um arquivo quickdump. Como isso é montado depende do usuário (diretamente, via cat com specload, gzipado via gzip com specload, etc.).
  3. Sem especificações, sem padrões. Como 2, mas loadConfiguration não verifica as especificações. Portanto, o aplicativo será executado, desde que a configuração esteja correta. Destinado a casos de uso em que a configuração foi pré-validada e não será alterada.

@haraldg escreveu:

Sim, que é uma das razões pelas quais essa coisa de especificação está no binário é indireta. Ter a especificação em um arquivo separado tornaria as coisas muito mais simples. E provavelmente também suporta bem aplicativos modulares.

A Elektra foi projetada para especificações externas e esta também é a forma normal na Elektra. Nas discussões sobre -c um resultado para mim foi que os desenvolvedores querem executar o LCDproc sem tê-lo instalado, o que não é possível se as especificações forem externas. Assim , @kodebach inventou uma maneira pela qual a especificação pode ser interna e ainda pode ser usada externamente (specload). Claro que não precisamos usar isso por padrão ou de todo, é apenas mais uma possibilidade que a Elektra oferece. (Obrigado @kodebach)

Deve ser fácil evitar acidentes gerando nomes de arquivos exclusivos. Você pode até usar somas de verificação para se proteger levemente contra temperagem, mas eu não recomendo.

Também suportamos assinaturas de arquivos de configuração. Mas a especificação integrada é a maneira mais fácil e segura de nunca ter uma especificação fora de sincronia. Claro que aumenta o tamanho do binário (mais do que pensávamos).

Eu deveria verificar quanto tamanho o analisador de configuração antigo leva

Eu também estou interessado. Infelizmente, o analisador YAML é bastante grande e precisa de uma biblioteca externa (yamlcpp). Esta também é uma decisão adicional para você tomar (qual formato de arquivo de configuração você deseja por padrão).

Em última análise, isso será bom ter. Mas sugiro que primeiro nos concentremos em como as coisas deveriam ser nos sistemas de produção e na ABI geral para acesso às especificações. Os recursos de desenvolvimento e as especificidades do lcdproc devem vir mais tarde.

Sim, você tem uma noção melhor do que as pessoas do LCDproc querem.

Sim, o ideal seria selecionar com configure se esta é uma compilação para depuração, produção, alvos incorporados, etc. configure configuraria as coisas para o gerador de código fazer a coisa certa. Eu não tenho uma opinião forte sobre os detalhes. Acho que concordamos com a ideia geral?

Ok, a opção deve ser chamada --works-as-standalone ou --minimize-binary-size? Ou perguntado de forma diferente: você tem planos de reutilizar a opção também para algumas outras otimizações?

Você também pode incorporar um nome de arquivo de fallback em vez de toda a especificação dentro do binário.

Isso não ajuda se não estiver instalado. E se a especificação estiver instalada, funcionará de qualquer maneira.

Eu entendi corretamente, que, se não tivermos a especificação disponível, o aplicativo ainda será (ou pelo menos poderá) funcionar, mas falhará (via retorno de chamada de erro fatal) quando acessarmos um valor que está faltando na configuração ?

Exatamente. Não teria padrões, validação, descrição da configuração, ....

Isso pode ser facilmente suportado por uma variante da API elektraGet: Algo como elektraGetLongDefault("key", default_value)

Devemos considerar aqui:

  1. a API já tem muitas variantes, isso duplicaria novamente 1/3 da API
  2. essa API seria desencorajada para ser usada em casos de não geração de código, pois os padrões documentados podem ser diferentes dos padrões usados ​​ou ainda pior: a mesma opção de configuração pode ter padrões diferentes em caminhos de código diferentes.

@kodebach escreveu:

Pode ter havido outras razões pelas quais essa API não é usada, mas foi decidida antes do meu tempo.

Veja acima.

Se eu entendi todas as preocupações corretamente, tudo deve ser mais ou menos coberto por esses três modos:

@haraldg você pode priorizar esses três modos?

Como isso é montado depende do usuário

Suponho que para o LCDproc devemos preparar alguns scripts de instalação?

Como 2, mas loadConfiguration não verifica a especificação.

Que tal alguma opção de tempo de execução para informar ao aplicativo que ele não deve falhar por erros na especificação (como nenhuma especificação)? Então teríamos apenas dois modos de compilação (a especificação está incluída ou não).

@kodebach Você já tentou compactar a especificação?

Nas discussões sobre -c um resultado para mim foi que os desenvolvedores querem executar o LCDproc sem tê-lo instalado, o que não é possível se as especificações forem externas. Assim , @kodebach inventou uma maneira pela qual a especificação pode ser interna e ainda pode ser usada externamente (specload).

O objetivo do specload é vincular versões executáveis ​​às especificações. A montagem ainda é necessária. Isso não afeta de forma alguma se um aplicativo pode ser executado imediatamente após a compilação ou não.

O que você pode fazer no entanto é compilar o aplicativo e montar este executável local via specload (°). Em seguida, você pode executar o executável local. Agora você pode simplesmente recompilar e executar novamente (sem outro procedimento de montagem) e ainda terá a especificação correta (mesmo se você a alterou antes de compilar).

(°) Se você tem uma versão instalada do seu executável, você precisa desmontar sua especificação e depois montar com specload. Alternativamente, você pode alterar manualmente a configuração do plugin specload para seu ponto de montagem para usar um caminho executável diferente. Infelizmente não existe uma ferramenta kdb para alterar a configuração de um plugin montado.

Exatamente. Não teria padrões, validação, descrição da configuração, ....

Por favor, não poste informações contraditórias. Como afirmei acima, os padrões podem ser fornecidos separadamente da especificação.

@haraldg você pode priorizar esses três modos?

Cada um dos 3 modos serve a um propósito totalmente diferente e a implementação desses modos não é realmente complicada, então a priorização não é realmente necessária. Em vez disso, seria bom saber se esses modos cobrem tudo ou se podemos precisar de modos adicionais.

@kodebach Você já tentou compactar a especificação?

Primeiro de tudo: agora usamos KeySets separados para padrões e especificações. Com certeza farei as alterações necessárias, para que possamos usar o mesmo para ambos. Isso já deve reduzir um pouco o tamanho do binário. Além disso, existem algumas possibilidades.

Se quisermos construir a especificação incorporada usando a API C (ksNew, keyNew), parece que não há muito o que fazer facilmente. Até onde alguns experimentos curtos mostraram, usar uma API diferente (sem varargs, muitas chamadas de funções; API keyNew diferente etc.) não ajudará muito. (Desconsiderando o quão complicada a implementação seria) keyCopyMeta provavelmente também não ajudaria, já que as strings idênticas devem ser desduplicadas pelo compilador de qualquer maneira. (pelo menos para o tamanho binário, é claro que reduziria o uso de RAM).

Outra opção seria incorporar uma única string (ou array de bytes) que codifica todo o KeySet e usar um plugin para usá-lo. Como temos que enviar o KeySet no formato quickdump para specload, o formato quickdump se ofereceria para incorporação. No entanto, agora o quickdump parece ser um pouco maior que o código C (no caso de lcdexec pelo menos). Veja #2788 para mais.

Para especificações não incorporadas, você pode simplesmente pegar um arquivo quickdump comprimi-lo com gzip e montar o arquivo resultante /path/x.eqd.gz com:

sudo kdb mount -R noresolver spec.eqd "spec/mountpoint" specload "app=/usr/bin/zcat" "app/args=#0" "app/args/#0=/path/x.eqd.gz" 

Você também pode usar qualquer outro método de compactar os dados do quickdump, desde que haja um executável que possa imprimir o arquivo descompactado em stdout.

Por favor, não poste informações contraditórias.

Não encontrei nenhuma documentação sobre isso, você pode me indicar? Eu só conheço um argumento em elektraOpen que aceita uma especificação e assumi que a API gerada por código passará a especificação usando este parâmetro e que o aplicativo chamado com --elektra-spec retornará exatamente isso.

Como afirmei acima, os padrões podem ser fornecidos separadamente da especificação.

Por que você faria isso?

Com certeza farei as alterações necessárias, para que possamos usar o mesmo para ambos.

Obrigada.

Antes de mais nada, devo salientar que não estou familiarizado o suficiente com o jargão da Elektra para entender completamente todas as suas observações. Pode haver mal-entendidos no que escrevo. Vou tentar estar ciente dos limites do meu entendimento, mas posso perder alguma coisa.

Quando você diz "montar uma especificação": Estou correto que isso basicamente significa ter uma entrada em um arquivo como /etc/elektra.conf apontar para o arquivo onde a especificação está armazenada, exceto no caso em que a especificação está incorporada no aplicativo e você faz algumas coisas em loadConfiguration(), que têm o mesmo efeito sem realmente tocar em arquivos persistentes?

Se eu entendi todas as preocupações corretamente, tudo deve ser mais ou menos coberto por esses três modos: [1, 2, 3]

Basicamente sim. Ainda acho que vale a pena explorar o modo 2.5 (API padrão barato), mas veja abaixo. Também por uma questão de completude, pode haver

  1. Use alguma configuração de exemplo (não necessariamente o padrão) e use os valores diretamente em elektragen.c sem vincular a libelektra. Este modo definitivamente não é útil para o lcdproc (com todos os plugins, etc.), mas pode, em teoria, ser útil para outros aplicativos em um contexto embutido profundo. É definitivamente uma prioridade muito baixa, mas como você já tem um gerador de código sofisticado, talvez queira exibi-lo um pouco. Também forneceria um benchmark muito bom para comparar. Talvez esses dados não sejam interessantes para a tese de Kodebach, mas posso ver isso como uma coisa muito legal para escrever alguns artigos. (E se este modo estiver disponível, eu definitivamente o usarei para comparar um pouco o lcdproc.)

  2. Forneça uma especificação (de que maneira sempre), mas retire todas as strings destinadas apenas a humanos, mas não programaticamente relevantes. (Talvez isso não seja uma tarefa para o gerador de código, mas alguma outra ferramenta. Não sei - apenas explorando o espaço de ideias.)

No entanto, também devo salientar que não entendo a diferença entre (1) e (2): Isso se comportaria da mesma maneira, apenas com a especificação armazenada de maneira diferente?

A Elektra foi projetada para especificações externas e esta também é a forma normal na Elektra. Nas discussões sobre -c um resultado para mim foi que os desenvolvedores querem executar o LCDproc sem tê-lo instalado, o que não é possível se as especificações forem externas.

Eu não vejo isso. Você pode em tempo de compilação (tempo de geração de código) armazenar a especificação atual em um arquivo com um nome exclusivo e incorporar o nome (caminho absoluto, é claro) no executável. Se a especificação não estiver montada, o executável retornará ao local armazenado. Não?

Assim , @kodebach inventou uma maneira pela qual a especificação pode ser interna e ainda pode ser usada externamente (specload). Claro que não precisamos usar isso por padrão ou de todo, é apenas mais uma possibilidade que a Elektra oferece. (Obrigado @kodebach)

Realmente deveríamos ter tido mais discussões antes, se esse fosse o resultado. Mas se isso for apenas um recurso de desenvolvimento, não me importo muito com o tamanho, na verdade. Fiquei com a impressão de que as construções de produção teriam um tamanho semelhante.

Se este é apenas um recurso de desenvolvimento, então não entendo por que você se incomoda em despejar a especificação para usuários externos?

Esta também é uma decisão adicional para você tomar (qual formato de arquivo de configuração você deseja por padrão).

Não tenho uma opinião forte. A menos que haja algo de errado com o ini, eu ficaria com ele.

Ok, a opção deve ser chamada --works-as-standalone ou --minimize-binary-size? Ou perguntado de forma diferente: você tem planos de reutilizar a opção também para algumas outras otimizações?

Eu não acredito que eu tenho a imagem completa ainda, então não posso realmente dizer. Também acredito que alguém possa adicionar mais modos ao gerador de código no futuro. Então, talvez apenas --mode={full-spec,no-spec,...} ou talvez --spec={include,check,ignore} ou algo assim?

a API já tem muitas variantes, isso duplicaria novamente 1/3 da API

Eu assumi que isso seria barato de implementar em termos de funções já existentes para suportar as APIs existentes. Se isso não for verdade, então a ideia é muito menos atraente.

esta API seria desencorajada para ser usada em casos sem geração de código

Exatamente. Talvez, exceto no caso em que as pessoas usam a API, mas decidem não usar uma especificação.

ou ainda pior: a mesma opção de configuração pode ter diferentes padrões em diferentes caminhos de código.

Alguém fazendo um trabalho tão desleixado pode muito bem acessar a chave errada em primeiro lugar. ;)

Suponho que para o LCDproc devemos preparar alguns scripts de instalação?

make install provavelmente deve resultar em um sistema que continua funcionando mesmo que os diretórios onde o lcdproc foi compilado sejam completamente deletados.

Que tal alguma opção de tempo de execução para informar ao aplicativo que ele não deve falhar por erros na especificação (como nenhuma especificação)? Então teríamos apenas dois modos de compilação (a especificação está incluída ou não).

Acho que não entendi muito bem onde você quer chegar com isso. Não vejo um caso de uso para ignorar erros que realmente detectamos com sucesso?

(°) Se você tem uma versão instalada do seu executável, você precisa desmontar sua especificação e depois montar com specload. Alternativamente, você pode alterar manualmente a configuração do plugin specload para seu ponto de montagem para usar um caminho executável diferente.

Passei horas lendo coisas e discutindo com vocês dois e ainda não entendo o que isso significa. Até Markus parece ficar confuso às vezes. Mantenho o que disse anteriormente: esse conceito me parece muito indireto. Como você pode esperar documentar isso de uma maneira que o usuário médio entenda?

Quando você diz "montar uma especificação":

A montagem no Elektra é sempre o mesmo conceito porque as especificações também são armazenadas como configurações. A montagem é o mecanismo para descrever como a Elektra pode encontrar a configuração abaixo de algum nome de chave.

Estou correto que isso basicamente significa ter uma entrada em um arquivo como o ponto /etc/elektra.conf

Sim, o arquivo que contém os pontos de montagem é chamado /etc/kdb/elektra.ecf por padrão. kdb file system/elektra fornece o nome real do arquivo.

para o arquivo onde a especificação está armazenada, exceto no caso em que a especificação está incorporada no aplicativo e você faz algumas coisas em loadConfiguration(), que têm o mesmo efeito sem realmente tocar em arquivos persistentes?

Normalmente a montagem refere-se ao arquivo no sistema de arquivos, mas não precisa ser assim. Você também pode montar "arquivos" do git, rede e quais executáveis ​​saem.

É definitivamente uma prioridade muito baixa, mas como você já tem um gerador de código sofisticado, talvez queira mostrá-lo um pouco

Eu preferiria ter apenas a funcionalidade necessária, mas a funcionalidade necessária funciona muito bem. A Elektra já tem recursos mais do que suficientes para "exibir", que tendem a assustar as pessoas porque nem tudo funciona como esperado.

Em particular, a Elektra já possui recursos para tornar os valores de configuração somente leitura. Ter várias maneiras de fazer a mesma coisa precisa de uma justificativa especialmente boa. Melhor desempenho pode ser uma justificativa, mas no momento ainda não sabemos nada sobre o tempo de execução no LCDproc.

Forneça uma especificação (de que maneira sempre), mas retire todas as strings destinadas apenas a humanos, mas não programaticamente relevantes.

Você também deve considerar que explodir um sistema de compilação com muitas opções introduz complexidade. Mas sim, uma opção para incluir apenas o necessário para as garantias (tipo + padrões) faz sentido.

Você pode em tempo de compilação (tempo de geração de código) armazenar a especificação atual em um arquivo com um nome exclusivo e incorporar o nome (caminho absoluto, é claro) no executável. Se a especificação não estiver montada, o executável retornará ao local armazenado. Não?

Claro que você pode fazer isso, mas enquanto o LCDproc não estiver instalado, o arquivo de especificação não estará neste caminho absoluto e o LCDproc falhará ao encontrar o arquivo. Além disso, ter nomes de arquivos absolutos dentro do executável tem outras desvantagens (o binário não seria mais realocado).

Se este é apenas um recurso de desenvolvimento, então não entendo por que você se incomoda em despejar a especificação para usuários externos?

Ter as especificações das aplicações disponíveis dentro da Elektra é um dos principais objetivos da Elektra: Só assim você pode consultar quais valores a aplicação irá obter, fornecer boas ferramentas e assim por diante.

Não tenho uma opinião forte. A menos que haja algo de errado com o ini, eu continuaria com ele.

O analisador INI chamado "ini" é aquele com mais recursos, mas também com mais bugs.

Exatamente. Talvez, exceto no caso em que as pessoas usam a API, mas decidem não usar uma especificação.

Em algumas discussões iniciais, também dissemos que a API deveria empurrar suavemente as pessoas para formas mais modernas e robustas de como acessar a configuração. Ter uma especificação é um must-have por muitas razões. Apenas em sistemas muito restritos, alguém deve considerar não ter nenhuma especificação: nos casos em que você não deseja nenhuma configuração de tempo de execução.

Acho que não entendi muito bem onde você quer chegar com isso. Não vejo um caso de uso para ignorar erros que realmente detectamos com sucesso?

Este seria um recurso de desenvolvedor para permitir que os desenvolvedores iniciassem o LCDproc sem especificação. A ideia aqui era reduzir o número de variantes de compilação para duas: especificações compiladas e especificações externas.

Como você pode esperar documentar isso de uma maneira que o usuário médio entenda?

A API de alto nível já tem uma boa documentação: https://www.libelektra.org/tutorials/high-level-api

Mas como você notou, os recursos recentes de geração de código atualmente não possuem tal documentação e às vezes também funcionam de forma diferente do que eu esperava. Assim, peço um número muito pequeno de casos de uso e implemente e documente exatamente esses 2 ou 3 casos.

É um resumo correto da sua descrição de montagem que a configuração
fontes (arquivos, qualquer outra coisa) podem ser montadas globalmente em elektra.ecf
ou process-local de dentro loadConfiguration()?

Claro que você pode fazer isso, mas enquanto o LCDproc não estiver instalado, o arquivo de especificação não estará neste caminho absoluto e o LCDproc falhará ao encontrar o arquivo.

Na verdade, sugeri armazenar o caminho do local não instalado. Ou seja, o
caminho no diretório de compilação.

Além disso, ter nomes de arquivos absolutos dentro do executável tem outras desvantagens (o binário não seria mais realocado).

Sim, isso seria apenas um recurso de desenvolvimento, para poder executar não instalado
versões do aplicativo. As compilações de produção provavelmente não deveriam incluir
este caminho ou pelo menos fique feliz em ignorá-lo.

Se este é apenas um recurso de desenvolvimento, então não entendo por que você se incomoda em despejar a especificação para usuários externos?

Ter as especificações das aplicações disponíveis dentro da Elektra é um dos principais objetivos da Elektra: Só assim você pode consultar quais valores a aplicação irá obter, fornecer boas ferramentas e assim por diante.

Não consigo ver como referenciar o executável não instalado correto em qualquer
ferramentas que você está pensando é mais fácil do que referenciar o correto
especificação não instalada.

Não tenho uma opinião forte. A menos que haja algo de errado com o ini, eu ficaria com ele.

O analisador INI chamado "ini" é aquele com mais recursos, mas também com mais bugs.

Não tenho ideia de quais analisadores INI estão disponíveis. Minha única declaração foi sobre
ficar com INI parece razoável. Mas eu suspeito que a maioria das pessoas não se importaria
muito sobre qual formato legível por humanos é usado.

Acho que não entendi muito bem onde você quer chegar com isso. Não vejo um caso de uso para ignorar erros que realmente detectamos com sucesso?

Este seria um recurso de desenvolvedor para permitir que os desenvolvedores iniciassem o LCDproc sem especificação. A ideia aqui era reduzir o número de variantes de compilação para duas: especificações compiladas e especificações externas.

Então, basicamente, isso significa que sempre temos que executar aplicativos eletrificados
em openwrt com algum switch de linha de comando, que ninguém usa em não-embedded
sistemas? As pessoas vão esquecer isso em seus scripts com mais frequência do que
cometer erros em sua configuração. Parece uma troca ruim.

É um resumo correto da sua descrição de montagem que a configuração
fontes (arquivos, qualquer outra coisa) podem ser montadas globalmente em elektra.ecf
ou process-local de dentro loadConfiguration()?

Existe alguma funcionalidade de montagem nas APIs (kdbEnsure), mas não deve ser usada para montar arquivos de configuração, mas apenas processar configurações locais, como argumentos de linha de comando. A razão para não usá-lo é que qualquer montagem não global destruiria a visão correta das ferramentas.

Na verdade, sugeri armazenar o caminho do local não instalado. Ou seja, o
caminho no diretório de compilação.

Isso só seria interessante para desenvolvedores onde podemos adicionar toda a especificação de qualquer maneira.

Não consigo ver como referenciar o executável não instalado correto em qualquer ferramenta em que você esteja pensando é mais fácil do que referenciar a especificação não instalada correta.

Estou falando das ferramentas da Elektra (kdb, qt-gui, web-ui, ...). Claro que os desenvolvedores também podem montar os arquivos de seus diretórios de construção. Infelizmente, você precisa ser root para montar porque a montagem grava em /etc/kdb/elektra.ecf. Veja #1074 para uma proposta para mudar isso.

Então, basicamente, isso significa que sempre temos que executar aplicativos eletrificados
em openwrt com algum switch de linha de comando, que ninguém usa em não-embedded
sistemas? As pessoas vão esquecer isso em seus scripts com mais frequência do que
cometer erros em sua configuração. Parece uma troca ruim.

Não, ao contrário: os desenvolvedores precisariam adicionar o switch quando receberem o erro de que a especificação está ausente.

Mas vamos primeiro nos concentrar nos casos de uso que precisamos oferecer suporte e, em seguida, decidir como implementá-los.

Ok, há muito o que descompactar aqui.

Quando você diz "montar uma especificação"

A montagem no Elektra é muito semelhante à montagem de um sistema de arquivos no UNIX. O Elektra possui uma configuração de pontos de montagem em todo o sistema. Esses pontos de montagem são apenas chaves que definiram que tudo abaixo dessas chaves (e não abaixo de outro ponto de montagem) é carregado usando o plug-in de armazenamento para este ponto de montagem.

Então, por exemplo, você pode montar a chave user/test/my/key com o plugin ini . Isso significa apenas que se alguém acessar as chaves abaixo user/test/my/key (por exemplo user/test/my/key/hello ), invocamos o plugin ini , que então carrega algum arquivo (qual arquivo é carregado é um pouco mais complicado, mas não é realmente relevante aqui).

UNIX tem sistemas de arquivos sintéticos como /proc ou /sys . Da mesma forma, o Elektra não exige que um plugin seja carregado de um arquivo. Existem plugins para carregar do git ou via curl ou invocando algum processo (como o specload).

Portanto, se você "montar uma especificação", modificará a configuração de pontos de montagem em todo o sistema para informar à Elektra como recuperar sua especificação. Isso não pode ser feito dentro loadConfiguration() , porque então ferramentas como kdb não funcionariam, porque apenas sua aplicação saberia sobre a especificação.
(Seria possível montar automaticamente na primeira inicialização, mas como a montagem requer acesso root, eu não recomendaria.)

Use alguma configuração de exemplo (não necessariamente o padrão) e use os valores diretamente em elektragen.c sem vincular a libelektra.

Também pensei nisso como uma possibilidade futura. Pode ser útil para os sistemas embarcados autônomos que você mencionou anteriormente. Basicamente, pode-se brincar com a especificação e configurações diferentes. Uma vez que a configuração final é encontrada, um dummy elektragen.c que sempre retorna os mesmos valores (e, portanto, deve ser totalmente embutido pelo compilador), pode ser gerado e usado para criar uma imagem de firmware.

Forneça uma especificação (de que maneira sempre), mas retire todas as strings destinadas apenas a humanos, mas não programaticamente relevantes.

Sim, eu diria que isso deve ser feito por meio de uma ferramenta diferente. A ferramenta pré-processaria a especificação antes de ser passada para o gerador.

Não entendo a diferença entre (1) e (2): Isso se comportaria da mesma maneira, apenas com a especificação armazenada de maneira diferente?

Sim, o binário seria menor. A especificação externa também pode ser compactada de outras maneiras, então é facilmente possível dentro do binário. Além disso, você pode armazenar binários e especificações em lugares diferentes. Por exemplo, o binário pode ser compilado em uma imagem de firmware, enquanto a especificação é armazenada em algum outro chip flash maior não destinado a código executável. Não faço ideia, se tais dispositivos existem. Basicamente (2) é um pouco mais flexível. Enquanto (1) garante que binário e especificação são sempre compatíveis.

Eu assumi que isso seria barato de implementar em termos de funções já existentes para suportar as APIs existentes. Se isso não for verdade, então a ideia é muito menos atraente.

Claro que é apenas um pouco de copiar/colar. Mas assim que houver diferentes opções para fazer as coisas, você deve ser muito explícito sobre as compensações entre as diferentes opções, caso contrário as pessoas ficarão confusas e poderão ser dissuadidas de usar o Elektra. Um objetivo da API de alto nível era facilitar o uso do Elektra para iniciantes, já que a API de baixo nível é bastante complexa e pouco intuitiva às vezes.

Exatamente. Talvez, exceto no caso em que as pessoas usam a API, mas decidem não usar uma especificação.

Como eu disse acima, já é possível usar a API de alto nível sem uma especificação completa. O parâmetro defaults de elektraOpen conteria os type se default s esperados para todos os nomes de chave conhecidos. Esta é uma espécie de especificação leve. A idéia é que ninguém possa simplesmente inserir uma linha elektraGetLong ("/some/key") sem que ninguém saiba que esta opção de configuração existe. (Encontrei alguns drivers no LCDproc onde nem todas as chaves de configuração usadas no código foram documentadas).

Não consigo ver como referenciar o executável não instalado correto em qualquer
ferramentas que você está pensando é mais fácil do que referenciar o correto
especificação não instalada.

Absolutamente não é. Novamente (agora) o único benefício real de usar specload é que o binário e a especificação são sempre compatíveis (porque são o mesmo arquivo). Além disso, a ideia principal por trás do specload (embora isso ainda não esteja implementado, pois precisa de mudanças mais amplas), era permitir que o usuário modificasse a especificação de maneira segura, evitando alterações inseguras. Por exemplo, é sempre seguro alterar os metadados description , porque não se destina ao processamento de máquina. Da mesma forma, seria seguro restringir os valores permitidos para a porta que LCDd usa.


@haraldg você provavelmente pode ignorar as coisas abaixo, ele conterá muita terminologia Elektra e basicamente fala sobre detalhes de implementação

Por favor, não poste informações contraditórias.

Não encontrei nenhuma documentação sobre isso, você pode me indicar? Eu só conheço um argumento em elektraOpen que aceita uma especificação e assumi que a API gerada por código passará a especificação usando este parâmetro e que o aplicativo chamado com --elektra-spec retornará exatamente isso.

Como afirmei acima, os padrões podem ser fornecidos separadamente da especificação.

Por que você faria isso?

Não há documentação real, mas olhar para um arquivo criado pelo gerador de código pode ajudar.

Como você disse, há o defaults KeySet passado para elektraOpen e há o KeySet impresso para stdout no modo specload ( --elektra-spec ). Esses dois estão em locais diferentes no código e, no momento, eles são criados por instruções ksNew separadas. Isso ocorre porque elektraOpen precisa das chaves sem a chave pai (por exemplo /lcdexec/address ) enquanto o modo specload precisa das chaves de namespace spec completas (por exemplo spec/sw/lcdproc/lcdexec/#0/current/lcdexec/address ). Estou trabalhando em uma correção para isso, para que possamos usar o mesmo KeySet para ambos.

Ainda é verdade, que o defaults KeySet passado para elektraOpen e a especificação (montada via specload ou não) são tecnicamente independentes e podem até ser contraditórias (°). O gerador de código (e até certo ponto specload) existe para evitar isso. Você está correto, o código gerado passa mais ou menos uma cópia da especificação para elektraOpen . E como eu disse no futuro, será exatamente o mesmo KeySet usado pelo modo specload.

No entanto, o KeySet defaults não precisa conter a especificação completa. Em primeiro lugar, se você tem certeza de que existe uma especificação montada, não precisa passar defaults . Se você passar defaults , os únicos requisitos (ainda não verificados em elektraOpen ) são que cada chave tenha um type e um default . Uma cópia de defaults (com a chave pai anexada a cada chave) é usada como o KeySet que é passado para kdbGet . Funciona porque as Chaves que criamos em elektraOpen são as mesmas que o plugin spec criaria e, portanto, são usadas como fallback em pesquisas em cascata.

E finalmente, apenas para esclarecer isso de uma vez por todas, specload não ajuda de forma alguma com a execução de executáveis ​​não instalados. Você ainda precisa montar a especificação para que specload funcione. Você está apenas montando de uma maneira diferente. O defaults KeySet até certo ponto permite executar binários sem montagem, mas não é um substituto completo para a especificação montada. A razão é simples, as chaves em defaults são processadas apenas por plugins globais (possivelmente excluindo postgetstorage plugins, eu preciso olhar para isso). Isso ocorre porque as chaves não pertencem a nenhum ponto de montagem. Para mitigar isso, uma de duas coisas teria que acontecer: 1) precisamos levar em conta as chaves em cascata em splitAppoint ou 2) kdbEnsure tem que suportar a criação de pontos de montagem. A opção 1) ainda requer que exista um ponto de montagem onde as chaves em cascata possam ser colocadas e que tenha os plug-ins necessários, portanto, não é realmente uma solução. Com a Opção 2) poderíamos simplesmente executar kdbEnsure para criar um ponto de montagem de especificação em elektraOpen e removê-lo novamente em elektraClose .

PS. Eu não tenho ideia de como o novo cache se encaixa nisso. Pode funcionar ou pode quebrar tudo. (Eu suspeito do último.) Criar e remover pontos de montagem em kdbEnsure provavelmente causaria problemas com o caso, porque kdbEnsure apenas modifica o identificador KDB , não a configuração real no disco . Eu acho que só causaria cachemiss na primeira vez. No entanto, não sei se criaríamos um cache desse pseudo-mountpoint e o que isso significaria para a próxima vez que o aplicativo fosse executado.

(°) AFAIK o plugin spec sobrescreve o que é passado como defaults para não haver nenhum tipo de conflito. Mas alterar defaults sem alterar a especificação, provavelmente não terá efeito.

Obrigado pela resposta detalhada!

Não faço ideia, se tais dispositivos existem. Basicamente (2) é um pouco mais flexível. Enquanto (1) garante que binário e especificação são sempre compatíveis.

Melhor apoiar apenas (1) então?

Não há documentação real, mas olhar para um arquivo criado pelo gerador de código pode ajudar.

Isso é definitivamente uma coisa importante a ser mudada. Você considerou que a API gerada por código parece idêntica à API não gerada por código? Isso desduplicaria muito da documentação. Idealmente, simplesmente descreveríamos uma maneira de usar a API de alto nível e, sem geração de código, você simplesmente incluiria um arquivo elektra.h diferente (não gerado).

E como eu disse no futuro, será exatamente o mesmo KeySet usado pelo modo specload.

Perfeito.

O KeySet padrão até certo ponto permite executar binários sem montagem, mas não é um substituto completo para a especificação montada. A razão é simples, as chaves nos padrões são processadas apenas por plugins globais (possivelmente excluindo plugins postgetstorage, eu preciso olhar para isso).

Você pode criar um problema/proposta sobre esses problemas? Para mim, o comportamento esperado é que o plugin spec copie os metadados para que fique disponível para todos os plugins. Idealmente, as chaves passadas para kdbGet são idênticas às chaves recuperadas. (Obviamente não para hits de cache ou NO_UPDATE onde kdbGet é essencialmente um não operacional.)

Você considerou que a API gerada por código parece idêntica à API não gerada por código?

De que maneira? Não vejo nenhuma peça que seja realmente idêntica. Muita coisa é parecida, já que uma é baseada na outra, mas nada é realmente idêntico.

Você pode criar um problema/proposta sobre esses problemas?

Quando eu souber o que estou propondo, posso fazer isso...

Para mim, o comportamento esperado é que o plugin spec copie os metadados para que fique disponível para todos os plugins.

Isso é exatamente o que acontece, não há outros plugins, se não existir ponto de montagem.

Idealmente, as chaves passadas para kdbGet são idênticas às chaves recuperadas. (Obviamente não para hits de cache ou NO_UPDATE onde kdbGet é essencialmente um não operacional.)

Não sei o que quer dizer com isso. A documentação para kdbGet afirma claramente que o returned KeySet pode conter chaves arbitrárias e que as chaves recuperadas são anexadas com ksAppendKey e, portanto, substituirão as existentes.

De que maneira? Não vejo nenhuma peça que seja realmente idêntica. Muita coisa é parecida, já que uma é baseada na outra, mas nada é realmente idêntico.

Especialmente em termos de API, por exemplo, ter elektraOpen e não loadConfiguration. O parâmetro "default" simplesmente não seria usado ou pode até ser outro KeySet anexado.

Existem outras diferenças na API gerada versus não gerada? (Exceto garantias adicionais na API gerada.)

@kodebach : Obrigado pela explicação detalhada. Isso ajuda muito, também levanta muitos novos "Por que vocês estão fazendo as coisas do jeito que vocês fazem?" perguntas. Eles provavelmente não são úteis no momento, então acho que tenho apenas uma pergunta restante:

É correto dizer que (1) garante que o aplicativo valide sua própria configuração em relação à especificação correta, mesmo que a especificação errada ou nenhuma seja montada?

É correto dizer que (1) garante que o aplicativo valide sua própria configuração em relação à especificação correta, mesmo que a especificação errada ou nenhuma seja montada?

Não há 100% de garantia, porque ainda usamos apenas o que a Elektra nos fornece. Se tudo estiver configurado corretamente, a especificação fornecida pela Elektra (via kdbGet ) virá do próprio aplicativo (via specload ). Mas a especificação ainda precisa ser montada, então não sabemos de onde ela vem.

Para ter 100% de certeza de que executando, por exemplo lcdproc , usaremos a especificação incorporada em lcdproc , precisaríamos de algumas alterações. Basicamente, teríamos que verificar de alguma forma se o executável que specload vai chamar é o mesmo que está em execução. A única maneira para isso (na qual consigo pensar agora) envolve o caminho absoluto para o executável atual. Infelizmente, não há uma maneira portátil de acessar esse caminho, teríamos que confiar em coisas como /proc ou chamadas de sistema WIN32 e OSX.

Por que você simplesmente não compara qual especificação fornecida e o que está embutido (e falha nas incompatibilidades)?

Seria muito bom escrever agora um resumo de quais modos vamos suportar.

Por que você simplesmente não compara qual especificação fornecida e o que está embutido (e falha nas incompatibilidades)?

Comparar dois KeySets potencialmente muito grandes será bastante caro, eu gostaria de evitá-lo, se possível. Além disso, como o KeySet que recebemos de kdbGet já passou por vários plugins, não seria exatamente igual ao que enviamos para specload . Mas eu tive algumas outras ideias, vou pesquisar.

Seria muito bom escrever agora um resumo de quais modos vamos suportar.

Vou implementar 3 opções para o gerador de código:

  • specLocation=( embed | external ) : Isso altera a saída da especificação processada. embed (padrão) o incorpora no binário, external produz um arquivo separado.
  • defaultsHandling=( embed | speconly ) : Isso altera como default s são tratados. embed (padrão) passa um defaults KeySet para elektraOpen (o KeySet é a especificação incorporada, caso contrário, é o KeySet mínimo necessário, ou seja, menos metadados do que a especificação). speconly passa NULL como defaults para elektraOpen e, portanto, elektraGet* falhará sem uma especificação montada.
  • specValidation=( none | minimal | full ) : Isso altera quais validações da especificação são feitas. none não é validação, minimal (padrão) verifica se a chave pai da especificação está presente e se a primeira chave na especificação pode ser recuperada (para garantir que spec-mount foi executado) . full não será implementado por enquanto, faria uma validação completa como a que você sugeriu.

Combinações dessas opções podem ser usadas para recriar os modos que sugeri. Existem algumas outras combinações também. Acho que essas opções são muito mais transparentes para os usuários, e a implementação também deve ser mais limpa.

Desculpe, se estou sendo pedante agora, mas isso ainda me confunde.

Mas a especificação ainda precisa ser montada, então não sabemos de onde ela vem.

Mas você está passando a especificação interna para kdbEnsure(), então o que acontece com isso?

Também acho que foi mencionado em algum lugar, que kdbEnsure() está lá para "sobrescrever" as chaves da configuração com valores da linha de comando. Então kdbEnsure() não substituiria a especificação montada pela fornecida?

Acho que entendi agora, por que você não gostou da minha proposta de nomes de arquivos exclusivos: Não ajuda porque, após a montagem, não sabemos qual arquivo foi usado de qualquer maneira.

Como alguém usou linux e gerenciadores de pacotes por quase toda a minha vida, quanto mais eu entendo o que você está fazendo, menos eu entendo o seu caso de uso. Mas acho que as pessoas ainda estão movendo binários manualmente em outros sistemas.

Eu acho que uma razão pela qual isso é tão confuso é que o elektra tenta fazer um círculo quadrado aqui: Por um lado, você quer que haja um banco de dados global seguindo a especificação única e verdadeira na medida em que você está montando a especificação no banco de dados. Por outro lado, você permite que os aplicativos evoluam suas especificações e estão tentando lidar com o fato de não haver uma especificação verdadeira, afinal.

Eu acredito que no final existem apenas duas maneiras de resolver isso:
a) Finja que existe uma especificação verdadeira de qualquer maneira e pare de se preocupar em manter aplicativos e especificações sincronizados e espere que os desenvolvedores de aplicativos exerçam a devida diligência ao atualizar suas especificações e executem scripts de instalação adequados e talvez façam algum tipo de detecção de versão.
b) Na verdade, não montando as especificações em si. Em vez disso, invente outra abstração baseada na ideia de integração de aplicativos: Como o elektra é grande em integração de aplicativos, você provavelmente já pensou em especificações sobrepostas, embora eu não tenha ideia de qual seja sua solução. Reconheça que várias versões da mesma especificação (mesmo aplicativo) é realmente apenas um caso especial de integração de aplicativos e forneça os mecanismos para fazê-los coexistir no mesmo namespace. (Pode dar muito trabalho, não tenho certeza se vale a pena.)

Minha intuição é ir com (a), mas vamos também explorar (b) com base no meu conhecimento muito irregular do que você está fazendo. O novo mecanismo teria as seguintes propriedades:

  1. Cada especificação tem um campo de versão, contendo uma (ou talvez mais?) versões que implementa e talvez alguns outros IDs, somas de verificação ou qualquer outra coisa.
  2. Cada aplicativo usando validação completa e estrita tem um valor compatível, que pode ser testado no campo de versão de (1).
  3. Cada especificação deve realmente especificar todas as chaves que um aplicativo pode acessar, mesmo que estejam fora de seu namespace nominal. (Não tenho certeza de como lidar com o acesso indireto à chave, onde a chave é acessada devido ao valor de uma outra chave.)
  4. as especificações não estão mais montadas. Vamos chamar o novo mecanismo de "instalação". Quando uma nova especificação (versão de uma especificação) é instalada, ela é verificada em relação às especificações já instaladas e a instalação falha se houver uma incompatibilidade. (O usuário precisa desinstalar uma outra especificação primeiro.)
    Além disso, o banco de dados é validado em relação à nova especificação e o usuário deve corrigir quaisquer conflitos antes que a instalação possa ser concluída.
  5. De todas as especificações instaladas, a especificação verdadeira é calculada mesclando todas as restrições e mesclando todos os campos de versão.
  6. A especificação verdadeira pode ser montada no banco de dados global. Não tenho certeza se recomendo isso, mas por motivos de compatibilidade, você provavelmente desejará fazer isso.
  7. Para o aplicativo, não importa se a configuração é validada em relação à especificação verdadeira ou apenas em seu próprio fragmento. A única coisa que importa é que o campo compatível seja validado em relação ao campo de versão.

Além de (b) ser muito trabalhoso para implementá-lo, também adiciona muita complexidade. Eu não tenho certeza se, por exemplo, os mantenedores do debian iriam te amar ou te odiar, se você fizesse isso. Talvez ambos. De qualquer forma, está definitivamente fora do escopo da tese de Kodebach.

Minha conclusão é que, no curto prazo, estamos presos com (a) de qualquer maneira e o caso de uso para a especificação interna parece bastante fino e leva a muita confusão e não é bem explorado. Portanto, recomendo colocar o mínimo de esforço possível no modo integrado e não usá-lo por padrão.

portanto, elektraGet* falhará sem uma especificação montada.

"vai falhar" ou "vai falhar se o banco de dados estiver incompleto"?

Desculpe, se estou sendo pedante agora, mas isso ainda me confunde.

Tudo bem, demorei algum tempo para entender como a Elektra funciona também. Pode ser muito complicado às vezes.

Mas você está passando a especificação interna para kdbEnsure(), então o que acontece com isso? Também acho que foi mencionado em algum lugar, que kdbEnsure() está lá para "sobrescrever" as chaves da configuração com valores da linha de comando. Então kdbEnsure() não substituiria a especificação montada pela fornecida?

Não estamos passando a especificação para kdbEnsure . kdbEnsure existe para modificar o handle KDB de forma não persistente. Isso é usado para habilitar o plug-in que processa as opções de linha de comando. Por causa de algumas limitações kdbEnsure agora não pode adicionar pontos de montagem. Mas mesmo que pudesse, ainda não seria sensato injetar a especificação dessa maneira. Usar kdbEnsure afetaria apenas nosso processo atual (na verdade, apenas o identificador KDB atual). Se injetarmos a especificação dessa maneira, a ferramenta de linha de comando kdb não saberia sobre ela e permitiria modificações que não estejam de acordo com a especificação.

Minha conclusão é que, no curto prazo, estamos presos com (a) de qualquer maneira e o caso de uso para a especificação interna parece bastante fino e leva a muita confusão e não é bem explorado. Portanto, recomendo colocar o mínimo de esforço possível no modo integrado e não usá-lo por padrão.

Acho que devemos pensar na especificação incorporada como uma maneira diferente de armazenar a especificação. Provavelmente deveríamos apenas descrevê-lo como uma opção de conveniência, pois permite empacotar um aplicativo em um único arquivo. Isso pode ser útil para aplicativos pequenos, desde que a especificação não aumente muito o tamanho do binário.

Se você empacotar seu aplicativo como, por exemplo, .deb ou .rpm qualquer maneira, eu recomendaria usar as opções specLocation=external e defaultsHandling=speconly para compilações de lançamento.

"vai falhar" ou "vai falhar se o banco de dados estiver incompleto"?

Falhará se o banco de dados estiver incompleto. Claro que ainda funciona, se você adicionou manualmente a chave com o tipo correto.

Não estamos passando a especificação para o kdbEnsure.

Hm, então eu preciso reler o código gerado. Devo ter me perdido em algum lugar.

Usar o kdbEnsure afetaria apenas nosso processo atual (na verdade, apenas o identificador KDB atual). Se injetarmos a especificação desta forma, a ferramenta de linha de comando kdb não saberia sobre ela e permitiria modificações que não estejam de acordo com a especificação.

Eu estive pensando que este era o ponto: Para detectar configurações incorretas validando contra uma especificação conhecida como correta, caso kdb tenha, por algum motivo, a ideia errada do que é a especificação e, portanto, modificações que não estejam em conformidade com o especificação escorregaram.

Claro, mesmo que você tenha feito como acima, você ainda gostaria de montar a especificação globalmente também.

De qualquer forma, acho que está claro agora e o modo (1) simplesmente não tem nenhum caso de uso para pessoas usando gerenciadores de pacotes ou construindo lcdproc localmente. Talvez seja útil para pessoas movendo binários em pendrives, embora eles precisem saber muito sobre elekra para realmente fazê-lo funcionar.

Comparar dois KeySets potencialmente muito grandes será bastante caro, eu gostaria de evitá-lo, se possível.

Você fez o benchmarking?

Vou implementar 3 opções para o gerador de código:

Eu acho que você está muito ansioso aqui. Vamos primeiro concordar com os casos de uso e depois como implementá-los.

specLocation=( embed | external ): Isso altera a saída da especificação processada. embed (padrão) incorpora no binário, external produz um arquivo separado.

Concordo, se tivéssemos um resultado seria esse. Talvez reformulando: embedSpec: completo, mínimo?

defaultsHandling=( embed | speconly ): Isso altera como os padrões são tratados. embed (padrão) passa um KeySet padrão para elektraOpen (o KeySet é a especificação incorporada, caso contrário, é o KeySet mínimo necessário, ou seja, menos metadados que a especificação). speconly passa NULL como padrão para elektraOpen e, portanto, elektraGet* falhará sem uma especificação montada.

Você pode explicar por favor? Por que você não passaria tudo para os padrões que nós incorporamos?

specValidation=( none | minimal | full ): Isso altera quais validações da especificação são feitas. none é nenhuma validação, minimal (padrão) verifica se a chave pai da especificação está presente e se a primeira chave na especificação pode ser recuperada (para garantir que a montagem da especificação foi executada). full não será implementado por enquanto, ele faria uma validação completa como a que você sugeriu.

Veja acima.

De qualquer forma, acho que está claro agora e o modo (1) simplesmente não tem nenhum caso de uso para pessoas usando gerenciadores de pacotes ou construindo lcdproc localmente. Talvez seja útil para pessoas movendo binários em pendrives, embora eles precisem saber muito sobre elekra para realmente fazê-lo funcionar.

Sim, acho que nosso objetivo agora deve ser encontrar uma quantidade mínima de casos de uso que possamos documentar e oferecer suporte. @haraldg você pode ajudar com isso?

Você fez o benchmarking?

Eu preciso? Comparar dois KeySets do mesmo tamanho é claramente O(m + n) ( m número total de metakeys, n número total de chaves). Para tamanhos diferentes seria O(1) . Se usarmos keyCompare a coisa toda seria basicamente 2*(m+n) strcmp se n memcmp s.

Também precisaríamos de dados do mundo real, porque o número médio de metachaves por chave pode fazer a diferença.

Fiz um teste rápido na minha máquina comparando dois KeySets com 1.000.001 chaves cada, onde cada chave tinha 1 metakey e tudo era o mesmo, exceto a última metakey. A execução completa levou cerca de 4,5 segundos, dos quais 3 foram gastos na construção dos KeySets.

Eu acho que você está muito ansioso aqui.

Sinto muito, mas meu tempo não é infinito.

Você pode explicar por favor? Por que você não passaria tudo para os padrões que nós incorporamos?

Se specLocation=external for usado, não haverá especificação incorporada. Nesse caso defaultsHandling=embed usaria um KeySet com apenas valores de chave e metadados type para economizar espaço binário.

No entanto, esta combinação é um pouco estranha. Eu não recomendaria a menos que haja uma necessidade muito específica para isso. Na documentação eu recomendaria usar defaultsHandling=speconly para specLocation=external .

Veja acima.

Não tenho certeza de qual "acima" você quer dizer, mas tentarei explicar os casos de uso para specValidation .

  • none é útil para sistemas onde uma especificação é garantida para ser montada e garantida para não ser removida. Por exemplo, um sistema embarcado onde tudo é compilado em uma imagem de firmware somente leitura.
  • minimal ou se implementado full são a configuração recomendada e padrão. full só pode ser usado com specLocation=embed , caso contrário, a especificação completa não é incorporada e não pode ser usada para validação. A única outra diferença entre eles é o desempenho.

quantidade mínima de casos de uso que podemos documentar e dar suporte

Não devemos documentar casos de uso. Devemos documentar as opções disponíveis no gerador de código. Como as pessoas usam essas opções depende delas. É claro que devemos dar recomendações e exemplos, mas por que limitar intencionalmente o que as pessoas podem fazer com nossas ferramentas? Contanto que indiquemos claramente as possíveis desvantagens de usar opções diferentes, não vejo problema.

Também precisaríamos de dados do mundo real

Temos dados do mundo real: a especificação do LCDproc.

A execução completa levou cerca de 4,5 segundos, dos quais 3 foram gastos na construção dos KeySets.

Obrigado pela referência! Portanto, comparar é muito mais rápido do que criar os KeySets. Mas talvez não precisemos disso de qualquer maneira: veja os casos de uso abaixo.

Sinto muito, mas meu tempo não é infinito.

Claro, exatamente por isso sugiro que você reduza o número de variantes que você implementa. A documentação e teste de tudo o que você sugeriu levaria muito tempo.

Sua maior prioridade agora deve ser criar um PR mínimo de trabalho (para um cliente) com boa documentação para um caso de uso (eu sugiro o 1. caso abaixo) para que possamos finalmente obter feedback público.

Não devemos documentar casos de uso. Devemos documentar as opções disponíveis no gerador de código. Como as pessoas usam essas opções depende delas. É claro que devemos dar recomendações e exemplos, mas por que limitar intencionalmente o que as pessoas podem fazer com nossas ferramentas? Contanto que indiquemos claramente as possíveis desvantagens de usar opções diferentes, não vejo problema.

Há um grande problema: a carga cognitiva que colocamos nas pessoas. Eles precisam entender muitos detalhes do Elektra e, mesmo que o façam, podem facilmente errar e escolher uma combinação das variantes 2 2 3=12 que não funciona mesmo assim.

Com base no que é implementado, quais são os objetivos da Elektra e o tempo restante, acho que deveríamos ter 3 casos de uso:

  1. (padrão) especificação completa incorporada, nada montado: para desenvolvedores que simplesmente desejam executar o LCDproc a partir do diretório de compilação.
  2. variante instalada de 1: os scripts durante make install montam a especificação no binário com specload. Este caso de uso é para brincar com configuração e especificação (uma vez que specload suporta salvar edição da especificação. Por enquanto as mudanças mínimas que permitimos devem ser suficientes).
  3. tamanho binário mínimo para sistemas embarcados: Assumimos que a especificação é montada via ni (e também fornecemos um script que monta o ni + a especificação, que é executado durante make install ). Neste modo, o binário não pode ser executado enquanto não estiver instalado. Mas assumimos que a especificação está correta por motivos de desempenho.

Vocês dois estão bem com isso? @kodebach : qual seria o conjunto mínimo de variantes no gerador de código e no sistema de compilação do LCDproc para suportar isso?

Acho que a proposta de Kodebach é bastante razoável. Não tenho certeza de que ajuda posso fornecer em cima disso.

A carga mental vem da compreensão dos conceitos como montagem, especificação etc, que devem ser investidos não importa como as opções disponíveis sejam apresentadas aos usuários. Acho que a maior confusão vem de "a especificação está embutida, mas não é usada sem montagem global", mas parece que vocês dois estão bastante convencidos disso. Eu não vejo muito mais para soltar, isso tornaria as coisas mais fáceis de entender.

eles podem facilmente errar e escolher uma combinação das variantes 223=12 que não funciona mesmo assim.

Acho que pode haver uma diferença entre as opções para o gerador de código e as opções para configurar. O gerador de código é usado por desenvolvedores (ou melhor, desenvolvedores que trabalham no sistema de compilação), enquanto o configure precisa ser compreensível para todos. Eu provavelmente apenas exporia as opções do gerador de código via configure, mas forneceria bons padrões, para que os usuários não precisassem pensar sobre eles.

(padrão) especificação completa incorporada, nada montado: para desenvolvedores que simplesmente desejam executar o LCDproc a partir do diretório de compilação.

Eu acho que foram as conclusões dos últimos dias de discussão, que esse cenário não está na mesa porque o specload não funciona dessa maneira e as alterações internas como kdbEnsure() teriam que acontecer de qualquer maneira para implementá-lo?

Além disso, para que isso seja útil para executar o LCDd a partir da árvore de compilação, alguns equivalentes à opção -c precisam estar funcionando, porque uma configuração padrão completa funcionaria apenas para muito poucos drivers. Eu acho que uma maneira de fazer isso funcionar seria ter alguma maneira de dizer ao elektra para usar $BUILD_DIR/elektra.ini em vez de /etc/kdb/elektra.ecf - mas não faço ideia de quão difícil isso seria e ainda é outro distração, então não vamos explorar isso agora.

Se você ainda quiser saber qual é o conjunto mínimo de variantes para o LCDproc, acho que em termos de opções de kodebachs seria:

specLocation=externo, defaultsHandling=speconly, specValidation=(nenhum | mínimo)

Então, sim, poderia ser reduzido a uma opção.

Eu acho que uma das questões em aberto é, o que 'specValidation' realmente faria, quando a especificação está faltando: falha? emitir um aviso, mas tentar continuar? Acho que o que decidirmos aqui, alguns outros desenvolvedores de algum outro aplicativo vão querer outra coisa. Talvez o espaço de ações possíveis nem seja expressável com opções, mas apenas com código. Ou seja, permite especificar algum retorno de chamada em caso de erro de validação. Mas, novamente, acho que isso não é algo que deve ser decidido agora, desde que o que fazemos agora não dificulte a implementação no futuro.

Acho que a maior confusão vem de "a especificação está embutida, mas não é usada sem montagem global", mas parece que vocês dois estão bastante convencidos disso.

É uma opção que já está disponível, então por que abandoná-la? Serei cuidadoso ao documentar a especificação interna e declararei explicitamente que é apenas uma maneira diferente de armazenar a especificação, mas na verdade não tem outras vantagens.

Acho que foram as conclusões dos últimos dias de discussão, que esse cenário não está na mesa porque o specload não funciona dessa maneira

Com a especificação completa incorporada e nada montado, você pode executar a partir do diretório de compilação, mas nenhuma validação ou conversão acontecerá, pois nenhum plug-in é executado sem configuração adicional.

Além disso, para que isso seja útil para executar o LCDd a partir da árvore de compilação, alguns equivalentes à opção -c precisam estar funcionando
Eu acho que uma maneira de fazer isso funcionar seria ter alguma maneira de dizer ao elektra para usar $BUILD_DIR/elektra.ini em vez de /etc/kdb/elektra.ecf

Já temos o namespace dir . Você pode montar uma configuração lá e ela será local para seu diretório de trabalho. Infelizmente, o namespace dir é muito estranho, porque a configuração é de todo o sistema. Veja também #1074.

Eu acho que uma das questões em aberto é, o que 'specValidation' realmente faria, quando a especificação está faltando: falha? emitir um aviso, mas tentar continuar?

loadConfiguration já pode retornar um erro que deve ser tratado pela aplicação. A validação de especificação com falha seria apenas outro tipo de erro. Se virmos a necessidade disso, também podemos fornecer uma maneira de continuar, apesar da falha na validação da especificação (não recomendo).

A carga mental vem da compreensão dos conceitos como montagem, especificação etc, que devem ser investidos não importa como as opções disponíveis sejam apresentadas aos usuários.

Isso não é necessariamente verdade: há muitos conceitos em Elektra que você não precisa entender, desde que não seja confrontado com eles. No caso de uso 1, você não seria confrontado com montagem e especificação.

Em geral, o Elektra é cuidadosamente projetado para que as distribuições já possam decidir muito para os usuários (por exemplo, montar todos os arquivos em algum formato de configuração). Em seguida, os usuários podem usar a distribuição de maneiras muito semelhantes às que usam as distribuições agora (somente essa configuração é unificada). No entanto, eles não precisariam entender a montagem. (Essa também é a razão pela qual a montagem só é possível para root: o recurso deve ser para distribuições ou make install , não para usuários finais.)

Acho que a maior confusão vem de "a especificação está embutida, mas não é usada sem montagem global", mas parece que vocês dois estão bastante convencidos disso. Eu não vejo muito mais para soltar, isso tornaria as coisas mais fáceis de entender.

Se você perceber que não devemos apoiar/documentar, ficaremos felizes em fazê-lo. Isso significa que a instalação do LCDproc será necessária para iniciá-lo. Eu acharia isso muito irritante.

Eu acho que foram as conclusões dos últimos dias de discussão, que esse cenário não está na mesa porque o specload não funciona dessa maneira e as alterações internas como kdbEnsure() teriam que acontecer de qualquer maneira para implementá-lo?

specload e kdbEnsure não seriam usados ​​aqui. Simplesmente perderíamos a maioria das garantias, pois nenhuma validação aconteceria. Isso seria apenas para desenvolver o LCDproc sem alterar as especificações ou conf. Mas imho isso também é um caso de uso válido. Se você não pensa assim, não vamos apoiá-lo.

mas fornece bons padrões, para que os usuários não precisem pensar neles.

Claro que isso é sempre um sonho, mas a realidade geralmente é que, se você errar no design de configuração, os usuários encontrarão muitas maneiras de cometer erros. E expor decisões que poderiam ser feitas por desenvolvedores é um design de configuração muito fedorento.

Então, sim, poderia ser reduzido a uma opção.

Então devemos fazê-lo.

É uma opção que já está disponível, então por que abandoná-la? Serei cuidadoso ao documentar a especificação interna e declararei explicitamente que é apenas uma maneira diferente de armazenar a especificação, mas na verdade não tem outras vantagens.

Você assume que todos lêem e entendem o documento. Na verdade, apenas as pessoas que estão tão profundamente envolvidas como estamos agora, podem realmente decidir qual a melhor maneira de armazenar a especificação. Portanto, é nossa responsabilidade decidir isso corretamente. Empurrar essa decisão para os usuários não é a maneira de fazê-lo.

loadConfiguration já pode retornar um erro que deve ser tratado pelo aplicativo. A validação de especificação com falha seria apenas outro tipo de erro. Se virmos a necessidade disso, também podemos fornecer uma maneira de continuar, apesar da falha na validação da especificação (não recomendo).

Sim, eu também não recomendaria fazer dessa forma. Mas os hackers descobrirão e usarão esse truque localmente. Espero que @haraldg não combine esse hack :smile:

No caso de uso 1, você não seria confrontado com montagem e especificação.

Os usuários finais (por exemplo, pessoas que usam LCDproc) nunca devem ser confrontados com a montagem, completamente independente dos modos de geração de código. A montagem deve ser sempre automatizada no processo de instalação.

Os desenvolvedores (por exemplo, pessoas que trabalham no LCDproc) sempre precisam entender a montagem, a menos que haja mudanças sérias no funcionamento do Elektra. Você não pode desenvolver uma aplicação usando a API de geração de código sem conhecer a montagem, simplesmente porque você tem que criar os scripts de instalação responsáveis ​​pela montagem e uma possível montagem manual durante o desenvolvimento.

Isso significa que a instalação do LCDproc será necessária para iniciá-lo.

A eliminação da especificação interna requer apenas uma maneira diferente de montagem. Você ainda pode executar o LCDproc de qualquer diretório e uma especificação montada válida é necessária em ambos os casos.

Você assume que todos lêem e entendem o documento.

É por isso que precisamos decidir bons padrões, não porque não devemos ter opções. Além disso, se um desenvolvedor usa uma API sem estudar adequadamente a documentação, ele não deve esperar obter os melhores resultados possíveis.


Como toda essa discussão não está nos levando a lugar algum, vou agora começar a implementação. Uma vez que eu tenha algum código que você possa experimentar, ainda podemos decidir o que manter, o que mudar e o que adicionar.

Eu concordo principalmente com @kodebach na discussão acima.

Se você perceber que não devemos apoiar/documentar, ficaremos felizes em fazê-lo. Isso significa que a instalação do LCDproc será necessária para iniciá-lo. Eu acharia isso muito irritante.

Sim, é muito chato, mas parece estar além do escopo do que @kodebach está fazendo, então temos que conviver com isso. Também parece rasgar a base do elektra para permitir qualquer configuração não global como o que a opção -c faz, então definitivamente devemos abrir um novo problema para este "modo fakeroot", se quisermos para trabalhar nele.

Isso seria apenas para desenvolver o LCDproc sem alterar as especificações ou conf. Mas imho isso também é um caso de uso válido. Se você não pensa assim, não vamos apoiá-lo.

Não me lembro de ter executado nenhum dos programas do LCDproc com a configuração padrão completa. Talvez eu tenha feito em alguma ocasião para alguns testes muito triviais, porém eu tenho 5 variantes diferentes do LCDd.conf (projetadas para testar diferentes aspectos) no meu diretório de compilação, que eu uso com -c frequentemente. Portanto, executar o LCDd a partir do diretório de compilação, mas sem "alterar o conf", parece um caso muito difícil para mim.

Sim, eu também não recomendaria fazer dessa forma. Mas os hackers descobrirão e usarão esse truque localmente. Espero que @haraldg não faça merge desse hack 😄

O que é mesclado depende em grande parte das pessoas explicando seu caso de uso e me convencendo de que isso faz mais bem do que mal. Se as pessoas às vezes precisarem executar um aplicativo sem uma especificação (digamos que esteja em um sistema de arquivos, que nem sempre é montado), mas ainda assim desejarem um aviso quando a especificação não estiver disponível, provavelmente não devo dizer a eles para "apenas use o modo OpenWRT, onde a especificação nunca é verificada", mas permita que eles adicionem uma opção ao sistema de compilação, que torna a especificação ausente menos fatal.

que eu uso com -c frequentemente

Se -c for usado apenas para desenvolvimento, você poderá kdb mount LCDd.ini dir/sw/lcdproc/lcdd/#0/current . Então, sempre que você executar LCDd , deve usar $PWD/.dir/LCDd.ini , se existir. Então você pode executar LCDd de diferentes diretórios para usar configurações diferentes, ou você pode simplesmente trocar o arquivo .dir/LCDd.ini sempre que quiser alterar a configuração. Como eu disse, essa é uma configuração super estranha e o #1074 definitivamente deve ser implementado (IMO, um ponto de montagem dir deve ser específico para um único diretório, não para todo o sistema), mas é uma solução por enquanto.

Se as pessoas às vezes precisarem executar um aplicativo sem uma especificação, [...] mas permitirem que eles adicionem uma opção ao sistema de compilação, isso torna a especificação ausente menos fatal.

Mudar o sistema de compilação seria uma solução aceitável (essa opção poderia apenas desabilitar a verificação). O que acho que não devemos implementar (a menos que haja necessidade absoluta disso) é gerar a verificação, mas permitir que a aplicação continue mesmo que a verificação tenha falhado. IMO se a verificação for gerada, ela deve ser bem-sucedida para que o aplicativo funcione.

[ ponto de montagem dir ]

Sim, tenho certeza de que todos encontrarão alguma solução alternativa que seja aceitável para eles.

IMO se a verificação for gerada, ela deve ser bem-sucedida para que o aplicativo funcione.

Então você acha que um aviso é pior do que nenhum cheque? (A propósito, este ramo da discussão não era sobre o que você deveria implementar, mas o que eu poderia me sentir tentado a mesclar, se alguém propuser isso para o LCDproc.)

Você não pode desenvolver uma aplicação usando a API de geração de código sem conhecer a montagem, simplesmente porque você tem que criar os scripts de instalação responsáveis ​​pela montagem e uma possível montagem manual durante o desenvolvimento.

Fazemos os scripts agora para o LCDproc. E adicionar alguns elektraGet em um módulo/driver esperançosamente será possível sem entender a montagem.

É por isso que precisamos decidir bons padrões, não porque não devemos ter opções.

Este é um equívoco geral: não é possível tornar a configuração sã apenas fornecendo bons padrões. No momento em que você precisa mudar alguma coisa, você se depara com a complexidade da configuração. Assim, você precisa estar sempre ciente de duas coisas:

  1. mantenha o design de configuração simples
  2. tem bons padrões

Além disso, se um desenvolvedor usa uma API sem estudar adequadamente a documentação, ele não deve esperar obter os melhores resultados possíveis.

Sim, as APIs são um excelente exemplo! Você gostaria de ter uma API onde você tem chamadas que decidem sobre alguns internos que você não tem ideia?

Como toda essa discussão não está nos levando a lugar algum, vou agora começar a implementação. Uma vez que eu tenha algum código que você possa experimentar, ainda podemos decidir o que manter, o que mudar e o que adicionar.

Sim, como já foi desejado várias vezes: faça as correções necessárias para que os clientes iniciem com sucesso, faça um PR no repositório do LCDproc e depois escreva para a lista de discussão.

Outros modos geradores de código e otimização do tamanho binário devem ser feitos após recebermos mais feedback.

Então você acha que um aviso é pior do que nenhum cheque? (A propósito, este ramo da discussão não era sobre o que você deveria implementar, mas o que eu poderia me sentir tentado a mesclar, se alguém propuser isso para o LCDproc.)

O problema com a continuação em erros de especificação é que talvez também estejam presentes outros erros que não são relatados (sempre relatamos apenas um erro).

Você não pode desenvolver uma aplicação usando a API de geração de código sem conhecer a montagem, simplesmente porque você tem que criar os scripts de instalação responsáveis ​​pela montagem e uma possível montagem manual durante o desenvolvimento.

Fazemos os scripts agora para o LCDproc. E adicionar alguns elektraGet em um módulo/driver esperançosamente será possível sem entender a montagem.

Você perdeu o ponto. Para desenvolver um aplicativo usando a API de geração de código, você precisa saber sobre montagem. Mais especificamente, a pessoa responsável pela criação dos scripts de instalação que fazem a montagem deve conhecer o conceito de montagem. No caso do LCDproc essa pessoa sou eu. Eu sei sobre montagem, mas não vou escrever os scripts para todos os projetos que desejam usar a API de geração de código.

O ponto principal foi que você deixou a impressão de que existe uma solução onde abstraímos completamente o conceito de montagem. Este não é o caso.

Assim, você precisa estar sempre ciente de duas coisas:

  1. mantenha o design de configuração simples
  2. tem bons padrões

Claro que a simplicidade também é importante. Mas se você se concentrar demais na simplicidade, perderá a flexibilidade. Em algum momento você tem que sacrificar a simplicidade, se quiser fornecer uma solução geral. A Elektra quer fornecer uma solução geral, então deve haver alguma complexidade.

Você gostaria de ter uma API onde você tem chamadas que decidem sobre alguns internos que você não tem ideia?

Não é esse o objetivo de ter uma API de alto nível, ou qualquer API para esse assunto? Ele oculta as partes internas dos níveis mais baixos, para que você não precise saber sobre todas as coisas complicadas.

Também este tipo de contradiz sua declaração anterior. Se a API não decidir por mim, ela tem que expor todas as opções. Portanto, não pode ser simples. Então você quer simples ou quer as opções?

Sim, como já desejou várias vezes: faça as correções necessárias para que os clientes iniciem com sucesso

Já afirmei algumas vezes, que os clientes (e também o servidor) já devem iniciar com sucesso. Também forneci instruções em https://github.com/ElektraInitiative/libelektra/issues/2748#issuecomment -500158389. Se não funcionar para você, por favor me avise.

O ponto principal foi que você deixou a impressão de que existe uma solução onde abstraímos completamente o conceito de montagem. Este não é o caso.

Ainda não, mas talvez você encontre uma maneira de escrever scripts de instalação reutilizáveis? Algo como:

magic.sh path-to-spec-file application-root-key-name

Quais são os dois argumentos, os desenvolvedores obviamente precisam saber.

Claro que a simplicidade também é importante. Mas se você se concentrar demais na simplicidade, perderá a flexibilidade. Em algum momento você tem que sacrificar a simplicidade, se quiser fornecer uma solução geral. A Elektra quer fornecer uma solução geral, então deve haver alguma complexidade.

Se alguém quiser algum recurso, então é claro que precisa mergulhar em alguma complexidade, por exemplo, lendo sobre alguns conceitos ou plugins (e estamos dispostos a fornecer também recursos complexos). E você fez um trabalho maravilhoso para muitos recursos diferentes.

Mas esses conceitos e plugins devem ser o mais simples possível e fazer apenas o que é necessário. Já temos monstros suficientes (:wave: ini, spec, list plugins; o antigo gerador de código). Precisamos nos livrar desses monstros e não introduzir novos. Assim, a Elektra tem um objetivo de qualidade muito claro: Prefira a simplicidade à robustez e extensibilidade (flexibilidade). Isso é declarado muito claramente em GOALS.md .

E também vivemos isso: ainda é possível (e sempre será) obter/definir valores no Elektra sem usar nenhum desses conceitos que falamos aqui.

Harald perguntou sobre 5 modos diferentes (que eu já acho exagerados, mas ok, ele é o cliente) mas não devemos apresentar 12 que ninguém pediu. Em particular, ainda não entendi o que qualquer usuário deve fazer com a opção "defaultsHandling". Parece-me um típico "Preciso de um if no código-fonte, então vamos adicionar uma opção". Definitivamente, não é assim que o design de configuração deve ser feito. Devemos sempre supor que as pessoas olham para nós como fazer um design de configuração adequado. Por exemplo, plug-in de especificação ou lista são muito embaraçosos e devemos nos livrar disso o mais rápido possível. Você já ajudou muito com o plugin spec mas tem muito mais coisas para serem removidas de lá. Claro que não perguntei porque você já fez muito trabalho que está além do que originalmente queria fazer.

Já afirmei algumas vezes, que os clientes (e também o servidor) já devem iniciar com sucesso. Também forneci instruções em #2748 (comentário). Se não funcionar para você, por favor me avise.

@haraldg já testou (veja alguns comentários abaixo do seu link) e ele disse:

Eu também tentei tomar alguns timings, mas qualquer cliente segfaults imediatamente.

Além disso, as instruções não devem ser escondidas em alguma discussão. Por favor, embrulhe tudo e faça um PR adequado para o repositório principal do LCDproc, que também inclui um tutorial (para instruções). Obrigada!

Atualizar o progresso em #2805

@haraldg já testou (veja alguns comentários abaixo do seu link) e ele disse:

Eu também tentei tomar alguns timings, mas qualquer cliente segfaults imediatamente.

Eu não tenho acesso a um dispositivo armhf , então não posso reproduzir isso. Pelas poucas informações fornecidas no comentário original, suspeito que o plugin gopts não foi encontrado.

Em particular, ainda não entendi o que qualquer usuário deve fazer com a opção "defaultsHandling".

A opção specValidation é um pouco independente das outras duas. defaultsHandling (DH) e specLocation (SL) sempre precisam ser coordenados em algum grau.

Atualmente, para DH e SL, o padrão é embed , pois permite maior flexibilidade.

Para reduzir o tamanho do binário, mas ainda permitir a execução do aplicativo sem a necessidade de kdb mount você pode definir SL para speconly (e deixar o DH como está). Nesta versão o binário não contém a especificação completa. Em vez disso, incorporamos um KeySet que possui apenas nomes de chaves, metadados padrão e de tipo. Isso é tudo o que precisamos para a API de alto nível.

Se você quiser um tamanho binário mínimo, você deve definir SL para external e DH para speconly . Nesse caso, nenhuma especificação estará contida no binário. Isso também economiza algum tempo durante a inicialização, que seria necessário para construir o(s) KeySet(s) (embora o impacto deva ser mínimo).

A última combinação specLocation=embed e defaultsHandling=speconly não tem tanta utilidade. A única vantagem que posso ver aqui é que você economiza um pouco de tempo ao não construir o KeySet com os padrões para elektraOpen, e não processá-lo no elektraOpen. Esta configuração é apenas uma consequência de ter duas opções separadas.

Além disso, as instruções não devem ser escondidas em alguma discussão.

Eu não dei as instruções adequadas ainda, porque elas podem ter mudado muito depois dessa discussão. Agora vou começar a trabalhar na documentação também.

faça um PR adequado para o repositório principal do LCDproc

Depois de adicionar os diferentes modos e mais alguns testes e experimentações esta semana, agora estou firmemente na opinião de que isso não é uma boa ideia. É claro que o feedback seria bom, mas não vejo como esse PR será mesclado em um futuro próximo (=neste verão). Quanto mais me aproximo de terminar meu trabalho, mais problemas encontram em outras partes da Elektra.

Muitos desses problemas são sistêmicos e obviamente surgem do fato de que muitos dos recursos do Elektras são hacks. Hacks significando partes que usam o núcleo de maneiras para as quais nunca foi projetado. Os mais proeminentes são os plugins spec e type . Além disso, ao usar chaves em cascata, muitas coisas simplesmente quebram por razões inexplicáveis.

Por exemplo, hoje eu encontrei esse problema. Não faço ideia, por que não apareceu até agora, ou por que é um problema, mas aqui estamos:

kdb set -N user /sw/lcdproc/lcdproc/#0/current/lcdproc/foreground true
#> Using name user/sw/lcdproc/lcdproc/#0/current/lcdproc/foreground
#> Set string to "true"

kdb get /sw/lcdproc/lcdproc/#0/current/lcdproc/foreground
#> Sorry, module type issued the error 52:
#> error in the type plugin: The key user/sw/lcdproc/lcdproc/#0/current/lcdproc/foreground was already normalized by a different plugin! Please ensure that there is only one plugin active that will normalize this key.

kdb get user/sw/lcdproc/lcdproc/#0/current/lcdproc/foreground
#> 1

Por contexto, o tipo plugin é o único montado neste ponto de montagem que faz a normalização. Também não é um problema com kdb , já que originalmente notei o erro com o próprio lcdproc . Então tem que ser um problema no próprio kdbGet . Por alguma razão, parece que type é chamado duas vezes com a mesma chave.

Para outro problema, veja #2806.

Neste ponto, o único caminho a seguir que vejo é que eu crie uma versão do LCDproc que seja utilizável o suficiente para fazer benchmarks para minha tese. Depois disso, acho que precisamos pelo menos esperar pelo novo sistema de plugins (necessário de qualquer maneira para uma implementação adequada do servidor sem soluções alternativas).

Kodebach escreve:

Eu não tenho acesso a um dispositivo armhf , então não posso reproduzir isso. Pelas poucas informações fornecidas no comentário original, suspeito que o plugin gopts não foi encontrado.

Eu acho que deveria pelo menos ter incluído um BT completo para o segfault.

De qualquer forma, acho muito improvável, que o problema seja específico do armhf.
(Especialmente porque o kdb funciona bem.) Erros do compilador são uma possibilidade, mas
é muito mais provável que você tenha apenas algumas alterações não confirmadas em
sua cópia de trabalho, que são realmente necessários para que as coisas funcionem.

Essa era a minha suposição de qualquer maneira. Se eu puder fazer alguma coisa para coletar informações,
que o ajude a identificar o problema, então por favor me avise.

você acabou de ter algumas alterações não confirmadas em sua cópia de trabalho [...] Se eu puder fazer alguma coisa para reunir informações que o ajudem a identificar o problema, por favor me avise.

Improvável. Você pode tentar novamente com o código de #2805. Eu apenas tentei com essa versão e iniciar e conectar os clientes funcionou.

Você também pode verificar a saída do CMake para quaisquer mensagens relacionadas gopts . E verifique se [prefix]/lib/elektra/libelektra-gopts.so existe (onde [prefix] é seu CMAKE_INSTALL_PREFIX, cujo padrão é /usr/local ) após a instalação.

Se você obtiver falhas novamente, um backtrace completo também seria útil.

Especialmente porque o kdb funciona bem.

kdb não usa o plugin gopts agora.

A opção specValidation é um tanto independente das outras duas. defaultsHandling (DH) e specLocation (SL) sempre precisam ser coordenados até certo ponto.

Mais um motivo para não exportar essa interface para o usuário.

A última combinação specLocation=embed e defaultsHandling=speconly não tem muita utilidade.

Outra razão.

Não sei por que você está tão relutante em tornar essa interface mais fácil de usar. Se é porque você já implementou tudo, então você pode simplesmente expor apenas uma opção (digamos, especificaçãoEstratégia) e desta opção você deriva as outras 3 opções.

Depois de adicionar os diferentes modos e mais alguns testes e experimentações esta semana, agora estou firmemente na opinião de que isso não é uma boa ideia. Claro que o feedback seria bom,

O feedback não é bom, mas essencial para o sucesso da eletrificação do LCDproc.

mas não vejo como esse PR será mesclado em um futuro próximo (=neste verão).

Este é um assunto completamente diferente que não é controlado por você.

Quanto mais perto eu chego de terminar meu trabalho, mais problemas em outras partes da Elektra encontram.

Bom, por favor, crie problemas.

Por exemplo, hoje eu encontrei esse problema.

Por favor, reporte este problema (mesmo que você não possa reproduzi-lo ainda). Para o LCDproc, esse problema é irrelevante, pois decidimos que não nos importaremos com a validação. Cada tentativa de definir um booleano para algo diferente de "1" ou "0" não é suportada. Eu não acho que as pessoas do LCDproc terão problemas para entender "1" ou "0".

Neste ponto, o único caminho a seguir que vejo é que eu crie uma versão do LCDproc que seja utilizável o suficiente para fazer benchmarks para minha tese.

Não, o único caminho a seguir é reduzir a um conjunto que funcione. Já decidimos excluir a validação e como próximo passo poderíamos excluir o mmap. Espero que não seja necessário, pois temos alguém trabalhando ativamente no mmap. Vejamos em #2806.

Depois disso, acho que precisamos pelo menos esperar pelo novo sistema de plugins (necessário de qualquer maneira para uma implementação adequada do servidor sem soluções alternativas).

As alterações no sistema de plugins estão relacionadas apenas ao número máximo de plugins. Não corrigirá erros magicamente.

Mais um motivo para não exportar essa interface para o usuário.

Usuário como usuário do gerador de código ou usuário como usuário da aplicação (por exemplo, usuário do LCDproc)?

Só será exportado para o primeiro e não para o segundo. E não exportá-lo para o primeiro significa limitar desnecessariamente os casos de uso do gerador de código.

então você poderia simplesmente expor apenas uma opção (digamos, SpecificationStrategy) e desta opção você derivará as outras 3 opções.

Isso é exatamente o que eu não quero fazer, porque isso não é muito transparente. Também não é à prova de futuro. Se usarmos a especificação para coisas novas, adicionamos mais estratégias? Todas as estratégias antigas têm o mesmo comportamento ou não? Com opções separadas, não precisamos pensar em nada disso. É muito claro o que as 3 opções fazem. O desenvolvedor deve decidir o que é melhor para seu caso de uso específico.

Bom, por favor, crie problemas.

Sinto muito, mas isso não é bom. É catastrófico. Já criei problemas suficientes para saber, eles não serão corrigidos tão cedo, a menos que eu mesmo faça isso ou alguém já esteja trabalhando ativamente na parte quebrada.

Cada tentativa de definir um booleano para algo diferente de "1" ou "0" não é suportada.

Esse é outro problema, porque "não suportado" na verdade significa "você não deve fazer isso, mesmo que funcione" neste caso. Não posso dizer agora como ou quando exatamente, mas definitivamente há casos em que um kdb set -N user ... é bem-sucedido, mesmo que o plugin type falhe.

Eu não acho que as pessoas do LCDproc terão problemas para entender "1" ou "0".

Não, mas eles certamente não vão gostar que a versão usando uma estrutura de configuração real não possa fazer o que o antigo analisador manuscrito minimalista poderia fazer. Eles também não vão gostar que todos os números (mesmo aqueles que não fazem sentido em decimal como IDs de fornecedores USB) tenham que estar em decimal, porque hexnumber está desabilitado, pois seria um plugin a mais.

O feedback não é bom, mas essencial para o sucesso da eletrificação do LCDproc.

Mas o feedback não ajuda para os problemas dentro da própria Elektra.

Não, o único caminho a seguir é reduzir a um conjunto que funcione. Já decidimos excluir a validação

Foi decidido que a validação não é exigida pelo pessoal do LCDproc.

Mas remover a validação e as conversões não é uma boa ideia. Eu sei que @haraldg disse que não se importa com isso. Mas (assumindo que a validação do Elektra realmente funciona) ter uma especificação simplifica muito o código que lê a configuração. Por exemplo, limitando o intervalo de um inteiro na especificação, você pode eliminar os if s verificando o valor depois de lê-lo.

Se desistirmos da validação através da Elektra, terei que colocar todo o código de volta.

Além disso, meu ponto principal foi: Criar um PR no repositório LCDproc e usar uma versão mesclável/mesclada na minha tese significa envolver ainda mais pessoas nessa discussão. E isso significa ainda mais atraso até que eu tenha uma versão com a qual eu possa fazer benchmarks.

Só será exportado para o primeiro e não para o segundo. E não exportá-lo para o primeiro significa limitar desnecessariamente os casos de uso do gerador de código.

Definitivamente não é desnecessariamente: descobrimos que muitas (a maioria?) combinações não fazem sentido. É muito necessário restringir tais configurações.

Isso é exatamente o que eu não quero fazer, porque isso não é muito transparente.

É muito transparente se você documentar as estratégias. E isso será necessário de qualquer maneira.

Também não é à prova de futuro. Se usarmos a especificação para coisas novas, adicionamos mais estratégias?

É improvável que alguém queira uma estratégia adicional no futuro próximo. Harald basicamente já quer todos eles que fazem sentido. É muito mais provável que algumas das estratégias raramente sejam necessárias e tragam dores de cabeça de manutenção.

Todas as estratégias antigas têm o mesmo comportamento ou não? Com opções separadas, não precisamos pensar em nada disso. É muito claro o que as 3 opções fazem. O desenvolvedor deve decidir o que é melhor para seu caso de uso específico.

Então você sugere: vamos passar a responsabilidade para outra pessoa?

Estou completamente feliz com duas estratégias: a que você implementou até agora e outra para deixar Harald feliz (minimizar o tamanho binário).

Eu não estou feliz com o pensamento "ohh, nós somos tão flexíveis, então deve ser culpa dos usuários quando algo está errado".

Sinto muito, mas isso não é bom. É catastrófico. Já criei problemas suficientes para saber, eles não serão corrigidos tão cedo, a menos que eu mesmo faça isso ou alguém já esteja trabalhando ativamente na parte quebrada.

Claro que é bom que você encontre. Ele será corrigido pela pessoa que implementar a validação (ninguém está trabalhando nisso agora). A validação não é uma meta agora. Você pode ter certeza de que é um objetivo de longo prazo para mim.

Esse é outro problema, porque "não suportado" na verdade significa "você não deve fazer isso, mesmo que funcione" neste caso.

Claro, esse é o status quo de quase todos os aplicativos FLOSS por aí. Se você estragar a configuração, pode não funcionar, pode travar, pode formatar o disco rígido. A Elektra está aí para mudar isso, mas um passo de cada vez. Nós pelo menos garantimos que não travamos ou formatamos o disco rígido. Poderíamos fornecer uma mensagem de erro melhor, portanto, informe o bug.

Não, mas eles certamente não vão gostar que a versão usando uma estrutura de configuração real não possa fazer o que o antigo analisador manuscrito minimalista poderia fazer. Eles também não vão gostar que todos os números (mesmo aqueles que não fazem sentido em decimal como IDs de fornecedores USB) tenham que estar em decimal, porque hexnumber está desabilitado, pois seria um plugin a mais.

Esta é a sua opinião, que valorizo ​​muito. E você faz o seu melhor para que tudo funcione muito bem na sua opinião. Mas também estamos interessados ​​nas opiniões do pessoal do LCDproc. No momento, não perguntamos o que eles realmente precisam, o que não é uma boa situação.

Mas o feedback não ajuda para os problemas dentro da própria Elektra.

Claro que ajuda: vamos fazer com que a Elektra evolua. Mas, sem feedback, podemos nos deparar com direções erradas. Especialmente os problemas de validação e transformação são muito específicos do domínio.

Mas (assumindo que a validação do Elektra realmente funciona) ter uma especificação simplifica muito o código que lê a configuração. Por exemplo, limitando o intervalo de um inteiro na especificação, você pode eliminar os ifs verificando o valor depois de lê-lo.

Exatamente!

Se desistirmos da validação através da Elektra, terei que colocar todo o código de volta.

Não, nós simplesmente assumimos que tudo está validado por enquanto para que você possa terminar sua tese. Em trabalhos posteriores, é claro, corrigiremos os problemas.

Além disso, meu ponto principal foi: Criar um PR no repositório LCDproc e usar uma versão mesclável/mesclada na minha tese significa envolver ainda mais pessoas nessa discussão.

Ok, é disso que se trata: não se preocupe com isso. Sua nota em sua tese definitivamente não dependerá se as pessoas do LCDproc gostarem ou não. Gosto do seu trabalho, exceto quando digo que não.

E isso significa ainda mais atraso até que eu tenha uma versão com a qual eu possa fazer benchmarks.

Para sua tese, você pode escolher qualquer versão que desejar. Não será a versão mais recente, isso nunca funciona, então não tente fazer isso. Alguma automação para benchmarking pode ser útil, às vezes é necessário repetir tudo, mesmo sem alterações no código.

descobrimos que muitas (a maioria?) combinações não fazem sentido

1 em cada 4 não é muitos nem a maioria.

É muito necessário restringir tais configurações.

Por quê? Qual é a vantagem de não permitir certas configurações? Se alguém tem um caso de uso, não pensamos em por que proibi-lo?

É muito mais provável que algumas das estratégias raramente sejam necessárias e tragam dores de cabeça de manutenção.

Exatamente por que as estratégias são uma má ideia. Opções separadas são muito mais fáceis de manter, pois cada uma delas afeta muito menos o código. Esse é o princípio básico da "separação de interesses".

e outra para deixar Harald feliz

Então, se o X aparecer e quiser uma versão otimizada para desempenho, adicionamos outra estratégia? E se Y quiser alguma outra versão, adicionamos isso também?

ohh, somos tão flexíveis, então deve ser culpa dos usuários quando algo está errado

Claro que não. Mas sua versão é basicamente "se você quiser, dizemos que funciona; se isso não se encaixa no seu caso de uso, não é nossa culpa". Também não consigo ver como alguma coisa pode estar bagunçada aqui. Existem diferentes pré-requisitos para que as diferentes combinações funcionem, mas esse ainda é o caso se tudo for controlado por uma opção e houver menos possibilidades. Já dissemos que não existe uma solução mágica que simplesmente funcione.

Por quê? Qual é a vantagem de não permitir certas configurações? Se alguém tem um caso de uso, não pensamos em por que proibi-lo?

https://cseweb.ucsd.edu/~tixu/papers/fse15.pdf

Exatamente por que as estratégias são uma má ideia. Opções separadas são muito mais fáceis de manter, pois cada uma delas afeta muito menos o código. Esse é o princípio básico da "separação de interesses".

https://en.wikipedia.org/wiki/Separation_of_concerns

Dica: o termo é sobre código e não dados (de configuração).

Então, se o X aparecer e quiser uma versão otimizada para desempenho, adicionamos outra estratégia? E se Y quiser alguma outra versão, adicionamos isso também?

É assim que o desenvolvimento de software funciona. Você adiciona suporte para outro caso de uso que você originalmente não pensou se alguém precisar dele . Então faz sentido torná-lo configurável para que, para outros, sua configuração antiga ainda funcione (então, neste caso: adicionar outra estratégia). Como você sabe, desenvolvimento de software não é: "vamos adicionar coisas aleatórias caso alguém precise". Infelizmente, no momento não é amplamente conhecido que o mesmo também é verdade para a configuração.

https://cseweb.ucsd.edu/~tixu/papers/fse15.pdf

O artigo fala sobre aplicativos com centenas de opções. Estamos falando da diferença entre ter um parâmetro com 2-3 valores e dois parâmetros com 2 valores cada.

Eu não quero entrar em mais discussão (IMO, o artigo apoia minha versão pelo menos tão bem quanto a sua), então quais estratégias você propõe?

Acho que pelo menos precisamos

  1. Especificação externa, padrão incorporado: permite iniciar sem montagem
  2. Especificação externa, padrões não incorporados: minimiza o tamanho binário

Possivelmente também:

  1. Spec incorporado, padrões incorporados: permite iniciar sem montagem, tudo está contido em um arquivo -> conflitos de versão são mais difíceis

E nesse ponto não importa realmente que haja uma quarta configuração, se isso tornar a documentação mais fácil de entender. No final, o mais importante é que os usuários do gerador de código entendam as opções.

Talvez seja apenas eu, mas se cada opção controlar apenas uma única coisa (uma para especificação, outra para padrões), isso é mais fácil do que uma única opção controlando ambas.

o termo é sobre código e não dados (de configuração).

Sim, e como o gerador de código funciona também é sobre código. Não tem nada a ver com configuração ou dados.

É assim que o desenvolvimento de software funciona. [...]

Trata-se do equilíbrio entre cumprir exatamente os casos de uso que você tem agora e fazer um software que geralmente é útil. A Elektra quer ser útil em geral não para um caso específico (por exemplo, sistema embarcado ou sistemas de servidor). Portanto, devemos ter alguma flexibilidade desde o início. Mais importante, o software deve sempre ser projetado de uma forma à prova de futuro. Muitas limitações e mudanças futuras podem se tornar muito difíceis (por exemplo, o limite da Elektra no número de plugins está tão profundamente arraigado na Elektra que é difícil mudar agora).

O artigo fala sobre aplicativos com centenas de opções.

No total, a Elektra tem centenas de opções.

então que estratégias você propõe?

Estou bem com todas as estratégias:

  • que temos um bom caso de uso para
  • são testados
  • são documentados

No final, o mais importante é que os usuários do gerador de código entendam as opções.

Este não é o único ponto, veja acima. Por exemplo, veja spec, list ou csvstorage. Eles têm apenas opções compreensíveis, mas ainda são dificilmente utilizáveis, pois é muito fácil errar essas opções porque a maioria das opções geralmente não é necessária e depende umas das outras de maneiras surpreendentes.

O limite da Elektra no número de plugins está tão profundamente arraigado na Elektra que é difícil mudar agora

Sim, foi errado limitar o número de plugins.

Sim, habilitar gopts no cmake e recompilar o libelektra corrigiu o segfault. Obrigado pela ajuda. Não teria descoberto isso sozinho facilmente.

Mas remover a validação e as conversões não é uma boa ideia. Eu sei que @haraldg disse que não se importa com isso. Mas (assumindo que a validação do Elektra realmente funciona) ter uma especificação simplifica muito o código que lê a configuração. Por exemplo, limitando o intervalo de um inteiro na especificação, você pode eliminar os ifs verificando o valor depois de lê-lo.

Sim, o número de linhas removidas do LCDproc foi um dos benchmarks que decidimos. No entanto, atualmente o LCDproc é muito inconsistente com a forma como ele verifica ou falha quando há um erro de configuração. Nós não pensamos muito nisso, portanto, não é importante.

Se desistirmos da validação através da Elektra, terei que colocar todo o código de volta.

Não, por favor, não. Como Markus disse: Apenas assuma que o libelektra será corrigido no tempo. Eu não posso mesclar nada antes que ele faça de qualquer maneira.

Além disso, meu ponto principal foi: Criar um PR no repositório LCDproc e usar uma versão mesclável/mesclada na minha tese significa envolver ainda mais pessoas nessa discussão.

Duvido que as pessoas acrescentem muito à discussão, a menos que tenham uma opinião muito forte sobre algo. OTOH Acredito que ajudaria muito para a eventual aceitação da elektra, se as pessoas se sentirem um pouco envolvidas. E obter feedback do tipo "isso nunca funcionará para o meu caso de uso" é algo que realmente queremos obter cedo, se existir.

No entanto, se você acha que o estado atual das coisas é muito embaraçoso para mostrar ao público, esta é sua decisão. Eu respeito isso. Mas ainda espero que os problemas com o número de plugins, etc., afetem principalmente o LCDd e possamos mostrar pelo menos alguns clientes razoavelmente funcionais em breve.

Não precisa ser um PR completo e nem precisa estar no repositório LCDproc (mas por que não?). Mas ter algum ramo do libelektra e algum ramo do seu fork LCDproc, que estão confirmados trabalhando juntos e não serão alterados durante o trabalho de desenvolvimento em andamento, ajudaria muito as partes interessadas.

E isso significa ainda mais atraso até que eu tenha uma versão com a qual eu possa fazer benchmarks.

Por curiosidade: Quais benchmarks você pretende fazer?

Sobre toda essa discussão sobre "qual maneira de apresentar opções de gerador de código aos usuários": acho que essa é uma preocupação muito pequena da interface do usuário, que realmente não é importante. O que quer que você decida, haverá muito menos incômodo para o desenvolvedor casual, então outras coisas como a Elektra inventando seus próprios tipos de dados, por exemplo.

Pessoalmente, eu trataria isso como o gcc trata as opções de otimização: todo o mundo sabe que eles podem escolher entre Og, Os, O2 e O3, mas todas as otimizações ainda são documentadas individualmente e podem ser selecionadas na linha de comando se você realmente quiser . Isto parece um bom compromisso entre usabilidade, especificação precisa de comportamento e estabilidade da interface - ninguém espera que todas as funcionalidades de optimização permaneçam estáveis ​​entre as versões do gcc, mas é claro que todo o mundo confia no significado de Os e assim por diante.

Então, talvez realmente devesse haver todas as opções técnicas, que @kodebach deseja, mas também uma opção de estratégia como @markus2330 acredita ser mais utilizável e seria realmente a interface estável.

Sim, habilitar gopts no cmake e recompilar o libelektra corrigiu o segfault. Obrigado pela ajuda. Não teria descoberto isso sozinho facilmente.

Que bom que isso foi corrigido. Você de alguma forma compilou em um estado impuro ou isso é algo que precisamos corrigir? De qualquer forma, deve segfault, mas informar que alguma biblioteca não está faltando, não é?

Duvido que as pessoas acrescentem muito à discussão, a menos que tenham uma opinião muito forte sobre algo.

Exatamente. E os formatos de configuração geralmente são algo sobre o qual as pessoas têm opiniões muito fortes.

OTOH Acredito que ajudaria muito para a eventual aceitação da elektra, se as pessoas se sentirem um pouco envolvidas.

Eu também acho. Isso também ficou muito claro no e-mail anterior sobre a Elektra para a lista de discussão.

Sobre toda essa discussão sobre "qual maneira de apresentar opções de gerador de código aos usuários": acho que essa é uma preocupação muito pequena da interface do usuário, que realmente não é importante.

Claro que cada opção individual desnecessária não é importante, mas uma vez que você tem centenas, torna-se um grande problema.

O que quer que você decida, haverá muito menos incômodo para o desenvolvedor casual, então outras coisas como a Elektra inventando seus próprios tipos de dados, por exemplo.

Do ponto de vista da teoria dos tipos, a Elektra não inventa nenhum tipo novo. (Em C struct cria novos tipos, mas typedef é apenas um alias para tipos existentes). Praticamente, typedefs podem ser irritantes. Se você quiser, podemos renomear todos os tipos no LCDproc para os tipos C99. (Talvez em um estágio posterior.) Como nas plataformas C99, esperamos sempre escolher os tipos C99 (se não, seria um bug).

Isso parece um bom compromisso entre usabilidade

Se você precisar das opções de baixo nível para brincar com as otimizações, é claro que podemos oferecê-las. Mas um grande aviso: como eu os entendo, eles não apenas ajustam o desempenho, mas também sacrificam a correção ou introduzem um comportamento diferente, algo que a maioria das opções de otimização do gcc não faz.

markus2330 escreve:

Que bom que isso foi corrigido. Você de alguma forma compilou em um estado impuro ou isso é algo que precisamos corrigir?

Eu compilei elektra de um clone git fresco algumas horas antes de @kodebach
escreveu suas instruções. Então, em vez de copiar suas linhas de comando, eu apenas
fez um avião cma...; faça; faça a sequência de instalação.

Parece que o gopts ainda não é construído por padrão. Se isso é intencional ou
um erro é com você.

Praticamente, typedefs podem ser irritantes.

Eles tornam o código mais difícil de ler para usuários casuais de elektra.

Se você quiser, podemos renomear todos os tipos no LCDproc para os tipos C99.

Sim, eu preferiria que typedefs não padrão vazassem no código LCDproc tão pouco
que possível.

Neste caso em particular, o navio provavelmente já partiu, mas
acredito que usar tipos não padrão na API Elektra é uma má escolha
também: apenas confunde as pessoas.

Se você precisar das opções de baixo nível para brincar com as otimizações, é claro que podemos oferecê-las. Mas um grande aviso: como eu os entendo, eles não apenas ajustam o desempenho, mas também sacrificam a correção ou introduzem um comportamento diferente, algo que a maioria das opções de otimização do gcc não faz.

Eu certamente não preciso deles, mas posso achá-los legais para brincar.
No entanto, meu ponto principal foi: Os pontos levantados por você e @kodebach ambos
têm seus méritos, então pode fazer sentido seguir ambos: você obtém seu
interface estável e @kodebach obtém suas opções com
comportamento.

Sim, essas opções de gerador de código resultarão em um comportamento diferente (no
menos em caminhos de erro) é claro. as opções do gcc são apenas um análogo, não
completamente equivalente.

Parece que o gopts ainda não é construído por padrão. Se isso é intencional ou um erro, depende de você.

Ele será incluído por padrão quando não for mais experimental.

Eles tornam o código mais difícil de ler para usuários casuais de elektra.

Sim eu concordo. Está tudo bem para você ter algo como int32_t x = elektraGetLong(... .

usar tipos não padrão na API Elektra também é uma má escolha: apenas confunde as pessoas.

Na API de baixo nível não temos esse problema, pois o usuário da API precisa fazer a conversão.

Na API de notificação interna, na verdade temos: tipos C e uma API para nossos typedefs.

Se mudarmos para os tipos C99, devemos fazer isso de forma consistente tanto na notificação interna quanto em alto nível. Seria um grande esforço, mas poderia ser feito sem quebrar a compatibilidade (já que typedefs são nomes diferentes para o mesmo e, como tal, devem ser compatíveis).

Outra questão é se a API de alto nível (e a notificação interna) é somente C ou também para outras linguagens. Iirc @kodebach também tinha em mente que a API de alto nível deveria ser fácil de escrever ligações.

Está tudo bem para você ter algo como int32_t x = elektraGetLong(... .

Certo. Desde que fique claro à primeira vista o que está acontecendo, tudo bem.

Está tudo bem para você ter algo como int32_t x = elektraGetLong(... .

Certo. Desde que fique claro à primeira vista o que está acontecendo, tudo bem.

Alternar dos tipos kdb_*_t para os tipos C99 deve ser apenas uma série de substituições de regex.

Se mudarmos para os tipos C99, devemos fazer isso de forma consistente tanto na notificação interna quanto em alto nível. [...]

No C99+, a mudança deve ser bastante simples, mas pelo menos partes do Elektra funcionam sem o C99 completo também. É por isso que kdbtypes.h é tão complicado em primeiro lugar.

Se precisarmos do C99+ para toda a Elektra, também devemos passar a usar os tipos C99 IMO.

Outra questão é se a API de alto nível (e a notificação interna) é somente C ou também para outras linguagens. Iirc @kodebach também tinha em mente que a API de alto nível deveria ser fácil de escrever ligações.

Encadernações realmente não desempenham um papel aqui. Outras linguagens têm tipos diferentes de qualquer maneira. Se qualquer coisa, as ligações seriam um ponto a favor do uso de tipos C99, porque eles podem ser mapeados automaticamente para tipos de linguagem padrão.

É por isso que kdbtypes.h é tão complicado em primeiro lugar.

sim.

@kodebach Você tentou se realmente funciona? (Com um compilador que não entende C99 como o VisualC++?)

Se não funcionar, devemos definitivamente nos livrar de todas as complicações e simplesmente usar (apenas) os tipos C99.

No C99+, a mudança deve ser bastante simples, mas pelo menos partes do Elektra funcionam sem o C99 completo também.

Sim, compilar Elektra precisa de C99 por um longo tempo, mas compilar Software usando Elektra (no momento) ainda pode funcionar com pré-C99 (se todos os #ifs realmente funcionarem para o seu sistema).

Do ponto de vista da comunicação, definitivamente devemos começar a dizer que "longo" é um inteiro de 32 bits assinado e parar de apontar para o padrão CORBA (só assusta as pessoas).

Pelo menos internamente, "src/include/kdbtypes.h" faz sentido de qualquer maneira (para verificar a disponibilidade e definir o especificador printf). Mas vejo que os aplicativos não querem usar esse arquivo.

Por falar nisso. Já temos um problema em aberto sobre unificar o sistema de tipos: #1092

Obrigado pela discussão, por favor abra novas questões para futuras discussões.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

e1528532 picture e1528532  ·  4Comentários

mpranj picture mpranj  ·  3Comentários

mpranj picture mpranj  ·  3Comentários

markus2330 picture markus2330  ·  4Comentários

mpranj picture mpranj  ·  3Comentários