Ir al contenido principal

This is my blog, more about me at marianoguerra.github.io

🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra

PHP vs web.py (cgi) vs python cgi en dreamhost

estoy indeciso sobre que usar para un proyectito web que tengo en mente, el hosting es dreamhost, y debido a que tiene la limitacion de 100MB de RAM por proceso python en fastcgi (cosa que no tiene ruby on rails :S), decidi probar las alternativas.

probe un hola mundo en PHP, web.py y un script de python pelado.

tanto web.py y el script son servidos por un apache con mod_rewrite, todos hacen lo mismo, que es imprimir "hola mundo!", los resultados son los siguientes:

para php

ab -c 4 -n 300 http://www.marianoguerra.com.ar/php/
This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.marianoguerra.com.ar (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software: Apache/2.2.9
Server Hostname: www.marianoguerra.com.ar
Server Port: 80

Document Path: /php/
Document Length: 12 bytes

Concurrency Level: 4
Time taken for tests: 28.272 seconds
Complete requests: 300
Failed requests: 0
Write errors: 0
Total transferred: 72980 bytes
HTML transferred: 3600 bytes
Requests per second: 10.61 [#/sec] (mean)
Time per request: 376.961 [ms] (mean)
Time per request: 94.240 [ms] (mean, across all concurrent requests)
Transfer rate: 2.52 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 24 33 10.1 30 93
Processing: 272 342 91.5 315 929
Waiting: 267 334 90.4 308 913
Total: 298 375 93.3 347 976

Percentage of the requests served within a certain time (ms)
50% 347
66% 365
75% 387
80% 398
90% 446
95% 562
98% 738
99% 844
100% 976 (longest request)

para web.py

ab -c 4 -n 300 http://www.marianoguerra.com.ar/py/
This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.marianoguerra.com.ar (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software: Apache/2.2.9
Server Hostname: www.marianoguerra.com.ar
Server Port: 80

Document Path: /py/
Document Length: 13 bytes

Concurrency Level: 4
Time taken for tests: 84.578 seconds
Complete requests: 300
Failed requests: 0
Write errors: 0
Total transferred: 65100 bytes
HTML transferred: 3900 bytes
Requests per second: 3.55 [#/sec] (mean)
Time per request: 1127.711 [ms] (mean)
Time per request: 281.928 [ms] (mean, across all concurrent requests)
Transfer rate: 0.75 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 24 32 7.1 30 89
Processing: 423 1095 485.1 981 2824
Waiting: 422 1094 485.1 980 2821
Total: 454 1127 484.6 1012 2851

Percentage of the requests served within a certain time (ms)
50% 1012
66% 1222
75% 1375
80% 1483
90% 1829
95% 2212
98% 2481
99% 2660
100% 2851 (longest request)


para python cgi

ab -c 4 -n 300 http://www.marianoguerra.com.ar/picgi/
This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.marianoguerra.com.ar (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software: Apache/2.2.9
Server Hostname: www.marianoguerra.com.ar
Server Port: 80

Document Path: /picgi/
Document Length: 323 bytes

Concurrency Level: 4
Time taken for tests: 19.617 seconds
Complete requests: 300
Failed requests: 0
Write errors: 0
Non-2xx responses: 300
Total transferred: 173100 bytes
HTML transferred: 96900 bytes
Requests per second: 15.29 [#/sec] (mean)
Time per request: 261.559 [ms] (mean)
Time per request: 65.390 [ms] (mean, across all concurrent requests)
Transfer rate: 8.62 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 24 32 7.4 30 81
Processing: 212 229 25.5 226 440
Waiting: 212 228 25.5 225 440
Total: 240 261 26.4 256 468

Percentage of the requests served within a certain time (ms)
50% 256
66% 260
75% 263
80% 265
90% 274
95% 283
98% 325
99% 463
100% 468 (longest request)

conclusion

una porqueria que dreamhost ponga ese limite a fastcgi, es explicable que web.py demore, ya que tiene que levantar el interprete e importar todo el framework cada vez que lo llamo solo para mostrar un hola mundo. Lo que me sorprende es que python/cgi pelado sea mas rapido que php, siendo que python tiene que levantar el interprete cada vez.
Aclaro que este microbenchmark comprueba realmente cuanto tarda en servir una peticion y no compara la velocidad de los lenguajes en si, pero eso es lo que me interesa, ya que python le pasa el trapo a php en performance :P http://shootout.alioth.debian.org/u32q/benchmark.php?test=all&lang=python&lang2=php

en resumen, no se si hacerlo en python en este host, ya que sacrificar el beneficio de un framework por una limitacion del hosting es un perno :S

Sonido, Wireless y flash en FreeBSD 7.1

Bueno, logre hacer andar todos, el unico que no quedo bien fue flash

para instalar flash compile gnash

cd /usr/ports/graphics/gnash
make install

todo bonito se carga el plugin, pero no puedo ver videos ni de youtube, ni de vimeo ni de blip.tv, asi que le falta a gnash para ser usable.

para el sonido segui las instrucciones del handbook de freebsd y salio andando como piña.

http://www.freebsd.org/doc/en/books/handbook/sound-setup.html

para la wireless segui el manual pero me faltaba algo, asi que lei la referencia de wpa_supplicant, aca estan los links

http://www.freebsd.org/doc/en/books/handbook/network-wireless.html

http://www.daemon-systems.org/man/wpa_supplicant.conf.5.html

todo parece estar andando seguire peleando :D

probando FreeBSD 7.1

Es la tercera vez que le doy una probada a FreeBSD, primero fue la 6.3, despues la 7.0 y ahora la 7.1, me baje el DVD de los torrents y empece de nuevo con todo lo que me había olvidado.

observaciones:
* el instalador es igual al 6.3, se podría hacer un poco mas simple incluso para los geeks ocultando preguntas que tienden a ser defaults todo el tiempo excepto raras excepciones, fuera de eso, si peleaste contra debian, no te va a sorprender nada.

* una vez instalado, no instale el bootloader para dejar grub y tuve que poner una entrada en el grub que fue distinta a las anteriores, en este caso fue:

title Free-BSD
root (hd0,2)
chainloader +1
boot

* como siempre, no me reconoció ni el sonido, ni la wireless, pero esta vez le sumo el touchpad, que la ultima vez me lo había reconocido, para solucionarlo seguí estos pasos y anduvieron a la perfección.

http://fixunix.com/xwindows/383881-how-use-alps-glidepoint-touchpad-xorg-7-3-freebsd-7-0-release-amd64.html

un poco mas de info acá

http://wiki.freebsd.org/SynapticsTouchpad

* no recuerdo de la ultima vez, pero tuve que escribir el .xinitrc a mano y correr xorgconfig para generar el xorg.conf, me hizo preguntas muchas mas técnicas que las que hacia dpkg-reconfigure xserver-xorg en ubuntu.

* a pesar de instalar sudo y tener una sola cuenta fuera de root, me tuve que agregar en /usr/local/etc/sudoers con la entrada

mariano ALL=(ALL) ALL

* como siempre también tuve que correr sudo vipw para cambiar el shell fiero que tiene por defecto por el querido bash, recuerden que aca no es /bin/bash sino /usr/local/bin/bash

* feo que tenga firefox 2.0.20

* todavía me tengo que poner a pelear con el sonido, la wireless y como siempre, flash...

del lado bueno

* los ports son algo que se extraña de gentoo
* aguante FreeBSD :D

Are my ideas being stolen?

de un articulo de slashdot sale el siguiente comentario

http://ask.slashdot.org/comments.pl?sid=1082003&cid=26345371

In "The Zen of Graphics Programming", Michael Abrash (a co-author of Quake and inventor of Mode X) wrote:

-------------
Our world is changing, and I'm concerned. By way of explanation, three anecdotes.

Anecdote the first: In one of his books, Frank Herbert, author of Dune, told me how he had once been approached by a friend who claimed he (the friend) had a killer idea for a SF story, and offered to tell it to Herbert. In return, Herbert had to agree that if he used the idea in a story, he'd split the money from the story with this fellow. Herbert's response was that ideas were a dime a dozen; he had more story ideas than he could ever write in a lifetime. The hard part was the writing, not the ideas.

Anecdote the second: I've been programming micros for 15 years, and been writing about them for more than a decade and, until about a year ago, I had never-not once!- had anyone offer to sell me a technical idea. In the last year, it's happened multiple times, generally via unsolicited email along the lines of Herbert's tale.

This trend toward selling ideas is one symptom of an attitude that I've noticed more and more among programmers over the past few years-an attitude of which software patents are the most obvious manifestation-a desire to think something up without breaking a sweat, then let someone else?s hard work make you money. Its an attitude that says, "I'm so smart that my ideas alone set me apart." Sorry, it doesn't work that way in the real world. Ideas are a dime a dozen in programming, too; I have a lifetime's worth of article and software ideas written neatly in a notebook, and I know several truly original thinkers who have far more yet. Folks, it's not the ideas; it's design, implementation, and especially hard work that make the difference.

Virtually every idea I've encountered in 3-D graphics was invented decades ago. You think you have a clever graphics idea? Sutherland, Sproull, Schumacker, Catmull, Smith, Blinn, Glassner, Kajiya, Heckbert, or Teller probably thought of your idea years ago. (I'm serious-spend a few weeks reading through the literature on 3-D graphics, and you'll be amazed at what's already been invented and published.) If they thought it was important enough, they wrote a paper about it, or tried to commercialize it, but what they didn't do was try to charge people for the idea itself.

A closely related point is the astonishing lack of gratitude some programmers show for the hard work and sense of community that went into building the knowledge base with which they work. How about this? Anyone who thinks they have a unique idea that they want to "own" and milk for money can do so-but first they have to track down and appropriately compensate all the people who made possible the compilers, algorithms, programming courses, books, hardware, and so forth that put them in a position to have their brainstorm.

Put that way, it sounds like a silly idea, but the idea behind software patents is precisely that eventually everyone will own parts of our communal knowledge base, and that programming will become in large part a process of properly identifylng and compensating each and every owner of the techniques you use. All I can say is that if we do go down that path, I guarantee that it will be a poorer profession for all of us - except the patent attorneys, I guess.

Anecdote the third: A while back, I had the good fortune to have lunch down by Seattle's waterfront with Neal Stephenson, the author of Snow Crash and The Diamond Age (one of the best SF books I've come across in a long time). As he talked about the nature of networked technology and what he hoped to see emerge, he mentioned that a couple of blocks down the street was the pawn shop where Jimi Hendrix bought his first guitar. His point was that if a cheap guitar hadn't been available, Hendrix's unique talent would never have emerged. Similarly, he views the networking of society as a way to get affordable creative tools to many people, so as much talent as possible can be unearthed and developed.

Extend that to programming. The way it should work is that a steady flow of informa-tion circulates, so that everyone can do the best work theyre capable of. The idea is that I don't gain by intellectually impoverishing you, and vice-versa; as we both compete and (intentionally or otherwise) share ideas, both our products become better, so the market grows larger and everyone benefits.

That's the way things have worked with programming for a long time. So far as I can see it has worked remarkably well, and the recent signs of change make me concerned about the future of our profession.

Book meme

Ningún Test lo es. - Sueñan los androides con ovejas eléctricas? Philip K. Dick
link: http://fitoria.net/2008/11/13/book-meme/

  • Agarra el libro mas cercano
  • Abrelo en la pagina 56.
  • Encuentra la quinta oración.
  • Postealo junto con estas instrucciones
  • No vayas por tu libro favorito solo agarra el que este MAS CERCA
  • Poner la url del site donde leíste el meme

Xml -> dict -> objetos

En este ejemplo se muestra como convertir un string a una estructura de diccionarios y listas anidadas usando expat, tambien se proveen dos clases que permiten manipular el resultado como si fueran objetos.

primero el codigo

import xml.parsers.expat

class XmlParser(object):
'''a class that parses a xml string an generates a nested
dict/list structure
'''

def __init__(self, text):
'''constructor'''
self.parser = xml.parsers.expat.ParserCreate()
self.parser.buffer_text = True

self.result = None
self.stack = []
self.current = None

self.parser.StartElementHandler = self.start_element
self.parser.EndElementHandler = self.end_element
self.parser.CharacterDataHandler = self.char_data
self.parser.Parse(text)

def start_element(self, name, attrs):
'''Start xml element handler'''
if self.current != None:
self.stack.append(self.current)

self.current = {}

for (key, value) in attrs.iteritems():
self.current[str(key)] = value

self.current['tag'] = name
self.current['childs'] = []

def end_element(self, name):
'''End xml element handler'''
if len(self.stack):
current = self.stack.pop()
current['childs'].append(self.current)
self.current = current
else:
self.result = self.current

def char_data(self, data):
'''Char xml element handler.
buffer_text is enabled, so this is the whole text element'''
self.current['childs'].append(data)

class DictObj(dict):
'''a class that allows to access a dict as an object
'''

def __init__(self, kwargs):
'''constructor'''
dict.__init__(self, kwargs)

def __getattribute__(self, name):
if name in self:
obj = self[name]

if type(obj) == dict:
return DictObj(obj)
elif type(obj) == list:
return ListObj(obj)

return obj
else:
return None

class ListObj(list):
'''a class that allows to access dicts inside a list as objects
'''

def __init__(self, args):
'''constructor'''
list.__init__(self, args)

def __getitem__(self, index):
if index > len(self):
raise IndexError('list index out of range')

obj = list.__getitem__(self, index)

if type(obj) == dict:
return DictObj(obj)
elif type(obj) == list:
return ListObj(obj)

return obj

def __iter__(self):
'''iterate over the list'''

count = 0

while count < len(self):
yield self[count]
count += 1

def raw_string(dct_):
'''return a string containing just the string parts removing all the
xml stuff'''

def helper(dct):
result = []

for child in dct.childs:
if type(child) == str or type(child) == unicode:
result.append(str(child))
else:
result = result + helper(child)

return result

return ''.join(helper(dct_))



Simplemente creamos un objeto de tipo XmlParser pasandole el string y obtenemos el resultado parseado en la variable result. Si no queremos andar preguntado si las llaves existen antes de accederlas para evitar excepciones podemos usar la clase DictObj que nos permite acceder a las llaves como si fueran atributos, las variables que no existan como llaves contendran None. Aca va un ejemplo en la consola interactiva


>>> import XmlParser
>>> p = XmlParser.XmlParser('google test foo!!')
>>> r = p.result
>>> d = XmlParser.DictObj(r)
>>> d
{'childs': [{'childs': [u'go', {'childs': [u'o'], 'tag': u's'}, u'gle'], 'href': u'google.com', 'tag': u'a'}, u' ', {'childs': [u'test'], 'tag': u'i'}, u' ', {'childs': [], 'src': u'foo.png', 'alt': u'foo', 'tag': u'img'}, u' ', {'childs': [u'!'], 'tag': u'u'}, {'childs': [u'!'], 'tag': u's'}], 'tag': u'span'}
>>> d.childs
[{'childs': [u'go', {'childs': [u'o'], 'tag': u's'}, u'gle'], 'href': u'google.com', 'tag': u'a'}, u' ', {'childs': [u'test'], 'tag': u'i'}, u' ', {'childs': [], 'src': u'foo.png', 'alt': u'foo', 'tag': u'img'}, u' ', {'childs': [u'!'], 'tag': u'u'}, {'childs': [u'!'], 'tag': u's'}]
>>> d.childs[0]
{'childs': [u'go', {'childs': [u'o'], 'tag': u's'}, u'gle'], 'href': u'google.com', 'tag': u'a'}
>>> d.childs[0].tag
u'a'
>>> d.childs[0].childs[0]
u'go'
>>> d.childs[0].childs[1].tag
u's'