Author: eudoxos
Date: 2009-02-28 20:06:57 +0100 (Sat, 28 Feb 2009)
New Revision: 1700

Added:
   trunk/examples/collider-perf/
   trunk/examples/collider-perf/README
   trunk/examples/collider-perf/perf.py
   trunk/examples/collider-perf/perf.table
Modified:
   trunk/gui/py/utils.py
   trunk/gui/py/yade-multi
   trunk/gui/py/yadeControl.cpp
Log:
1. Make the yade-multi scheduler multithread-aware.
2. Add the collider performance test as example
3. added utils.replaceCollider(anotherCollider) for convenience
4. added Omega().isChildClassOf(classNameChild,classNameParent)


Added: trunk/examples/collider-perf/README
===================================================================
--- trunk/examples/collider-perf/README 2009-02-28 10:26:47 UTC (rev 1699)
+++ trunk/examples/collider-perf/README 2009-02-28 19:06:57 UTC (rev 1700)
@@ -0,0 +1,21 @@
+This example tests performance of PersistentSAPCollider and 
SpatialQuickSortCollider
+and was basis for http://yade.wikia.com/wiki/Colliders_performace .
+
+To run the test, say:
+
+ yade-trunk-multi perf.table perf.py
+
+and wait. If you feel brave enough, uncomment lines with many spheres in 
perf.table. 
+They take very long time (128k spheres with SAP collider over 3 hours)
+
+
+1. File with nSpheres spheres (loose packing) is generated first, it if 
doesn't exist yet.
+   This can take a few minutes, but is done only the first time for the 
particular sphere
+       number.
+2. First iteration on the scene (TriaxialTest and the selected collider) with 
timings is
+   done and timing.stats() printed (appears in the log file).
+3. Another 100 iterations are measured with siming.stats(), after which the 
test exits.
+
+To get the results, grep the resulting log files, named like perf.64k.log (64k 
spheres with
+PersistentSAPCollider), perf.32k.q.log (32k spheres, SpatialQuickSortCollider) 
etc.
+

Added: trunk/examples/collider-perf/perf.py
===================================================================
--- trunk/examples/collider-perf/perf.py        2009-02-28 10:26:47 UTC (rev 
1699)
+++ trunk/examples/collider-perf/perf.py        2009-02-28 19:06:57 UTC (rev 
1700)
@@ -0,0 +1,27 @@
+
+utils.readParamsFromTable(nSpheres=1000,collider='PersistentSAPCollider',noTableOk=True)
+# name of file containing sphere packing with given number of spheres
+spheresFile="packing-%dk.spheres"%(nSpheres/1000)
+
+import os
+if not os.path.exists(spheresFile):
+       print "Generating packing"
+       
p=Preprocessor('TriaxialTest',{'numberOfGrains':nSpheres,'radiusMean':1e-3,'lowerCorner':[0,0,0],'upperCorner':[1,1,1]})
+       p.load()
+       utils.spheresToFile(spheresFile)
+       O.reset()
+       print "Packing %s done"%spheresFile
+else: print "Packing found (%s), using it."%spheresFile
+
+from yade import timing
+O.timingEnabled=True
+
+p=Preprocessor('TriaxialTest',{'importFilename':spheresFile}).load()
+utils.replaceCollider(StandAloneEngine(collider))
+
+O.step()
+timing.stats()
+timing.reset()
+O.run(100,True)
+timing.stats()
+quit()

