Publi

Ejecutando tareas desde consola simultáneamente con GNU Parallel


Los ordenadores cada día son más potentes. Y una de las formas de poder ejecutar tareas de forma más rápida, es simultanearlas. Por un lado, es lógico que si una tarea está esperando un dato (ese dato puede venir del disco, del usuario por medio del teclado o el ratón, o descargándoselo de Internet), es normal que el ordenador decida ejecutar otras cosas para no perder el tiempo. Y de hecho se ha aprovechado esto para tener varios programas en ejecución, constantemente dando y quitando el acceso al procesador. Aunque, con la evolución en el mundo del hardware, ahora es común tener ordenadores que comprenden varios núcleos de procesador y son capaces de disponer de varios hilos de ejecución simultáneos. Lo que quiere decir, que pueden hacer realmente varias cosas a la vez, por lo que tiene más sentido incluso ejecutar tareas en paralelo.

Ejecución concurrente en la shell

Normalmente, si queremos ejecutar varias tareas al mismo tiempo, o concurrentes desde una terminal Unix. Basta con ejecutar un programa de la siguiente manera:

programa &
19238
… ejecuto más cosas …

Con un ampersand (&) al final. De esta forma, el terminal nos devolverá el control a nosotros como usuarios, permitiéndonos ejecutar otras cosas mientras la tarea que le hemos pedido que ejecute lo hace de fondo (en background). Es conveniente que la tarea no escriba muchas cosas en pantalla, porque si no va a ser un jaleo.
También podemos ejecutarla de forma normal, y en mitad de la ejecución pulsar Control+Z (Representado por ^Z). Control+Z enviará una señal SIGSTOP al programa en cuestión, por lo que la ejecución se detendrá, permitiéndonos ahora ejecutar otras cosas, y luego continuar la tarea anterior tanto en el frente (foreground), escribiendo fg o, de fondo (como hacíamos antes, en background), escribiendo bg.
programa
^Z
[1]+ Stopped   programa
… ejecuto más cosas …
fg
programa
… programa sigue ejecutándose …
^Z
[1]+ Stopped    programa
bg
[1]+ programa &
… puedo ejecutar lo que quiera …

Generalmente, si tenemos un ordenador con varios núcleos y capaz de ejecutar varias tareas al mismo tiempo. El núcleo del sistema operativo se encargará de repartir estas tareas entre los núcleos disponibles para que la ejecución global sea más rápida. Si tenemos 2 tareas que se ejecutan en un solo hilo y nuestro ordenador puede ejecutar dos hilos de forma simultánea, ¡vamos a ejecutar los dos al mismo tiempo y terminarán antes!
Concretamente, si tenemos dos programas:

  • pi : Calcula el número PI con 4000 decimales, y tarda 10 segundos.
  • color : Corrige el color de una imagen jpg enorme, y tarda 14 segundos.

Si ejecutamos los dos programas de forma secuencial (primero uno y luego otro), tardaremos 24 segundos en realizar las dos tareas. Pero si las ejecutamos a la vez, como nuestro procesador tiene dos núcleos y no está haciendo nada más, tardaremos en completar las dos tareas 14 segundos (lo que tarda la más lenta de las dos).

Cuando se nos queda corto…

Cuando hablamos de paralelizar tareas, podemos hacerlo en casi cualquier lenguaje o entorno. Es más, justo con la información de antes podemos aprovechar para lanzar varias tareas. Incluso con algo de programación, establecer algún que otro bloqueo para guardar el orden de las ejecuciones. O incluso para poder ejecutar las tareas de dos en dos, o de tres en tres… aunque tratándose de Bash puede ser un poco duro programar ciertas cosas, sobre todo cuando tenemos prisa.

GNU Parallel nos permite ejecutar tareas de forma simultánea, agrupar tareas, ordenar la salida, e incluso nos evita tener que pelearnos con aquellos conceptos de concurrencia que a más de uno nos han traído de cabeza. Como extras, nos permite jugar con los argumentos de entrada de los programas de forma muy divertida a la par que útil.

Instalación

GNU Parallel suele venir en los repositorios de las distribuciones de GNU/Linux más populares, así que podemos hacer:

sudo apt install parallel

En caso de Debian/Ubuntu y derivados

dnf install parallel

