1
1

Adding a new OPAL hash table routine. Please read the algorithm description in opal/class/opal_hash_table.c for more precise details on the design and implementation. This algorithm was contributed by David Linden of H.P. in partnership with Mellanox Technologies. This contribution achieves two objectives:

1. It's actually hashing now, whereas the old OPAL hash table was not. Thus, it is a bug fix for and, as such, should be included in the 1.8 series.

2. It is dynamic and can grow and shrink the number of buckets in accordance with job size, whereas the old OPAL hash table had a fixed number of buckets which resulted in poor retrieval performance at large scale.

This scheme has been deployed in the field on very large H.P./Mellanox systems and has been demonstrated to significantly decrease job start-up time (~ 20% improvement) when launching applications directly with srun in SLURM environments. However, neither SLURM nor direct launch are prerequisites to take advantage of this change as any entity that utilizes OPAL hash table objects can benefit (at least partially) from this contribution.
Этот коммит содержится в:
Joshua Ladd 2014-10-09 17:24:23 +02:00
родитель ffa8674e01
Коммит 1cabd73522
3 изменённых файлов: 749 добавлений и 428 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -9,6 +9,10 @@
* University of Stuttgart. All rights reserved.
* Copyright (c) 2004-2005 The Regents of the University of California.
* All rights reserved.
* Copyright (c) 2014-2015 Hewlett-Packard Development Company, LP.
* All rights reserved.
* Copyright (c) 2014-2015 Mellanox Technologies, Inc.
* All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
@ -42,11 +46,18 @@ OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_hash_table_t);
struct opal_hash_table_t
{
opal_object_t super; /**< subclass of opal_object_t */
opal_list_t ht_nodes; /**< free list of hash nodes */
opal_list_t *ht_table; /**< each item is an array of opal_fhnode_t nodes */
struct opal_hash_element_t * ht_table; /**< table of elements (opaque to users) */
size_t ht_capacity; /**< allocated size (capacity) of table */
size_t ht_size; /**< number of extant entries */
size_t ht_growth_trigger; /**< size hits this and table is grown */
int ht_density_numer, ht_density_denom; /**< max allowed density of table */
int ht_growth_numer, ht_growth_denom; /**< growth factor when grown */
const struct opal_hash_type_methods_t * ht_type_methods;
// FIXME
// Begin KLUDGE!! So ompi/debuggers/ompi_common_dll.c doesn't complain
size_t ht_table_size; /**< size of table */
size_t ht_size; /**< number of values on table */
size_t ht_mask;
// End KLUDGE
};
typedef struct opal_hash_table_t opal_hash_table_t;
@ -65,12 +76,6 @@ typedef struct opal_hash_table_t opal_hash_table_t;
OPAL_DECLSPEC int opal_hash_table_init(opal_hash_table_t* ht, size_t table_size);
/**
* Alternative form
*/
OPAL_DECLSPEC int opal_hash_table_init2(opal_hash_table_t* ht, size_t estimated_max_size,
int density_numer, int density_denom,
int growth_numer, int growth_denom);
/**
* Returns the number of elements currently stored in the table.
@ -109,7 +114,7 @@ OPAL_DECLSPEC int opal_hash_table_remove_all(opal_hash_table_t *ht);
*/
OPAL_DECLSPEC int opal_hash_table_get_value_uint32(opal_hash_table_t* table, uint32_t key,
void** ptr);
void** ptr);
/**
* Set value based on uint32_t key.
@ -148,7 +153,7 @@ OPAL_DECLSPEC int opal_hash_table_remove_value_uint32(opal_hash_table_t* table,
*/
OPAL_DECLSPEC int opal_hash_table_get_value_uint64(opal_hash_table_t *table, uint64_t key,
void **ptr);
void **ptr);
/**
* Set value based on uint64_t key.
@ -187,7 +192,7 @@ OPAL_DECLSPEC int opal_hash_table_remove_value_uint64(opal_hash_table_t *table,
*/
OPAL_DECLSPEC int opal_hash_table_get_value_ptr(opal_hash_table_t *table, const void* key,
size_t keylen, void **ptr);
size_t keylen, void **ptr);
/**
* Set value based on arbitrary length binary key.
@ -236,7 +241,7 @@ OPAL_DECLSPEC int opal_hash_table_remove_value_ptr(opal_hash_table_t *table, con
*/
OPAL_DECLSPEC int opal_hash_table_get_first_key_uint32(opal_hash_table_t *table, uint32_t *key,
void **value, void **node);
void **value, void **node);
/**
@ -254,8 +259,8 @@ OPAL_DECLSPEC int opal_hash_table_get_first_key_uint32(opal_hash_table_t *table,
*/
OPAL_DECLSPEC int opal_hash_table_get_next_key_uint32(opal_hash_table_t *table, uint32_t *key,
void **value, void *in_node,
void **out_node);
void **value, void *in_node,
void **out_node);
/**
@ -272,7 +277,7 @@ OPAL_DECLSPEC int opal_hash_table_get_next_key_uint32(opal_hash_table_t *table,
*/
OPAL_DECLSPEC int opal_hash_table_get_first_key_uint64(opal_hash_table_t *table, uint64_t *key,
void **value, void **node);
void **value, void **node);
/**
@ -290,8 +295,8 @@ OPAL_DECLSPEC int opal_hash_table_get_first_key_uint64(opal_hash_table_t *table,
*/
OPAL_DECLSPEC int opal_hash_table_get_next_key_uint64(opal_hash_table_t *table, uint64_t *key,
void **value, void *in_node,
void **out_node);
void **value, void *in_node,
void **out_node);
END_C_DECLS