Added: trunk/examples/collider-perf/perf.table
===================================================================
--- trunk/examples/collider-perf/perf.table     2009-02-28 10:26:47 UTC (rev 
1699)
+++ trunk/examples/collider-perf/perf.table     2009-02-28 19:06:57 UTC (rev 
1700)
@@ -0,0 +1,51 @@
+description nSpheres collider
+#128k 128000 'PersistentSAPCollider'
+#96k 96000 'PersistentSAPCollider'
+#64k 64000 'PersistentSAPCollider'
+#56k 56000 'PersistentSAPCollider'
+#48k 48000 'PersistentSAPCollider'
+#40k 40000 'PersistentSAPCollider'
+36k 36000 'PersistentSAPCollider'
+32k 32000 'PersistentSAPCollider'
+28k 28000 'PersistentSAPCollider'
+24k 24000 'PersistentSAPCollider'
+20k 20000 'PersistentSAPCollider'
+18k 18000 'PersistentSAPCollider'
+16k 16000 'PersistentSAPCollider'
+14k 14000 'PersistentSAPCollider'
+12k 12000 'PersistentSAPCollider'
+10k 10000 'PersistentSAPCollider'
+9k 9000 'PersistentSAPCollider'
+8k 8000 'PersistentSAPCollider'
+7k 7000 'PersistentSAPCollider'
+6k 6000 'PersistentSAPCollider'
+5k 5000 'PersistentSAPCollider'
+4k 4000 'PersistentSAPCollider'
+3k 3000 'PersistentSAPCollider'
+2k 2000 'PersistentSAPCollider'
+1k 1000 'PersistentSAPCollider'
+128k.q 128000 'SpatialQuickSortCollider'
+96k.q 96000 'SpatialQuickSortCollider'
+64k.q 64000 'SpatialQuickSortCollider'
+56k.q 56000 'SpatialQuickSortCollider'
+48k.q 48000 'SpatialQuickSortCollider'
+40k.q 40000 'SpatialQuickSortCollider'
+36k.q 36000 'SpatialQuickSortCollider'
+32k.q 32000 'SpatialQuickSortCollider'
+28k.q 28000 'SpatialQuickSortCollider'
+24k.q 24000 'SpatialQuickSortCollider'
+20k.q 20000 'SpatialQuickSortCollider'
+18k.q 18000 'SpatialQuickSortCollider'
+16k.q 16000 'SpatialQuickSortCollider'
+14k.q 14000 'SpatialQuickSortCollider'
+12k.q 12000 'SpatialQuickSortCollider'
+10k.q 10000 'SpatialQuickSortCollider'
+9k.q 9000 'SpatialQuickSortCollider'
+8k.q 8000 'SpatialQuickSortCollider'
+7k.q 7000 'SpatialQuickSortCollider'
+6k.q 6000 'SpatialQuickSortCollider'
+5k.q 5000 'SpatialQuickSortCollider'
+4k.q 4000 'SpatialQuickSortCollider'
+3k.q 3000 'SpatialQuickSortCollider'
+2k.q 2000 'SpatialQuickSortCollider'
+1k.q 1000 'SpatialQuickSortCollider'

Modified: trunk/gui/py/utils.py
===================================================================
--- trunk/gui/py/utils.py       2009-02-28 10:26:47 UTC (rev 1699)
+++ trunk/gui/py/utils.py       2009-02-28 19:06:57 UTC (rev 1700)
@@ -354,10 +354,12 @@
                for i in range(len(names)):
                        if names[i]=='description': 
o.tags['description']=values[i]
                        else:
