Tengo un servidor xmlrpc que usa Twisted. El servidor tiene una gran cantidad de datos almacenados en la memoria. ¿Es posible tener un servidor xmlrpc secundario separado en ejecución que pueda acceder al objeto en memoria en el primer servidor?

Entonces, el servidor A se inicia y crea un objeto. El servidor B se inicia y puede leer desde el objeto en el servidor A.

* EDITAR *

Los datos a compartir son una lista de 1 millón de tuplas.

68
anon 12 ago. 2009 a las 23:33

9 respuestas

La mejor respuesta

Sin una reescritura profunda y oscura del tiempo de ejecución del núcleo de Python (para permitir el forzamiento de un asignador que utiliza un segmento dado de memoria compartida y asegura direcciones compatibles entre procesos dispares) no hay forma de "compartir objetos en la memoria" en ningún sentido general. Esa lista tendrá un millón de direcciones de tuplas, cada tupla compuesta de direcciones de todos sus elementos, y cada una de estas direcciones habrá sido asignada por pymalloc de una manera que inevitablemente varía entre procesos y se extiende por todo el montón.

En casi todos los sistemas, excepto en Windows, es posible generar un subproceso que tenga esencialmente acceso de solo lectura a los objetos en el espacio del proceso principal ... siempre que el proceso principal tampoco altere esos objetos. Esto se obtiene con una llamada a os.fork(), que en la práctica "captura" todo el espacio de memoria del proceso actual e inicia otro proceso simultáneo en la copia / captura. En todos los sistemas operativos modernos, esto es realmente muy rápido gracias a un enfoque de "copia en escritura": las páginas de memoria virtual que no son alteradas por ninguno de los procesos después de la bifurcación no se copian realmente (el acceso a las mismas páginas se comparte) ; tan pronto como cualquiera de los procesos modifica cualquier bit en una página previamente compartida, poof, esa página se copia y la tabla de páginas se modifica, por lo que el proceso de modificación ahora tiene su propia copia mientras el otro proceso todavía ve la original.

Esta forma extremadamente limitada de compartir puede ser un salvavidas en algunos casos (aunque es extremadamente limitada: recuerde, por ejemplo, que agregar una referencia a un objeto compartido cuenta como "alterar" ese objeto, debido a los recuentos de referencia, y así forzará una página copiar!) ... excepto en Windows, por supuesto, donde no está disponible. Con esta única excepción (que no creo que cubra su caso de uso), compartir gráficos de objetos que incluyen referencias / punteros a otros objetos es básicamente inviable, y casi cualquier conjunto de objetos de interés en lenguajes modernos (incluido Python) cae bajo esta clasificación.

En casos extremos (pero suficientemente simples) se puede obtener el intercambio renunciando a la representación de memoria nativa de tales gráficos de objetos. Por ejemplo, una lista de un millón de tuplas, cada una con dieciséis flotadores, en realidad podría representarse como un solo bloque de 128 MB de memoria compartida, todos los flotadores de 16M en representación IEEE de doble precisión colocados de extremo a extremo, con una pequeña cuña para "hacer que parezca", estás abordando las cosas de la manera normal (y, por supuesto, la cuña no tan pequeña también tendría que ocuparse de los problemas de sincronización entre procesos extremadamente peludos que seguramente surgirán ;-). Solo se vuelve más peludo y más complicado a partir de ahí.

Los enfoques modernos de la concurrencia desprecian cada vez más los enfoques de cualquier cosa compartida a favor de los nada compartidos, donde las tareas se comunican mediante el paso de mensajes (incluso en sistemas multinúcleo que utilizan subprocesos y espacios de direcciones compartidos, los problemas de sincronización y el rendimiento llegan al HW incurre en términos de almacenamiento en caché, paradas de canalización, etc., cuando grandes áreas de memoria son modificadas activamente por múltiples núcleos a la vez, están alejando a las personas).

Por ejemplo, el módulo de multiprocesamiento en la biblioteca estándar de Python se basa principalmente en el decapado y el envío de objetos de un lado a otro, no en el intercambio de memoria (¡seguramente no en una forma R / W!).

Me doy cuenta de que esta no es una buena noticia para el OP, pero si necesita poner en funcionamiento múltiples procesadores, será mejor que piense en hacer que todo lo que deben compartir resida en lugares donde se pueda acceder y modificar mediante la transmisión de mensajes. - una base de datos, un clúster de memoria caché, un proceso dedicado que no hace nada más que mantener esos datos en la memoria y enviarlos y recibirlos a pedido, y otras arquitecturas centradas en el paso de mensajes.

