Changeset 5854
- Timestamp:
- Oct 21, 2008, 6:15:06 PM (16 years ago)
- Location:
- anuga_core/source/anuga/caching
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
anuga_core/source/anuga/caching/caching.py
r5791 r5854 43 43 # 44 44 from os import getenv 45 import types 45 46 46 47 import os … … 258 259 259 260 # Create cache directory if needed 260 #261 261 CD = checkdir(cachedir,verbose) 262 262 263 263 # Handle the case cache('clear') 264 #265 264 if type(func) == types.StringType: 266 265 if string.lower(func) == 'clear': … … 269 268 270 269 # Handle the case cache(func, 'clear') 271 #272 270 if type(args) == types.StringType: 273 271 if string.lower(args) == 'clear': … … 276 274 277 275 # Force singleton arg into a tuple 278 #279 276 if type(args) != types.TupleType: 280 277 args = tuple([args]) 281 278 282 279 # Check that kwargs is a dictionary 283 #284 280 if type(kwargs) != types.DictType: 285 281 raise TypeError … … 288 284 289 285 # Hash arguments (and keyword args) to integer 290 #291 286 arghash = myhash((args,kwargs)) 292 287 293 288 # Get sizes and timestamps for files listed in dependencies. 294 289 # Force singletons into a tuple. 295 #296 297 290 if dependencies and type(dependencies) != types.TupleType \ 298 291 and type(dependencies) != types.ListType: … … 301 294 302 295 # Extract function name from func object 303 #304 296 funcname = get_funcname(func) 305 297 306 298 # Create cache filename 307 #308 299 FN = funcname+'['+`arghash`+']' # The symbol '(' does not work under unix 309 300 … … 331 322 332 323 # Check if previous computation has been cached 333 #334 324 if evaluate: 335 325 Retrieved = None # Force evaluation of func regardless of caching status. 336 reason = 4326 reason = 5 337 327 else: 338 328 (T, FN, Retrieved, reason, comptime, loadtime, compressed) = \ … … 349 339 350 340 # Remove expired files automatically 351 #352 341 if options['expire']: 353 342 DeleteOldFiles(CD,verbose) … … 355 344 # Save args before function is evaluated in case 356 345 # they are modified by function 357 #358 346 save_args_to_cache(CD,FN,args,kwargs,compression) 359 347 360 348 # Execute and time function with supplied arguments 361 #362 349 t0 = time.time() 363 350 T = apply(func,args,kwargs) … … 369 356 370 357 # Save results and estimated loading time to cache 371 #372 358 loadtime = save_results_to_cache(T, CD, FN, func, deps, comptime, \ 373 359 funcname, dependencies, compression) … … 770 756 'No cached result', 771 757 'Dependencies have changed', 772 'Byte code or arguments have changed', 758 'Arguments have changed', 759 'Bytecode has changed', 773 760 'Recomputation was requested by caller', 774 761 'Cached file was unreadable'] … … 803 790 1: No cached result, 804 791 2: Dependencies have changed, 805 3: Arguments or Bytecode have changed 806 4: Recomputation was forced 792 3: Arguments have changed 793 4: Bytecode has changed 794 5: Recomputation was forced 795 6: Unreadable file 807 796 comptime -- Number of seconds it took to computed cachged result 808 797 loadtime -- Number of seconds it took to load cached result … … 889 878 890 879 # Check if arguments or bytecode have changed 891 #892 880 if compare(argsref,args) and compare(kwargsref,kwargs) and \ 893 881 (not options['bytecode'] or compare(bytecode,coderef)): 894 882 895 883 # Arguments and dependencies match. Get cached results 896 #897 884 T, loadtime, compressed, reason = load_from_cache(CD,FN,compressed) 898 ###if T == None and reason > 0: #This doesn't work if T is a numeric array899 885 if reason > 0: 900 return(None,FN,None,reason,None,None,None) # Recompute using same FN886 return(None,FN,None,reason,None,None,None) # Recompute using same FN 901 887 902 888 Retrieved = 1 … … 925 911 # else: 926 912 # print 'Match found !' 913 914 # The real reason is that args or bytecodes have changed. 915 # Not that the recursive seach has found an unused filename 927 916 if not Retrieved: 928 reason = 3 #The real reason is that args or bytecodes have changed. 929 #Not that the recursive seach has found an unused filename 917 if not compare(bytecode,coderef): 918 reason = 4 # Bytecode has changed 919 else: 920 reason = 3 # Arguments have changed 921 930 922 931 923 return((T, FN, Retrieved, reason, comptime, loadtime, compressed)) … … 1125 1117 t0 = time.time() 1126 1118 T, reason = myload(datafile,compressed) 1127 #loadtime = round(time.time()-t0,2) 1119 1128 1120 loadtime = time.time()-t0 1129 1121 datafile.close() … … 1228 1220 #print 'ERROR (caching): Could not decompress ', file.name 1229 1221 #raise Exception 1230 reason = 5 #(Unreadable file)1222 reason = 6 # Unreadable file 1231 1223 return None, reason 1232 1224 … … 1240 1232 except: 1241 1233 #Catch e.g., file with 0 length or corrupted 1242 reason = 5 #(Unreadable file)1234 reason = 6 # Unreadable file 1243 1235 return None, reason 1244 1236 … … 1285 1277 raise MemoryError, msg 1286 1278 else: 1287 # Compressed pickling1279 # Compressed pickling 1288 1280 TsC = zlib.compress(Ts, comp_level) 1289 1281 file.write(TsC) 1290 1282 else: 1291 # Uncompressed pickling1283 # Uncompressed pickling 1292 1284 pickler.dump(T, file, bin) 1293 1285 … … 1328 1320 # ----------------------------------------------------------------------------- 1329 1321 1330 def myhash(T): 1331 """Compute hashed integer from hashable values of tuple T 1322 def myhash(T, ids=None): 1323 """Compute hashed integer from a range of inputs. 1324 If T is not hashable being e.g. a tuple T, myhash will recursively 1325 hash the values individually 1332 1326 1333 1327 USAGE: … … 1335 1329 1336 1330 ARGUMENTS: 1337 T -- Tuple 1338 """ 1339 1340 import types 1341 1331 T -- Anything 1332 """ 1333 1334 from types import TupleType, ListType, DictType, InstanceType 1335 from Numeric import ArrayType 1336 1337 if type(T) in [TupleType, ListType, DictType, InstanceType]: 1338 1339 # Keep track of unique id's to protect against infinite recursion 1340 if ids is None: ids = [] 1341 1342 # Check if T has already been encountered 1343 i = id(T) 1344 1345 if i in ids: 1346 # FIXME (Ole): It seems that different objects get the same id 1347 # T has been hashed already 1348 1349 #print 'T has already been hashed:', T, id(T) 1350 return 0 1351 else: 1352 #print 'Appending', T, id(T) 1353 ids.append(i) 1354 1355 1356 # Start hashing 1357 1358 1342 1359 # On some architectures None, False and True gets different hash values 1343 1360 if T is None: 1344 return(-1)1361 return(-1) 1345 1362 if T is False: 1346 return(0)1363 return(0) 1347 1364 if T is True: 1348 return(1) 1349 1350 # Get hash vals for hashable entries 1351 # 1352 if type(T) == types.TupleType or type(T) == types.ListType: 1353 hvals = [] 1354 for k in range(len(T)): 1355 h = myhash(T[k]) 1356 hvals.append(h) 1357 val = hash(tuple(hvals)) 1358 elif type(T) == types.DictType: 1359 val = dicthash(T) 1365 return(1) 1366 1367 # Get hash values for hashable entries 1368 if type(T) in [TupleType, ListType]: 1369 hvals = [] 1370 for t in T: 1371 h = myhash(t, ids) 1372 hvals.append(h) 1373 val = hash(tuple(hvals)) 1374 elif type(T) == DictType: 1375 val = myhash(T.items(), ids) 1376 elif type(T) == ArrayType: 1377 val = myhash(tuple(T), ids) 1378 elif type(T) == InstanceType: 1379 val = myhash(T.__dict__, ids) 1360 1380 else: 1361 try:1362 val = hash(T)1363 except:1364 val = 11365 1381 try: 1366 import Numeric 1367 if type(T) == Numeric.ArrayType: 1368 hvals = [] 1369 for e in T: 1370 h = myhash(e) 1371 hvals.append(h) 1372 val = hash(tuple(hvals)) 1382 val = hash(T) 1383 except: 1384 val = 1 1385 1386 return(val) 1387 1388 # ----------------------------------------------------------------------------- 1389 1390 def compare(A, B, ids=None): 1391 """Safe comparison of general objects 1392 1393 USAGE: 1394 compare(A,B) 1395 1396 DESCRIPTION: 1397 Return 1 if A and B they are identical, 0 otherwise 1398 """ 1399 1400 from types import TupleType, ListType, DictType, InstanceType 1401 1402 1403 # Keep track of unique id's to protect against infinite recursion 1404 if ids is None: ids = {} 1405 1406 1407 # Check if T has already been encountered 1408 iA = id(A) 1409 iB = id(B) 1410 1411 if ids.has_key((iA, iB)): 1412 # A and B have been compared already 1413 #print 'Found', (iA, iB), A, B 1414 return ids[(iA, iB)] 1415 else: 1416 ids[(iA, iB)] = True 1417 1418 1419 #print 'Comparing', A, B, (iA, iB) 1420 #print ids 1421 #raw_input() 1422 1423 if type(A) in [TupleType, ListType] and type(B) in [TupleType, ListType]: 1424 N = len(A) 1425 if len(B) != N: 1426 identical = False 1373 1427 else: 1374 val = 1 #Could implement other Numeric types here 1375 except: 1376 pass 1377 1378 return(val) 1379 1380 # ----------------------------------------------------------------------------- 1381 1382 def dicthash(D): 1383 """Compute hashed integer from hashable values of dictionary D 1384 1385 USAGE: 1386 dicthash(D) 1387 """ 1388 1389 keys = D.keys() 1390 1391 # Get hash values for hashable entries 1392 # 1393 hvals = [] 1394 for k in range(len(keys)): 1395 try: 1396 h = myhash(D[keys[k]]) 1397 hvals.append(h) 1398 except: 1399 pass 1400 1401 # Hash obtained values into one value 1402 # 1403 return(hash(tuple(hvals))) 1404 1405 # ----------------------------------------------------------------------------- 1406 1407 def compare(A,B): 1408 """Safe comparison of general objects 1409 1410 USAGE: 1411 compare(A,B) 1412 1413 DESCRIPTION: 1414 Return 1 if A and B they are identical, 0 otherwise 1415 """ 1416 1417 try: 1418 identical = (A == B) 1419 except: 1420 try: 1421 identical = (pickler.dumps(A) == pickler.dumps(B)) 1422 except: 1423 identical = 0 1424 1425 return(identical) 1428 identical = True 1429 for i in range(N): 1430 if not compare(A[i], B[i], ids): 1431 identical = False; break 1432 1433 elif type(A) == DictType and type(B) == DictType: 1434 if len(A) != len(B): 1435 identical = False 1436 else: 1437 identical = True 1438 for key in A.keys(): 1439 if not B.has_key(key): 1440 identical = False; break 1441 1442 if not compare(A[key], B[key], ids): 1443 identical = False; break 1444 1445 elif type(A) == type(B) == types.InstanceType: 1446 # Take care of special case where elements are instances 1447 # Base comparison on attributes 1448 1449 a = A.__dict__ 1450 b = B.__dict__ 1451 1452 identical = compare(a, b, ids) 1453 1454 1455 1456 else: 1457 # Fall back to general code 1458 try: 1459 identical = (A == B) 1460 except: 1461 try: 1462 identical = (pickler.dumps(A) == pickler.dumps(B)) 1463 except: 1464 identical = 0 1465 1466 # Record result of comparison and return 1467 ids[(iA, iB)] = identical 1468 1469 return(identical) 1426 1470 1427 1471 # ----------------------------------------------------------------------------- … … 2339 2383 argstr = argstr + "'"+str(args)+"'" 2340 2384 else: 2341 # Truncate large Numeric arrays before using str()2385 # Truncate large Numeric arrays before using str() 2342 2386 import Numeric 2343 2387 if type(args) == Numeric.ArrayType: -
anuga_core/source/anuga/caching/dummy_classes_for_testing.py
r5853 r5854 10 10 def copy(self): 11 11 return Dummy(self.value, self.another) 12 13 def __repr__(self): 14 return str(self.value) + ', ' + str(self.another) 12 15 13 16 -
anuga_core/source/anuga/caching/test_caching.py
r5853 r5854 35 35 return A.value+B.value, A.another+B.another 36 36 37 37 38 def f_generic(A): 39 return A 38 40 39 41 def clear_and_create_cache(Dummy, verbose=False): … … 114 116 115 117 118 116 119 def test_caching_of_numeric_arrays(self): 117 120 """test_caching_of_numeric_arrays … … 190 193 191 194 # Retrieve 195 #T2 = cache(f_object, (A1, B1), 196 # compression=comp, verbose=verbose) 197 198 # Retrieve 192 199 T2 = cache(f_object, (A1, B1), 193 200 compression=comp, test=1, verbose=verbose) … … 205 212 206 213 207 214 def test_caching_of_circular_structures(self): 215 """test_caching_of_circular_structures 216 217 Test that Caching doesn't recurse infinitely in case of 218 circular or self-referencing structures 219 """ 220 221 verbose = False 222 223 # Create input argument 224 A = Dummy(5, 7) 225 B = {'x': 10, 'A': A} 226 C = [B, 15] 227 A.value = C # Make it circular 228 229 230 # Test caching 231 comprange = 2 232 for comp in range(comprange): 233 234 # Evaluate and store 235 T1 = cache(f_generic, A, evaluate=1, 236 compression=comp, verbose=verbose) 237 238 # Retrieve 239 T2 = cache(f_generic, A, 240 compression=comp, test=1, verbose=verbose) 241 242 # Check for presence of cached result 243 msg = 'Cached object was not found' 244 assert T2 is not None, msg 245 246 # Reference result 247 T3 = f_generic(A) # Compute without caching 248 249 250 msg = 'Cached result does not match computed result' 251 assert str(T1) == str(T2), msg 252 assert str(T2) == str(T3), msg 253 254 255 256 257 258 259 def XXtest_caching_of_simple_circular_structures(self): 260 261 # FIXME (Ole): This one recurses infinitly on 262 # arg strings. 263 264 """test_caching_of_circular_structures 265 266 Test that Caching doesn't recurse infinitely in case of 267 circular or self-referencing structures 268 """ 269 270 verbose = True 271 272 # Create input argument 273 A = {'x': 10, 'B': None} 274 B = [A, 15] 275 A['B'] = B # Make it circular 276 277 print A 278 279 # Test caching 280 comprange = 2 281 for comp in range(comprange): 282 283 # Evaluate and store 284 T1 = cache(f_generic, A, evaluate=1, 285 compression=comp, verbose=verbose) 286 287 import sys; sys.exit() 288 289 290 # Retrieve 291 T2 = cache(f_generic, A, 292 compression=comp, test=1, verbose=verbose) 293 294 # Check for presence of cached result 295 msg = 'Cached object was not found' 296 assert T2 is not None, msg 297 298 # Reference result 299 T3 = f_generic(A) # Compute without caching 300 301 302 assert T1 == T2, 'Cached result does not match computed result' 303 assert T2 == T3, 'Cached result does not match computed result' 304 305 208 306 209 307 … … 429 527 This test shows how instances can't be effectively cached. 430 528 myhash uses hash which uses id which uses the memory address. 529 530 This will be a NIL problem once caching can handle instances with different id's and 531 identical content. 532 533 The test is disabled. 431 534 """ 535 536 537 432 538 verbose = True 433 539 #verbose = False … … 526 632 527 633 # Cache created for use with 'test_objects_are_created_memory' 528 initial_addr = `Dummy_memorytest` 529 clear_and_create_cache(Dummy_memorytest, verbose=False) 530 531 532 533 534 535 536 634 #initial_addr = `Dummy_memorytest` 635 #clear_and_create_cache(Dummy_memorytest, verbose=False) 636 537 637 538 638 539 639 #------------------------------------------------------------- 540 640 if __name__ == "__main__": 541 suite = unittest.makeSuite(Test_Caching, 'test')641 suite = unittest.makeSuite(Test_Caching, 'test') 542 642 runner = unittest.TextTestRunner() 543 643 runner.run(suite)
Note: See TracChangeset
for help on using the changeset viewer.