Tue, 18 Jun 2013 12:31:07 -0700
8015237: Parallelize string table scanning during strong root processing
Summary: Parallelize the scanning of the intern string table by having each GC worker claim a given number of buckets. Changes were also reviewed by Per Liden <per.liden@oracle.com>.
Reviewed-by: tschatzl, stefank, twisti
1.1 --- a/src/share/vm/classfile/symbolTable.cpp Fri Jun 14 08:02:32 2013 +0200 1.2 +++ b/src/share/vm/classfile/symbolTable.cpp Tue Jun 18 12:31:07 2013 -0700 1.3 @@ -598,6 +598,8 @@ 1.4 1.5 bool StringTable::_needs_rehashing = false; 1.6 1.7 +volatile int StringTable::_parallel_claimed_idx = 0; 1.8 + 1.9 // Pick hashing algorithm 1.10 unsigned int StringTable::hash_string(const jchar* s, int len) { 1.11 return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) : 1.12 @@ -761,8 +763,18 @@ 1.13 } 1.14 } 1.15 1.16 -void StringTable::oops_do(OopClosure* f) { 1.17 - for (int i = 0; i < the_table()->table_size(); ++i) { 1.18 +void StringTable::buckets_do(OopClosure* f, int start_idx, int end_idx) { 1.19 + const int limit = the_table()->table_size(); 1.20 + 1.21 + assert(0 <= start_idx && start_idx <= limit, 1.22 + err_msg("start_idx (" INT32_FORMAT ") oob?", start_idx)); 1.23 + assert(0 <= end_idx && end_idx <= limit, 1.24 + err_msg("end_idx (" INT32_FORMAT ") oob?", end_idx)); 1.25 + assert(start_idx <= end_idx, 1.26 + err_msg("Ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT, 1.27 + start_idx, end_idx)); 1.28 + 1.29 + for (int i = start_idx; i < end_idx; i += 1) { 1.30 HashtableEntry<oop, mtSymbol>* entry = the_table()->bucket(i); 1.31 while (entry != NULL) { 1.32 assert(!entry->is_shared(), "CDS not used for the StringTable"); 1.33 @@ -774,6 +786,27 @@ 1.34 } 1.35 } 1.36 1.37 +void StringTable::oops_do(OopClosure* f) { 1.38 + buckets_do(f, 0, the_table()->table_size()); 1.39 +} 1.40 + 1.41 +void StringTable::possibly_parallel_oops_do(OopClosure* f) { 1.42 + const int ClaimChunkSize = 32; 1.43 + const int limit = the_table()->table_size(); 1.44 + 1.45 + for (;;) { 1.46 + // Grab next set of buckets to scan 1.47 + int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize; 1.48 + if (start_idx >= limit) { 1.49 + // End of table 1.50 + break; 1.51 + } 1.52 + 1.53 + int end_idx = MIN2(limit, start_idx + ClaimChunkSize); 1.54 + buckets_do(f, start_idx, end_idx); 1.55 + } 1.56 +} 1.57 + 1.58 void StringTable::verify() { 1.59 for (int i = 0; i < the_table()->table_size(); ++i) { 1.60 HashtableEntry<oop, mtSymbol>* p = the_table()->bucket(i);
2.1 --- a/src/share/vm/classfile/symbolTable.hpp Fri Jun 14 08:02:32 2013 +0200 2.2 +++ b/src/share/vm/classfile/symbolTable.hpp Tue Jun 18 12:31:07 2013 -0700 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -246,12 +246,19 @@ 2.11 // Set if one bucket is out of balance due to hash algorithm deficiency 2.12 static bool _needs_rehashing; 2.13 2.14 + // Claimed high water mark for parallel chunked scanning 2.15 + static volatile int _parallel_claimed_idx; 2.16 + 2.17 static oop intern(Handle string_or_null, jchar* chars, int length, TRAPS); 2.18 oop basic_add(int index, Handle string_or_null, jchar* name, int len, 2.19 unsigned int hashValue, TRAPS); 2.20 2.21 oop lookup(int index, jchar* chars, int length, unsigned int hashValue); 2.22 2.23 + // Apply the give oop closure to the entries to the buckets 2.24 + // in the range [start_idx, end_idx). 2.25 + static void buckets_do(OopClosure* f, int start_idx, int end_idx); 2.26 + 2.27 StringTable() : Hashtable<oop, mtSymbol>((int)StringTableSize, 2.28 sizeof (HashtableEntry<oop, mtSymbol>)) {} 2.29 2.30 @@ -277,9 +284,12 @@ 2.31 unlink_or_oops_do(cl, NULL); 2.32 } 2.33 2.34 - // Invoke "f->do_oop" on the locations of all oops in the table. 2.35 + // Serially invoke "f->do_oop" on the locations of all oops in the table. 2.36 static void oops_do(OopClosure* f); 2.37 2.38 + // Possibly parallel version of the above 2.39 + static void possibly_parallel_oops_do(OopClosure* f); 2.40 + 2.41 // Hashing algorithm, used as the hash value used by the 2.42 // StringTable for bucket selection and comparison (stored in the 2.43 // HashtableEntry structures). This is used in the String.intern() method. 2.44 @@ -315,5 +325,8 @@ 2.45 // Rehash the symbol table if it gets out of balance 2.46 static void rehash_table(); 2.47 static bool needs_rehashing() { return _needs_rehashing; } 2.48 + 2.49 + // Parallel chunked scanning 2.50 + static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; } 2.51 }; 2.52 #endif // SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
3.1 --- a/src/share/vm/memory/sharedHeap.cpp Fri Jun 14 08:02:32 2013 +0200 3.2 +++ b/src/share/vm/memory/sharedHeap.cpp Tue Jun 18 12:31:07 2013 -0700 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -47,7 +47,6 @@ 3.11 SH_PS_SystemDictionary_oops_do, 3.12 SH_PS_ClassLoaderDataGraph_oops_do, 3.13 SH_PS_jvmti_oops_do, 3.14 - SH_PS_StringTable_oops_do, 3.15 SH_PS_CodeCache_oops_do, 3.16 // Leave this one last. 3.17 SH_PS_NumElements 3.18 @@ -127,6 +126,8 @@ 3.19 { 3.20 if (_active) { 3.21 outer->change_strong_roots_parity(); 3.22 + // Zero the claimed high water mark in the StringTable 3.23 + StringTable::clear_parallel_claimed_index(); 3.24 } 3.25 } 3.26 3.27 @@ -154,14 +155,16 @@ 3.28 // Global (strong) JNI handles 3.29 if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do)) 3.30 JNIHandles::oops_do(roots); 3.31 + 3.32 // All threads execute this; the individual threads are task groups. 3.33 CLDToOopClosure roots_from_clds(roots); 3.34 CLDToOopClosure* roots_from_clds_p = (is_scavenging ? NULL : &roots_from_clds); 3.35 - if (ParallelGCThreads > 0) { 3.36 - Threads::possibly_parallel_oops_do(roots, roots_from_clds_p ,code_roots); 3.37 + if (CollectedHeap::use_parallel_gc_threads()) { 3.38 + Threads::possibly_parallel_oops_do(roots, roots_from_clds_p, code_roots); 3.39 } else { 3.40 Threads::oops_do(roots, roots_from_clds_p, code_roots); 3.41 } 3.42 + 3.43 if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do)) 3.44 ObjectSynchronizer::oops_do(roots); 3.45 if (!_process_strong_tasks->is_task_claimed(SH_PS_FlatProfiler_oops_do)) 3.46 @@ -189,8 +192,12 @@ 3.47 } 3.48 } 3.49 3.50 - if (!_process_strong_tasks->is_task_claimed(SH_PS_StringTable_oops_do)) { 3.51 - if (so & SO_Strings) { 3.52 + // All threads execute the following. A specific chunk of buckets 3.53 + // from the StringTable are the individual tasks. 3.54 + if (so & SO_Strings) { 3.55 + if (CollectedHeap::use_parallel_gc_threads()) { 3.56 + StringTable::possibly_parallel_oops_do(roots); 3.57 + } else { 3.58 StringTable::oops_do(roots); 3.59 } 3.60 }