Hi, I have written a function with 461 queries in a MySQL database. With macirb, it takes more or less 1s for all queries. I put my file in a Xcode project and when I click on a button, the action is to run this function. However, it takes more or less 1s for EACH query! I use normal schedule, not deployment as explained in the definitive guide, (I don't want give the exact title to not make advertisement in a post lol ) What's wrong? François
2011/12/20 François Boone <francois.boone@usherbrooke.ca>
Hi,
I have written a function with 461 queries in a MySQL database. With macirb, it takes more or less 1s for all queries. I put my file in a Xcode project and when I click on a button, the action is to run this function. However, it takes more or less 1s for EACH query! I use normal schedule, not deployment as explained in the definitive guide, (I don't want give the exact title to not make advertisement in a post lol )
What's wrong?
Hi François, Can you give a bit more context, perhaps? In macirb, are you loading a file with the function or entering it at the prompt? In Xcode, do the queries result from a pure ruby file, or are you doing a ruby call from Obj-C? What libraries/gems are you using to connect to MySQL? Is there an 'eval' call involved in making the queries? To be honest, things should be much faster from Xcode, since macruby_deploy will compile all of your ruby sources to .rbo files. The exception would be if your code contains copious use of "eval". It is also possible that you have stumbled on a perf bug (the best kind of bug, IMHO), in which case it would be great if you could reduce the code down to something minimal that other devs could run locally. Cheers, Josh
Le 2011-12-21 à 15:17, Joshua Ballanco a écrit :
2011/12/20 François Boone <francois.boone@usherbrooke.ca> Hi,
I have written a function with 461 queries in a MySQL database. With macirb, it takes more or less 1s for all queries. I put my file in a Xcode project and when I click on a button, the action is to run this function. However, it takes more or less 1s for EACH query! I use normal schedule, not deployment as explained in the definitive guide, (I don't want give the exact title to not make advertisement in a post lol )
What's wrong?
Hi François,
Can you give a bit more context, perhaps? In macirb, are you loading a file with the function or entering it at the prompt? In Xcode, do the queries result from a pure ruby file, or are you doing a ruby call from Obj-C? What libraries/gems are you using to connect to MySQL? Is there an 'eval' call involved in making the queries?
To be honest, things should be much faster from Xcode, since macruby_deploy will compile all of your ruby sources to .rbo files. The exception would be if your code contains copious use of "eval". It is also possible that you have stumbled on a perf bug (the best kind of bug, IMHO), in which case it would be great if you could reduce the code down to something minimal that other devs could run locally.
Cheers,
Josh _______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
Hi Josh, In macirb, I use: load "actionAffiche.rb" 461 queries, real time: 11.578257s In Xcode: I use rubygems and mysql macgems I have one Button and one table with two columns: when I press the button, the function actionAffiche is running. I think that it's a complete minimal example. 461 queries, real time: 64.244236s Apart MainMenu.xib, all my code is in AppDelegate.rb. I put this file at the end of this mail (Is there a better way?) I don't have control on mysql tables since they come from another software. I am a novice, then you can criticize my code :) Regards, François ------- # # AppDelegate.rb # ecm: exemple complet minimal # # Created by Boone François on 11-12-21. # Copyright 2011 Boone François. All rights reserved. # require 'rubygems' require 'mysql' $data = [] class AppDelegate attr_accessor :window def applicationDidFinishLaunching(a_notification) # Insert code here to initialize your application end end class CtrButton attr_writer :individu def awakeFromNib @individu.dataSource = self $ref_individu = @individu end def affiche(sender) actionAffiche end def numberOfRowsInTableView(individu) $data.size end def tableView(individu, objectValueForTableColumn:column, row:index) item = $data[index] case column.identifier when 'iden' item.iden when 'name' item.name end end end class Person attr_accessor :iden, :name end def actionAffiche t1 = Time.now # Connexion au serveur MySQL begin dbh = Mysql.real_connect("localhost", "root", "", "Boone") ## Récupération de la version du serveur et affichage puts "Version du serveur: " + dbh.get_server_info rescue Mysql::Error => e puts "Code d'erreur : #{e.errno}" puts "Message d'erreur : #{e.error}" puts "SQLSTATE d'erreur : #{e.sqlstate}" if e.respond_to?("sqlstate") ensure dbh.query("SET NAMES utf8") end # Requête pour le nom de famille dbh.query("SET NAMES utf8") requete = "SELECT handle,surname FROM surname ORDER BY surname" reponse = dbh.query(requete) num_max = reponse.num_rows num = 1 # Pour chaque nom de famille, on cherche le prenom reponse.each_hash do |lastname| puts "Individu " + num.to_s + " sur " + num_max.to_s individu = Person.new individu.iden = lastname["handle"] individu.name = lastname["surname"] # pour chaque nom, recherche du prenom à partir du handle. requete2 = "SELECT first_name,xcall FROM name WHERE handle ='" + lastname["handle"] + "'" reponse2 = dbh.query(requete2) reponse2.each_hash do |firstname| individu.name = individu.name + ", " + firstname["first_name"] if firstname["xcall"] != "" individu.name = individu.name + " (" + firstname["xcall"] + ")" end end $data.push(individu) num += 1 end # On ferme le lien mySQL dbh.close t2 = Time.now puts (t2-t1) # Mise à jour de l'interface $ref_individu.reloadData end
2011/12/21 François Boone <francois.boone@usherbrooke.ca>
Hi Josh,
In macirb, I use: load "actionAffiche.rb" 461 queries, real time: 11.578257s
In Xcode: I use rubygems and mysql macgems I have one Button and one table with two columns: when I press the button, the function actionAffiche is running. I think that it's a complete minimal example. 461 queries, real time: 64.244236s Apart MainMenu.xib, all my code is in AppDelegate.rb. I put this file at the end of this mail (Is there a better way?)
I don't have control on mysql tables since they come from another software.
I am a novice, then you can criticize my code :)
Not at all. Your code is quite ok. The only comments I would have is that you should avoid using globals (variables starting with '$') whenever possible, and your indenting is slightly non-traditional. Typically, in a begin/rescue/ensure/end, all of the keywords are indented to the same level so that it is easier to identify each block of the statement.
Regards, François
------- # # AppDelegate.rb # ecm: exemple complet minimal # # Created by Boone François on 11-12-21. # Copyright 2011 Boone François. All rights reserved. #
require 'rubygems' require 'mysql'
$data = []
class AppDelegate attr_accessor :window def applicationDidFinishLaunching(a_notification) # Insert code here to initialize your application end end
class CtrButton attr_writer :individu
def awakeFromNib @individu.dataSource = self $ref_individu = @individu end
def affiche(sender) actionAffiche end
def numberOfRowsInTableView(individu) $data.size end
def tableView(individu, objectValueForTableColumn:column, row:index) item = $data[index] case column.identifier when 'iden' item.iden when 'name' item.name end end end
class Person attr_accessor :iden, :name end
def actionAffiche
t1 = Time.now # Connexion au serveur MySQL begin dbh = Mysql.real_connect("localhost", "root", "", "Boone") ## Récupération de la version du serveur et affichage puts "Version du serveur: " + dbh.get_server_info rescue Mysql::Error => e puts "Code d'erreur : #{e.errno}" puts "Message d'erreur : #{e.error}" puts "SQLSTATE d'erreur : #{e.sqlstate}" if e.respond_to?("sqlstate") ensure dbh.query("SET NAMES utf8") end
# Requête pour le nom de famille dbh.query("SET NAMES utf8") requete = "SELECT handle,surname FROM surname ORDER BY surname" reponse = dbh.query(requete)
num_max = reponse.num_rows num = 1
# Pour chaque nom de famille, on cherche le prenom reponse.each_hash do |lastname| puts "Individu " + num.to_s + " sur " + num_max.to_s individu = Person.new individu.iden = lastname["handle"] individu.name = lastname["surname"] # pour chaque nom, recherche du prenom à partir du handle. requete2 = "SELECT first_name,xcall FROM name WHERE handle ='" + lastname["handle"] + "'" reponse2 = dbh.query(requete2) reponse2.each_hash do |firstname| individu.name = individu.name + ", " + firstname["first_name"] if firstname["xcall"] != "" individu.name = individu.name + " (" + firstname["xcall"] + ")" end end
$data.push(individu) num += 1 end
# On ferme le lien mySQL dbh.close
t2 = Time.now puts (t2-t1) # Mise à jour de l'interface $ref_individu.reloadData end
So, two quick comments. First, you don't have to put all of this code into AppDelegate.rb. Instead, you can make new files for each class, just as you would with any other Ruby project. I did something like this for the talk I gave to Boston.rb last week (slides here: http://www.slideshare.net/jballanc/macruby-for-fun-and-profit). However, that is unrelated to the slowness you are observing. The source of your slowness is here: $data.push(individu) without seeing how you have everything hooked up, I can already guess that this is causing updates to the UI after every query. Typically, updating a UI is one of the slower things that you can do. An immediate speed up would be to collect all of your query results into a temporary array, and then at the end append them all to your $data object at once (but don't use a global – probably better to make that a method in a class, and probably better to write a separate class for your data source and UI controller – but those changes aren't necessary for this fix). - Josh P.S. If you really want your UI to remain responsive while performing these queries, then you will need to do the queries on a thread. Luckily, GCD makes that relatively easy to do!
participants (2)
-
François Boone
-
Joshua Ballanco