123
Alex Martelli 12 ago. 2009 a las 22:20

Puede escribir una biblioteca C para crear y manipular arreglos de memoria compartida para su propósito específico, y luego usar ctypes para acceder a ellos desde Python.

O póngalos en el sistema de archivos en / dev / shm (que es tmpfs). Ahorraría mucho esfuerzo de desarrollo para muy poca sobrecarga de rendimiento: las lecturas / escrituras de un sistema de archivos tmpfs son poco más que una memoria.

4
scavscav 13 ago. 2009 a las 12:06

¿Por qué no simplemente usar una base de datos para los datos compartidos? Tiene una multitud de opciones livianas donde no necesita preocuparse por los problemas de concurrencia: sqlite, cualquiera de los tipos de bases de datos nosql / key-value, etc.

-1
ars 13 ago. 2009 a las 09:03

Si sus datos son simplemente tuplas, y está dispuesto a acceder a ellas como

  • (nrows x tuplewidth) np.ndarray s, o
  • n 1d np.ndarrays

Entonces recomiendo utilizar el contenedor de numpy para memmap.

Mi entendimiento es:

  • guarda sus matrices numpy como un archivo de mapa de memoria plano que contiene el contenido de la matriz sin procesar
  • cada proceso señala un ndarray al archivo memmap como sus datos de respaldo. El enlace de documentación muestra cómo.

Esto funciona muy bien para datos de solo lectura. Si desea leer y escribir, deberá usar los bloqueos multiproceso para proteger el acceso.

Debido a que memmap utiliza la paginación para cargar los datos, es una forma increíblemente rápida de acceder a grandes conjuntos de datos desde el disco. De hecho, no creo que los sistemas operativos modernos tengan más velocidad para cargar datos del disco en la memoria que esto: no se trata de serialización.

0
user48956 23 mar. 2018 a las 17:34

¿Por qué no pegar los datos compartidos en el servidor memcache? entonces ambos servidores pueden acceder a él con bastante facilidad.

2
Anti Veeranna 13 ago. 2009 a las 09:13

Simple en realidad. Solo puede usar la memoria compartida. Este ejemplo crea una lista de tuplas (python) en C ++ y la comparte con un proceso de python que luego puede usar la lista de tuplas. Para usar entre dos procesos de Python, simplemente haga su acceso como ACCESS_WRITE en el proceso del remitente y llame al método write.

C ++ (proceso del remitente):

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#include <string>

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("[(1, 2, 3), ('a', 'b', 'c', 'd', 'e'), (True, False), 'qwerty']");

int _tmain(int argc, _TCHAR* argv[])
{
     HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = CreateFileMapping(
                 INVALID_HANDLE_VALUE,    // use paging file
                 NULL,                    // default security
                 PAGE_READWRITE,          // read/write access
                 0,                       // maximum object size (high-order DWORD)
                 BUF_SIZE,                // maximum object size (low-order DWORD)
                 szName);                 // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not create file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }
   pBuf = (LPTSTR) MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_ALL_ACCESS, // read/write permission
                        0,
                        0,
                        BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

       CloseHandle(hMapFile);
       return 1;
   }

   CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);
    return 0;
}

Python (proceso receptor):

import mmap
shmem = mmap.mmap(0,256,"Global\\MyFileMappingObject",mmap.ACCESS_READ)
msg_bytes = shmem.read()
msg_utf16 = msg_bytes.decode("utf-16")
code = msg_utf16.rstrip('\0')
yourTuple = eval(code)
3
12 may. 2017 a las 19:27

Puede usar el módulo de multiprocesamiento Python.

http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes

2
Unknown 12 ago. 2009 a las 20:20

Puede usar shared_memory en 3.8.

https://docs.python.org/3.8/library/multiprocessing.shared_memory.html#module-multiprocessing.shared_memory

10
ImPerat0R_ 5 nov. 2019 a las 07:30
mmap.mmap(0, 65536, 'GlobalSharedMemory')

Creo que la etiqueta ("GlobalSharedMemory") debe ser la misma para todos los procesos que deseen compartir la misma memoria.

http://docs.python.org/library/mmap.html

17
Joe Koberg 12 ago. 2009 a las 19:36