Si estamos en Fedora.

pacman -S parallel

El programa no tiene muchas dependencias y es totalmente multiplataforma, ya que está escrito en Perl. Podremos ejecutarlo en nuestro ordenador, un servidor, un móvil, una Raspberry PI o cualquier cacharro que corra GNU/Linux.

Es importante saber la versión que tenemos instalada. Podemos averiguarlo con:

parallel --version
GNU parallel 20161222

Aunque casi todos los ejemplos del post corren con versiones anteriores, para algunas cosas necesitaremos como mínimo la versión 20161222. Si tu distribución no la tiene por defecto, siempre podemos descargarlo de la página oficial.

Calcular el número PI

Un programa que suelo utilizar para este tipo de ejemplos es el cálculo del número Pi. Sobre todo porque es una tarea que tarda un rato y utiliza intensamente el procesador. Sólo necesitamos tener bc instalado. El script es muy sencillo y lo he llamado pi:

1
2
#!/bin/bash
echo "scale=4000; a(1)*4" | bc -l

Ahora debemos dar permiso de ejecución a dicho archivo:

chmod +x pi

Y podemos hacer una prueba sin concurrencia ni nada para ver lo que tarda en ejecutarse en nuestro ordenador:
time ./pi
3.141592653589793238462643383279502884197169399375105820974944592307\
….
66983895228684783123552658213144957685726243344189303968642624341077\
3226978028073189154411010446823252716201052652272111660396
real 0m10.800s
user 0m10.792s
sys 0m0.008s

Si quieres seguir esta guía y ves que la ejecución de pi tarda mucho, puedes bajar el número de dígitos (donde pone scale=4000, puedes bajar el número). El objetivo es no eternizarnos, pero darnos cuenta de cómo se está ejecutando todo.

Varias ejecuciones de un programa

GNU Parallel está diseñado para pasar argumentos de forma variable a cada una de las instancias que ejecutemos del programa. Siempre tendremos que pasar argumentos al programa a ejecutar aunque, en este caso, solo queremos ejecutar varias veces pi, sin argumentos. Una forma fácil es generar una secuencia de números del 1 al total de veces que queremos ejecutar el programa, lo podemos hacer con seq (por ejemplo con 10):

seq 10
1
2
3
4
5
6
7
8
9
10

Una vez que tenemos esta entrada, se la podemos pasar a GNU Parallel. En este caso, le diremos que a pi no le pase argumentos (-n0):
seq 10 | parallel -n0 ./pi

Esta ejecución tardará un tiempo. Eso sí, mientras se ejecuta, estaría bien que investigáramos un poco qué está haciendo el programa. En mi ordenador, puedo ejecutar hasta 8 hilos de forma simultánea. Eso quiere decir que, como pi es una tarea que tira mucho de CPU, ejecutar las 10 instancias del programa al mismo tiempo no haría que la ejecución global fuera más rápido que una ejecución secuencial puesto que dos procesos se pelearían por utilizar la CPU y el sistema operativo tendría que dar y quitar el paso a CPU de los procesos. La situación se agravaría si en lugar de 10 ejecuciones hiciéramos 100 o más, incluso podríamos tener otros problemas si intentamos ejecutar una tarea muy complicada demasiadas veces al mismo tiempo.

Podemos ver qué está haciendo la CPU en cada momento ejecutando top, un gestor de tareas o un monitor de CPU.
Uso de CPU
Lo que quiero que observemos es el número de procesos que estamos utilizando en cada momento. En este caso, la ejecución se hará de 8 en 8 porque GNU Parallel ha detectado que mi sistema puede correr 8 hilos simultáneamente. Podemos comprobarlo si mientras ejecutamos el comando anterior, en otro terminal ejecutamos:

pidof bc | wc -w
8

Veremos ese 8 todo el tiempo. Ya que parallel controla ese número de ejecuciones.

Alternativamente, podemos especificar nosotros cuántas ejecuciones simultáneas tendrá el programa con el argumento -j. De la siguiente manera:

seq 10 | parallel -j2 -n0 ./pi

En este caso, ejecutaremos las tareas de 2 en 2. Podemos especificar el número de tareas simultáneas que necesitemos. Incluso puede exceder el número de núcleos o hilos disponibles en nuestro sistema. El objetivo es que da igual lo que dure la ejecución de pi, mantendremos el número de ejecuciones que le digamos a parallel hasta que ya no haya más instancias por lanzar.

