ObjFW  Diff

Differences From Artifact [0500525bf9]:

To Artifact [e9b6e354b6]:

  • File src/runtime/class.m — part of check-in [84a724dd4b] at 2013-12-06 00:52:26 on branch trunk — Add a fast path for objc_classname_to_class().

    This should improve the performance for the GCC ABI, as
    objc_classname_to_class() is used for all sorts of class lookups, e.g.
    objc_lookup_class().

    As this performance improvement needs RAM, it is only used after 128
    calls into objc_classname_to_class(), so that if the ObjFW ABI is used
    and the user does not call into objc_getClass() or similar in a loop, no
    memory is wasted.

    Runtime internal usage of objc_classname_to_class() does not use the
    fast path and does not count as a call into objc_classname_to_class().
    The reason for this is that if the runtime calls
    objc_classname_to_class(), it already has the lock and thus the
    performance gain would be small, but it would waste memory. (user: js, size: 15844) [annotate] [blame] [check-ins using]


18
19
20
21
22
23
24




25
26
27
28
29


30
31
32
33
34
35
36

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#import "runtime.h"
#import "runtime-private.h"





static struct objc_hashtable *classes = NULL;
static Class *load_queue = NULL;
static size_t load_queue_cnt = 0;
static struct objc_sparsearray *empty_dtable = NULL;



static void
register_class(struct objc_abi_class *cls)
{
	if (classes == NULL)
		classes = objc_hashtable_new(2);








>
>
>
>





>
>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#import "runtime.h"
#import "runtime-private.h"

struct sparsearray {
	void *next[256];
};

static struct objc_hashtable *classes = NULL;
static Class *load_queue = NULL;
static size_t load_queue_cnt = 0;
static struct objc_sparsearray *empty_dtable = NULL;
static unsigned lookups_till_fast_path = 128;
static struct sparsearray *sparsearray = NULL;

static void
register_class(struct objc_abi_class *cls)
{
	if (classes == NULL)
		classes = objc_hashtable_new(2);

63
64
65
66
67
68
69
70
71
72
73
74
75
76






































77

78




























79
80
81
82
83
84
85
	for (ml = cls->methodlist; ml != NULL; ml = ml->next)
		for (i = 0; i < ml->count; i++)
			objc_register_selector(
			    (struct objc_abi_selector*)&ml->methods[i]);
}

inline Class
objc_classname_to_class(const char *name)
{
	Class c;

	if (classes == NULL)
		return Nil;







































	objc_global_mutex_lock();

	c = (Class)((uintptr_t)objc_hashtable_get(classes, name) & ~1);




























	objc_global_mutex_unlock();

	return c;
}

static void
call_method(Class cls, const char *method)







|






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
	for (ml = cls->methodlist; ml != NULL; ml = ml->next)
		for (i = 0; i < ml->count; i++)
			objc_register_selector(
			    (struct objc_abi_selector*)&ml->methods[i]);
}

inline Class
objc_classname_to_class(const char *name, bool cache)
{
	Class c;

	if (classes == NULL)
		return Nil;

	/*
	 * Fast path
	 *
	 * Instead of looking up the string in a dictionary, which needs
	 * locking, we use a sparse array to look up the pointer. If
	 * objc_classname_to_class() gets called a lot, it is most likely that
	 * the GCC ABI is used, which always calls into objc_lookup_class(), or
	 * that it is used in a loop by the user. In both cases, it is very
	 * likely that the same string pointer is passed again and again.
	 *
	 * This is not used before objc_classname_to_class() has been called a
	 * certain amount of times, so that no memory is wasted if it is only
	 * used rarely, for example if the ObjFW ABI is used and the user does
	 * not call it in a loop.
	 *
	 * Runtime internal usage does not use the fast path and does not count
	 * as a call into objc_classname_to_class(). The reason for this is
	 * that if the runtime calls into objc_classname_to_class(), it already
	 * has the lock and thus the performance gain would be small, but it
	 * would waste memory.
	 */
	if (cache && sparsearray != NULL) {
		uintptr_t clsidx = (uintptr_t)name;
		struct sparsearray *iter = sparsearray;
		uint_fast8_t i;

		for (i = 0; i < sizeof(uintptr_t) - 1; i++) {
			uintptr_t idx = (clsidx >>
			    ((sizeof(uintptr_t) - i - 1) * 8)) & 0xFF;

			if ((iter = iter->next[idx]) == NULL)
				break;
		}

		if (iter != NULL && (c = iter->next[clsidx & 0xFF]) != Nil)
			return c;
	}

	objc_global_mutex_lock();

	c = (Class)((uintptr_t)objc_hashtable_get(classes, name) & ~1);

	if (cache && sparsearray == NULL && --lookups_till_fast_path == 0)
		if ((sparsearray = calloc(1,
		    sizeof(struct sparsearray))) == NULL)
			OBJC_ERROR("Failed to allocate memory for class lookup "
			    "fast path!");

	if (cache && sparsearray != NULL) {
		uintptr_t clsidx = (uintptr_t)name;
		struct sparsearray *iter = sparsearray;
		uint_fast8_t i;

		for (i = 0; i < sizeof(uintptr_t) - 1; i++) {
			uintptr_t idx = (clsidx >>
			    ((sizeof(uintptr_t) - i - 1) * 8)) & 0xFF;

			if (iter->next[idx] == NULL)
				if ((iter->next[idx] = calloc(1,
				    sizeof(struct sparsearray))) == NULL)
					OBJC_ERROR("Failed to allocate memory "
					    "for class lookup fast path!");

			iter = iter->next[idx];
		}

		iter->next[clsidx & 0xFF] = c;
	}

	objc_global_mutex_unlock();

	return c;
}

