вторник, 19 февраля 2008 г.

Оптимизация кода

Сегодня удалось добиться примерно десятикратного ускорения работы моего парсера. Проблема вообще возникла из-за того, что я относился к обработке строк в С/С++ стиле - преобразовывал строку с массив символов и переберал их для нахождения начала и окончания блоков. Делать это на Ruby - далеко не лучшая идея. На языке высокого уровня нет смысла использовать низкоуровневый подход. Если в С при переборе символов строки достаточно инкрементировать значение указателя, то здесь будет создан(а затем и уничтожен) объект для каждого символа. В общем совершенно ничего хорошего. Поэтому использовавние функции String#index с простейшим регекспом даёт значительный эффект. Что конечно же не может не радовать.
Для обзначения начала и окончания блоков используются символы '{' и '}'. Для поиска конца блока использовалась следующая функция


def find_end(str)
arr = str.split(//)
open_count = 0
close_count = 0
pos = 0
arr.each do |x|
open_count += 1 if(x == "{")
close_count += 1 if(x == "}")
if(open_count == close_count)
return pos
end
pos += 1
end
nil
end


Теперь эта функция выглядит так:

def find_end(str)
open_count = 0
close_count = 0
pos = 0
while(pos != nil)
pos = str.index(/\{|\}/, pos)
if(pos != nil)
open_count += 1 if str[pos] == 123
close_count += 1 if str[pos] == 125
end
if(open_count == close_count)
return pos
end
pos += 1
end
nil
end


Вероятно этот код можно ещё ускорить, но я пока не знаю как. Кроме ускорения за счёт оптимального использования возможностей языка, значительного эффекта можно добиться за счёт применения более эффективного алгоритма. Здесь у меня тоже есть над чем поработать. =)

пятница, 15 февраля 2008 г.

Проблемы с производительностью

В проекте, над которым я сейчас работаю, достаточно активно используются файлы в xml-подобном формате. И я решил написать для него парсер на Ruby. Сделать это оказалось достаточно просто, но вылезла другая проблема - проблема производительности. Файл в 650 строк на моём стариньком ноутбуке (500 МГц P3) парсится примерно 20 секунд. В Ruby есть очень полезаная библиотека profiler, позволяющая посмотреть сколько именно времени занимает каждая из частей программы. Причём для того, чтобы её применить не нужно ничего менять в своём коде, а достаточно только подключить профайлер. Так вот использование профайлера показало мне, что больше всего работы происходит внутри Array#each. Сейчас думаю, каким образом можно всё ускорить. Возможность сделать всё быстро доказывается наличием парсера rexml, полностью написаного на Ruby.

пятница, 8 февраля 2008 г.

Ещё одна книжка.

Предыдущую книгу я осилил почти неделю назад. Теперь взялся за следующую - Everyday Scripting with Ruby: for Teams, Testers, and You by Brian Marick. Эта книга также из той же серии, что и Programming Ruby. Здесь рассмотрено несколько практических примеров использования Ruby для всяческой автоматизации работы. Например инвентаризация файла или подсчёт количества изменений в SVN.

понедельник, 21 января 2008 г.

Книжка по Ruby

Нашёл и скачал в интернете неплохую книгу по Ruby - Programming Ruby. The Pragmatic Programmer's by Guide. David Thomas Andrew Hunt. Это книга тех же авторов, что и книги под названием "Программист-прагматик. Путь от подмастерья к мастеру", которую я читал ранее. Несмотря на то, что эта книга на английском, читается достаточно легко, чтобы я не бросил это занятие =).
Книга состоит из четырёх основных частей.

  1. Facets of Ruby. В этой части рассмотрены основные особенности Ruby и его отличия от других языков.
  2. Ruby in Its Setting. Здесь рассмотрено каким образом Ruby взаимодействует со своим окружением.
  3. Ruby Crystallized. Эта часть содержит в себе более продвинутый материал, относящийся к "сложным" частям языка.
  4. Ruby Library Reference. Описание встроенных классов и функций языка.

четверг, 17 января 2008 г.

Реальное применение

Вчера на работе понадобилось преобразовать файл из SVG-формата, в наш собственный. И для этого я решил написать скрипт. Вначале попробовал это сделать на Tcl, но у него таки несколько грамоздкий синтаксис, когда нужно заниматься поиском подстрок и тому подобными вещами(допускаю, что я просто недостаточно хорошо разобрался). Поэтому я использовал Ruby. Здесь проблем вообще не возникло. Я использовал то, что уже делал раньше и всё легко получилось. Так что похоже это и есть первый случай реального применения мной Ruby.

вторник, 15 января 2008 г.

Странная находка

Неожиданно обнаружил, что в составе Ruby присутствует библиотека tcltk, позволяющая использовать интерпритатор Tcl в Ruby-программе. Никак не могу придумать зачем это может понадобиться...

понедельник, 14 января 2008 г.

А теперь на Ruby

Сегодня я реализовал на Ruby ту же программу для копирования с пропусками, что ранее сделал на Tcl. На Ruby получилось следующее:



require 'find'
require 'fileutils'
#счётчики количества файлов и папок
file_count = 0
dir_count = 0

if(ARGV.size != 2)
puts "Script usage:"
puts "ruby no_svn_copy.rb source destination"
end

#получаем и нормализуем пути
src = File.expand_path(ARGV[0])
dst = File.expand_path(ARGV[1])
puts src + ' => ' + dst

#обрабатываем всё что есть в директории src
Find.find(src) do |path|
if(path.scan(".svn").size != 0)
next
end
#формируем путь к цели
filename = dst + path.gsub(src, '')

puts path
#если это директория, то создаём её в новом месте
if (FileTest.directory?(path))
dir_count += 1
FileUtils.mkdir_p filename
end
#если файл, то копируем его
if (FileTest.file?(path))
file_count += 1
FileUtils.cp path, filename
end
end
#выводим статистику
puts "dirs: " + dir_count.to_s +
" files: " + file_count.to_s



Основное отличие программы на Ruby от Tcl заключается в том, что я использовал возможности модуля Find, что позволило сразу получить список всех исходных файлов и папок, а затем обойти его в цикле, а не делать это самостоятельно с помощью рекурсии. Время выполнения в обоих случаях примерно одинаковое.