Cambiando los argumentos de entrada

La gracia de ejecutar tareas en paralelo es que podamos variar los argumentos de entrada de dicho programa. Es más, un programa podrá tener argumentos fijos y argumentos variables. Esto lo podemos poner en práctica ejecutando echo de forma concurrente con diferentes parámetros. Vale, ejecutar echo no es ninguna tarea intensiva, pero nos vale para ver cómo se están produciendo las ejecuciones y algunas cosas curiosas más.

Argumentos desde teclado (entrada estándar)

Una forma muy sencilla de pasar argumentos de entrada a parallel es de forma interactiva, escribiéndolos por teclado y pulsando Control+D al finalizar:

parallel echo
parallel: Warning: Input is read from the terminal. Only experts do this on purpose. Press CTRL-D to exit.
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería

Argumentos desde pipe

Estos argumentos podemos pasarlos desde una pipe o desde un archivo, pero tiene que haber uno por línea. Podemos introducir las provincias en un archivo o hacer lo siguiente (con tr estamos convirtiendo los espacios en retornos de carro), además, estamos introduciendo un argumento fijo «Saludos desde»:

echo «Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería» | tr ‘ ‘ ‘\n’ | parallel echo «Saludos desde»
Saludos desde Jaén
Saludos desde Córdoba
Saludos desde Sevilla
Saludos desde Huelva
Saludos desde Cádiz
Saludos desde Málaga
Saludos desde Granada
Saludos desde Almería

Argumentos desde pipe en orden

Pero claro, no todos los programas aceptan los argumentos en el mismo lugar, imaginemos que queremos decir algo más tras el nombre de la provincia andaluza, para ello tendremos que introducir alguna cosa más:

echo «Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería» | tr ‘ ‘ ‘\n’ | parallel echo «Saludos desde» {}», una bonita provincia andaluza.»
Saludos desde Jaén, una bonita provincia andaluza.
Saludos desde Córdoba, una bonita provincia andaluza.
Saludos desde Sevilla, una bonita provincia andaluza.
Saludos desde Huelva, una bonita provincia andaluza.
Saludos desde Cádiz, una bonita provincia andaluza.
Saludos desde Málaga, una bonita provincia andaluza.
Saludos desde Granada, una bonita provincia andaluza.
Saludos desde Almería, una bonita provincia andaluza.

Varios argumentos variables por ejecución

Ahora, vamos a crear un archivo, en el que introduciremos un número (el número de letras que tiene la provincia) y la provincia, de forma alternativa en cada línea. Lo llamaremos andalucia.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4
Jaén
7
Córdoba
7
Sevilla
6
Huelva
5
Cádiz
6
Málaga
7
Granada
7
Almería

Con este archivo podríamos hacer lo siguiente:

cat andalucia.txt | parallel -n 2 echo «Dame {1} letras para {2}»
Dame 4 letras para Jaén
Dame 7 letras para Córdoba
Dame 7 letras para Sevilla
Dame 6 letras para Huelva
Dame 5 letras para Cádiz
Dame 6 letras para Málaga
Dame 7 letras para Granada
Dame 7 letras para Almería

Con -n2 estamos indicando que echo llevará dos argumentos, por lo que cada ejecución de echo tomará dos líneas del fichero como argumentos. Con {1} y {2} indicamos el número de parámetro que estamos colocando. También podríamos hacer lo siguiente:
cat andalucia.txt | parallel -n 2 echo «{2} tiene {1} letras.»
Jaén tiene 4 letras.
Córdoba tiene 7 letras.
Sevilla tiene 7 letras.
Huelva tiene 6 letras.
Cádiz tiene 5 letras.
Málaga tiene 6 letras.
Granada tiene 7 letras.
Almería tiene 7 letras.

Argumentos de parallel como argumentos del programa

Otra forma de incluir argumentos variables es la siguiente:

parallel echo ::: Ávila Burgos León Palencia Salamanca Segovia Soria Valladolid Zamora
Ávila
Burgos
León
Palencia
Salamanca
Segovia
Soria
Valladolid
Zamora

Argumentos y combinaciones

