17 votos

¿A qué velocidad debo esperar PostGIS a direcciones bien formateado geocode?

¿A qué velocidad debo esperar PostGIS para geocodificar bien-formato de direcciones?

He instalado PostgreSQL 9.3.7 y PostGIS 2.1.7, cargado de la nación de datos y todos los estados de los datos, pero se han encontrado geocodificación a ser mucho más lento de lo que yo esperaba. ¿Puedo fijar mis expectativas demasiado altas? Estoy recibiendo un promedio de 3 individuales geocodifica por segundo. Tengo que hacer cerca de 5 millones y no quiero esperar tres semanas para esto.

Esta es una máquina virtual para el procesamiento de gigante R matrices y he instalado esta base de datos en el lado de modo que la configuración puede parece un poco tonto. Si una alteración importante en la configuración de la máquina virtual de ayuda, puede modificar la configuración.

Especificaciones de Hardware

Memoria: 65GB procesadores: 6 lscpu me da esto:

# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                6
On-line CPU(s) list:   0-5
Thread(s) per core:    1
Core(s) per socket:    1
Socket(s):             6
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 58
Stepping:              0
CPU MHz:               2400.000
BogoMIPS:              4800.00
Hypervisor vendor:     VMware
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              30720K
NUMA node0 CPU(s):     0-5

Sistema operativo es centos, uname -rv da esto:

# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015

Postgresql config

> select version()
"PostgreSQL 9.3.7 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit"
> select PostGIS_Full_version()
POSTGIS="2.1.7 r13414" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, released 2012/10/08" LIBXML="2.7.6" LIBJSON="UNKNOWN" TOPOLOGY RASTER"

Basado en las sugerencias anteriores para estos tipos de consultas, he subido shared_buffers en la postgresql.conf de archivo de hasta 1/4 de la memoria RAM disponible y efectiva tamaño de la caché a 1/2 de la RAM:

shared_buffers = 16096MB     
effective_cache_size = 31765MB

He installed_missing_indexes() y (después de la resolución de duplicados se inserta en algunas tablas) no tiene ningún error.

Geocodificación de SQL ejemplo #1 (lote) ~ el tiempo medio es de 2,8/seg.

Estoy siguiendo el ejemplo de http://postgis.net/docs/Geocode.html, lo que me tiene crear una tabla que contiene la dirección de geocodificación y, a continuación, hacer un SQL UPDATE:

UPDATE addresses_to_geocode
              SET  (rating, longitude, latitude,geo) 
              = ( COALESCE((g.geom).rating,-1),
              ST_X((g.geom).geomout)::numeric(8,5), 
              ST_Y((g.geom).geomout)::numeric(8,5),
              geo )
              FROM (SELECT "PatientId" as PatientId
              FROM addresses_to_geocode 
              WHERE "rating" IS NULL ORDER BY PatientId LIMIT 1000) As a
              LEFT JOIN (SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom
              FROM addresses_to_geocode As ag
              WHERE ag.rating IS NULL ORDER BY PatientId LIMIT 1000) As g ON a.PatientId = g.PatientId
              WHERE a.PatientId = addresses_to_geocode."PatientId";

Estoy usando un tamaño de lote de 1000 arriba y lo devuelve en el 337.70 segundos. Es un poco más lento para lotes más pequeños.

Geocodificación de SQL ejemplo #2 (fila por fila) ~ el tiempo medio es de 1.2/seg.

Al profundizar en mis direcciones haciendo el geocodifica de una en una con una declaración de que se parece a esto (por cierto, el ejemplo a continuación tomó 4.14 segundos),

SELECT g.rating, ST_X(g.geomout) As lon, ST_Y(g.geomout) As lat, 
    (addy).address As stno, (addy).streetname As street, 
    (addy).streettypeabbrev As styp, (addy).location As city, 
    (addy).stateabbrev As st,(addy).zip 
FROM geocode('6433 DROMOLAND Cir NW, MASSILLON, OH 44646',1) As g;

