Existem alternativas para empilhar + heap + modelo de memória estática?

9

Todos os programas que eu já vi organizam sua memória de dados em uma ou mais pilhas de chamadas (geralmente tamanho fixo, mas às vezes não), pilha e memória estática. Ultimamente, o armazenamento estático local de thread também foi adicionado a isso.

Houve alguma tentativa de organizar o layout da memória de dados de uma maneira radicalmente diferente, por exemplo, sem a pilha de chamadas? Ou organizar a memória de uma maneira diferente que realize a mesma coisa?

ikh
fonte
Depende do que você quer dizer com "pilha". Você pode colocar os quadros da pilha de chamadas no heap (vinculá-los aos ponteiros). Então você não tem uma região de memória linear dedicada para a pilha, mas conceitualmente ainda possui uma pilha de chamadas.
e depende do que você quer dizer com "recentemente". Acho que o armazenamento local de threads é tão antigo quanto os threads. Porém, anteriormente era acessível por meio de chamadas do sistema, enquanto os idiomas mais novos agora dão acesso diretamente a ele.
DXM 29/12
A pilha de chamadas é necessária porque as funções procedurais precisam saber quem as chamou para que possam retornar resultados e continuar a execução. O mecanismo atual se fazer isso é realmente muito barato em termos de ciclos de CPU e com x64, pelo menos, quase todos os argumentos de função são passados através de registos
James
2
Você pode encontrar a publicação de Eric Lippert, Why Have a Stack? de interesse. Seu ponto principal é que uma pilha fornece uma maneira eficiente e simples de rastrear locais de memória. Ele discute uma alternativa em vários posts muito mais antigos, o estilo de passagem de continuação .
30713 Brian

Respostas:

8

Você pode dar um passo atrás e ver de onde e por que esses modelos existentes vêm. Quando um processo é criado, é simplesmente fornecida uma área de armazenamento plana, que é simplesmente indexada de 0 a N. Como essa área de armazenamento (falando de RAM aqui) é suportada por um hardware dedicado e alguns semicondutores sofisticados, ela é bem rápida, mas não é o único desse tipo. Outros dispositivos, como discos rígidos, são essencialmente a mesma coisa, espaço plano endereçável por um índice, mas muitas ordens de magnitude mais lentas.

A razão pela qual "uma pilha" existe é porque seria impraticável para cada aplicativo tentar gerenciar o uso da RAM por si só. No passado, foi exatamente assim que aconteceu, os programadores planejaram com antecedência exatamente para que cada local de RAM seria usado. Como o software ficou mais complexo, alguém disse: não seria bom se eu pudesse ir a alguma caixa preta e dizer "Preciso de 10 bytes, então me dê" e não precisar me preocupar com todos os detalhes complexos de onde e como esses 10 bytes provêm ou como são recuperados. Isso é o que é um monte, realmente não fica mais básico do que isso.

Cada vez que um encadeamento é criado, existem algumas estruturas de dados (e uma pilha), que são adquiridas usando a mesma "operação gimme" que acabei de descrever. Uma pilha usada universalmente porque se encaixa perfeitamente com os quadros de pilha de chamadas de função e sua natureza LIFO. Em teoria, cada chamada de função e variáveis ​​locais poderiam ser alocadas no heap, mas isso seria muito caro, comparado com apenas algumas instruções de montagem necessárias para atualizar o registro do ponteiro de pilha (ESP on x86).

O armazenamento local de encadeamento (TLS) também é construído sobre o heap. Quando um encadeamento é criado, como parte de uma viagem ao heap para alocar memória para estruturas de gerenciamento, um espaço separado para TLS também é alocado a partir do heap.

Portanto, no final, tudo o que você realmente tem é um alocador de memória genérico (ou seja, o heap) e todo o resto é uma forma especializada em cima disso. Em outras palavras, se você estiver disposto a desistir de algum aspecto de "Quero alocar o quanto quiser (ou tão pouco) quanto quiser), mantenha-o pelo tempo que desejar e livre sempre que quiser", você poderá fugir da negociação alocador de heap genérico para outro modelo que ofereça velocidade, mas ao custo de alguma outra limitação.

Pegue a pilha. É incrivelmente rápido quando comparado ao monte, mas as duas compensações são: 1) você não controla quando a memória é liberada; em vez disso, depois que a função terminar, o que quer que você tenha alocado se foi e 2) porque as pilhas geralmente têm tamanho limitado, você deve ter cuidado ao alocar grandes quantidades de dados diretamente na pilha.

