Las variables volátiles

Son aquellas variables cuyo valor siempre que se lee, se lee de memoria, y siempre que se escribe, se escribe en memoria. Pasan el mínimo tiempo posible en los registros del microprocesador.
Al declararlas volátiles se le está indicando al compilador que no haga optimizaciones que eviten accesos a memoria para leerlas/escribirlas.

En un código como éste:

moehren+=7;
moehren*=2;

El compilador podría optimizar y generar código que:

  1. lee el valor de la variable de la pila (p.ej. x<-[moehren]) y lo guarda en un registro del micro
  2. suma 7 al registro (x+7)
  3. multiplica el registro por 2 ((x+7)*2)
  4. guarda el registro en la posición de la pila dedicada a la variable moehren ((x+7)*2->[moehren])

Hasta aquí todo en orden, el valor de mohren==(mohren+7)*2 y el número de accesos a memoria se minimiza, lo que (casi) siempre es de agradecer.

Y ahora vamos a suponer, que mientras una sentencia y otra, se recibe una interrupción y se ejecuta entre medias una sentencia:

moehren-=8;

El orden de ejecución sería como sigue:

  1. moehren+=7;
  2. moehren-=8;
  3. moehren*=2;

Con las optimizaciones del compilador esto queda:

  1. lee el valor de la variable (x<-[moehren]) de la pila y lo guarda en un registro del micro
  2. suma 7 al registro (x+7)
  3. salta a la ISR (rutina de servicio de la interrupción, que es, sencillamente, una función que se ejecutará en el medio)
  4. guarda los registros en la pila
  5. lee el valor de la variable de la pila (y<-[moehren]) y lo guarda en un registro del micro
  6. le resta 8 a ese registro (y-8)
  7. guarda ese registro en la posición de la pila dedicada a la variable moehren (y-8 -> [moehren])
  8. restaura los registros guardados en la pila y retorna a donde estaba antes de saltar a la ISR
  9. multiplica el registro por 2 ((x+7)*2)
  10. guarda el registro en la posición de la pila dedicada a la variable moehren ((x+7)*2->[moehren])

Es decir, que el valor de moehren==(moehren+7)*2, como si la ISR no se hubiera ejecutado.

Si por el contratio declaramos moehren como volatile:

volatile int moehren;

Cada vez que se lea/escriba esa variable, se accederá a memoria, lo cual es más lento, pero nos evita situaciones como la anterior.

El orden de ejecución del código generado en un caso similar al ejemplo anterior sería algo así:

  1. lee el valor de la variable (x<-[moehren]) de la pila a un registro
  2. suma 7 al registro (x+7)
  3. guarda el registro en la variable moehren (x+7 -> [moehren])
  4. salta a la ISR
  5. guarda los registros en la pila
  6. lee el valor de la variable (y<-[moehren], o sea y==x+7) de la pila a un registro
  7. le resta 8 a ese registro (y-8, que es lo mismo que x+7-8)
  8. guarda el registro en la variable moehren (y -> [moehren], o bien x+7-8 -> [moehren])
  9. restaura los registros guardados en la pila y retorna a donde estaba antes de saltar a la ISR
  10. lee el valor de la variable (z<-[moehren], z==x+7-8) de la pila a un registro
  11. multiplica el registro por 2 (z*2, (x+7-8)*2)
  12. guarda el registro en la variable moehren (z -> [moehren], o bien (x+7-8)*2 -> [moehren])

El valor que tiene ahora moehren==(moehren-1)*2. Ahora sí que se nota la ejecución de la ISR.

Ojo, porque con esto no se evita la necesidad de las primitivas de sincronización. Nada garantiza que dos procesos no accedan paralelamente y de forma no atómica a la variable en memoria. Si estamos trabajando en un entorno monotarea (un microcontrolador, normalmente), esto nos dará igual.

Lenguajes de Programación

Comments (0)

Permalink

Charles Simonyi

Hace un par de semanas leía un artículo traducido al español de Joel Spolsky. Trataba sobre la notación Húngara, un estilo de codificación muy útil si es bien entendida y bien utilizada, cosa que no suele ocurrir (por eso a nivel popular se le tiene tanto rechazo).

Para saber más acerca de la notación húngara recomiendo leer el artículo de Spolsky: Haciendo que el código defectuoso se vea defectuoso.

En ese mismo artículo se menciona que Simonyi era un programador húngaro que inicialmente trabajaba en Xerox Parc en un procesador de textos llamado Bravo, y se acabó pasando a Microsoft para trabajar en el equipo del Word, que es donde inventó la notación húngara con la finalidad de saber de qué clase es cada variable (no del tipo).