es un poco más lento (2.5 x por registro), pero puedo mirar la distribución de los tiempos de consulta y ver que es una minoría de largas consultas que se están desacelerando esta abajo de la mayoría (sólo la primera 2600 de 5 millones de ha de búsqueda veces). Es decir, el 10% superior está tomando un promedio de alrededor de 100 ms, la parte inferior del 10% promedio de 3,69 segundos, mientras que la media es de 754 ms y la mediana es de 340 ms.

# Just some interaction with the data in R
> range(lookupTimes[1:2600])
[1]  0.00 11.54
> median(lookupTimes[1:2600])
[1] 0.34
> mean(lookupTimes[1:2600])
[1] 0.7541808
> mean(sort(lookupTimes[1:2600])[1:260])
[1] 0.09984615
> mean(sort(lookupTimes[1:2600],decreasing=TRUE)[1:260])
[1] 3.691269
> hist(lookupTimes[1:2600]

Geocoding times for first 2600 rows

Otros pensamientos

Si no puedo obtener una orden de magnitud de aumento en el rendimiento, pensé que al menos podría hacer una conjetura acerca de la predicción de lento geocodificar veces, pero no es obvio para mí ¿por qué el más lento direcciones parecen estar tomando mucho más tiempo. Me estoy quedando en la dirección original a través de un custom paso de normalización para asegurarse de que está bien formateado antes de la geocode() función obtiene:

sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")

donde myAddress es [Address], [City], [ST] [Zip] cadena compilado a partir de un usuario de la tabla de direcciones de un no-la base de datos postgresql.

He intentado (no) para instalar el pagc_normalize_address extensión, pero no está claro que esto traerá el tipo de mejora que estoy buscando. Editado para añadir el monitoreo de información como por la sugerencia

Rendimiento

Una CPU está vinculado: [editar, sólo un procesador por consulta, así que tengo 5 no utilizado Cpu]

top - 14:10:26 up 1 day,  3:11,  4 users,  load average: 1.02, 1.01, 0.93
Tasks: 219 total,   2 running, 217 sleeping,   0 stopped,   0 zombie
Cpu(s): 15.4%us,  1.5%sy,  0.0%ni, 83.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:  65056588k total, 64613476k used,   443112k free,    97096k buffers
Swap: 262139900k total,    77164k used, 262062736k free, 62745284k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 3130 postgres  20   0 16.3g 8.8g 8.7g R 99.7 14.2 170:14.06 postmaster
11139 aolsson   20   0 15140 1316  932 R  0.3  0.0   0:07.78 top
11675 aolsson   20   0  135m 1836 1504 S  0.3  0.0   0:00.01 wget
    1 root      20   0 19364 1064  884 S  0.0  0.0   0:01.84 init
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.06 kthreadd

Ejemplo de actividad de disco en la partición de datos, mientras que un proc está vinculada al 100%: [edit: un solo procesador en el uso de esta consulta]

# dstat -tdD dm-3 1
----system---- --dsk/dm-3-
  date/time   | read  writ
12-06 14:06:36|1818k 3632k
12-06 14:06:37|   0     0
12-06 14:06:38|   0     0
12-06 14:06:39|   0     0
12-06 14:06:40|   0    40k
12-06 14:06:41|   0     0
12-06 14:06:42|   0     0
12-06 14:06:43|   0  8192B
12-06 14:06:44|   0  8192B
12-06 14:06:45| 120k   60k
12-06 14:06:46|   0     0
12-06 14:06:47|   0     0
12-06 14:06:48|   0     0
12-06 14:06:49|   0     0
12-06 14:06:50|   0    28k
12-06 14:06:51|   0    96k
12-06 14:06:52|   0     0
12-06 14:06:53|   0     0
12-06 14:06:54|   0     0 ^C

Analizar que SQL

Esto es de EXPLAIN ANALYZE en esa consulta:

"Update on addresses_to_geocode  (cost=1.30..8390.04 rows=1000 width=272) (actual time=363608.219..363608.219 rows=0 loops=1)"
"  ->  Merge Left Join  (cost=1.30..8390.04 rows=1000 width=272) (actual time=110.934..324648.385 rows=1000 loops=1)"
"        Merge Cond: (a.patientid = g.patientid)"
"        ->  Nested Loop  (cost=0.86..8336.82 rows=1000 width=184) (actual time=10.676..34.241 rows=1000 loops=1)"
"              ->  Subquery Scan on a  (cost=0.43..54.32 rows=1000 width=32) (actual time=10.664..18.779 rows=1000 loops=1)"
"                    ->  Limit  (cost=0.43..44.32 rows=1000 width=4) (actual time=10.658..17.478 rows=1000 loops=1)"
"                          ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode addresses_to_geocode_1  (cost=0.43..195279.22 rows=4449758 width=4) (actual time=10.657..17.021 rows=1000 loops=1)"
"                                Filter: (rating IS NULL)"
"                                Rows Removed by Filter: 24110"
"              ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode  (cost=0.43..8.27 rows=1 width=152) (actual time=0.010..0.013 rows=1 loops=1000)"
"                    Index Cond: ("PatientId" = a.patientid)"
"        ->  Materialize  (cost=0.43..18.22 rows=1000 width=96) (actual time=100.233..324594.558 rows=943 loops=1)"
"              ->  Subquery Scan on g  (cost=0.43..15.72 rows=1000 width=96) (actual time=100.230..324593.435 rows=943 loops=1)"
"                    ->  Limit  (cost=0.43..5.72 rows=1000 width=42) (actual time=100.225..324591.603 rows=943 loops=1)"
"                          ->  Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode ag  (cost=0.43..23534259.93 rows=4449758000 width=42) (actual time=100.225..324591.146 rows=943 loops=1)"
"                                Filter: (rating IS NULL)"
"                                Rows Removed by Filter: 24110"
"Total runtime: 363608.316 ms"

Ver mejor avería en el http://explain.depesz.com/s/vogS

7voto

dracodoc Puntos 118

Me he pasado un montón de tiempo experimentando con esto, creo que es mejor publicar por separado, ya que son de diferente ángulo.

Este es realmente un tema complejo, ver más detalles en mi blog acerca de la geocodificación de la instalación del servidor y el script que usa., he aquí algunos breves resúmenes:

Un servidor con sólo 2 Estados de datos es siempre más rápido que un servidor cargado con todos los 50 estados de los datos.

He verificado con la pc de mi casa en diferentes momentos y en dos diferentes Amazon AWS servidor.

Mi capa gratuita de AWS servidor con 2 estados de datos sólo tiene 1G de RAM, pero es coherente a los 43 ~ 59 ms rendimiento de los datos con 1000 registros y 45.000 registros.

He utilizado exactamente el mismo procedimiento de instalación de una 8G de RAM AWS servidor con todos los estados de carga, exactamente la misma secuencia de comandos y de datos, y el rendimiento se redujo a 80 ~ 105 ms.

Mi teoría es que cuando geocoder no puede coincidir con la dirección exactamente, empezó a amplia el rango de búsqueda y omitir alguna parte, como el código postal o ciudad. Es por eso que geocodificar documento se jactan de que se pueden recolonizar dirección equivocada código postal, aunque le costó 3000 ms.

Con sólo 2 estados de carga de datos, el servidor va a tomar mucho menos tiempo en la infructuosa búsqueda o un partido con puntuación muy baja, ya que sólo se pueden buscar en 2 estados.

Traté de limitar mediante la configuración de la restrict_region parámetro para el estado multipolígonos en función de geocodificación, con la esperanza de que se evite la infructuosa búsqueda, ya estoy bastante seguro de que la mayoría de las direcciones han estado correcto. Comparar estos dos versiones:

  select geocode('501 Fairmount DR , Annapolis, MD 20137',1); 
  select geocode('501 Fairmount DR , Annapolis, MD 20137', 1, the_geom) from tiger.state where statefp = '24';

La única diferencia que se hace por la segunda versión es que normalmente si se ejecuta la misma consulta inmediatamente de nuevo será mucho más rápido debido a que los datos relacionados que se almacenan en caché, pero la segunda versión de la movilidad de este efecto.

Por lo que el restrict_region no está funcionando como yo lo deseaba, tal vez sólo se utiliza para filtrar los múltiples golpear resultado, no para limitar los rangos de búsqueda.

Puede afinar su postgre conf un poco.

Los sospechosos usuales de instalar la ausencia de índices, de vacío analizar no hace ninguna diferencia para mí, porque la descarga de secuencia de comandos de haber hecho el mantenimiento necesario ya, a menos que te equivocaste con ellos.

Sin embargo la configuración de postgre conf de acuerdo con este post lo hizo ayudado. Mi escala completa server con 50 estados, fue el haber 320 ms con la configuración por defecto para algunos peor forma de datos, mejora de 185 ms con 2G shared_buffer, 5G de caché, y se fue a 100 ms adelante con la mayoría de los valores ajustados de acuerdo a la publicación.

Esto es más relevante a postgis y su configuración parecía ser similar.

El tamaño de lote de cada commit no importa mucho para mi caso. La geocodificación de documentación utilizado un tamaño de lote 3. He experimentado los valores de 1, 3, 5 hasta 10. Yo no encuentro ninguna diferencia significativa con esto. Con menor tamaño de lote a hacer más compromete y actualizaciones, pero creo que el verdadero cuello de botella no está aquí. En realidad, yo estoy usando el tamaño de lote 1 ahora. Porque siempre hay algo inesperado mal formado dirección hará excepción, pondré todo el lote con el error como ignorados y proceder de las filas restantes. Con el tamaño de lote 1 no necesito proceso de la tabla de la segunda vez geocodificar las posibles buenas registros en el lote marcado como ignorados.

Por supuesto, esto dependerá de cómo su secuencia de comandos por lotes obras. Voy a publicar mi script con más detalles más adelante.

Puedes probar a utilizar normalizar la dirección para filtrar la mala dirección si es adaptarse a su uso. Vi a alguien mencionó en alguna parte, pero yo no estaba seguro de cómo funciona esto ya que la función de normalización sólo funciona en el formato, en realidad no puedo decir que la dirección no es válida.

Más tarde me di cuenta de que si la dirección es obviamente en mal estado y desea omitir ellos, esto podría ayudar. Por ejemplo yo tengo un montón de direcciones falta el nombre de la calle o incluso los nombres de las calles. Normalizar todas las direcciones primera será relativamente rápido, a continuación, puede filtrar la evidente mala dirección para usted, a continuación, saltar en ellos. Sin embargo, esto no la suite de mi uso desde una dirección sin número de la calle o incluso el nombre de la calle todavía podría ser asignado a la calle o a la ciudad, y que la información es útil para mí.

Y la mayoría de las direcciones que no pueden ser geocodificada en mi caso en realidad tiene todos los campos, simplemente no hay ninguna coincidencia en la base de datos. Usted puede filtrar estas direcciones sólo por la normalización de ellos.

EDITAR Para más detalles, ver mi blog acerca de la geocodificación de la instalación del servidor y el script que he usado.

EDIT 2 he terminado de geocodificación de 2 millones de direcciones de e hice un montón de limpiar en direcciones basado en la geocodificación resultado. Con mejor limpiar la entrada, el siguiente trabajo por lotes se ejecuta mucho más rápido. Por lo limpio me refiero a algunas direcciones son obviamente erróneo y debe ser eliminado, o tener contenido inesperado para geocoder para causar problema en la geocodificación. Mi teoría es la siguiente: La eliminación de malas direcciones puede evitar echar a perder la memoria caché, lo que mejora el rendimiento en las buenas direcciones de manera significativa.

Me separé de entrada basado en el estado para asegurarse de que cada puesto de trabajo puede tener todos los datos necesarios para la geocodificación en caché en RAM. Sin embargo, cada mala dirección en el trabajo de hacer el geocoder para buscar en más estados, lo que podría estropear la caché.

2voto

dracodoc Puntos 118
  1. De acuerdo a este hilo de discusión, se supone que para utilizar el mismo procedimiento de normalización para procesar el Tigre de datos y su dirección de entrada. Desde el Tigre de datos han sido procesados con la incorporada en el normalizador, es mejor utilizar sólo normalizador interno. Incluso si conseguiste el pagc_normalizer de trabajo, no puede ayudar a usted si usted no lo utiliza para actualizar el Tigre de datos.

    Dicho esto, creo que de geocodificación() será llamada normalizador de todos modos, así normalizar la dirección antes de geocodificación no puede ser realmente útil. Un posible uso de normalizador se podría comparar la normalizado de la dirección y la dirección devuelta por geocodificación(). Con ambos normalizado, podría ser más fácil de encontrar el mal resultado de geocodificación.

    Si puede filtrar la mala dirección de geocodificación por normalizador, que realmente va a ayudar. Sin embargo no veo normalizador tienen nada parecido a un partido de puntuación o calificación.

  2. El mismo hilo de discusión también se menciona un modificador de depuración en geocode_address para mostrar más información. Nodo geocode_address necesidad normalizado de entrada de dirección.

  3. Geocoder es rápido para coincidencia exacta, pero se tarda mucho más tiempo para los casos difíciles. Allí he encontrado es un parámetro restrict_region y pensó que tal vez se limita la infructuosa búsqueda si puedo configurar el límite estatal, ya que estoy bastante seguro de que el estado va a ser. Resultó ajuste a un mal estado no deje de geocodificación para obtener la dirección correcta, aunque a tomar algún tiempo.

    Así que tal vez geocoder buscará en todos los lugares posibles si la primera búsqueda exacta no tiene partido. Esto hace que sea capaz de procesar la entrada con algunos errores, pero también hacen algunas de búsqueda muy lento.

    Yo creo que es bueno para un servicio interactivo para aceptar la entrada con errores, pero a veces podemos querer renunciar a un pequeño conjunto de dirección incorrecta para tener un mejor rendimiento en la geocodificación por lotes.

1voto

aaryno Puntos 531

Voy a publicar esta respuesta pero espero que otro colaborador de ayudar a descomponer la siguiente, que creo que va a pintar una mayor coherencia de la imagen:

¿Cuál es el impacto del número de estados cargado en la geocodificación? Tengo 50 y estoy viendo a un rendimiento mucho menor que @LR1234567 (es decir, 8x vez por geocode).

¿Cuál es el método más eficaz para granel geocodificación? Estoy ejecutando una serie de procesos, ejecución de lotes de 100 repetidamente hasta que toda la backload está terminado. Multi-hilo enfoque sería preferible, pero ¿qué enfoques se recomienda?

¿Cuál es el impacto de la virtualización en PostgreSQL geocodificación? Estoy suponiendo que el 10% se basa en algunos otros posts, pero tienen poca confianza en que la respuesta

Ahora mi respuesta, que es sólo una anécdota:

La mejor me estoy haciendo (basado en una única conexión) es un promedio de 208 ms por geocode. Esto se mide mediante la selección de direcciones al azar de mis datos, que se extiende a través de Estados Unidos. Incluye algunos datos sucios, pero la de más larga duración geocodes no parecen estar mal de forma obvia.

El quid de la cuestión es que me parece ser la CPU y que una sola consulta está enlazado a un único procesador. Me pueden paralelizar este por tener múltiples conexiones con UPDATE ocurren en la complementaria los segmentos de la addresses_to_geocode tabla en la teoría. Mientras tanto, estoy recibiendo geocode a un promedio de 208 ms de la nación en conjunto de datos. La distribución es sesgada, tanto en términos de dónde la mayoría de mis direcciones y en términos del tiempo que toma (por ejemplo, ver el histograma anterior) y la tabla de abajo.

Mi mejor enfoque hasta ahora es hacerlo en lotes de 10000, con algunos estimable mejoramiento de hacer más por lote. Para lotes de 100 yo estaba recibiendo sobre 251ms, con 10000 estoy recibiendo 208ms.

UPDATE addresses_to_geocode 
SET (rating, longitude, latitude, geo) = 
   (COALESCE((g.geom).rating,-1), 
            ST_X((g.geom).geomout)::numeric(8,5),   
            ST_Y((g.geom).geomout)::numeric(8,5), 
            geo) 
   FROM (
       SELECT "PatientId" as PatientId 
       FROM addresses_to_geocode  
       WHERE "rating" IS NULL 
       ORDER BY PatientId LIMIT 100) As a 
   LEFT JOIN (
       SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom 
       FROM addresses_to_geocode As ag 
       WHERE ag.rating IS NULL 
       ORDER BY PatientId LIMIT 100) As g 
   ON a.PatientId = g.PatientId 
   WHERE a.PatientId = addresses_to_geocode."PatientId";

Tengo que citar los nombres de campo debido a cómo RPostgreSQL crea tablas con dbWriteTable

Que trata de 4x tan rápido como si los hago de un registro a la vez. Cuando hago de uno en uno que puede obtener un desglose por estado (ver más abajo). Hice esto para ver si uno o más de los TIGER estados tenido una mala carga o índice, que me espera como resultado una pobre geocode de rendimiento statewise. He obviamente tiene algunos malos datos (algunas direcciones son incluso direcciones de correo electrónico!), pero la mayoría de ellos están bien formateado. Como he dicho antes, algunos de los de más larga duración de las consultas no tienen evidentes deficiencias en su formato. A continuación hay una tabla con el número mínimo de tiempo de consulta, significa el momento de la consulta, y el máximo tiempo de consulta para los estados de 3000-aleatoria de direcciones a partir de mis datos:

       state   n  min      mean   max
1          .   1 0.00 0.0000000  0.00
12        DC   6 0.07 0.0900000  0.10
9  CHIHUAHUA   1 0.16 0.1600000  0.16
2         00   1 0.18 0.1800000  0.18
6         AR   1 0.37 0.3700000  0.37
27        MT  17 0.14 0.4229412  1.01
14        GA  37 0.22 0.4340541  2.78
10        CO   1 0.54 0.5400000  0.54
16        IL 390 0.16 0.5448974  3.75
8         CA 251 0.17 0.5546614  3.58
5         AL   4 0.13 0.5575000  0.86
18        KS   3 0.43 0.5966667  0.75
23        ME 121 0.14 0.6266116  7.88
35        SC 390 0.14 0.6516923  6.88
24        MI  62 0.12 0.6524194  3.36
40        WA   3 0.23 0.7500000  1.41
32        OK 145 0.17 0.7538621  5.84
20        LA   1 0.76 0.7600000  0.76
31        OH 551 0.00 0.7623775 10.27
17        IN 108 0.19 0.7864815  3.64
43      <NA>  89 0.00 0.8152809  4.98
15        IA   1 0.82 0.8200000  0.82
30        NY 227 0.19 0.8227753 28.47
19        KY   3 0.56 0.8333333  1.36
36        TN 333 0.11 0.8566667  6.45
28        NC 129 0.24 0.8843411  4.07
13        FL  70 0.28 0.9131429  4.65
7         AZ 101 0.20 0.9498020  6.33
34        PA  56 0.14 0.9594643  3.61
29        NJ   1 1.03 1.0300000  1.03
33        OR 101 0.24 1.0966337 14.89
26        MS  28 0.25 1.1503571 11.89
3          9   6 0.58 1.2133333  1.93
4         AK   1 1.25 1.2500000  1.25
22        MD   9 0.50 1.3055556  4.17
25        MO  22 0.31 1.3381818  4.20
42        WY   1 1.38 1.3800000  1.38
38        VA 127 0.20 1.3873228  5.69
37        TX   4 0.53 1.4800000  3.28
21        MA   4 0.47 1.5725000  3.63
11        CT   5 0.38 1.6760000  4.68
39        VT   1 2.25 2.2500000  2.25
41        WI   2 2.27 2.2850000  2.30

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X