Posted by Jarosław Zabiełło
Mon, 01 Jan 2007 18:36:00 GMT
Korzystając z tego, że niedawno do repozytorium Rubiego został dodany YARV (wirtualna maszyna Rubiego) pokusiłem się o małe porównanie wydajności. Jako test użyłem prostego programu jaki pojawił się na liście pl.comp.lang.pyhon (musiałem tylko trochę zwiększyć pętle do 10 milionów iteracji, aby były widoczne jakieś wyniki) Użyty sprzęt: Athlon64 3700+, 64bit Ubuntu 6.0.6.
Sprawdziłem też z ciekawości PHP 5.2 na tym samym sprzęcie. Dla tak prostego testu powinien móc dać z siebie wszystko. Jest jednak wolniejszy od Rubiego 1.9.
Kod dla Pythona:
import time
t=time.time()
a=0
for i in xrange(10000000):
a += i
print a
print time.time() - t
Kod dla Lua:
t = os.clock()
a = 0
for i = 1,10000000 do
a=a+i
end
print(a)
print(os.clock() - t)
Kod dla Rubiego:
t = Time.now
a = 0
for i in 1..10_000_000
a = a+i
end
puts a
puts Time.now - t
Kod dla PHP:
<?php
function microtime_float() {
$time = microtime();
return (double)substr($time, 11) + (double)substr($time, 0, 8);
}
$t = microtime_float();
$a = 0;
for ($i = 0; $i < 10000000; $i++) {
$a += $i;
}
print "$a\n";
print microtime_float()- $t;
?>
Wyniki:
- Lua: 1.14 s
- Ruby 1.9: 1.49 s
- PHP 5.2: 1.74 s
- Python 2.5: 2.64 s
- Python 2.4.3: 2.72 s
- Ruby 1.8.5: 3.83 s.
Drugie podejście
Tym razem wynik uśredniam dla 10 prób. Zwiększyłem ilość iteracji do 20 mln. No i… Ruby 1.9 pobił wszystko. Zarówno Pythona jak i Lua! (PHP 5.2 po 30 sek. zgłosił timeout. Musiałem więc uśrednić po 5 wynikach.)
PYTHON
import time
def bench():
a, t = 0, time.time()
for i in xrange(20000000):
a+=i
return time.time()-t
result = 0
for i in range(10):
result += bench()
print result / 10.0
RUBY
def bench
a, t = 0, Time.now
20_000_000.times { |i| a += i }
Time.now - t
end
result = 0
10.times { |i| result += bench }
puts result / 10
LUA
function bench()
a = 0
t = os.time()
for i = 1, 20000000 do
a = a + i
end
return os.time() - t
end
result = 0
for x = 1,10 do
result = result + bench()
end
print(result/10)
PHP
<?php
function bench() {
$a = 0;
$t = time();
for ($i = 0; $i < 20000000; $i++) $a += $i;
return time() - $t;
}
$result = 0;
for ($x = 1; $x <= 5; $x++) $result += bench();
print $result / 5.0;
?>
Wyniki
- Ruby 1.9: 2.0953268 s.
- Lua 5.1: 2.4 s.
- Python 2.5: 2.49481592178 s.
- Python 2.4.3: 2.71687791348 s.
- PHP 5.2: 4.2 s.
- Ruby 1.8.5: 9.0153751 s.
Jestem ciekaw kiedy wyjdzie Rails dostosowany do Ruby 1.9. Zapowiada się bardzo obiecująco!
Posted in PHP, Python, Ruby | Tags benchmark, lua, php, python, ruby, yarv | 19 comments
Posted by Jarosław Zabiełło
Thu, 14 Dec 2006 23:48:00 GMT
Nowy Skype 3 wprowadza małą rewolucję w stos. do poprzedniej wersji. Można nie tylko rozmawiać, ale pograć w szachy, kółko i krzyżyk i inne gry (są b. ładnie zrobione we Flashu 9). Można nagrywać rozmowy na dysk. Można tworzyć publiczne czaty. I właśnie w tej sprawie piszę ten tekst bo
stworzyłem polski czat dla miłośników frameworków Ruby on Rails, Django i Pylons.
Wygodniej jest czasem skonsultować coś w czasie rzeczywistym niż na grupie czy forum dyskusyjnym.
Posted in Ruby on Rails, Pylons, Python, Django (en), Ruby, Django | Tags django, pylons, rails | 17 comments
Posted by Jarosław Zabiełło
Fri, 01 Dec 2006 00:47:00 GMT