-                               if names[i] not in kw.keys() and (not unknownOk 
or names[i][0]=='!'): raise NameError("Parameter `%s' has no default value 
assigned"%names[i])
-                               if names[i] in kw.keys(): kw.pop(names[i])
-                               eq="%s=%s"%(names[i],repr(values[i]))
-                               exec('__builtin__.%s=%s'%(names[i],values[i])); 
tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
+                               print 'Parameter name:',names[i],names[i][0]
+                               if names[i] not in kw.keys():
+                                       if (not unknownOk) and 
names[i][0]!='!': raise NameError("Parameter `%s' has no default value 
assigned"%names[i])
+                               else: kw.pop(names[i])
+                               if names[i][0]!='!':
+                                       
exec('__builtin__.%s=%s'%(names[i],values[i])); 
tagsParams+=['%s=%s'%(names[i],values[i])]; dictParams[names[i]]=values[i]
        defaults=[]
        for k in kw.keys():
                exec("__builtin__.%s=%s"%(k,repr(kw[k])))
@@ -420,3 +422,13 @@
     f = 
DeusExMachina('PythonRunnerFilter',{'command':command,'isFilterActivated':isFilterActivated})
     O.engines+=[f]
     return f
+
+def replaceCollider(colliderEngine):
+       """Replaces collider (BroadInteractor) engine with the engine supplied. 
Raises error if no collider is in engines."""
+       colliderIdx=-1
+       for i,e in enumerate(O.engines):
+               if O.isChildClassOf(e.name,"BroadInteractor"):
+                       colliderIdx=i
+                       break
+       if colliderIdx<0: raise RuntimeError("No BroadInteractor found within 
O.engines.")
+       
O.engines=O.engines[:colliderIdx]+[colliderEngine]+O.engines[colliderIdx+1:]

Modified: trunk/gui/py/yade-multi
===================================================================
--- trunk/gui/py/yade-multi     2009-02-28 10:26:47 UTC (rev 1699)
+++ trunk/gui/py/yade-multi     2009-02-28 19:06:57 UTC (rev 1700)
@@ -6,9 +6,11 @@
 import os, sys, thread, time
 
 class JobInfo():
-       def __init__(self,num,id,command,log):
+       def __init__(self,num,id,command,log,nSlots):
                
self.started,self.finished,self.duration,self.exitStatus=None,None,None,None
-               self.command=command; self.num=num; self.log=log; self.id=id
+               self.command=command; self.num=num; self.log=log; self.id=id; 
self.nSlots=nSlots
+               self.status='PENDING'
+               self.threadNum=None
        def saveInfo(self):
                log=file(self.log,'a')
                log.write("""
@@ -22,85 +24,40 @@
 """%(self.id,self.exitStatus,'OK' if self.exitStatus==0 else 
'FAILED',self.duration,self.command,time.asctime(time.localtime(self.started)),time.asctime(time.localtime(self.finished))));
                log.close()
 
-#
-# _MANY_ thanks to Mark Pettit for concurrent jobs handling!
-# http://code.activestate.com/recipes/534160/
-#
 
 
-def __concurrent_batch(cmd,thread_num,completion_status_dict,exit_code_dict):
-       """Helper routine for 'concurrent_batches."""
-       job=jobs[thread_num]
+def runJob(job):
+       job.status='RUNNING'
        job.started=time.time();
-       print '#%d started on %s'%(thread_num,time.asctime())
-       exit_code_dict[thread_num] = os.system(cmd)
-       completion_status_dict[thread_num] = 1  # for sum() routine
-       job.finished=time.time(); dt=job.finished-job.started;
-       job.exitStatus=exit_code_dict[thread_num]
+       print '#%d (%s%s) started on %s'%(job.num,job.id,'' if job.nSlots==1 
else '/%d'%job.nSlots,time.asctime())
+       job.exitStatus=os.system(job.command)
+       job.finished=time.time()
+       dt=job.finished-job.started;
        job.duration='%02d:%02d:%02d'%(dt//3600,(dt%3600)//60,(dt%60))
        strStatus='done   ' if job.exitStatus==0 else 'FAILED '
-       print "#%d %s (exit status %d), duration %s, log 
%s"%(thread_num,strStatus,exit_code_dict[thread_num],job.duration,job.log)
+       job.status='DONE'
+       print "#%d (%s%s) %s (exit status %d), duration %s, log 
%s"%(job.num,job.id,'' if job.nSlots==1 else 
'/%d'%job.nSlots,strStatus,job.exitStatus,job.duration,job.log)
        job.saveInfo()
+       
+def runJobs(jobs,numSlots):
+       running,pending=0,len(jobs)
+       inf=1000000
+       while (running>0) or (pending>0):
+               pending,running,done=sum([j.nSlots for j in jobs if 
j.status=='PENDING']),sum([j.nSlots for j in jobs if 
j.status=='RUNNING']),sum([j.nSlots for j in jobs if j.status=='DONE'])
+               #print [j.status for j in jobs]
+               freeSlots=numSlots-running
+               minRequire=min([inf]+[j.nSlots for j in jobs if 
j.status=='PENDING'])
+               if minRequire==inf: minRequire=0
+               #print 
pending,'pending;',running,'running;',done,'done;',freeSlots,'free;',minRequire,'min'
+               if minRequire>freeSlots and running==0:
+                       freeSlots=minRequire
+               for j in [j for j in jobs if j.status=='PENDING']:
+                       if j.nSlots<=freeSlots:
+                               thread.start_new_thread(runJob,(j,))
+                               break
+               time.sleep(.5)
 
-def concurrent_batches(batchlist,maxjobs=0,maxtime=0):
-       """Run a list of batch commands simultaneously.
 
-       'batchlist' is a list of strings suitable for submitting under 
os.system().
-       Each job will run in a separate thread, with the thread ending when the
-       subprocess ends.
-       'maxjobs' will, if greater then zero, be the maximum number of 
simultaneous
-       jobs which can concurrently run.  This would be used to limit the 
number of
-       processes where too many could flood a system, causing performance 
issues.
-       'maxtime', when greater than zero, be the maximum amount of time 
(seconds)
-       that we will wait for processes to complete.  After that, we will 
return,
-       but no jobs will be killed.  In other words, the jobs still running will
-       continue to run, and hopefully finish in the course of time.
-
-       example: concurrent_batches(("gzip abc","gzip def","gzip xyz"))
-
-       returns: a dictionary of exit status codes, but only when ALL jobs are
-                        complete, or the maximum time has been exceeded.
-                        Note that if returning due to exceeding time, the 
dictionary will
-                        continue to be updated by the threads as they complete.
-                        The key of the dictionary is the thread number, which 
matches the
-                        index of the list of batch commands.  The value is the 
result of
-                        the os.system call.
-
-       gotcha:  If both the maxjobs and maxtime is set, there is a possibility 
that
-                        not all jobs will be submitted.  The only way to 
detect this will be
-                        by checking for the absence of the KEY in the returned 
dictionary.
-       """
-
-       if not batchlist: return {}
-       completion_status_dict, exit_code_dict = {}, {}
-       num_jobs = len(batchlist)
-       start_time = time.time()
-       for thread_num, cmd in enumerate(batchlist):
-               exit_code_dict[thread_num] = None
-               completion_status_dict[thread_num] = 0 # for sum() routine
-               thread.start_new_thread(__concurrent_batch,
-                         
(cmd,thread_num,completion_status_dict,exit_code_dict))
-               while True:
-                       completed = sum(completion_status_dict.values())
-                       if num_jobs == completed:
-                               return exit_code_dict     # all done
-                       running = thread_num - completed + 1
-                       if maxtime > 0:
-                               if time.time() - start_time > maxtime:
-                                       return exit_code_dict
-                       if not maxjobs:
-                               if thread_num < num_jobs-1:  # have we 
submitted all jobs ?
-                                       break                             #  
no, so break to for cmd loop
-                               else:
-                                       time.sleep(.2)           #  yes, so 
wait until jobs are complete
-                                       continue
-                       if running < maxjobs and thread_num < num_jobs-1:
-                               break   # for next for loop
-                       time.sleep(.2)
-#
-# now begins the yade code
-#
-
 import sys,re,optparse
 def getNumCores(): return len([l for l in open('/proc/cpuinfo','r') if 
l.find('processor')==0])
 
@@ -151,16 +108,16 @@
                elif l==1: print "WARNING: skipping line 1 that should contain 
variable labels"
                else: useLines+=[l]
 else: useLines=availableLines
-print "Will use lines ",', '.join([str(i) for i in useLines])+'.'
-# find column where description is
 try:
        idColumn=headings.index('description')
        idStrings={}
        for i in useLines: idStrings[i]=values[i][idColumn] # textual 
descripion of respective lines 
-       print idStrings
+       print "Will use lines ",', '.join([str(i)+' (%s)'%idStrings[i] for i in 
useLines])+'.'
+       #print idStrings
 except ValueError:
        idColumn=None
        idStrings=None
+       print "Will use lines ",', '.join([str(i) for i in useLines])+'.'
 
 
 jobs=[]
@@ -169,14 +126,18 @@
        if idStrings: logFile=logFile.replace('@',idStrings[l])
        else: logFile=logFile.replace('@',str(l))
        envVars=[]
+       nSlots=1
        for col,head in enumerate(headings):
                if head=='!EXEC': executable=values[l][col]
+               if head=='!OMP_NUM_THREADS': nSlots=int(values[l][col])
                if head[0]=='!': envVars+=['%s=%s'%(head[1:],values[l][col])]
-       jobs.append(JobInfo(i,idStrings[l] if idStrings else 
'#'+str(i),'PARAM_TABLE=%s:%d %s nice -n %d %s -N PythonUI -- -n -x %s > %s 
2>&1'%(table,l,' '.join(envVars),nice,executable,simul,logFile),logFile))
+       if nSlots>maxJobs: print 'WARNING: job #%d wants %d slots but only %d 
are available'%(i,nSlots,maxJobs)
+       jobs.append(JobInfo(i,idStrings[l] if idStrings else 
'#'+str(i),'PARAM_TABLE=%s:%d %s nice -n %d %s -N PythonUI -- -n -x %s > %s 
2>&1'%(table,l,' '.join(envVars),nice,executable,simul,logFile),logFile,nSlots))
 
 print "Job summary:"
 for job in jobs:
-       print '   #%d (%s):'%(job.num,job.id),job.command
+       print '   #%d (%s%s):'%(job.num,job.id,'' if job.nSlots==1 else 
'/%d'%job.nSlots),job.command
+
 # OK, go now
-concurrent_batches([job.command for job in jobs],maxjobs=maxJobs)
+runJobs(jobs,maxJobs)
 print 'All jobs finished, bye.'

Modified: trunk/gui/py/yadeControl.cpp
===================================================================
--- trunk/gui/py/yadeControl.cpp        2009-02-28 10:26:47 UTC (rev 1699)
+++ trunk/gui/py/yadeControl.cpp        2009-02-28 19:06:57 UTC (rev 1700)
@@ -584,6 +584,10 @@
                return ret;
        }
 
+       bool isChildClassOf(const string& child, const string& base){
+               return (Omega::instance().isInheritingFrom(child,base));
+       }
+
        pyTags tags_get(void){assertRootBody(); return 
pyTags(OMEGA.getRootBody());}
 
        void interactionContainer_set(string clss){
@@ -663,6 +667,7 @@
                .add_property("actions",&pyOmega::actions_get)
                .add_property("tags",&pyOmega::tags_get)
                .def("childClasses",&pyOmega::listChildClasses)
+               .def("isChildClassOf",&pyOmega::isChildClassOf)
                
.add_property("bodyContainer",&pyOmega::bodyContainer_get,&pyOmega::bodyContainer_set)
                
.add_property("interactionContainer",&pyOmega::interactionContainer_get,&pyOmega::interactionContainer_set)
                
.add_property("actionContainer",&pyOmega::physicalActionContainer_get,&pyOmega::physicalActionContainer_set)


_______________________________________________
Mailing list: https://launchpad.net/~yade-dev
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~yade-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to