Cuando tenemos que lanzar muchas tareas, a veces es útil que los argumentos hagan combinaciones de parámetros. De esta manera no tendremos que generar un fichero que reúna las combinaciones y luego pasárselo a Parallel. Por ejemplo, si queremos hacer una operación lógica AND, podemos hacer lo siguiente:

parallel ‘echo -n {1} \&\& {2} = ; (({1}&&{2})) && echo 1 || echo 0’ ::: 0 1 ::: 0 1
0 && 0 =0
0 && 1 =0
1 && 0 =0
1 && 1 =1

O, mucho más sencillo, si queremos todas las combinaciones que podemos hacer con 3 bits, podemos hacer lo siguiente:

parallel echo ::: 0 1 ::: 0 1 ::: 0 1
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

Un paso más, ahora queremos averiguar con bc su valor en decimal:

parallel echo -n {1}{2}{3} = \;’echo «ibase=2;{1}{2}{3}» | bc’ ::: 0 1 ::: 0 1 ::: 0 1
000 =0
001 =1
010 =2
011 =3
100 =4
101 =5
110 =6
111 =7

Datos desde un fichero separado por columnas

Podemos utilizar un archivo cuyo contenido pueda ser determinado por columnas, como un CSV (Comma Separated Values) o TSV (Tab Separated Values). Podríamos coger el archivo de provincias y hacer lo siguiente (andalucia2.csv):

1
2
3
4
5
6
7
8
4,Jaén,jiennense
7,Córdoba,cordobés
7,Sevilla,sevillano
6,Huelva,onubense
5,Cádiz,gaditano
6,Málaga,malagueño
7,Granada,granadino
7,Almería,almeriense

En este caso descartaremos el primer valor. No tenemos por qué utilizarlo si no queremos.

parallel --colsep ‘,’ echo «Un habitante de {2} se llama {3}» :::: andalucia2.csv
Un habitante de Jaén se llama jiennense
Un habitante de Córdoba se llama cordobés
Un habitante de Sevilla se llama sevillano
Un habitante de Huelva se llama onubense
Un habitante de Cádiz se llama gaditano
Un habitante de Málaga se llama malagueño
Un habitante de Granada se llama granadino
Un habitante de Almería se llama almeriense

El orden de ejecución

Como se lanzan varios procesos a la vez y no todos tienen por qué tardar lo mismo. El orden en el que vemos los resultados puede variar. Lo podemos comprobar de la siguiente forma:

parallel sleep {}\; echo {} ::: 4 8 2 6
2
4
6
8

De esta forma hemos ejecutado 4 procesos que generarán esperas de 4, 8, 2 y 6 segundos, eso sí, la salida se irá generando por el orden en el que los procesos lleguen al echo. Aunque el orden de salida no es el orden de los argumentos que le hemos pasado. Es más, si queremos, podemos hacerlo más enrevesado:
parallel -j2 sleep {}\; echo {} ::: 4 8 2 6
4
2
8
6