Outro tipo de "modelo de memória" é o Virtual Memory Manager (VMM) oferecido por praticamente todos os principais sistemas operacionais por meio de chamadas do sistema. O VMM é muito semelhante ao heap, no sentido de que você pode solicitar qualquer quantidade de memória e mantê-la pelo tempo que desejar. No entanto, a limitação é que você só pode alocar memória em múltiplos de tamanho de página (por exemplo, 4KB), portanto, o uso direto do VMM causaria muita sobrecarga em um aplicativo típico que geralmente aloca de 8 a 24 bytes por vez. De fato, praticamente todas as implementações de heap são criadas no VMM especificamente para propósitos de permitir uma alocação de blocos pequenos muito genérica e não especializada . O heap vai para o VMM sempre que precisar de mais memória e distribui muitos pequenos pedaços dessa memória para o aplicativo.

Se você tiver um aplicativo que precise alocar blocos grandes, considere ir diretamente ao VMM, embora alguns heaps tenham uma instrução if dentro de malloc () e se o tamanho do bloco for maior que algum limite, eles simplesmente acessam o VMM para voce.

Outra forma de alocadores, em vez de usar diretamente heap, seria pools. Um pool é um alocador especializado em que todos os blocos são do mesmo tamanho. Pools (assim como a pilha e o TLS) são criados sobre o heap ou o VMM. Os pools são úteis em locais onde você aloca muitos (milhões) de pequenos objetos de vida curta e do mesmo tamanho. Pense em um serviço de rede processando solicitações recebidas. Cada solicitação de cliente pode resultar na alocação da mesma estrutura de N bytes para lidar com essa solicitação. O problema de usar pools é que cada pool lida apenas com um tamanho de bloco (mas você pode criar vários pools). A vantagem dos pools é que, como todos os objetos têm o mesmo tamanho, ele não requer lógica complexa. Em vez disso, sempre que você precisar de um novo bloco, ele fornecerá o que foi liberado recentemente.

E, por último, lembre-se da coisa que mencionamos no disco rígido. Você pode ter um modelo de memória que se comporte como um sistema de arquivos e duplique a mesma idéia de entradas de diretório e nós-i para permitir a alocação hierárquica de blocos de dados em que cada bloco de dados é endereçado com um caminho. É exatamente isso que o tmpfs faz.

Além das coisas que mencionei, tenho certeza de que existem outros modelos mais especializados, mas no final, já que tudo é baseado em espaço de endereço plano (isto é, até que alguns genuis surjam com algum tipo de espaço esquisito - um $$ não plano ), tudo remonta ao alocador genérico "gimme", que é o VMM ou o heap.

DXM
fonte
1

Os únicos casos em que consigo pensar são em hardware especializado, onde você pode ter tudo em execução em locais fixos na memória. Praticamente tudo no modelo de memória atual é necessário se você deseja programas totalmente flexíveis.

Sem a pilha, você não pode ter variáveis ​​locais, chamar pilhas, etc. Qualquer outra coisa que você escrever para implementar acabará parecendo muito com a pilha.

Memória estática e o heap que você pode potencialmente deixar cair para certos aplicativos, mas novamente você precisará deles de uma forma ou de outra para fazer algo mais avançado.

Então, qualquer coisa que você inventar para substituir um desses três acabará parecendo muito com um desses três no final ...

Para abordá-lo de outro ângulo, o que você pode acrescentar que é novo? Você poderia argumentar que coisas como gráficos / processadores físicos / caches de CPU / etc são um novo local de memória, mas na verdade eles são apenas uma instância separada ou uma maneira de acelerar o acesso aos modelos existentes.

... até que alguém dê um salto conceitual gigante de algum tipo, acho improvável que vejamos grandes mudanças nessa área por um longo tempo ...

Tim B
fonte
4
A maioria das pessoas tende a supor que a maneira atual é a melhor / única e, se for dada uma lousa em branco, copiaria o que já existe. As outras pessoas são as que realmente avançam no progresso tecnológico. Para não dizer que eu conheço pessoalmente quaisquer modelos concorrentes sérios (a menos que você conte computadores quânticos), mas afirmar que qualquer coisa que alguém possa criar teria a mesma aparência do que já existe é essencialmente uma forma de raciocínio circular.
Aaronaught
@Aaronaught: o lado oposto do seu argumento é que outras pessoas gastam toneladas de tempo, dinheiro e energia pensando fora da caixa e, para cada 1000 (talvez muito mais) delas, um pode eventualmente avançar no progresso tecnológico, enquanto o restante não chega a lugar algum . Considerando que o primeiro grupo, que se poderia considerar mais prático, leva estes modelos existentes como está e inova em cima deles :)
DXM
@aaronaught Acho que cobri isso com "até que alguém chegue a um salto conceitual gigante de algum tipo";) Se você tem um modelo alternativo melhor, sinta-se à vontade para sugerir ... se não, é um pouco hipócrita reclamar "algumas pessoas" quando você é um deles :)
Tim B
11
@DXM: Então? Eu disse que todos devemos investir nosso tempo na pesquisa de novos modelos de memória? Eu estava apenas apontando a falha (significativa) na alegação de que uma pessoa só pode inventar coisas que já foram inventadas.
Aaronaught
Uma afirmação que eu nunca fiz ...
Tim B