.. Acá va lo que decidí hacer en base al análisis anterior y sus razones.
- ESTADO: EMPEZADO
+ ESTADO: TERMINADO
.. _solucion:
Solución adoptada
============================================================================
-Como hemos visto en :ref:`dgc_bad`, la mejora del recolector de basura puede
-ser abordada desde múltiples flancos. Por lo tanto, para reducir la cantidad
-de posibilidades hay que tener en cuenta uno de los principales objetivos de
-este trabajo: encontrar una solución que tenga una buena probabilidad de ser
-adoptada por el lenguaje, o alguno de sus compiladores al menos. Para asegurar
-esto, la solución debe tener un alto grado de aceptación en la comunidad, lo
-que implica algunos puntos claves:
+Como hemos visto en :ref:`dgc`, la mejora del recolector de basura puede ser
+abordada desde múltiples flancos, con varias alternativas viables. Por lo
+tanto, para reducir la cantidad de posibilidades hay que tener en cuenta uno
+de los principales objetivos de este trabajo: encontrar una solución que tenga
+una buena probabilidad de ser adoptada por el lenguaje, o alguno de sus
+compiladores al menos. Para asegurar esto, la solución debe tener un alto
+grado de aceptación en la comunidad, lo que implica algunos puntos claves:
* La eficiencia general de la solución no debe ser notablemente peor, en
ningún aspecto, que la implementación actual.
hacerlo sin alejarse demasiado del objetivo principal.
+.. _sol_bench:
+
Banco de pruebas
----------------------------------------------------------------------------
grandes categorías.
+.. _sol_bench_synth:
+
Pruebas sintetizadas
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
El objetivo de estos programas es medir el impacto de las pausas del
recolector. Se espera medir dos tipos de pausa principales, por un lado el
-tiempo máximo de pausa total, que puede involucrar a más de un hilo y por otro
+tiempo máximo de pausa real, que puede involucrar a más de un hilo y por otro
el tiempo de *stop-the-world*, es decir, el tiempo en que los hilos son
efectivamente pausados por el recolector para tomar una *foto* de la pila
y registros para agregarlos al *root set*.
de texto resultantes de partir el texto en palabras. Fue escrito por Leonardo
Maffi y también hallado__ en el grupo de noticias de D_. Su objetivo era
mostrar lo ineficiente que puede ser concatenar datos a un mismo arreglo
-repetidas veces y ha desembocado en una pequeña `optimización`__ que sirvió
-para apalear el problema de forma razonablemente efectiva.
+repetidas veces y ha desembocado en una pequeña optimización que sirvió para
+paliar el problema de forma razonablemente efectiva [PAN09]_.
El código es el siguiente::
}
__ http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=67673
-__ http://d.puremagic.com/issues/show_bug.cgi?id=1923
``rnddata``
}
+.. _sol_bench_small:
+
Programas pequeños
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
para probar el lenguaje de programación Olden__; un lenguaje diseñado para
paralelizar programas automáticamente en arquitecturas con memoria
distribuida. Son programas relativamente pequeños (entre 400 y 1000 líneas de
-código fuente cada uno) que realizan una tarea secuencial que aloca
+código fuente cada uno) que realizan una tarea secuencial que asigna
estructuras de datos dinámicamente. Las estructuras están usualmente
organizadas como listas o árboles, y muy raramente como arreglos. Los
programas pasan la mayor parte del tiempo alocando datos y el resto usando los
__ http://www.martincarlisle.com/olden.html
La traducción a D_ fue realizada por Leonardo Maffi y están basadas a su vez
-en la traducción de este juego de pruebas a Java_, JOlden__ [CMK01]_. En
-general (salvo para el programa ``voronoï``) está disponible el código fuente
-portado a D_, Java_ y Python_, e incluso varias versiones con distintas
+en la traducción de este juego de pruebas a Java_, JOlden__ [CMK01]_. En Java_
+no se recomienda utilizar este conjunto de pruebas para medir la eficiencia
+del recolector de basura, dado que se han creado mejores pruebas para este
+propósito, como DaCapo__ [BLA06]_, sin embargo, dada la falta de programas
+disponibles en general, y de un conjunto de pruebas especialmente diseñado
+para evaluar el recolector de basura en D_, se decide utilizarlas en este
+trabajo de todos modos. Sin embargo sus resultados deben ser interpretados con
+una pizca de sal por lo mencionado anteriormente.
+
+__ http://www-ali.cs.umass.edu/DaCapo/benchmarks.html
+__ http://www.dacapobench.org/
+
+En general (salvo para el programa ``voronoï``) está disponible el código
+fuente portado a D_, Java_ y Python_, e incluso varias versiones con distintas
optimizaciones para reducir el consumo de tiempo y memoria. Además provee
comparaciones de tiempo entre todas ellas. Los programas utilizados en este
banco de pruebas son la versión traducida más literalmente de Java_ a D_, ya
que hace un uso más intensivo del recolector que las otras versiones.
-__ http://www-ali.cs.umass.edu/DaCapo/benchmarks.html
-
A continuación se da una pequeña descripción de cada uno de los 5 programas
traducidos y los enlaces en donde encontrar el código fuente (y las
comparaciones de tiempos estar disponibles).
Código fuente disponible en: http://codepad.org/xGDCS3KO
+.. _sol_bench_real:
+
Programas *reales*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lectura más cercana a la realidad del uso de un recolector.
+.. _sol_mod:
Modificaciones propuestas
----------------------------------------------------------------------------
name: `namec` `namec`* <nombre de la opción>
value: `valuec`* <valor de la opción>
namec: `valuec` - '='
- valuec: [0x01-0xFF] - ':' <cualquiera salvo '\0' y ':'>
+ valuec: [0x01-0xFF] - ':' <cualquier char salvo '\0' y ':'>
Es decir, se compone de una lista de opciones separadas por **:**. Cada opción
se especifica con un nombre, opcionalmente seguido por un valor (separados por
fundamental a la hora de ser mantenido o extendido), se hacen otras pequeñas
mejoras, que se detallan a continuación.
-Remoción de memoria encomendada
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Remoción de memoria *no-encomendada*
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Se elimina la distinción entre memoria *encomendada* y *no-encomendada* (ver
+:ref:`dgc_committed`), pasando a estar *encomendada* toda la memoria
+administrada por el recolector.
+
Si bien a nivel de eficiencia este cambio no tuvo impacto alguno (cuando en un
principio se especuló con que podría dar alguna ganancia en este sentido), se
elimina el concepto de memoria *encomendada* para quitar complejidad al
Esta mejora no afecta a la corrección del algoritmo, ya que a nivel lógico el
recolector solo ve la memoria *encomendada*.
-Micro-optimizaciones
-^^^^^^^^^^^^^^^^^^^^
-Si bien se realizan varias micro-optimizaciones, probablemente la más
-relevante es la inclusión de un caché de tamaño de bloque para el método
-``findSize()`` de un *pool*. Esto acelera considerablemente las operaciones
-que necesitan pedir el tamaño de un bloque reiteradamente, por ejemplo, al
-añadir nuevos elementos a un arreglo dinámico.
+.. _sol_minor_findsize:
+
+Caché de ``Pool.findSize()``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Se crea un caché de tamaño de bloque para el método ``findSize()`` de un
+*pool*. Esto acelera considerablemente las operaciones que necesitan pedir el
+tamaño de un bloque reiteradamente, por ejemplo, al añadir nuevos elementos
+a un arreglo dinámico. En esencia es una extensión a una de las optimizaciones
+propuestas por Vladimir Panteleev [PAN09]_, que propone un caché global para
+todo el recolector en vez de uno por *pool*.
Esta mejora tampoco afecta a la corrección del algoritmo, ya que nuevamente no
afecta su comportamiento a nivel lógico, solo cambia detalles en la
Marcado preciso
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-En paralelo con este trabajo, David Simcha comienza a explorar la posibilidad
-de agregar precisión parcial al recolector, generando información sobre la
-ubicación de los punteros para cada tipo [DBZ3463]_. Su trabajo se limita
-a una implementación a nivel biblioteca de usuario y sobre `D 2.0`_.
-Desafortunadamente su trabajo pasa desapercibido por un buen tiempo.
+Para agregar el soporte de marcado preciso se aprovecha el trabajo realizado
+por Vincent Lang (ver :ref:`dgc_via_art`) [DBZ3463]_, dado que se basa en `D
+1.0`_ y Tango_, al igual que este trabajo. Dado el objetivo y entorno común,
+se abre la posibilidad de adaptar sus cambios a este trabajo, utilizando una
+versión modificada de DMD_ (dado que los cambios aún no son integrados al
+compilador oficial).
-Luego Vincent Lang (mejor conocido como *wm4* en la comunidad de D_), retoma
-este trabajo, pero modificando el compilador DMD_ y trabajando con `D 1.0`_
-y Tango_, al igual que este trabajo. Dado el objetivo y entorno común, se abre
-la posibilidad de adaptar los cambios de Vincent Lang a este trabajo,
-utilizando una versión modificada de DMD_ (dado que los cambios aún no son
-integrados al compilador oficial).
+.. TODO: Apéndice con parches a DMD y Tango?
Información de tipos provista por el compilador
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:aspect: 55
:scale: 110
- /---- "bit de 'end1'"
- |
- | /---- "bit de 'middle'"
- | |
- | "bits de" | "bits de" /---- "bit de 'begin1'"
- | "'ints'" | "'bytes'" |
- |/------------\|/-------------\|
+ /---- "bit de 'end1'" -\
+ | | "Significado"
+ | /---- "bit de 'middle'" | "de bits"
+ | | | "en la"
+ | "bits de" | "bits de" /---- "bit de 'begin1'" | "primera"
+ | "'ints'" | "'bytes'" | | "palabra"
+ |/------------\|/-------------\| -/
V| |V| |V
+----------------------------------+
| 00000000000000000000000000100100 | "Tamaño en cantidad de palabras (36)"
| 00000000000000000000000000001001 | | "su posición"
+----------------------------------+ --/
| |AAAA
- \--------------------------/||||
- "bits de relleno" ||||
- ||||
- "bit de 's'" ||||
- | ||||
- \---------------/||\---- "bit de 'begin2'"
- ||
- /---------------/\---- "bit de 'i'"
- |
- "bit de 'u'"
+ \--------------------------/|||| -\
+ "bits de relleno" |||| |
+ |||| | "Significado"
+ "bit de 's'" |||| | "de bits"
+ | |||| | "en la"
+ \---------------/||\---- "bit de 'begin2'" | "segunda"
+ || | "palabra"
+ /---------------/\---- "bit de 'i'" |
+ | |
+ "bit de 'u'" -/
Si una implementación quisiera mover memoria (ver :ref:`gc_moving`), debería
mantener inmóvil a cualquier objeto que sea apuntado por una palabra de estas
Esto, sin embargo, no significa que la memoria física sea realmente duplicada;
en general todos los sistemas operativos modernos (como Linux_) utilizan una
-técnica llamada *copy-on-write* (*copiar-al-escribir* en castellano) que
-retrasa la copia de memoria hasta que alguno de los dos procesos escribe en un
-segmento. Recién en ese momento el sistema operativo realiza la copia de **ese
-segmento solamente**. Es por esto que la operación puede ser muy eficiente,
-y la copia de memoria es proporcional a la cantidad de cambios que hayan.
+técnica llamada *COW* (de *copy-on-write* en inglés, *copiar-al-escribir* en
+castellano) que retrasa la copia de memoria hasta que alguno de los dos
+procesos escribe en un segmento. Recién en ese momento el sistema operativo
+realiza la copia de **ese segmento solamente**. Es por esto que la operación
+puede ser muy eficiente, y la copia de memoria es proporcional a la cantidad
+de cambios que hayan.
:manpage:`fork(2)` tiene otra propiedad importante de mencionar: detiene todos
los hilos de ejecución en el proceso hijo. Es decir, el proceso hijo se crear
function collect() is
stop_the_world()
- child_pid = fork()
fflush(null) // evita que se duplique la salida de los FILE* abiertos
+ child_pid = fork()
if child_pid is 0 // proceso hijo
mark_phase()
exit(0) // termina el proceso hijo
.. _sol_eager_alloc:
-Creación ansiosa de *pools*
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Creación ansiosa de *pools* (*eager allocation*)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Esta mejora, que puede ser controlada a través de la opción ``eager_alloc``
(ver :ref:`sol_config_spec`), consiste en crear un nuevo *pool* cuando un
pedido de memoria no puede ser satisfecho, justo después de lanzar la
.. _sol_early_collect:
-Recolección temprana
-^^^^^^^^^^^^^^^^^^^^
+Recolección temprana (*early collection*)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Esta mejora, que puede ser controlada a través de la opción ``early_collect``
(ver :ref:`sol_config_spec`), consiste en lanzar una recolección preventiva,
antes de que una petición de memoria falle. El momento en que se lanza la
else if early // Agregado
return //
stop_the_world()
- child_pid = fork()
fflush(null)
+ child_pid = fork()
if child_pid is 0 // proceso hijo
mark_phase()
exit(0)
Resultados
----------------------------------------------------------------------------
-TODO
+Los resultados de las modificación propuestas en la sección anterior (ver
+:ref:`sol_mod`) se evalúan utilizando el conjunto de pruebas mencionado en la
+sección :ref:`sol_bench`).
+
+En esta sección se describe la forma en la que el conjunto de pruebas es
+utilizado, la forma en la que se ejecutan los programas para recolectar dichos
+resultados y las métricas principales utilizadas para analizarlos.
+
+A fines prácticos, y haciendo alusión al nombre utilizado por Tango_, en esta
+sección se utiliza el nombre **TBGC** (acrónimo para el nombre en inglés
+*Tango Basic Garbage Collector*) para hacer referencia al recolector original
+provisto por Tango_ 0.99.9 (que, recordamos, es el punto de partida de este
+trabajo). Por otro lado, y destacando la principal modificación propuesta por
+este trabajo, haremos referencia al recolector resultante de éste utilizando
+el nombre **CDGC** (acrónimo para el nombre en inglés *Concurrent D Garbage
+Collector*).
+
+
+Ejecución del conjunto de pruebas
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Dado el indeterminismo inherente a los sistemas operativos de tiempo
+compartido modernos, se hace un particular esfuerzo por obtener resultados lo
+más estable posible.
+
+Hardware y software utilizado
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Para realizar las pruebas se utiliza el siguiente hardware:
+
+* Procesador Intel(R) Core(TM)2 Quad CPU Q8400 @ 2.66GHz.
+* 2GiB de memoria RAM.
+
+El entorno de software es el siguiente:
+
+* Sistema operativo Debian_ Sid (para arquitectura *amd64*).
+* Linux_ 2.6.35.7.
+* DMD_ 1.063 modificado para proveer información de tipos al recolector (ver
+ :ref:`sol_precise`).
+* *Runtime* Tango_ 0.99.9 modificado para utilizar la información de tipos
+ provista por el compilador modificado.
+* GCC_ 4.4.5.
+* Embedded GNU_ C Library 2.11.2.
+
+Si bien el sistema operativo utiliza arquitectura *amd64*, dado que DMD_
+todavía no soporta 64 bits, se compila y corren los programas de D_ en 32
+bits.
+
+Opciones del compilador
+^^^^^^^^^^^^^^^^^^^^^^^
+Los programas del conjunto de pruebas se compilan utilizando las siguientes
+opciones del compilador DMD_:
+
+``-O``
+ Aplica optimizaciones generales.
+
+``-inline``
+ Aplica la optimización de expansión de funciones. Consiste en sustituir la
+ llamada a función por el cuerpo de la función (en general solo para
+ funciones pequeñas).
+
+``-release``
+ No genera el código para verificar pre y post-condiciones, invariantes de
+ representación, operaciones fuera de los límites de un arreglo y
+ *assert*\ 's en general (ver :ref:`d_dbc`).
+
+Parámetros de los programas
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Los programas de prueba se ejecutan siempre con los mismos parámetros (a menos
+que se especifique lo contrario), que se detallan a continuación.
+
+.. highlight:: none
+
+``conalloc``
+ ``40 4 bible.txt``
+
+ Procesa 40 veces un archivo de texto plano (de 4MiB de tamaño) [#solbible]_
+ utilizando 4 hilos (más el principal).
+
+``concpu``
+ ``40 4 bible.txt``
+
+ Procesa 40 veces un archivo de texto plano (de 4MiB de tamaño) [#solbible]_
+ utilizando 4 hilos (más el principal).
+
+``split``
+ ``bible.txt 2``
+
+ Procesa dos veces un archivo de texto plano (de 4MiB de tamaño)
+ [#solbible]_.
+
+``sbtree``
+ ``16``
+
+ Construyen árboles con profundidad máxima 16.
+
+``bh``
+ ``-b 4000``
+
+ Computa las interacciones gravitatorias entre 4.000 cuerpos.
+
+``bisort``
+ ``-s 2097151``
+
+ Ordena alrededor de 2 millones de números (exactamente :math:`2^21
+ = 2097151`).
+
+``em3d``
+ ``-n 4000 -d 300 -i 74``
+
+ Realiza 74 iteraciones para modelar 4.000 nodos con grado 300.
+
+``tsp``
+ ``-c 1000000``
+
+ Resuelve el problema del viajante a través de una heurística para un
+ millón de ciudades.
+
+``voronoi``
+ ``-n 30000``
+
+ Se construye un diagrama con 30.000 nodos.
+
+``dil``
+ ``ddoc $dst_dir -hl --kandil -version=Tango -version=TangoDoc
+ -version=Posix -version=linux $tango_files``
+
+ Genera la documentación de todo el código fuente de Tango_ 0.99.9, donde
+ ``$dst_dir`` es el directorio donde almacenar los archivos generados
+ y ``$tango_files`` es la lista de archivos fuente de Tango_.
+
+.. highlight:: d
+
+El resto de los programas se ejecutan sin parámetros (ver :ref:`sol_bench`
+para una descripción detallada sobre cada uno).
+
+.. [#solbible] El archivo contiene la Biblia completa, la versión traducida al
+ inglés autorizada por el Rey Jaime o Jacobo (*Authorized King James
+ Version* en inglés). Obtenida de: http://download.o-bible.com:8080/kjv.gz
+
+Recolectores y configuraciones utilizadas
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+En general se presentan resultados para TBGC y varias configuraciones de CDGC,
+de manera de poder tener una mejor noción de que mejoras y problemas puede
+introducir cada una de las modificaciones más importantes.
+
+CDGC se utiliza con siguientes configuraciones:
+
+.. highlight:: none
+
+cons
+ En modo conservativo. Específicamente, utilizando el juego de opciones::
+
+ conservative=1:fork=0:early_collect=0:eager_alloc=0
+
+prec
+ En modo preciso (ver :ref:`sol_precise`). Específicamente, utilizando el
+ juego de opciones::
+
+ conservative=0:fork=0:early_collect=0:eager_alloc=0
+
+fork
+ En modo preciso activando el marcado concurrente (ver :ref:`sol_fork`).
+ Específicamente, utilizando el juego de opciones::
+
+ conservative=0:fork=1:early_collect=0:eager_alloc=0
+
+ecol
+ En modo preciso activando el marcado concurrente con recolección temprana
+ (ver :ref:`sol_early_collect`). Específicamente, utilizando el juego de
+ opciones::
+
+ conservative=0:fork=1:early_collect=1:eager_alloc=0
+
+eall
+ En modo preciso activando el marcado concurrente con creación ansiosa de
+ *pools* (ver :ref:`sol_eager_alloc`). Específicamente, utilizando el juego
+ de opciones::
+
+ conservative=0:fork=1:early_collect=0:eager_alloc=1
+
+todo
+ En modo preciso activando el marcado concurrente con recolección temprana
+ y creación ansiosa de *pools*. Específicamente, utilizando el juego de
+ opciones::
+
+ conservative=0:fork=1:early_collect=1:eager_alloc=1
+
+.. highlight:: d
+
+Métricas utilizadas
+^^^^^^^^^^^^^^^^^^^
+Para analizar los resultados se utilizan varias métricas. Las más importantes
+son:
+
+* Tiempo total de ejecución.
+* Tiempo máximo de *stop-the-world*.
+* Tiempo máximo de pausa real.
+* Cantidad máxima de memoria utilizada.
+* Cantidad total de recolecciones realizadas.
+
+El tiempo total de ejecución es una buena medida del **rendimiento** general
+del recolector, mientras que la cantidad total de recolecciones realizadas
+suele ser una buena medida de su **eficacia** [#soleficacia]_.
+
+Los tiempos máximos de pausa, *stop-the-world* y real, son una buena medida de
+la **latencia** del recolector; el segundo siendo una medida más realista dado
+que es raro que los demás hilos no utilicen servicios del recolector mientras
+hay una recolección en curso. Esta medida es particularmente importante para
+programas que necesiten algún nivel de ejecución en *tiempo-real*.
+
+En general el consumo de tiempo y espacio es un compromiso, cuando se consume
+menos tiempo se necesita más espacio y viceversa. La cantidad máxima de
+memoria utilizada nos da un parámetro de esta relación.
+
+.. [#soleficacia] Esto no es necesariamente cierto para recolectores con
+ particiones (ver :ref:`gc_part`) o incrementales (ver :ref:`gc_inc`), dado
+ que en ese caso podría realizar muchas recolecciones pero cada una muy
+ velozmente.
+
+Métodología de medición
+^^^^^^^^^^^^^^^^^^^^^^^
+Para medir el tiempo total de ejecución se utiliza el comando
+:manpage:`time(1)` con la especificación de formato ``%e``, siendo la medición
+más realista porque incluye el tiempo de carga del ejecutable, inicialización
+del *runtime* de D_ y del recolector.
+
+Todas las demás métricas se obtienen utilizando la salida generada por la
+opción ``collect_stats_file`` (ver :ref:`sol_stats`), por lo que no pueden ser
+medidos para TBGC. Sin embargo se espera que para esos casos los resultados no
+sean muy distintos a CDGC utilizando la configuración **cons** (ver sección
+anterior).
+
+Cabe destacar que las corridas para medir el tiempo total de ejecución no son
+las mismas que al utilizar la opción ``collect_stats_file``; cuando se mide el
+tiempo de ejecución no se utiliza esa opción porque impone un trabajo extra
+importante y perturbaría demasiado la medición del tiempo. Sin embargo, los
+tiempos medidos internamente al utilizar la opción ``collect_stats_file`` son
+muy precisos, dado que se hace un particular esfuerzo para que no se haga un
+trabajo extra mientras se está midiendo el tiempo.
+
+Al obtener el tiempo de *stop-the-world* se ignoran los apariciones del valor
+``-1``, que indica que se solicitó una recolección pero que ya había otra en
+curso, por lo que no se pausan los hilos realmente. Como tiempo de pausa real
+(ver :ref:`sol_fork` para más detalles sobre la diferencia con el tiempo de
+*stop-the-world*) se toma el valor del tiempo que llevó la asignación de
+memoria que disparó la recolección.
+
+Para medir la cantidad de memoria máxima se calcula el valor máximo de la
+sumatoria de: memoria usada, memoria libre, memoria desperdiciada y memoria
+usada por el mismo recolector (es decir, el total de memoria pedida por el
+programa al sistema operativo, aunque no toda este siendo utilizada por el
+*mutator* realmente).
+
+Por último, la cantidad total de recolecciones realizadas se calcula contando
+la cantidad de entradas del archivo generado por ``collect_stats_file``,
+ignorando la cabecera y las filas cuyo valor de tiempo de *stop-the-world* es
+``-1``, debido a que en ese caso no se disparó realmente una recolección dado
+que ya había una en curso.
+
+Además, ciertas pruebas se corren variando la cantidad de procesadores
+utilizados, para medir el impacto de la concurrencia en ambientes con un
+procesador solo y con múltiples procesadores. Para esto se utiliza el comando
+:manpage:`taskset`, que establece la *afinidad* de un proceso, *atándolo*
+a correr en un cierto conjunto de procesadores. Si bien las pruebas se
+realizan utilizando 1, 2, 3 y 4 procesadores, los resultados presentados en
+general se limitan a 1 y 4 procesadores, ya que no se observan diferencias
+sustanciales al utilizar 2 o 3 procesadores con respecto a usar 4 (solamente
+se ven de forma más atenuadas las diferencias entre la utilización de
+1 o 4 procesadores). Dado que de por sí ya son muchos los datos a procesar
+y analizar, agregar más resultados que no aportan información valiosa termina
+resultando contraproducente.
+
+En los casos donde se utilizan otro tipo de métricas para evaluar aspectos
+particulares sobre alguna modificación se describe como se realiza la medición
+donde se utiliza la métrica especial.
+
+Variabilidad de los resultados entre ejecuciones
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Es de esperarse que haya una cierta variación en los resultados entre
+corridas, dada la indeterminación inherente a los sistemas operativos de
+tiempo compartido, que compiten por los recursos de la computadora.
+
+Para minimizar esta variación se utilizan varias herramientas. En primer
+lugar, se corren las pruebas estableciendo máxima prioridad (-19 en Linux_) al
+proceso utilizando el comando :manpage:`nice(1)`. La variación en la
+frecuencia del reloj los procesadores (para ahorrar energía) puede ser otra
+fuente de variación, por lo que se usa el comando :manpage:`cpufreq-set(1)`
+para establecer la máxima frecuencia disponible de manera fija.
+
+Sin embargo, a pesar de tomar estas precauciones, se sigue observando una
+amplia variabilidad entre corridas. Además se observa una variación más
+importante de la esperada no solo en el tiempo, también en el consumo de
+memoria, lo que es más extraño. Esta variación se debe principalmente a que
+Linux_ asigna el espacio de direcciones a los procesos con una componente
+azarosa (por razones de seguridad). Además, por omisión, la llamada al sistema
+:manpage:`mmap(2)` asigna direcciones de memoria altas primero, entregando
+direcciones más bajas en llamadas subsiguientes [LWN90311]_.
+
+El comando :manpage:`setarch(8)` sirve para controlar éste y otros aspectos de
+Linux_. La opción ``-L`` hace que se utilice un esquema de asignación de
+direcciones antiguo, que no tiene una componente aleatoria y asigna primero
+direcciones bajas. La opción ``-R`` solamente desactiva la componente azarosa
+al momento de asignar direcciones.
+
+.. ftable:: t:sol-setarch
+
+ Variación entre corridas para TBGC.
+
+ Variación entre corridas para TBGC. La medición está efectuada utilizando
+ los valores máximo, mínimo y media estadística de 20 corridas, utilizando
+ la siguiente métrica: :math:`\frac{max - min}{\mu}`. La medida podría
+ realizarse utilizando el desvío estándar en vez de la amplitud máxima, pero
+ en este cuadro se quiere ilustrar la variación máxima, no la típica.
+
+ .. subtable::
+
+ Del tiempo total de ejecución.
+
+ ======== ======== ======== ========
+ Programa Normal ``-R`` ``-L``
+ ======== ======== ======== ========
+ bh 0.185 0.004 0.020
+ bigarr 0.012 0.002 0.016
+ bisort 0.006 0.003 0.006
+ conalloc 0.004 0.004 0.004
+ concpu 0.272 0.291 0.256
+ dil 0.198 0.128 0.199
+ em3d 0.006 0.033 0.029
+ mcore 0.009 0.009 0.014
+ rnddata 0.015 0.002 0.011
+ sbtree 0.012 0.002 0.012
+ split 0.025 0.000 0.004
+ tsp 0.071 0.068 0.703
+ voronoi 0.886 0.003 0.006
+ ======== ======== ======== ========
+
+ .. subtable::
+
+ Del consumo máximo de memoria.
+
+ ======== ======== ======== ========
+ Programa Normal ``-R`` ``-L``
+ ======== ======== ======== ========
+ bh 0.001 0.000 0.001
+ bigarr 0.001 0.000 0.001
+ bisort 0.000 0.000 0.000
+ conalloc 0.753 0.000 0.001
+ concpu 0.002 0.000 0.001
+ dil 0.055 0.028 0.013
+ em3d 0.000 0.001 0.001
+ mcore 0.447 0.482 0.460
+ rnddata 0.000 0.000 0.000
+ sbtree 0.000 0.000 0.000
+ split 0.000 0.000 0.000
+ tsp 0.000 0.001 0.000
+ voronoi 0.001 0.000 0.000
+ ======== ======== ======== ========
+
+Ambas opciones, reducen notablemente la variación en los resultados (ver
+cuadro :vref:`t:sol-setarch`). Esto probablemente se debe a la naturaleza
+conservativa del recolector, dado que la probabilidad de tener *falsos
+punteros* depende directamente de los valores de las direcciones de memoria,
+aunque las pruebas en la que hay concurrencia involucrada, se siguen viendo
+grandes variaciones, que probablemente estén vinculadas a problemas de
+sincronización que se ven expuestos gracias al indeterminismo inherente a los
+programas multi-hilo.
+
+Si bien se obtienen resultados más estables utilizando un esquema diferente al
+utilizado por omisión, se decide no hacerlo dado que las mediciones serían
+menos realistas. Los usuarios en general no usan esta opción y se presentaría
+una visión más acotada sobre el comportamiento de los programas. Sin embargo,
+para evaluar el este efecto en los resultados, siempre que sea posible se
+analizan los resultados de un gran número de corridas observando
+principalmente su mínima, media, máxima y desvío estándar.
+
+
+
+Resultados para pruebas sintizadas
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A continuación se presentan los resultados obtenidos para las pruebas
+sintetizadas (ver :ref:`sol_bench_synth`). Se recuerda que este conjunto de
+resultados es útil para analizar ciertos aspectos puntuales de las
+modificaciones propuestas, pero en general distan mucho de como se comporta un
+programa real, por lo que los resultados deben ser analizados teniendo esto
+presente.
+
+``bigarr``
+^^^^^^^^^^
+.. fig:: fig:sol-bigarr-1cpu
+
+ Resultados para ``bigarr`` (utilizando 1 procesador).
+
+ Resultados para ``bigarr`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-bigarr-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-bigarr-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-bigarr-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-bigarr-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-bigarr-1cpu.pdf
+
+.. fig:: fig:sol-bigarr-4cpu
+
+ Resultados para ``bigarr`` (utilizando 4 procesadores).
+
+ Resultados para ``bigarr`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-bigarr-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-bigarr-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-bigarr-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-bigarr-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-bigarr-4cpu.pdf
+
+En la figura :vref:`fig:sol-bigarr-1cpu` se pueden observar los resultados
+para ``bigarr`` al utilizar un solo procesador. En ella se puede notar que el
+tiempo total de ejecución en general aumenta al utilizar CDGC, esto es
+esperable, dado esta prueba se limitan a usar servicios del recolector. Dado
+que esta ejecución utiliza solo un procesador y por lo tanto no se puede sacar
+provecho a la concurrencia, es de esperarse que el trabajo extra realizado por
+las modificaciones se vea reflejado en los resultados. En la
+:vref:`fig:sol-bigarr-4cpu` (resultados al utilizar 4 procesadores) se puede
+observar como al usar solamente *eager allocation* se recupera un poco el
+tiempo de ejecución, probablemente debido al incremento en la concurrencia
+(aunque no se observa el mismo efecto al usar *early collection*).
+
+Observando el tiempo total de ejecución, no se esperaba un incremento tan
+notorio al pasar de TBGC a una configuración equivalente de CDGC **cons**,
+haciendo un breve análisis de las posibles causas, lo más probable parece ser
+el incremento en la complejidad de la fase de marcado dada capacidad para
+marcar de forma precisa (aunque no se use la opción, se paga el precio de la
+complejidad extra y sin obtener los beneficios). Además se puede observar
+como el agregado de precisión al marcado mejora un poco las cosas (donde sí se
+obtiene rédito de la complejidad extra en el marcado).
+
+En general se observa que al usar *eager allocation* el consumo de memoria
+y los tiempos de pausa se disparan mientras que la cantidad de recolecciones
+disminuye drásticamente. Lo que se observa es que el programa es
+más veloz pidiendo memoria que recolectándola, por lo que crece mucho el
+consumo de memoria. Como consecuencia la fase de barrido (que no corre en
+paralelo al *mutator* como la fase de marcado) empieza a ser predominante en
+el tiempo de pausa por ser tan grande la cantidad de memoria a barrer. Este
+efecto se ve tanto al usar 1 como 4 procesadores, aunque el efecto es mucho
+más nocivo al usar 1 debido a la alta variabilidad que impone la competencia
+entre el *mutator* y recolector al correr de forma concurrente.
+
+Sin embargo, el tiempo de *stop-the-world* es siempre considerablemente más
+pequeño al utilizar marcado concurrente en CDGC, incluso cuando se utiliza
+*eager allocation*, aunque en este caso aumenta un poco, también debido al
+incremento en el consumo de memoria, ya que el sistema operativo tiene que
+copiar tablas de memoria más grandes al efectuar el *fork* (ver
+:ref:`sol_fork`).
+
+``concpu``
+^^^^^^^^^^
+.. fig:: fig:sol-concpu-1cpu
+
+ Resultados para ``concpu`` (utilizando 1 procesador).
+
+ Resultados para ``concpu`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-concpu-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-concpu-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-concpu-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-concpu-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-concpu-1cpu.pdf
+
+.. fig:: fig:sol-concpu-4cpu
+
+ Resultados para ``concpu`` (utilizando 4 procesadores).
+
+ Resultados para ``concpu`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-concpu-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-concpu-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-concpu-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-concpu-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-concpu-4cpu.pdf
+
+En la figura :vref:`fig:sol-concpu-1cpu` se pueden observar los resultados
+para ``concpu`` al utilizar un solo procesador. En ella se aprecia que el
+tiempo total de ejecución disminuye levemente al usar marcado concurrente
+mientras no se utilice *eager allocation* pero aumenta al utilizarlo.
+
+Con respecto a la cantidad de recolecciones, uso máximo de memoria y tiempo de
+*stop-the-world* se ve un efecto similar al descripto para ``bigarr`` (aunque
+magnificado), pero sorprendentemente el tiempo total de pausa se dispara,
+además con una variabilidad sorprendente, cuando se usa marcado concurrente
+(pero no *eager allocation*). Una posible explicación podría ser que al
+realizarse el *fork*, el sistema operativo muy probablemente entregue el
+control del único procesador disponible al resto de los hilos que compiten por
+él, por lo que queda mucho tiempo pausado en esa operación aunque realmente no
+esté haciendo trabajo alguno (simplemente no tiene tiempo de procesador para
+correr). Este efecto se cancela al usar *eager allocation* dado que el
+*mutator* nunca se bloquea esperando que el proceso de marcado finalice.
+
+Además se observa una caída importante en la cantidad de recolecciones al
+utilizar marcado concurrente. Esto probablemente se deba a que solo un hilo
+pide memoria (y por lo tanto dispara recolecciones), mientras los demás hilos
+también estén corriendo. Al pausarse todos los hilos por menos tiempo, el
+trabajo se hace más rápido (lo que explica la disminución del tiempo total de
+ejecución) y son necesarias menos recolecciones, por terminar más rápido
+también el hilo que las dispara.
+
+En la :vref:`fig:sol-concpu-4cpu` se pueden ver los resultados al utilizar
+4 procesadores, donde el panorama cambia sustancialmente. El efecto mencionado
+en el párrafo anterior no se observa más (pues el sistema operativo tiene más
+procesadores para asignar a los hilos) pero todos los resultados se vuelven
+más variables. Los tiempos de *stop-the-world* y pausa real (salvo por lo
+recién mencionado) crecen notablemente, al igual que su variación. No se
+encuentra una razón evidente para esto; podría ser un error en la medición
+dado que al utilizar todos los procesadores disponibles del *hardware*,
+cualquier otro proceso que compita por tiempo de procesador puede afectarla
+más fácilmente.
+
+El tiempo total de ejecución crece considerablemente, como se espera, dado que
+el programa aprovecha los múltiples hilos que pueden correr en paralelo en
+procesadores diferentes.
+
+Sin embargo, no se encuentra una razón clara para explicar el crecimiento
+dramático en la cantidad de recolecciones solo al no usar marcado concurrente
+para 4 procesadores.
+
+``conalloc``
+^^^^^^^^^^^^
+.. fig:: fig:sol-conalloc-1cpu
+
+ Resultados para ``conalloc`` (utilizando 1 procesador).
+
+ Resultados para ``conalloc`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-conalloc-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-conalloc-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-conalloc-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-conalloc-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-conalloc-1cpu.pdf
+
+.. fig:: fig:sol-conalloc-4cpu
+
+ Resultados para ``conalloc`` (utilizando 4 procesadores).
+
+ Resultados para ``conalloc`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-conalloc-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-conalloc-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-conalloc-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-conalloc-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-conalloc-4cpu.pdf
+
+En la figura :vref:`fig:sol-conalloc-1cpu` se pueden observar los resultados
+para ``conalloc`` al utilizar un solo procesador. Los cambios con respecto
+a lo observado para ``concpu`` son mínimos. El efecto de la mejoría al usar
+marcado concurrente pero no *eager allocation* no se observa más, dado que
+``conalloc`` pide memoria en todos los hilos, se crea un cuello de botella. Se
+ve claramente como tampoco baja la cantidad de recolecciones hecha debido
+a esto y se invierte la variabilidad entre los tiempos pico de pausa real
+y *stop-the-world* (sin una razón obvia, pero probablemente relacionado que
+todos los hilos piden memoria).
+
+Al utilizar 4 procesadores (figura :vref:`fig:sol-conalloc-4cpu`), más allá de
+las diferencias mencionadas para 1 procesador, no se observan grandes cambios
+con respecto a lo observado para ``concpu``, excepto que los tiempos de pausa
+(real y *stop-the-world*) son notablemente más pequeños, lo que pareciera
+confirmar un error en la medición de ``concpu``.
+
+``split``
+^^^^^^^^^
+.. fig:: fig:sol-split-1cpu
+
+ Resultados para ``split`` (utilizando 1 procesador).
+
+ Resultados para ``split`` (utilizando 1 procesador). Se presenta el mínimos
+ (en negro), la media centrada entre dos desvíos estándar (en gris), y el
+ máximo (en blanco) calculados sobre 50 corridas (para tiempo de ejecución)
+ o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-split-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-split-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-split-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-split-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-split-1cpu.pdf
+
+Este es el primer caso donde se aprecia la sustancial mejora proporcionada por
+una pequeña optimización, el caché de ``findSize()`` (ver
+:ref:`sol_minor_findsize`). En la figura :vref:`fig:sol-split-1cpu` se puede
+observar con claridad como, para cualquier configuración de CDGC, hay una
+caída notable en el tiempo total de ejecución. Sin embargo, a excepción de
+cuando se utiliza *eager allocation*, la cantidad de recolecciones y memoria
+usada permanece igual.
+
+La utilización de *eager allocation* mejora (aunque de forma apenas
+apreciable) el tiempo de ejecución, la cantidad de recolecciones baja a un
+tercio y el tiempo de pausa real cae dramáticamente. Al usar marcado
+concurrente ya se observa una caída determinante en el tiempo de
+*stop-the-world*. Todo esto sin verse afectado el uso máximo de memoria,
+incluso al usar *eager allocation*.
+
+Se omiten los resultados para más de un procesador por ser prácticamente
+idénticos para este análisis.
+
+``mcore``
+^^^^^^^^^
+.. fig:: fig:sol-mcore-1cpu
+
+ Resultados para ``mcore`` (utilizando 1 procesador).
+
+ Resultados para ``mcore`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-mcore-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-mcore-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-mcore-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-mcore-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-mcore-1cpu.pdf
+
+.. fig:: fig:sol-mcore-4cpu
+
+ Resultados para ``mcore`` (utilizando 4 procesadores).
+
+ Resultados para ``mcore`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-mcore-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-mcore-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-mcore-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-mcore-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-mcore-4cpu.pdf
+
+El caso de ``mcore`` es interesante por ser, funcionalmente, una combinación
+entre ``concpu`` y ``split``, con un agregado extra: el incremento notable de
+la competencia por utilizar el recolector entre los múltiples hilos.
+
+Los efectos observados (en la figura :vref:`fig:sol-mcore-1cpu` para
+1 procesador y en la figura :vref:`fig:sol-mcore-4cpu` para 4) confirman esto,
+al ser una suma de los efectos observados para ``concpu`` y ``split``, con el
+agregado de una particularidad extra por la mencionada competencia entre
+hilos. A diferencia de ``concpu`` donde el incremento de procesadores resulta
+en un decremento en el tiempo total de ejecución, en este caso resulta en una
+disminución, dado que se necesita mucha sincronización entre hilos, por
+utilizar todos de forma intensiva los servicios del recolector (y por lo tanto
+competir por su *lock* global).
+
+Otro efecto común observado es que cuando el tiempo de pausa es muy pequeño
+(del orden de los milisegundos), el marcado concurrente suele incrementarlo en
+vez de disminuirlo.
+
+``rnddata``
+^^^^^^^^^^^
+.. fig:: fig:sol-rnddata-1cpu
+
+ Resultados para ``rnddata`` (utilizando 1 procesador).
+
+ Resultados para ``rnddata`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-rnddata-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-rnddata-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-rnddata-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-rnddata-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-rnddata-1cpu.pdf
+
+En la figura :vref:`fig:sol-rnddata-1cpu` se presentan los resultados para
+``rnddata`` utilizando 1 procesador. Una vez más estamos ante un caso en el
+cual se observa claramente la mejoría gracias a una modificación en particular
+principalmente. En esta caso es el marcado preciso. Se puede ver claramente
+como mejora el tiempo de total de ejecución a algo más que la mitad (en
+promedio, aunque se observa una anomalía donde el tiempo baja hasta más de
+3 veces). Sin embargo, a menos que se utilice *eager allocation* o *early
+collection* (que en este caso prueba ser muy efectivo), la cantidad de
+recolecciones aumenta considerablemente.
+
+La explicación puede ser hallada en el consumo de memoria, que baja unas
+3 veces en promedio usando marcado preciso que además hace disminuir
+drásticamente (unas 10 veces) el tiempo de pausa (real y *stop-the-world*). El
+tiempo de *stop-the-world* disminuye unas 10 veces más al usar marcado
+concurrente y el tiempo de pausa real al usar *eager allocation*, pero en este
+caso el consumo de memoria aumenta también bastante (aunque no tanto como
+disminuye el tiempo de pausa, por lo que puede ser un precio que valga la pena
+pagar si se necesitan tiempos de pausa muy pequeños).
+
+El aumento en el variación de los tiempos de ejecución al usar marcado preciso
+probablemente se debe a lo siguiente: con marcado conservativo, debe estar
+sobreviviendo a las recolecciones el total de memoria pedida por el programa,
+debido a falsos punteros (por eso no se observa prácticamente variación en el
+tiempo de ejecución y memoria máxima consumida); al marcar con precisión
+parcial, se logra disminuir mucho la cantidad de falsos punteros, pero el
+*stack* y la memoria estática, se sigue marcado de forma conservativa, por lo
+tanto dependiendo de los valores (aleatorios) generados por la prueba, aumenta
+o disminuye la cantidad de falsos punteros, variando así la cantidad de
+memoria consumida y el tiempo de ejecución.
+
+No se muestran los resultados para más de un procesador por ser demasiado
+similares a los obtenidos utilizando solo uno.
+
+``sbtree``
+^^^^^^^^^^
+Los resultados para ``sbtree`` son tan similares a los obtenidos con
+``bigarr`` que directamente se omiten por completo, dado que no aportan ningún
+tipo de información nueva. Por un lado es esperable, dado que ambas pruebas se
+limitan prácticamente a pedir memoria, la única diferencia es que una pide
+objetos grandes y otra objetos pequeños, pero esta diferencia parece no
+afectar la forma en la que se comportan los cambios introducidos en este
+trabajo.
+
+
+Resultados para pruebas pequeñas
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A continuación se presentan los resultados obtenidos para las pruebas pequeñas
+(ver :ref:`sol_bench_small`). Se recuerda que si bien este conjunto de pruebas
+se compone de programas reales, que efectúan una tarea útil, están diseñados
+para ejercitar la asignación de memoria y que no son recomendados para evaluar
+el desempeño de recolectores de basura. Sin embargo se las utiliza igual por
+falta de programas más realistas, por lo que hay que tomarlas como un grado de
+suspicacia.
+
+``bh``
+^^^^^^
+.. fig:: fig:sol-bh-1cpu
+
+ Resultados para ``bh`` (utilizando 1 procesador).
+
+ Resultados para ``bh`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-bh-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-bh-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-bh-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-bh-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-bh-1cpu.pdf
+
+En la figura :vref:`fig:sol-bh-1cpu` se pueden observar los resultados
+para ``bh`` al utilizar un solo procesador. Ya en una prueba un poco más
+realista se puede observar el efecto positivo del marcado preciso, en especial
+en la cantidad de recolecciones efectuadas (aunque no se traduzca en un menor
+consumo de memoria).
+
+Sin embargo se observa también un efecto nocivo del marcado preciso en el
+consumo de memoria que intuitivamente debería disminuir, pero crece, y de
+forma considerable (unas 3 veces en promedio). La razón de esta particularidad
+es el incremento en el espacio necesario para almacenar objetos debido a que
+el puntero a la información del tipo se guarda al final del bloque (ver
+:ref:`sol_precise`). En el cuadro :vref:`t:sol-prec-mem-bh` se puede observar
+la cantidad de memoria pedida por el programa, la cantidad de memoria
+realmente asignada por el recolector (y la memoria desperdiciada) cuando se
+usa marcado conservativo y preciso. Estos valores fueron tomados usando la
+opción ``malloc_stats_file`` (ver :ref:`sol_stats`).
+
+.. ftable:: t:sol-prec-mem-bh
+
+ Memoria pedida y asignada para ``bh`` según modo de marcado.
+
+ Memoria pedida y asignada para ``bh`` según modo de marcado conservativo
+ o preciso (acumulativo durante toda la vida del programa).
+
+ ============== ============== ============== =================
+ Memoria Pedida (MiB) Asignada (MiB) Desperdicio (MiB)
+ ============== ============== ============== =================
+ Conservativo 302.54 354.56 52.02 (15%)
+ Preciso 302.54 472.26 169.72 (36%)
+ ============== ============== ============== =================
+
+Más allá de esto, los resultados son muy similares a los obtenidos para
+pruebas sintetizadas que se limitan a ejercitar el recolector (como ``bigarr``
+y ``sbtree``), lo que habla de lo mucho que también lo hace este pequeño
+programa.
+
+No se muestran los resultados para más de un procesador por ser extremadamente
+similares a los obtenidos utilizando solo uno.
+
+``bisort``
+^^^^^^^^^^
+.. fig:: fig:sol-bisort-1cpu
+
+ Resultados para ``bisort`` (utilizando 1 procesador).
+
+ Resultados para ``bisort`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-bisort-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-bisort-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-bisort-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-bisort-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-bisort-1cpu.pdf
+
+La figura :vref:`fig:sol-bisort-1cpu` muestra los resultados para ``bisort``
+al utilizar 1 procesador. En este caso el parecido es con los resultados para
+la prueba sintetizada ``split``, con la diferencia que el tiempo de ejecución
+total prácticamente no varía entre TBGC y CDGC, ni entre las diferentes
+configuraciones del último (evidentemente en este caso no se aprovecha el
+caché de ``findSize()``).
+
+Otra diferencia notable es la considerable reducción del tiempo de pausa real
+al utilizar *early collection* (más de 3 veces menor en promedio comparado
+a cuando se marca conservativamente, y más de 2 veces menor que cuando se hace
+de forma precisa), lo que indica que la predicción de cuando se va a necesitar
+una recolección es más efectiva que para ``split``.
+
+No se muestran los resultados para más de un procesador por ser extremadamente
+similares a los obtenidos utilizando solo uno.
+
+``em3d``
+^^^^^^^^
+.. fig:: fig:sol-em3d-1cpu
+
+ Resultados para ``em3d`` (utilizando 1 procesador).
+
+ Resultados para ``em3d`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-em3d-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-em3d-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-em3d-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-em3d-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-em3d-1cpu.pdf
+
+Los resultados para ``em3d`` (figura :vref:`fig:sol-em3d-1cpu`) son
+sorprendentemente similares a los de ``bisort``. La única diferencia es que en
+este caso el marcado preciso y el uso de *early collection** no parecen
+ayudar; por el contrario, aumentan levemente el tiempo de pausa real.
+
+Una vez más no se muestran los resultados para más de un procesador por ser
+extremadamente similares a los obtenidos utilizando solo uno.
+
+``tsp``
+^^^^^^^^
+.. fig:: fig:sol-tsp-1cpu
+
+ Resultados para ``tsp`` (utilizando 1 procesador).
+
+ Resultados para ``tsp`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-tsp-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-tsp-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-tsp-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-tsp-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-tsp-1cpu.pdf
+
+Los resultados para ``tsp`` (figura :vref:`fig:sol-tsp-1cpu`) son
+prácticamente idénticos a los de ``bisort``. La única diferencia es que la
+reducción del tiempo de pausa real es un poco menor.
+
+Esto confirma en cierta medida la poca utilidad de este juego de pruebas para
+medir el rendimiento de un recolector, dado que evidentemente, si bien todas
+resuelven problemas diferentes, realizan todas el mismo tipo de trabajo.
+
+Una vez más no se muestran los resultados para más de un procesador por ser
+extremadamente similares a los obtenidos utilizando solo uno.
+
+``voronoi``
+^^^^^^^^^^^
+.. fig:: fig:sol-voronoi-1cpu
+
+ Resultados para ``voronoi`` (utilizando 1 procesador).
+
+ Resultados para ``voronoi`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-voronoi-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-voronoi-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-voronoi-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-voronoi-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-voronoi-1cpu.pdf
+
+.. fig:: fig:sol-voronoi-4cpu
+
+ Resultados para ``voronoi`` (utilizando 4 procesadores).
+
+ Resultados para ``voronoi`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-voronoi-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-voronoi-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-voronoi-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-voronoi-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-voronoi-4cpu.pdf
+
+En la figura :vref:`fig:sol-voronoi-1cpu` se presentan los resultados para
+``voronoi``, probablemente la prueba más interesante de este conjunto de
+pruebas pequeñas.
+
+Por un lado se puede observar una vez más como baja dramáticamente el tiempo
+total de ejecución cuando se empieza a utilizar CDGC. Ya se ha visto que esto
+es común en programas que se benefician del caché de ``findSize()``, pero en
+este caso no parece provenir toda la ganancia solo de ese cambio, dado que
+para TBGC se ve una variación entre los resultados muy grande que desaparece
+al cambiar a CDGC, esto no puede ser explicado por esa optimización. En
+general la disminución de la variación de los resultados hemos visto que está
+asociada al incremento en la precisión en el marcado, dado que los falsos
+punteros ponen una cuota de aleatoriedad importante. Pero este tampoco parece
+ser el caso, ya que no se observan cambios apreciables al pasar a usar marcado
+preciso.
+
+Lo que se observa en esta oportunidad es un caso patológico de un mal factor
+de ocupación del *heap* (ver :ref:`sol_ocup`). Lo que muy probablemente está
+sucediendo con TBGC es que luego de ejecutar una recolección, se libera muy
+poco espacio, entonces luego de un par de asignaciones, es necesaria una nueva
+recolección. En este caso es donde dificulta la tarea de analizar los
+resultados la falta de métricas para TBGC, dado que no se pueden observar la
+cantidad de recolecciones ni de consumo máximo de memoria. Sin embargo es
+fácil corroborar esta teoría experimentalmente, gracias a la opción
+``min_free``. Utilizando la ``min_free=0`` para emular el comportamiento de
+TBGC (se recuerda que el valor por omisión es ``min_free=5``), se obtiene una
+media de 4 segundos, mucho más parecida a lo obtenido para TBGC.
+
+Otra particularidad de esta prueba es que al utilizar *early collection* el
+tiempo de pausa real aumenta notablemente al usar un procesador, mientras que
+al usar 4 (ver figura :vref:`fig:sol-voronoi-4cpu` disminuye levemente (además
+de otros cambios en el nivel de variación, pero en general las medias no
+cambian).
+
+
+Resultados para pruebas reales
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A continuación se presentan los resultados obtenidos para las pruebas reales
+(ver :ref:`sol_bench_real`). Recordamos que solo se pudo halla un programa que
+pueda ser utilizado a este fin, Dil_, y que el objetivo principal de este
+trabajo se centra alrededor de obtener resultados positivos para este
+programa, por lo que a pesar de ser una única prueba, se le presta particular
+atención.
+
+``dil``
+^^^^^^^
+.. fig:: fig:sol-dil-1cpu
+
+ Resultados para ``dil`` (utilizando 1 procesador).
+
+ Resultados para ``dil`` (utilizando 1 procesador). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-dil-1cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-dil-1cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-dil-1cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-dil-1cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-dil-1cpu.pdf
+
+.. fig:: fig:sol-dil-4cpu
+
+ Resultados para ``dil`` (utilizando 4 procesadores).
+
+ Resultados para ``dil`` (utilizando 4 procesadores). Se presenta el
+ mínimos (en negro), la media centrada entre dos desvíos estándar (en gris),
+ y el máximo (en blanco) calculados sobre 50 corridas (para tiempo de
+ ejecución) o 20 corridas (para el resto).
+
+ .. subfig::
+
+ Tiempo de ejecución (seg)
+
+ .. image:: plots/time-dil-4cpu.pdf
+
+ .. subfig::
+
+ Cantidad de recolecciones
+
+ .. image:: plots/ncol-dil-4cpu.pdf
+
+ .. subfig::
+
+ Uso máximo de memoria (MiB)
+
+ .. image:: plots/mem-dil-4cpu.pdf
+
+ .. subfig::
+
+ *Stop-the-world* máximo (seg)
+
+ .. image:: plots/stw-dil-4cpu.pdf
+
+ .. subfig::
+
+ Pausa real máxima (seg)
+
+ .. image:: plots/pause-dil-4cpu.pdf
+
+En la figura :vref:`fig:sol-dil-1cpu` se presentan los resultados para
+``dil`` al utilizar un procesador. Una vez más vemos una mejoría inmediata del
+tiempo total de ejecución al pasar de TBGC a CDGC, y una vez más se debe
+principalmente al mal factor de ocupación del *heap* de TBGC, dado que
+utilizando CDGC con la opción ``min_free=0`` se obtiene una media del orden de
+los 80 segundos, bastante más alta que el tiempo obtenido para TBGC.
+
+Sin embargo se observa un pequeño incremento del tiempo de ejecución al
+introducir marcado preciso, y un incremento bastante más importante (de
+alrededor del 30%) en el consumo máximo de memoria. Nuevamente, como pasa con
+la prueba ``bh``, el efecto es probablemente producto del incremento en el
+espacio necesario para almacenar objetos debido a que el puntero a la
+información del tipo se guarda al final del bloque (ver :ref:`sol_precise`).
+En el cuadro :vref:`t:sol-prec-mem-dil` se puede observar la diferencia de
+memoria desperdiciada entre el modo conservativo y preciso.
+
+El pequeño incremento en el tiempo total de ejecución podría estar dado por la
+mayor probabilidad de tener *falsos punteros* debido al incremento del tamaño
+del *heap*; se recuerda que el *stack* y memoria estática se siguen marcado de
+forma conservativa, incluso en modo preciso.
+
+.. ftable:: t:sol-prec-mem-dil
+
+ Memoria pedida y asignada para ``dil`` según modo de marcado.
+
+ Memoria pedida y asignada para ``dil`` según modo de marcado conservativo
+ o preciso (acumulativo durante toda la vida del programa).
+
+ ============== ============== ============== =================
+ Memoria Pedida (MiB) Asignada (MiB) Desperdicio (MiB)
+ ============== ============== ============== =================
+ Conservativo 307.48 399.94 92.46 (23%)
+ Preciso 307.48 460.24 152.76 (33%)
+ ============== ============== ============== =================
+
+También se puede observar una gran disminución del tiempo total de ejecución
+(cerca de un 60%, y más de un 200% comparado con TBGC) alrededor de la mitad)
+al empezar a usar *eager allocation*, acompañado como es usual de una baja en
+la cantidad de recolecciones realizadas (esta vez mayor, de más de 3 veces)
+y de una caída drástica del tiempo de pausa real (alrededor de 40 veces más
+pequeño); todo esto con un incremento marginal en el consumo total de memoria
+(aproximadamente un 5%). En este caso el uso de *early collection* apenas
+ayuda a bajar el tiempo de pausa real en un 20% en promedio aproximadamente.
+El tiempo de *stop-the-world* cae dramáticamente al empezar a realizar la fase
+de marcado de manera concurrente; es 200 veces más pequeño.
+
+Al utilizar 4 procesadores (ver figura :vref:`fig:sol-dil-4cpu`), hay algunos
+pequeños cambios. El tiempo total de ejecución es reducido todavía más (un 20%
+que cuando se usa 1 procesador) cuando se utiliza *eager allocation*. Además
+al utilizar *early collection*, hay otra pequeña ganancia de alrededor del
+10%, tanto para el tiempo total de ejecución como para el tiempo de pausa
+real.
+
+
+.. _sol_accept:
+
+Aceptación
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Los avances de este trabajo fueron comunicados regularmente a la comunidad de
+D_ a través de un blog [LMTDGC]_ y del grupo de noticias de D_. Los
+comentarios hechos sobre el primero son en general positivos y denotan una
+buena recepción por parte de la comunidad a las modificaciones propuestas.
+
+Una vez agregado el marcado concurrente se hace un anuncio en el grupo de
+noticias que también muestra buenos comentarios y aceptación, en particular
+por parte de Sean Kelly, encargado de mantener el *runtime* de `D 2.0`_, que
+comienza a trabajar en adaptar el recolector con idea de tal vez incluirlo en
+el futuro [NGA19235]_. Poco después Sean Kelly publica una versión preliminar
+de la adaptación en la lista de correos que coordina el desarrollo del
+*runtime* de `D 2.0`_ [DRT117]_.
+
+También se ha mostrado interés de incluirlo en Tango_, aunque no se han ha
+comenzado aún con la adaptación, pero debería ser trivial dado que este
+trabajo se desarrolla usando Tango_ (y el recolector está basado en el de
+Tango_) [TT1997]_.
.. include:: links.rst