Mimo że o języku Ruby mówi się szerzej raczej od niedawna, ilość książek i dokumentacji dostępnej do Rubiego zdążyły już pobić to, co zostało napisane dla języka Python i stale się powiększa. Trudno aby taką sytuację nie wykorzystały polskie firmy wydawnicze.
Jeszcze w grudniu 2006 powinna pojawić się w księgarniach polska wersja kultowej książki na temat Rubiego – tzw. PickAxe21 czyli Programming Ruby: The Pragmatic Programmers. Pierwsze wydanie tej książki przyczyniło się w znacznej mierze do popularyzacji języka poza Japonią. Jest ono dostępne darmo w internecie. Część druga, poza tym, że jest znacznie obszerniejsza, i uwzględnia nowości w języku w stos. do starego wydania z 1993 roku.
1 Nazwa PickAxe pochodzi od charakterystycznej okładki z kilofem (ang. pick axe) Dwójka oznacza drugie wydanie.
Posted in Ruby | Tags ruby | 1 comment
Posted by Jarosław Zabiełło
Wed, 22 Nov 2006 01:26:00 GMT
Każdy, kto zajmował się kiedykolwiek programowaniem stron internetowych wie, że protokół HTTP jest bezstanowy. Innymi słowy, każde przeładowanie strony internetowej generuje kompletnie niezależne wywołanie serwera i nie ma on możliwości aby powiązać wywołanie z konkretnym klientem. Aby temu zaradzić, wprowadzono prostą sztuczkę. Wprowadzono tzw. mechanizm cookies (dosł. ciasteczek), malutkich plików które serwer może wysyłać do klienta (tam i z powrotem). Ten unikalny identyfikator (sesji) można też trzymać w adresie URL, ale ta metoda jest zdecydowanie odradzana z powodu łatwości nadużyć (łatwo wykraść taki numer i podszyć się pod autora) Od strony klienta jedynym wymaganiem jest aby nie blokował sobie w przeglądarce mechanizmu cookies.
Serwer znając identyfikator sesji, może związać z nim szereg danych, np. aktualną listę zamawianych produktów włożonych do koszyka w sklepie internetowym. Dane te zwykle trzyma się w bazie lub w pamięci operacyjnej. Oczywiście czas życia danych jest ustalany przez serwet i w normalnym wypadku są one cyklicznie kasowane. (Z tym “normalnym” wypadkiem to różnie bywa, bo wiele frameworków tego nie zapewnia i trzeba samemu o to zadbać)
Mechanizm sesji jest dosyć wydajny, bo zapytania wysyłane do serwera mogą być obsługiwane asynchronicznie, czyli bez żadnych blokad w oczekiwaniu na ukończenie obsługi zapytania. Przez lata “stanowość” protokołu HTTP zapewniało się właśnie za pomocą sesji.
Ten mechanizm ma jednak swoje wady. Największą jest złożoność jaką wprowadza po stronie skryptów na serwerze. (I to nie jest wcale taka trywialna sprawa jak to można było zaobserwować u developerów frameworka CherryPy którzy miesiącami nie potrafili napisać stabilnie działającego mechanizmu obsługi sesji.)
Kontynuacje
Kompletnie innym podejściem jest skorzystanie z tzw. mechanizmu kontynuacji. Mechanizm ten podobny jest do gracza, który w dowolnym momencie może zapisać i wczytać stan gry. Kontynuacje zapewniają taki mechanizm zapisu stanu programu aby w dowolnym momencie później go przywrócić. Niektórzy porównują to do maszyny czasu, bo można w dowolnym momencie przywrócić wcześniejszy stan aplikacji.
Zalet z takiego podejścia jest wiele. Weźmy np. sprawę obsługi “nieśmiertelnego” przycisku Back w przeglądarce. Obsługa tego przycisku sprawia spore problemy. Programiści najchętniej w ogóle by go zablokowali. W wypadku kontynuacji ten problem w ogóle nie istnieje. Kliknął ktoś Back aby cofnąć się do poprzedniej strony? Żaden problem. Serwer przywróci cały wcześniejszy stan aplikacji bez żadnego problemu. I to w sposób przezroczysty dla programisty! Programowanie stron webowych staje się znacznie prostsze, niczym tworzenie aplikacji desktopowej włącznie z debugowaniem krok po kroku. Wg różnych prognoz, tak będziemy programować serwisy internetowe za kilka lat!
Sama idea nie jest najwyraźniej jeszcze powszechnie znana, skoro w rozmowie z developerami irlandzkiego Google, zaskoczyłem ich informacją na ten temat. Kto wie, czy Google mocniej nie zainwestuje w Rubiego (aktualnie mocno korzysta z Pythona, który jest podstawą większości ich skryptów).
Smalltalk i Seaside
Smalltalk jest bardzo starym językiem bo jego początki sięgają lat 70-tych. Od zawsze był językiem w pełni obiektowym i dynamicznym. To m.in. Smalltalk pierwszy wymyślił wirtualną maszynę której to ideę później skopiowali twórcy Javy. W Smalltalku napisano najlepszy (w chwili obecnej) na świecie serwer kontynuacyjny – Seaside.
Czy to spowoduje renesans na Smalltalka? Osobiście w to wątpię. Główną wadą Smalltalka jest jego monolityczność i hermentyczność. Uruchamiając środowisko Smalltalak uruchamia się cały, zamknięty świat Smalltalka. Np. nie ma tam dostępu do plików z zewnątrz tak, jak w innych językach. To spowodowało, że język ten, mimo pewnych sukcesów, nigdy nie zdobył sobie powszechnego uznania. I moim zdanie, nigdy już nie zdobędzie, bo w międzyczasie wyrosła mu młoda, obiecująca konkurencja.
Ruby
Ruby jest językiem relatywnie młodym. Tzn. został po raz pierwszy udostępniony publicznie w tym samym roku co Java – 1995. Ale przez większość lat był mało znany poza Japonią (skąd pochodzi jego twórca). Ruby jest także jednym z tych nielicznych języków, które (podobnie jak Smalltalk) posiadają wbudowaną obsługę kontynuacji. Istnieją co prawda różne próby implementacji tego mechanizmu w Javie1, ale tylko Ruby posiada ten mechanizm jako coś naturalnego i nie trzeba stosować żadnych specjalnych sztuczek aby go emulować. Z tego co pamiętam, Seaside pierwotnie był tworzony w Ruby, ale w tamtym okresie, kontynuacje w Ruby były jeszcze niedojrzałe, więc twórcy przerzucili się na Smalltalka. Ruby jednak szybko się rozwija, nabiera dojrzałości i moim zdaniem, ma bardzo duże szanse, aby wyprzedzić pozostałe języki przy tworzeniu serwisów internetowych nowej generacji, serwisów opartych na kontynuacjach.
Przykład kontynuacji.
Poniższy przykład2 pokazuje implementację generatora za pomocą kontynuacji.
class Generator
def initialize
do_generation
end
def next
callcc do |here|
@main_context = here;
@generator_context.call
end
end
private
def do_generation
callcc do |context|
@generator_context = context;
return
end
generating_loop
end
def generate(value)
callcc do |context|
@generator_context = context;
@main_context.call(value)
end
end
end
A oto oparty na nim generator ciągu liczb Fibbonacciego:
class FibGenerator < Generator
def generating_loop
generate(1)
a, b = 1, 1
loop do
generate(b)
a, b = b, a+b
end
end
end
fib = FibGenerator.new
15.times { print "#{fib.next} " }
1 Np. Riff lub Jetty 6.
2 Przykład z książki “The Ruby Way”, 1ed.
Posted in Ruby, Java, SmallTalk | Tags continuations, java, ruby, smalltalk | 23 comments
Posted by Jarosław Zabiełło
Tue, 07 Nov 2006 23:34:00 GMT
Społeczność Rubiego jest może mniejsza niż Pythona, ale jest jakaś bardziej aktywna i zorganizowana (przynajmniej tak to widać w Polsce). Nie tylko trwają prace nad tłumaczeniem strony domowej Rubiego na język polski, ale także coraz częściej są organizowane spotkania miłośników tego języka.
Najbliższe spotkanie jest planowane na 15 listopada 2006 w Warszawie. Niedługo później, 18 listopada jest organizowane spotkanie w Krakowie.
Od niedawna też oficjalna strona języka Ruby http://ruby-lang.org zgłasza się po polsku (w wypadku przegłądarki ustawionej na jęz. polski). Inni też mogą wejść na stronę polską, która jest bezpośrednio dostępna spod adresu: http://www.ruby-lang.org/pl/.
Co ciekawe, zaczyna się pojawiać coraz więcej ofert pracy dla programistów Rubiego. Oczywiście siłą napędową języka jest wciąż Rails. :)
Posted in Ruby | Tags rails, ruby | no comments
Posted by Jarosław Zabiełło
Tue, 07 Nov 2006 23:13:00 GMT
RadRails, zintegrowany edytor dla Rubiego i Railsów zaczyna coraz bardziej niwelować wcześniejsze zachwyty nad komercyjnym edytorem TextMate. Nie dość, że jest darmowy i działa na Windows, MacOS-X i Linuksie (TextMate tylko na MacOS-X), to na dodatek uzyskał to, co do tej pory było główną atrakcją TextMate – makra przyśpieszające pisanie kodu, czyli tzw. snippety. RadRaila ma już przygotowanych 199 snippetów do Rubiego i 50 do RHTML!
Do tego wystarczy dorzucić kolorowanie kodu a’la TextMate i RadRails ma już wszystko (no może do pełni doskonałości brakuje mu uzupełniania kodu tak, jak to robi komercyjny edytor Komodo)
Posted in Ruby on Rails, Ruby | Tags edytory, radrails, rails, ruby | no comments
Posted by Jarosław Zabiełło
Thu, 12 Oct 2006 14:55:31 GMT
Poza polskim forum mamyod niedawna kanał #rubyonrails.pl na serwerze irc.freenode.net gdzie można bardziej interaktywnie wymieniać się doświadczeniem na temat Rubiego i/lub Railsów. Na razie nie ma wiele osób, ale może się bardziej rozkręci.
Posted in Ruby on Rails, Ruby | 5 comments
Posted by Jarosław Zabiełło
Sat, 07 Oct 2006 00:57:00 GMT
Postanowiłem troszkę poprawić napisany wcześniej skrypt startowy do Subversion. Dorzuciłem też wersję w Pythonie.
VERBOSE = true
USER = "nobody"
APP = "/usr/bin/svnserve"
DIR = "/home/svn-repository"
PID = "/var/run/svnserve.pid"
def script action
def start
unless File.exists?(PID)
system %Q|su -c "#{APP} -d -r #{DIR}" #{USER}|
system "echo `pidof -o %PPID #{APP}` > #{PID}"
end
end
def stop
return false unless File.exists?(PID)
system "/bin/kill -TERM #{File.read(PID)}"
system "/bin/rm -f #{PID}"
true
end
puts "#{APP} #{action}" if VERBOSE
$defout.flush
case action
when :start
start
when :stop
puts "#{File.basename(APP)} cannot be stopped because it cannot find #{PID}" unless stop
when :restart
stop
start
end
end
if %w{start stop restart}.include? ARGV.first
script ARGV.first.to_sym
else
puts "Usage: sudo #{__FILE__} {start|stop|restart}"
end
Ten sam skrypt w Pythonie:
import os, sys
VERBOSE = True
USER = "nobody"
APP = "/usr/bin/svnserve"
DIR = "/home/svn-repository"
PID = "/var/run/svnserve.pid"
def script(action):
def start():
if not os.path.exists(PID):
os.system('su -c "%s -d -r %s" %s' % (APP, DIR, USER))
os.system('echo `pidof -o %%PPID %s` > %s' % (APP, PID))
def stop():
if not os.path.exists(PID): return False
os.system('/bin/kill -TERM %s' % file(PID).read())
return True
if VERBOSE: print "%s %s" % (APP, action),
if action == 'start':
start()
elif action == 'stop':
if not stop():
print "%s cannot be stopped because it cannot find %s" % (os.path.basename(APP), PID)
elif action == 'restart':
stop()
start()
if len(sys.argv) == 2 and sys.argv[1] in ('start', 'stop', 'restart'):
script(sys.argv[1])
else:
if VERBOSE: print "Usage: sudo %s {start|stop|restart}" % sys.argv[0]
Posted in Python, Ruby | Tags python, ruby, subversion | 2 comments
Posted by Jarosław Zabiełło
Thu, 05 Oct 2006 14:37:00 GMT
Subversion to świetny, darmowy system wersjonowania kodu. Jest znacznie lepszy od starego CVS’a. Niestety, nigdzie nie mogłem się doszukać skryptu startującego do serwera. Co gorsze, skrytp svnserve w ogóle nie ma opcji zapisu numeru swojego procesu co utrudnia pisanie kodu który go “zabije”. Na szczęście Linux posiada komendę pidof. Poniżej gotowy skrypt napisany w Ruby. Należy go umieścić w /etc/init.d, dodać atrybuty wykonywalności (chmod a+x subversion.rb) i dodać linki symboliczne aby startowała utomatycznie razem ze startem serwera (pod Debianem/Ubuntu robi to komenda update-rc.d -f subversion.rb defaults, pod RedHatem/CentOS/Fedorą jest do tego komenda chkconfig)
$USER = "nobody"
$APP = "/usr/bin/svnserve"
$DIR = "/home/SVN"
$PID = "/var/run/svnserve.pid"
def script action
def start
system(%Q|su -c "#{$APP} -d -r #{$DIR}" #{$USER}|)
system(%Q|echo `pidof -o %PPID #{$APP}` > #{$PID}|)
end
def stop
return false if not File.exists?($PID)
system(%Q|/bin/kill -TERM #{File.read($PID)}|)
true
end
print "#{$APP} #{action}..."
$defout.flush
case action
when :start
start()
puts "done"
when :stop
if not stop()
puts "#{File.basename($APP)} cannot be stopped because it cannot find #{$PID}"
else
puts "done"
end
when :restart
stop()
start()
puts "done"
end
end
case ARGV.first
when "start": script :start
when "stop": script :stop
when "restart": script :restart
end
unless %w{start stop restart}.include? ARGV.first
puts "Usage: sudo #{__FILE__} {start|stop|restart}"
exit
end
Posted in Ruby | Tags linux, ruby, subversion | 2 comments
Posted by Jarosław Zabiełło
Fri, 29 Sep 2006 12:04:00 GMT
Ruby całkiem dobrze nadaje się do pisania skryptów systemowych. W sumie nie wiem dlaczego do tej pory pisałem skrypty tego typu w uniksowym bash’u. :)
MONGREL = "/usr/bin/mongrel_rails"
PROJECT = "/home/httpd/somedomain.com/rails_project"
def execute(action, project)
Dir.chdir(project)
print "#{MONGREL} #{action}..."
$defout.flush
`
puts 'done'
end
case ARGV.first
when 'start': execute 'start', PROJECT
when 'stop': execute 'stop', PROJECT
when 'restart': execute 'restart', PROJECT
end
unless %w{start stop restart}.include? ARGV.first
puts "Usage: sudo #{__FILE__} {start|stop|restart}"
exit
end
Posted in Ruby | Tags linux, mongrel, ruby | 1 comment