Ahora estaba leyendo que el notas va a viajar por segunda vez a la Estación Espacial Internacional en primavera, así que pareceser que con el Word, pese a Clippo, no le fue nada mal.

Lenguajes de Programación
Personajes

Comments (4)

Permalink

Pilas y colas en Python

Las listas son un tipo de objeto mutable de Python. Por mutable, en Python se entiende que el objeto es modificable una vez definido, por ejemplo:

>>> a=[1,2]

(definimos la lista a con dos elementos)

>>> a.append(3)

(añadimos otro elemento)

>>> a
[1, 2, 3]

A diferencia de las tuplas, que no son modificables:

>>> b=(1,2)

(definimos la tupla b, que se quedará así por los siglos de los siglos…)

>>> b=None

(…o hasta que pase el recolector de basura)

Las listas por ser mutables tienen definidas unas operaciones sobre ellas de las que las tuplas carecen, por ejemplo el método append(<elemento>) que se ha visto antes, o por ejemplo los siguientes métodos:

>>> c=["a","b","c"]
>>> c
['a', 'b', 'c']
>>> c.insert(0,”d”)
>>> c
['d', 'a', 'b', 'c']
>>> c.insert(2,”e”)
>>> c
['d', 'a', 'e', 'b', 'c']
>>> c.pop()
‘c’
>>> c
['d', 'a', 'e', 'b']
>>>

Como se puede apreciar pop() toma el último elemento (, o c[len(c)-1], como se prefiera), lo saca de la lista (la lista se queda sin ese valor) y nos lo devuelve. Junto con append() es lo justo para crear una pila:

>>> pila=[]

(creamos la pila, vacía)

>>> pila.pop()

(sacamos datos de una pila vacía, y nos dan una excepción)

Traceback (most recent call last):
File “<stdin>”, line 1, in ?
IndexError: pop from empty list
>>> pila.append(1)

(añadimos datos a la pila)

>>> pila.append(2)
>>> pila.append(3)
>>> pila
[1, 2, 3]
>>> while pila:
… print “Ultimo elemento de la pila: %d (pila: %s)” % (pila.pop(),pila)…
Ultimo elemento de la pila: 3 (pila: [1, 2])
Ultimo elemento de la pila: 2 (pila: [1])
Ultimo elemento de la pila: 1 (pila: [])
>>>

El otro método utilizado era insert(<posición>,<elemento>), que como creo que cabe esperar realiza este comportamiento:

>>> lista=[]

(creamos la lista)

>>> lista.insert(1,”a”)

(insertamos en la posición 1, pero como está vacía realmente queda en la posición lista[0])

>>> lista
['a']
>>> lista.insert(5,”a”)

(insertamos en la posición 5, pero como está vacía realmente queda en la posición lista[1])

>>> lista
['a', 'a']
>>> lista.insert(0,”b”)

(insertamos al principio)

>>> lista
['b', 'a', 'a']
>>> lista.insert(-1,”c”)

(insertamos justo antes de la última posición)

>>> lista
['b', 'a', 'c', 'a']
>>> lista.insert(-2,”d”)

(insertamos en la antepenúltima posición)

>>> lista
['b', 'a', 'd', 'c', 'a']
>>>

¿Y para insertar en la última posición? lista.append(‘u’), mismamente:

>>> lista.append(‘u’)
>>> lista
['b', 'a', 'd', 'c', 'a', 'u']
>>>

Ahora ya crear una cola es coser y cantar, a base de insert(0,<elemento>) y pop():

>>> cola=[]

(crear la cola)

>>> cola.pop()

(sacar de una cola vacía nos da una excepción

IndexError

, como en la pila)

Traceback (most recent call last):
File “<stdin>”, line 1, in ?
IndexError: pop from empty list
>>> cola.insert(0,”1″) (

introducimos datos)

>>> cola.insert(0,”2″)
>>> cola.insert(0,”3″)
>>> cola
['3', '2', '1']
>>> while cola:

(y vamos sacando…)

… print “Cabeza de la cola: %c (cola: %s)” % (cola.pop(),cola)

Cabeza de la cola: 1 (cola: ['3', '2'])
Cabeza de la cola: 2 (cola: ['3'])
Cabeza de la cola: 3 (cola: [])
>>>

¿Cuánto tiempo nos podemos ahorrar por programarlo en Python en vez de en C?

Aunque también es mucho más ineficiente ejecutarlo en Python que en C, pero normalmente es preferible ahorrar tiempo de programación que tiempo de ejecución: mi tiempo es importante, el del microprocesador no tanto.

Estructuras de datos
Python

Comments (0)

Permalink