Pointers vs. Values en Go: cómo no matar el rendimiento

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Pointers vs. Values en Go: La línea entre rendimiento y seguridad

\n\n

Imagina la situación: estás escribiendo en Go, todo va bien, pero de repente notas que la aplicación comienza a «ralentizarse» con grandes volúmenes de datos. Abres el perfilador y ves que allocs/op (asignaciones por operación) se disparan. ¿Te suena? Lo más probable es que el problema esté en cómo pasas los datos a las funciones y los devuelves. La pregunta «Pointers vs. values in parameters and return values» es una de las más candentes en Stack Overflow (¡miles de visitas!), y hoy la analizaremos en español, de forma dinámica y con ejemplos reales.

\n\n

Go es un lenguaje que ama la simplicidad. Pero esta simplicidad es engañosa: la elección entre pasar un valor (value) y un puntero (pointer) puede cambiar drásticamente la velocidad del programa y su consumo de memoria. Averigüemos cuándo usar cada uno para que tu código vuele, no se arrastre.

\n\n

¿Por qué es tan importante este tema?

\n\n

A diferencia de Java o Python, donde todo es una referencia a un objeto, en Go eliges explícitamente: pasar una copia de la estructura o su dirección. Parece un detalle menor, pero en realidad es una decisión fundamental que afecta a:

\n\n
    \n
  • Velocidad de ejecución — copiar estructuras grandes lleva tiempo.
  • \n
  • Consumo de memoria — copias adicionales = asignaciones adicionales.
  • \n
  • Seguridad de los datos — los punteros permiten mutar el original, los valores no.
  • \n
  • Trabajo con interfaces — las interfaces en Go son un mecanismo complejo que funciona de manera diferente con punteros y valores.
  • \n
\n\n

Vayamos directo al grano y veamos el código.

\n\n

Ejemplo n.º 1: Estructura pequeña

\n\n

Empecemos con algo simple. Supongamos que tenemos una estructura Point con dos coordenadas:

\n\n
type Point struct {\n    X, Y float64\n}\n\n// Paso por valor\nfunc MoveByValue(p Point, dx, dy float64) Point {\n    p.X += dx\n    p.Y += dy\n    return p\n}\n\n// Paso por puntero\nfunc MoveByPointer(p *Point, dx, dy float64) {\n    p.X += dx\n    p.Y += dy\n}
\n\n

En el primer caso, creamos una copia de la estructura, la modificamos y devolvemos una nueva. En el segundo, modificamos el original directamente. ¿Qué opción es más rápida? Para una estructura tan pequeña (16 bytes) — casi no hay diferencia. Copiar 16 bytes es una operación atómica y el GC (recolector de basura) no genera carga adicional. De hecho, pasar por valor puede ser incluso más rápido, porque el puntero requiere una desreferenciación (dereference) adicional.

\n\n

Conclusión: Para estructuras pequeñas (hasta 4-8 campos de tipos básicos), usa valores sin miedo. Es más seguro y no genera sobrecarga.

\n\n

Ejemplo n.º 2: Estructura grande

\n\n

Ahora imaginemos que trabajamos con datos de usuario:

\n\n
type User struct {\n    ID        int64\n    Name      string\n    Email     string\n    Avatar    []byte // 1 MB de imagen\n    CreatedAt time.Time\n    UpdatedAt time.Time\n}
\n\n

Aquí Avatar es un slice, que en Go es un tipo de referencia (cabecera: puntero a los datos, longitud, capacidad). Pero la estructura en sí, al copiarse, solo copiará la cabecera del slice (24 bytes), no los bytes en sí. Sin embargo, si la estructura tiene muchos campos, copiar 100+ bytes en cada llamada a función ya se nota.

\n\n

Ejecutemos un benchmark simple:

\n\n
func BenchmarkPassByValue(b *testing.B) {\n    u := User{ID: 1, Name: "John", Avatar: make([]byte, 1024*1024)}\n    for i := 0; i < b.N; i++ {\n        _ = processUserByValue(u)\n    }\n}\n\nfunc BenchmarkPassByPointer(b *testing.B) {\n    u := &User{ID: 1, Name: "John", Avatar: make([]byte, 1024*1024)}\n    for i := 0; i < b.N; i++ {\n        _ = processUserByPointer(u)\n    }\n}
\n\n

Resultado (en hardware real):

\n
    \n
  • PassByValue: ~120 ns/op, 0 asignaciones
  • \n
  • PassBy

Blogs

Book Recommendations