Просмотреть файл

@ -10,6 +10,10 @@
* Copyright (c) 2004-2005 The Regents of the University of California.
* All rights reserved.
* Copyright (c) 2008-2010 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2014-2015 Hewlett-Packard Development Company, LP.
* All rights reserved.
* Copyright (c) 2014-2015 Mellanox Technologies, Inc.
* All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
@ -62,6 +66,23 @@ char *perm_keys[] = {
NULL
};
/*
* This data specifically knows about the April'2014 version of hash tables.
* It inserts some keys.
* It inserts some more with a capacity offset to generate collisions.
* Then it checks the table via traversal.
* Then... it removes a key and checks again (via traversal)
* and removes another key and re-checks.
*/
static char* remove_keys[] = {
"1", "A", "2", "B", "4", "D", "6", "F", "10", "J", NULL, /* insert as-is: ...AB.D.F...J... */
"2", "b", "4", "d", "5", "e", "3", "c", NULL, /* insert with capacity-offset: ...ABbDdFec.J... */
"ABbDdFecJ", /* traversal expectation */
"4", "ABbdeFcJ", /* remove D (...ABbdeFc..J...) then expected traversal */
"2", "AbcdeFJ", /* remove B (...AbcdeF...J...) then expected traversal */
NULL /* end removals and expectations */
};
typedef union {
uint32_t uvalue;
void *vvalue;
@ -92,6 +113,67 @@ static void validate_table(opal_hash_table_t *table, char *keys[], int is_numeri
test_verify_int(j/2, opal_hash_table_get_size(table));
}
static void
validate_remove_traversal(opal_hash_table_t * table, const char * expected_chars)
{
/* all values are single-character strings */
/* expected_chars are those single characters as a string */
const int debug = 0; /* turn this on if you want to see the details */
int rc, problems = 0;
const char * expected_scanner = expected_chars;
uint32_t key;
void * raw_value;
void * node;
if (debug) {
fprintf(stderr, "debug: expecting '%s' capacity is %d\n",
expected_chars, (int) table->ht_capacity);
}
for (rc = opal_hash_table_get_first_key_uint32(table, &key, &raw_value, &node);
OPAL_SUCCESS == rc;
rc = opal_hash_table_get_next_key_uint32(table, &key, &raw_value, node, &node)) {
const char * value = (const char *) raw_value;
char expected, actual;
if (debug) {
fprintf(stderr, "key %d (probe at %d) value '%s' excpected_scanner '%s'\n",
key, (int) (key%table->ht_capacity), value, expected_scanner);
}
if (1 != strlen(value)) {
fprintf(stderr, "key %d's value '%s' is not a one-character string\n", key, value);
problems += 1;
continue; /* might as well be completely noisy */
}
if ('\0' == *expected_scanner) {
fprintf(stderr, "Found key %d value '%s' but not expected!\n", key, value);
problems += 1;
continue;
}
expected = *expected_scanner++;
actual = *value;
if (actual != expected) {
fprintf(stderr, "Expected '%c' but got '%c'\n", expected, actual);
problems += 1;
continue;
}
}
/* final checks */
if (OPAL_ERROR != rc) {
fprintf(stderr, "table traversal did not end in OPAL_ERROR?!?\n");
problems += 1;
}
if ('\0' != *expected_scanner) {
fprintf(stderr, "Still expecting more key/values: '%s'\n", expected_scanner);
problems += 1;
}
/* resolution */
if (problems > 0) {
fflush(stderr);
test_failure("validate_remove_traversal");
} else {
test_success();
}
}
static void test_htable(opal_hash_table_t *table)
{
int j;
@ -131,6 +213,26 @@ static void test_htable(opal_hash_table_t *table)
opal_hash_table_remove_all(table);
test_verify_int(0, opal_hash_table_get_size(table));
fprintf(error_out, "\nTesting removal and traversal...\n");
j = 0;
char * str;
while (NULL != (str = remove_keys[j++])) {
opal_hash_table_set_value_uint32(table, atoi(str), remove_keys[j++]);
}
while (NULL != (str = remove_keys[j++])) {
/* generate collisions */
opal_hash_table_set_value_uint32(table, atoi(str) + table->ht_capacity, remove_keys[j++]);
}
validate_remove_traversal(table, remove_keys[j++]);
while (NULL != (str = remove_keys[j++])) {
opal_hash_table_remove_value_uint32(table, atoi(str));
validate_remove_traversal(table, remove_keys[j++]);
}
/* remove all values for next test */
opal_hash_table_remove_all(table);
test_verify_int(0, opal_hash_table_get_size(table));
fprintf(error_out, "\n\n");
}