static void
call_method(Class cls, const char *method)
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
{
	const char *superclass;

	if (cls->info & OBJC_CLASS_INFO_SETUP)
		return;

	if ((superclass = ((struct objc_abi_class*)cls)->superclass) != NULL) {
		Class super = objc_classname_to_class(superclass);

		if (super == nil)
			return;

		setup_class(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))







|







308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
{
	const char *superclass;

	if (cls->info & OBJC_CLASS_INFO_SETUP)
		return;

	if ((superclass = ((struct objc_abi_class*)cls)->superclass) != NULL) {
		Class super = objc_classname_to_class(superclass, false);

		if (super == nil)
			return;

		setup_class(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		}
	}
}

id
objc_lookUpClass(const char *name)
{
	Class cls = objc_classname_to_class(name);

	if (cls == NULL)
		return Nil;

	if (cls->info & OBJC_CLASS_INFO_SETUP)
		return cls;

	objc_global_mutex_lock();








|

|







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
		}
	}
}

id
objc_lookUpClass(const char *name)
{
	Class cls;

	if ((cls = objc_classname_to_class(name, true)) == NULL)
		return Nil;

	if (cls->info & OBJC_CLASS_INFO_SETUP)
		return cls;

	objc_global_mutex_lock();

629
630
631
632
633
634
635














636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660



661
662
663
664
	rcls->dtable = NULL;

	if (rcls->superclass != Nil)
		cls->superclass = rcls->superclass->name;

	objc_hashtable_set(classes, cls->name, NULL);
}















void
objc_free_all_classes(void)
{
	uint32_t i;

	if (classes == NULL)
		return;

	for (i = 0; i <= classes->last_idx; i++) {
		if (classes->data[i] != NULL) {
			Class cls = (Class)classes->data[i]->obj;

			if (cls == Nil || (uintptr_t)cls & 1)
				continue;

			objc_free_class(cls);
			objc_free_class(cls->isa);
		}
	}

	if (empty_dtable != NULL) {
		objc_sparsearray_free(empty_dtable);
		empty_dtable = NULL;
	}




	objc_hashtable_free(classes);
	classes = NULL;
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>




|




















>
>
>




702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
	rcls->dtable = NULL;

	if (rcls->superclass != Nil)
		cls->superclass = rcls->superclass->name;

	objc_hashtable_set(classes, cls->name, NULL);
}

static void
free_sparsearray(struct sparsearray *sa, size_t depth)
{
	uint_fast16_t i;

	if (sa == NULL || depth == 0)
		return;

	for (i = 0; i < 256; i++)
		free_sparsearray(sa->next[i], depth - 1);

	free(sa);
}

void
objc_free_all_classes(void)
{
	uint_fast32_t i;

	if (classes == NULL)
		return;

	for (i = 0; i <= classes->last_idx; i++) {
		if (classes->data[i] != NULL) {
			Class cls = (Class)classes->data[i]->obj;

			if (cls == Nil || (uintptr_t)cls & 1)
				continue;

			objc_free_class(cls);
			objc_free_class(cls->isa);
		}
	}

	if (empty_dtable != NULL) {
		objc_sparsearray_free(empty_dtable);
		empty_dtable = NULL;
	}

	free_sparsearray(sparsearray, sizeof(uintptr_t));
	sparsearray = NULL;

	objc_hashtable_free(classes);
	classes = NULL;
}