En este caso ejecutamos dos hilos de forma simultánea, por lo que se lanzan los procesos que tardan 4 y 8 segundos a la vez. El de 4 segundos termina, y se lanza el de 2 (ahora están trabajando el proceso que tarda 8 (que va por su cuarto segundo) y el que tarda 2. Lógicamente acaba primero el que tarda 2 segundos y se lanza el de 6, pero al de 8 le quedan 2 segundos, por lo que acaba antes que el de 6.
El caso es que puedo forzar a parallel para que muestre la salida en el mismo orden en el que le paso los argumentos de entrada con el argumento -k:
parallel -k sleep {}\; echo {} ::: 4 8 2 6
4
8
2
6

Da igual qué hilos esté lanzando y cómo termine, la salida generada me la mostrará parallel en el orden que yo le he dicho, lo cual es útil para nuestros scripts si necesitamos controlar la salida generada.

Ejecutando funciones de Bash

Como hemos visto parallel nos permite ejecutar echo, palabra reservada de Bash, pero también es posible crear nuestras propias funciones y ejecutarlas. De esta forma, no solo podemos ejecutar un programa, podremos crear funciones y ejecutarlas también. Solo tenemos que exportarlas:

1
2
3
4
5
6
7
8
function distancia() {
  local ORIGEN="$(echo -n $1 | jq -s -R -r @uri)";
  local DESTINO="$(echo -n $2 | jq -s -R -r @uri)";
  curl -s  "http://maps.googleapis.com/maps/api/directions/json?origin=${ORIGEN}&destination=${DESTINO}&sensor=false" 2>/dev/null | tee "${ORIGEN}_${DESTINO}.txt" | jq '.routes[0].legs[0].distance.text';
}

export -f distancia
parallel echo -n "De {1} a {2} =\> " \; distancia {1} {2} ::: "La Coruna" "Lugo" "Orense" "Pontevedra" ::: "Alicante" "Castellon" "Valencia"

Con este ejemplo sacaremos muchas distancias entre ciudades con la ayuda de la API de mapas de Google. Es un ejemplo donde podemos ver que cada petición tarda un tiempo diferente, los resultados salen en orden de llegada.

Reemplazos de argumentos

Parallel permite no solo pasar los argumentos a los programas que ejecuta tal cual, sino que podemos hacer alguna operación de reemplazo por si no queremos el parámetro tal cual nos lo pasan. Algunos parámetros comunes pueden ser nombres de archivo (a los que podremos quitarle rutas o extensiones), cadenas de caracteres (que podremos recortar), o incluso extraer información de las tareas. Incluso podremos cambiar las cadenas si nos resulta incómodo utilizar estos caracteres en nuestros scripts. Esto podría ser muy extenso, así que voy a extraer algunos ejemplos rápidos:

Convertir imágenes de JPG a PNG

Con esta orden podremos buscar todas las imágenes jpg que hay en el directorio actual y convertirlas a png. Eso sí, utilizando toda la potencia de nuestro ordenador, iniciando tantos procesos convert simultáneos como núcleos tenga nuestra CPU. Podemos ver que {} muestra el argumento tal cual, pero {/.} extrae la ruta y la extensión. Si queremos investigar, {/} solo extrae la ruta y {.} solo extrae la extensión. En este caso guardamos las imágenes convertidas en el directorio convertidas.

find -maxdepth 1 -name ‘*.jpg’ | parallel convert -verbose {} convertidas/{/.}.png

Slots y tareas

Podemos saber qué número de tarea estamos ejecutando en cada momento. Es muy útil si por ejemplo cada tarea tiene que repartirse un tamaño muy grande y estas deben saber qué trozo de tarea les corresponde. Pensad en una imagen, divididla en cuadros numerados secuencialmente y cada tarea que lancemos debe atacar a uno de los cuadros. En este caso cada una de las tareas debe saber a qué cuadro tienen que ir. Por otro lado, los slots son grupos, como si fueran «cajas» en las que se ejecutan las tareas. Es decir, si vamos a ejecutar 3 tareas simultáneamente, tendremos 3 cajas y una tarea en ejecución ocupará una caja. De tal forma que nunca tendremos dos trabajos corriendo al mismo tiempo en el mismo slot. Podemos ver cómo funciona esto de los slots así:

parallel -j2 sleep {} \; echo {%} {#} --- {} ::: 5 2 1 3 4
2 2 --- 2
2 3 --- 1
1 1 --- 5
2 4 --- 3
1 5 --- 4

Reemplazo con funciones

Parallel también soporta funciones (casi todas las funciones están disponibles a partir de la versión 20161222. Por ejemplo podemos decirle a una tarea el número total de tareas que vamos a ejecutar. Aunque {= ‘$_=total_jobs($_)’ =} podemos sustituirlo por {##}:

seq 100 | parallel echo Job {#} of {= ‘$_=total_jobs($_)’ =}

Aunque algo muy útil es la implementación de condiciones dentro de la propia ejecución, de la siguiente manera:

seq 50 | parallel echo Hola Mundo {%} ‘> {= $_= $job->seq() % 2 == 0 ? «/dev/tty» : «/dev/null» =}’

Con esto le decimos que la salida se produzca por /dev/tty si el número de la tarea (job->seq()) es par (si su división por dos da resto cero), y si no, mandamos la salida a /dev/null.

Hay muchos más reemplazos

Progreso

Como siempre, el usuario se desespera, y muchas veces necesita que le demos algo de información para que no se desespere. Una buena idea es mostrar un progreso para que sepa por dónde va la ejecución. Como parallel está pensado para ejecutar muchas tareas, puede medir el progreso mirando todas aquellas tareas que han finalizado y midiendo tiempos. Podemos utilizar el mismo ejemplo de antes para convertir fotos de la siguiente manera:

find -maxdepth 1 -name ‘*.jpg’ | parallel --progress convert {} convertidas/{/.}.png

O también podemos utilizar –bar. O incluso mejor, pasarle la salida del progreso a un programa como zenity, para ver la salida de forma bonita:
find -maxdepth 1 -name ‘*.jpg’ | parallel --bar convert {} convertidas/{/.}.png 2> >(zenity --progress --auto-kill)

También podemos utilizar –eta para que nos indique el tiempo estimado de finalización de la tarea.

Muchas más opciones

Las utilidades de este programa no han terminado. El propio parallel nos permite ejecutar tareas simultáneas en servidores desde nuestra máquina cliente. Además, junto con GNU Parallel encontramos utilidades para acceso a base de datos y para la realización de operaciones. Es un programa muy flexible que nos permite realizar multitud de operaciones muy útiles y muy interesantes.

Sobre GNU Parallel

El autor nos pide que le citemmos si utilizamos este programa para una publicación. Sólo hay que mencionar al autor, además, en el enlace encontramos un manual muy completo y actualizado:

Encontramos otras formas de mención muy útiles si estamos escribiendo en LaTeX:

parallel --bibtex

Foto principal: unsplash-logoJonathan Pendleton

También podría interesarte....

There are 59 comments left Ir a comentario

  1. 먹튀검증 /
    Usando Google Chrome Google Chrome 116.0.0.0 en Windows Windows NT

    This blog is very helpful. I really appreciate you sharing this kindness with me and others! I am a regular reader on your website and would be happy to acknowledge the work you have done while writing blog articles. Thank you for sharing!먹튀검증

    1. Curso De Joyas /
      Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

      Aqui tambien puedes ver algun curso para joyas https://kameronxwut61514.jts-blog.com/

      1. Wopi /
        Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

        Genial .Tambien hablare de cursos en https://wopi.es/las-mejores-joyas/

    2. Seguidores De Tik Tok /
      Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

      Ok. Aquí también tienes algo que te puede servir https://kameronxwut61514.jts-blog.com/25558756/la-influencia-del-tik-tok

  2. 먹튀신고사이트 /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    Unbelievable!! The problem I was thinking about was solved. You are really awesome. 먹튀신고사이트

  3. 먹튀검증사이트 /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    I am extremely impressed with your writing skills and also with the layout on your blog. 먹튀검증사이트

  4. lisa /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    It’s essential to recognize that while body fat analyzers offer valuable insights, they are just one part of the overall health and fitness puzzle. Factors like genetics, lifestyle choices, and overall health play significant roles in determining an individual’s well-being. Therefore, it’s crucial to interpret the results from these devices in conjunction with other health indicators and seek guidance from healthcare professionals when needed.body fat analyzer

  5. Amanda /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    In fact, this has been exploited to keep numerous programs running, repeatedly granting and revoking CPU access. However, for El Paso Fence Contractors with the advancement of hardware, it is now usual to have computers with multiple processing cores and the ability to run many threads of code at the same time.

  6. dehraduncarrental /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    Best article I have read till date, I appreciate you for sharing this information. Car Rental in Dehradun

  7. 토토사이트 /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    Like, like, very good. I hope to see similar articles frequently in the future. Thank you.토토사이트

  8. ceiling damage /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    It’s more efficient than ever to execute tasks concurrently, making the most of your hardware resources for faster performance.

  9. Through Hole /
    Usando Google Chrome Google Chrome 116.0.0.0 en Windows Windows NT

    It’s crucial to work with contractors who understand the unique climate challenges of Phoenix. The extreme heat and occasional dust storms mean that your windows must be durable and well-insulated. Experienced professionals can guide you toward the best choices for your specific circumstances.

  10. michaelarrington /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    Tienes razón, los avances en la potencia de los ordenadores y la capacidad de ejecutar tareas en paralelo han revolucionado la informática y han llevado a mejoras significativas en el rendimiento de las aplicaciones y sistemas io games

  11. 토토사이트검증소 /
    Usando Google Chrome Google Chrome 117.0.0.0 en Windows Windows NT

    I’m learning by looking at your posts and reviewing them. Please try my website properly online and let me know what you think. 토토사이트검증소

  12. mandiripinjamandana /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    Dapatkan pinjaman dan tunai paling tinggi hanya dengan gadai bpkb mobil dan pembiaayaan kedit mobil bekas, proses cepat suku bunga rendah
    https://www.mandiripinjamandana.com/

  13. 먹튀검증 /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    A very informative article on how to improve one’s expression, mood, and sentence. Please refer to it here.먹튀검증

  14. paulhenge /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    Running concurrently has its own advantages and disadvantages. Sometimes it gets clogged due to too many impacts at the same time. A better solution is bad ice cream

  15. someone to pray for me /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    This prompt helped me to resolve my issues. I need someone to pray for me to understand and apply this to my future projects fully.

  16. 토토사이트추천 /
    Usando Google Chrome Google Chrome 118.0.0.0 en Windows Windows NT

    Recently, I am looking for a similar blog, and your article is very attractive, which is exactly what I am looking for..토토사이트추천

  17. lisa /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    I am so impressed with the informative content on this website. Please share any similar websites you know of, written in a perfect style. I am currently working on a venture and could use some additional guidance.about us

  18. ahmedabadcarrentals /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    This is so cool and wonderful. I’m just amazed. I hope you will keep up the good work in future too, Car Rental in Ahmedabad

  19. 먹튀신고 /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    We really appreciate your blog post. After visiting your post, you will find many methods. I’m looking for it. Thanks for posts like this, please keep it up. Well done.먹튀신고

  20. timothyferriss /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    GNU Parallel es una herramienta de línea de comandos que permite ejecutar tareas de forma simultánea en un retro bowl ordenador. Esto puede ser muy útil para acelerar la ejecución de tareas que tardan mucho tiempo, como el cálculo del número PI o la conversión de imágenes de un formato a otro.

  21. Gujarat Tour Packages /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Awsome website! I am loving it!! Will be back later to read some more. I am bookmarking your feeds. Gujarat Tour Packages

  22. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    The sheer size and selection of these web slots are mind-blowing. Great job. สล็อตค่ายใหญ่

  23. John Nixon /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Thanks for spreading this knowledge to us. I also write some words about trainer shoes manufacturer must visit my page.

  24. Quality Roofing Contractor /
    Usando Mozilla Firefox Mozilla Firefox 121.0 en Windows Windows NT

    From providing detailed estimates to outlining project timelines, clear communication is integral to building trust and ensuring that clients have realistic expectations regarding the progress of the roofing project.

  25. mud and tape /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Embracing parallel processing is a logical step forward in a world where hardware is designed to handle multiple tasks concurrently.

  26. WilliamSEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    llo there mates, it is incredible composed piece completely characterized, proceed with the great work always. pasar123

  27. WilliamSEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I feel exceptionally appreciative that I read this. It is exceptionally useful and extremely useful and I extremely took in a great deal from it. 토토사이트

  28. Billie /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    GNU Parallel is a powerful suika game command-line tool used to run tasks simultaneously in parallel.

  29. WilliamSEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    These things are vital, great think so – I suspect as much as well… 먹튀커뮤니티

  30. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    This is an important discussion. I’ve had my fair share of slots breaking unexpectedly, and it’s never fun. เว็บสล็อต

  31. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Welcome to the gathering of my life here you will master every little thing about me. แหล่งรวมสล็อตทุกค่าย

  32. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I utilize fundamentally unrivaled textures : you will find these items by: เว็บสล็อตโรม่า

  33. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    This substance is just energizing and innovative. I have been settling on an institutional move and this has helped me with one angle. เกมป๊อกเด้งออนไลน์

  34. SHOULDER WORKOUT /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Shoulder Workout

  35. otiscavin /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    GNU Parallel es una herramienta poderosa que permite ejecutar tareas en paralelo desde la línea de comandos. Te permite aprovechar al máximo la capacidad de procesamiento de tu sistema al ejecutar múltiples comandos simultáneamente A Small World Cup

  36. quickbooks integration /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Integration Quickbooks Online Accounting Software Usa. Qbis Helps You To Sync Your Data With Quickbooks Online As Well As Quickbooks Desktop.

  37. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    A commitment of gratefulness is all together for the educational and satisfying post, obviously in your blog everything is unprecedented.. 대구맛집

  38. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    2up Sports Social Game APP is the newest type of sports social entertainment game in the world in 2020’s. 2up Sports adapts to the web3 era and the wave of mobile internet, oriented to the personality, health, fashion, popularity, new trend, all-day socialization concept of the young generation of 00’s. 2up

  39. rajpatel /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Shoulder Workout Gain Muscle

  40. Shopify quickbooks Integration /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Shopify quickbooks Integration It is a leading cloud-based E-commerce platform designed for small and medium-sized businesses.

  41. Woocommerce Quickbooks Integration /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Woocommerce Quickbooks Integration is an Open source eCommerce plugin that transforms your WordPress website into a fully featured eCommerce store. You can send your customer and order information from WooCommerce to QuickBooks Online and QuickBooks Desktop.

  42. Opencart Quickbooks Integration /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Integrating OpenCart and QuickBooks with QBIS can be a great way to save time and improve the accuracy of your e-commerce and financial records.

  43. WilliamSEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    To start with You got an awesome blog .I will be keen on more comparative points. I see you got extremely exceptionally valuable themes, I will be continually checking your blog much appreciated. 안전 토토사이트

  44. Rank Xone /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I appreciate that online casinos promote responsible gaming. Setting limits and being mindful of your play is encouraged, ensuring a positive and enjoyable experience. 플러스카지노

  45. UFAAUTO789 /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    ยูฟ่า789 เว็บแทงบอลที่ใหญ่ และการเงินมั่นคงที่สุด สล็อตออนไลน์ ไม่มีขั้นต่ำ ใช้ทุนน้อย เล่นได้ทุกเกม

  46. ufa168 /
    Usando Google Chrome Google Chrome 107.0.0.0 en Windows Windows NT

    Wonderful post however , I was wanting to know if you could write a litte more on this subject? I’d be very thankful if you could elaborate a little bit further. ufa168

  47. lisa /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Restorative dentistry comes into play when dental issues require intervention. Dental crowns, bridges, and implants are among the various solutions available to restore function and appearance. These interventions are essential for individuals with missing or damaged teeth, enabling them to regain normal oral function and prevent further complications.turismo dentale

  48. lisa /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Casio, a renowned Japanese multinational electronics company, has firmly established itself as a prominent player in the consumer electronics and musical instruments industry. Since its inception in 1946 by Tadao Kashio, Casio has consistently demonstrated innovation and commitment to quality across its diverse product range. The company initially gained recognition for its calculators, a market it entered in the early 1950s. Casio’s calculators quickly became synonymous with precision and reliability, setting the stage for the brand’s broader expansion into various electronic devices.Casio albania

  49. lisa /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    lawyers are integral to the functioning of the legal system, serving as advocates, advisors, and guardians of justice. Their diverse responsibilities, from providing legal counsel to courtroom advocacy, demand a multifaceted skill set and unwavering commitment to ethics and integrity. Legal education, adaptability, and a sense of social responsibility are hallmarks of the legal profession. Despite its challenges, the legal field offers opportunities for meaningful contributions to society, emphasizing the importance of justice, equality, and the rule of law.avokat për heqje ekspulsi

  50. jsimitseo /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Gee… I translate writes on a similar to issue, be that as it may I never went by your blog. I added it to populars additionally i’ll be your reliable preliminary. concierge doctor

  51. WilliamSEO /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    These things are vital, great think so – I suspect as much as well… จำนำรถจอด

  52. Wilmington Chimney Sweep /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Thank you for sharing your wisdom to Wilmington Chimney Sweep! We appreciate it!

  53. Rank Xone /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    I am scanning for and I need to post a comment that «The substance of your post is magnificent» Great work! จำนำรถ

  54. cracked sidewalk /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Absolutely, leveraging parallel processing has become crucial in maximizing the efficiency of modern computers.

  55. BETFLIXSUPERVIP /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    I’m going to read this. I’ll be sure to come back. thanks for sharing. and also This article gives the light in which we can observe the reality. this is very nice one and gives indepth information. thanks for this nice article. betflix vip

  56. Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    A good blog and very interesting what else is the concept applied to give deep meaning, You are very helpful

Leave a Reply