En aquest tema, explorarem diverses tècniques per optimitzar el codi i millorar el rendiment dels algorismes. La optimització de codi és crucial per assegurar que els nostres programes s'executin de manera eficient, utilitzant el mínim de recursos possibles.

Objectius del Tema

  • Comprendre la importància de l'optimització de codi.
  • Aprendre tècniques per identificar colls d'ampolla en el rendiment.
  • Aplicar estratègies per millorar l'eficiència del codi.

  1. Importància de l'Optimització de Codi

1.1. Rendiment i Escalabilitat

  • Rendiment: Un codi optimitzat s'executa més ràpidament, millorant l'experiència de l'usuari.
  • Escalabilitat: Un codi eficient pot manejar un major volum de dades i usuaris sense degradar el rendiment.

1.2. Ús de Recursos

  • Memòria: Reduir l'ús de memòria pot evitar problemes de memòria insuficient.
  • CPU: Optimitzar l'ús de la CPU pot reduir el temps de processament i el consum d'energia.

  1. Identificació de Colls d'Ampolla

2.1. Anàlisi de Perfils (Profiling)

  • Eines de Profiling: Utilitza eines com gprof, valgrind, o perf per identificar les parts del codi que consumeixen més temps.
  • Exemple de Profiling amb gprof:
    gcc -pg -o programa programa.c
    ./programa
    gprof programa gmon.out > analysis.txt
    

2.2. Mesura de Temps d'Execució

  • Funcions de Temps: Utilitza funcions com clock() en C o time en Python per mesurar el temps d'execució de diferents parts del codi.
  • Exemple en Python:
    import time
    
    start_time = time.time()
    # Codi a mesurar
    end_time = time.time()
    print(f"Temps d'execució: {end_time - start_time} segons")
    

  1. Estratègies d'Optimització

3.1. Optimització de Bucles

  • Reducció de Complexitat: Simplifica els bucles per reduir la complexitat temporal.
  • Exemple:
    # Codi no optimitzat
    for i in range(len(arr)):
        for j in range(len(arr)):
            if arr[i] == arr[j]:
                # Operació
    
    # Codi optimitzat
    for i in range(len(arr)):
        for j in range(i+1, len(arr)):
            if arr[i] == arr[j]:
                # Operació
    

3.2. Ús de Estructures de Dades Adequades

  • Estructures de Dades: Tria l'estructura de dades adequada per a cada problema (llistes, conjunts, diccionaris, etc.).
  • Exemple:
    # Ús de diccionari per comptar freqüències
    freq = {}
    for item in arr:
        if item in freq:
            freq[item] += 1
        else:
            freq[item] = 1
    

3.3. Memòria Cau (Caching)

  • Memòria Cau: Emmagatzema resultats de càlculs costosos per reutilitzar-los.
  • Exemple en Python amb functools.lru_cache:
    from functools import lru_cache
    
    @lru_cache(maxsize=None)
    def fibonacci(n):
        if n < 2:
            return n
        return fibonacci(n-1) + fibonacci(n-2)
    

3.4. Paral·lelització

  • Paral·lelització: Divideix el treball en múltiples processos o fils per executar-lo simultàniament.
  • Exemple en Python amb multiprocessing:
    from multiprocessing import Pool
    
    def treball(p):
        # Codi a paral·lelitzar
        return resultat
    
    if __name__ == "__main__":
        with Pool(4) as p:
            resultats = p.map(treball, dades)
    

Exercicis Pràctics

Exercici 1: Optimització de Bucles

Descripció: Optimitza el següent codi per reduir la seva complexitat temporal.

# Codi original
def suma_parella(arr):
    suma = 0
    for i in range(len(arr)):
        for j in range(len(arr)):
            if i != j and arr[i] + arr[j] == 10:
                suma += 1
    return suma

Solució:

# Codi optimitzat
def suma_parella(arr):
    suma = 0
    complements = set()
    for num in arr:
        if 10 - num in complements:
            suma += 1
        complements.add(num)
    return suma

Exercici 2: Ús de Memòria Cau

Descripció: Implementa una funció per calcular el factorial d'un nombre utilitzant memòria cau.

# Codi original
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n-1)

Solució:

from functools import lru_cache

@lru_cache(maxsize=None)
def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n-1)

Resum

En aquest tema, hem après la importància de l'optimització de codi per millorar el rendiment i l'eficiència dels nostres programes. Hem explorat diverses tècniques per identificar colls d'ampolla i aplicar estratègies d'optimització, com ara l'optimització de bucles, l'ús d'estructures de dades adequades, la memòria cau i la paral·lelització. A més, hem practicat aquestes tècniques amb exercicis pràctics per reforçar els conceptes apresos.

© Copyright 2024. Tots els drets reservats