Re: Desafío 1brc

From: Enrique Herrera Noya <enrique(dot)herreranoya(at)gmail(dot)com>
To: pgsql-es-ayuda(at)lists(dot)postgresql(dot)org
Subject: Re: Desafío 1brc
Date: 2024-01-09 11:38:58
Message-ID: 6c36c834-2274-4118-b7ac-7ff1b73dd81e@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-es-ayuda

hola
estos temas lo vi para mi examen de titulo, veo dos planos acá

1. el tuning propiamente tal de postgres (para solicitudes en paralelo,
y también uso de indices, etc),
ya te han dado algunas consideraciones a tener...

2. usar código eficiente en java

te comparto lo que desarrolle como prueba de concepto, de manera que te
hagas una idea,
(el primero permitió una mejora sustancial respecto del código ineficiente)

el segundo aun mas , pero con mas consumo de memoria. (donde se ejecuta
java)

código eficiente sin hilos
...

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.SQLException;

import java.util.Random;

import java.lang.management.ManagementFactory;

import java.lang.management.MemoryMXBean;

public class EfficientProcessingExample {

public static void main(String[] args) {

String jdbcUrl = "jdbc:postgresql://localhost:5432/carrera";

String username = "admin";

String password = "l3r0l3r0";

try {

Connection connection = DriverManager.getConnection(jdbcUrl, username,
password);

PreparedStatement preparedStatement = connection.prepareStatement(

"UPDATE curso SET promedio = ?, aprobado = ? WHERE indice = ?;"

);

int totalRecords = 40000;

Random random = new Random();

long startTime = System.currentTimeMillis();

for (int idx = 1; idx <= totalRecords; idx++) {

int nota1 = random.nextInt(7) + 1;

int nota2 = random.nextInt(7) + 1;

int nota3 = random.nextInt(7) + 1;

double promedio = (nota1 + nota2 + nota3) / 3.0;

boolean aprobado = promedio > 4.0;

preparedStatement.setDouble(1, promedio);

preparedStatement.setBoolean(2, aprobado);

preparedStatement.setInt(3, idx);

preparedStatement.addBatch(); // Agregar la consulta al lote

if (idx % 1000 == 0) {

preparedStatement.executeBatch(); // Ejecutar el lote de consultas cada
1000 registros

preparedStatement.clearBatch(); // Limpiar el lote

}

}

preparedStatement.executeBatch(); // Ejecutar cualquier consulta
restante en el lote

preparedStatement.close(); // Cerrar la declaración

long endTime = System.currentTimeMillis();

long elapsedTime = endTime - startTime;

System.out.println("Tiempo de ejecución: " + elapsedTime + " ms");

// Obtener información sobre el uso de memoria

MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();

long usedMemory = memoryMXBean.getHeapMemoryUsage().getUsed();

String sizeUnit;

double sizeValue;

if (usedMemory < 1024) {

sizeValue = usedMemory;

sizeUnit = "bytes";

} else if (usedMemory < 1024 * 1024) {

sizeValue = (double) usedMemory / 1024;

sizeUnit = "KB";

} else if (usedMemory < 1024 * 1024 * 1024) {

sizeValue = (double) usedMemory / (1024 * 1024);

sizeUnit = "MB";

} else {

sizeValue = (double) usedMemory / (1024 * 1024 * 1024);

sizeUnit = "GB";

}

System.out.println("Uso de memoria: " + sizeValue + " " + sizeUnit);

connection.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

...

código eficiente con hilos

...
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Random;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
public class EfficientProcessingWithThreadsExample {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:postgresql://localhost:5432/carrera";
        String username = "admin";
        String password = "l3r0l3r0";
        try {
            Connection connection =
DriverManager.getConnection(jdbcUrl, username, password);
            int totalRecords = 40000;
            EfficientProcessorThread[] threads = new
EfficientProcessorThread[totalRecords];
            for (int idx = 1; idx <= totalRecords; idx++) {
                threads[idx - 1] = new EfficientProcessorThread(idx,
connection);
                threads[idx - 1].start();
            }
            long startTime = System.currentTimeMillis();
            for (EfficientProcessorThread thread : threads) {
                thread.join();
            }
            long endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            // Obtener información sobre el uso de memoria
            MemoryMXBean memoryMXBean =
ManagementFactory.getMemoryMXBean();
            long usedMemory = memoryMXBean.getHeapMemoryUsage().getUsed();
            String sizeUnit;
            double sizeValue;
            if (usedMemory < 1024) {
                sizeValue = usedMemory;
                sizeUnit = "bytes";
            } else if (usedMemory < 1024 * 1024) {
                sizeValue = (double) usedMemory / 1024;
                sizeUnit = "KB";
            } else if (usedMemory < 1024 * 1024 * 1024) {
                sizeValue = (double) usedMemory / (1024 * 1024);
                sizeUnit = "MB";
            } else {
                sizeValue = (double) usedMemory / (1024 * 1024 * 1024);
                sizeUnit = "GB";
            }
            System.out.println("Uso de memoria: " + sizeValue + " " +
sizeUnit);
            System.out.println("Tiempo de ejecución: " + elapsedTime +
" ms");
            connection.close();
        } catch (SQLException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class EfficientProcessorThread extends Thread {
    private final int idx;
    private final Connection connection;
    private final Random random;
    public EfficientProcessorThread(int idx, Connection connection) {
        this.idx = idx;
        this.connection = connection;
        this.random = new Random();
    }
    @Override
    public void run() {
        try {
            int nota1 = random.nextInt(7) + 1;
            int nota2 = random.nextInt(7) + 1;
            int nota3 = random.nextInt(7) + 1;
            double promedio = (nota1 + nota2 + nota3) / 3.0;
            boolean aprobado = promedio > 4.0;
            PreparedStatement preparedStatement =
connection.prepareStatement(
                "UPDATE curso SET promedio = ?, aprobado = ? WHERE
indice = ?;"
            );
            preparedStatement.setDouble(1, promedio);
            preparedStatement.setBoolean(2, aprobado);
            preparedStatement.setInt(3, idx);
            preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

...

el código ineficiente:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Random;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
public class InefficientProcessingExample {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:postgresql://localhost:5432/carrera";
        String username = "admin";
        String password = "l3r0l3r0";

        try {
            Connection connection =
DriverManager.getConnection(jdbcUrl, username, password);
            PreparedStatement preparedStatement =
connection.prepareStatement(
                "UPDATE curso SET promedio = ?, aprobado = ? WHERE
indice = ?;"
            );

            int totalRecords = 40000;
            Random random = new Random();

            long startTime = System.currentTimeMillis();

            for (int idx = 1; idx <= totalRecords; idx++) {
                int nota1 = random.nextInt(7) + 1;
                int nota2 = random.nextInt(7) + 1;
                int nota3 = random.nextInt(7) + 1;
                double promedio = (nota1 + nota2 + nota3) / 3.0;
                boolean aprobado = promedio > 4.0;

                preparedStatement.setDouble(1, promedio);
                preparedStatement.setBoolean(2, aprobado);
                preparedStatement.setInt(3, idx);
                preparedStatement.executeUpdate();
            }

            long endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;

            System.out.println("Tiempo de ejecución: " + elapsedTime +
" ms");

            // Obtener información sobre el uso de memoria
            MemoryMXBean memoryMXBean =
ManagementFactory.getMemoryMXBean();
            long usedMemory = memoryMXBean.getHeapMemoryUsage().getUsed();
            String sizeUnit;
            double sizeValue;

            if (usedMemory < 1024) {
                sizeValue = usedMemory;
                sizeUnit = "bytes";
            } else if (usedMemory < 1024 * 1024) {
                sizeValue = (double) usedMemory / 1024;
                sizeUnit = "KB";
            } else if (usedMemory < 1024 * 1024 * 1024) {
                sizeValue = (double) usedMemory / (1024 * 1024);
                sizeUnit = "MB";
            } else {
                sizeValue = (double) usedMemory / (1024 * 1024 * 1024);
                sizeUnit = "GB";
            }
            System.out.println("Uso de memoria: " + sizeValue + " " +
sizeUnit);
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

....

El 08-01-24 a las 23:59, Jairo Graterón escribió:
> Saludos lista
>
> Hay un reto para crear un algoritmo en java para para recuperar
> valores de medición de temperatura de un archivo de texto y calcular
> la temperatura mínima, media y máxima por estación meteorológica
> https://www.morling.dev/blog/one-billion-row-challenge/
>
> Pero se están haciendo implementaciones en otros lenguajes y por
> supuesto en bases de datos por ejemplo
> https://ftisiot.net/posts/1brows/ y
> https://rmoff.net/2024/01/03/1%EF%B8%8F%E2%83%A3%EF%B8%8F-1brc-in-sql-with-duckdb/
>
> Ya inserté los mil millones de registros en mi máquina y al realizar
> la consulta
> image.png
> Tarda casi 2 minutos, así que seguí investigando como mejorar el
> tiempo y al encontrar estas otras pruebas
> https://gist.github.com/FranckPachot/50a6a491b85b0ddb3da6399d54653085
> me llamó la atención ésta línea
> select/*+ parallel(8) gather_plan_statistics*/
>
> Revisando postgres tiene un parámetro para aumentar el número de
> workers en paralelo si la consulta lo
> necesita max_parallel_workers_per_gather
>
> image.png
> Mejoró bastante, 40 segundos menos.
>
> *¿Qué otras optimizaciones se podrían realizar en postgres para
> disminuir el tiempo?*
>
> Con  Apache Pinot tarda aprox 1.9s
> https://hubertdulay.substack.com/p/1-billion-row-challenge-in-apache?r=46sqk&utm_campaign=post&utm_medium=web
> <https://hubertdulay.substack.com/p/1-billion-row-challenge-in-apache?r=46sqk&utm_campaign=post&utm_medium=web>
>
> Otro tardó 20 segundos
> https://twitter.com/_TylerHillery/status/1742971310123487429
>
>
> Por supuesto eso depende de las especificaciones del equipo pero es
> interesante que compartan sus experiencias.
>
> Las especificaciones de mi máquina son:
> Ryzen 5 6 cores/12 Threads a 3.0ghz
> Disco nvme KINGSTON
> Ubuntu 22.04
> Postgresql 14
>
>
>
>

Enrique Herrera Noya
--
+56 992303151
Red Hat Certified Engineer RHCE Nº100223072 (RH6.0)
Red Hat Certified System Administrato RHCSA Nº100223072 (RH6.0)
Red Hat Certified Technician (RHCT) Nº605010753835478 (RH5.0)
Novell Certified Linux Professional CLP 10
Red Hat Delivery Specialist -Container Platform Application Deployment I
Red Hat Delivery Specialist - Container Platform Administration I
RED HAT SPECIALIST
How to Sell Red Hat OpenShift for Infrastructure
How to Sell Red Hat OpenShift for Developers
Red Hat Sales Engineer Specialist - Container Platform
Red Hat Sales Engineer Specialist – Automation

In response to

Browse pgsql-es-ayuda by date

  From Date Subject
Next Message Guillermo E. Villanueva 2024-01-19 12:36:00 Barman y directorios diferentes de debian/ubuntu
Previous Message kernel 2024-01-09 08:08:45 Re: lista de stopwords