Skip to content

Commit 4c0196d

Browse files
authored
PYTHON-3717 Speed up _type_marker check in BSON (#1219)
1 parent bda9e3a commit 4c0196d

File tree

3 files changed

+47
-40
lines changed

3 files changed

+47
-40
lines changed

bson/_cbsonmodule.c

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct module_state {
5555
PyObject* DatetimeMS;
5656
PyObject* _min_datetime_ms;
5757
PyObject* _max_datetime_ms;
58+
PyObject* _type_marker_str;
5859
};
5960

6061
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
@@ -378,6 +379,9 @@ static int _load_python_objects(PyObject* module) {
378379
PyObject* compiled = NULL;
379380
struct module_state *state = GETSTATE(module);
380381

382+
/* Python str for faster _type_marker check */
383+
state->_type_marker_str = PyUnicode_FromString("_type_marker");
384+
381385
if (_load_object(&state->Binary, "bson.binary", "Binary") ||
382386
_load_object(&state->Code, "bson.code", "Code") ||
383387
_load_object(&state->ObjectId, "bson.objectid", "ObjectId") ||
@@ -428,12 +432,12 @@ static int _load_python_objects(PyObject* module) {
428432
*
429433
* Return the type marker, 0 if there is no marker, or -1 on failure.
430434
*/
431-
static long _type_marker(PyObject* object) {
435+
static long _type_marker(PyObject* object, PyObject* _type_marker_str) {
432436
PyObject* type_marker = NULL;
433437
long type = 0;
434438

435-
if (PyObject_HasAttrString(object, "_type_marker")) {
436-
type_marker = PyObject_GetAttrString(object, "_type_marker");
439+
if (PyObject_HasAttr(object, _type_marker_str)) {
440+
type_marker = PyObject_GetAttr(object, _type_marker_str);
437441
if (type_marker == NULL) {
438442
return -1;
439443
}
@@ -450,13 +454,6 @@ static long _type_marker(PyObject* object) {
450454
if (type_marker && PyLong_CheckExact(type_marker)) {
451455
type = PyLong_AsLong(type_marker);
452456
Py_DECREF(type_marker);
453-
/*
454-
* Py(Long|Int)_AsLong returns -1 for error but -1 is a valid value
455-
* so we call PyErr_Occurred to differentiate.
456-
*/
457-
if (type == -1 && PyErr_Occurred()) {
458-
return -1;
459-
}
460457
} else {
461458
Py_XDECREF(type_marker);
462459
}
@@ -504,13 +501,12 @@ int cbson_convert_type_registry(PyObject* registry_obj, type_registry_t* registr
504501
return 0;
505502
}
506503

507-
/* Fill out a codec_options_t* from a CodecOptions object. Use with the "O&"
508-
* format spec in PyArg_ParseTuple.
504+
/* Fill out a codec_options_t* from a CodecOptions object.
509505
*
510506
* Return 1 on success. options->document_class is a new reference.
511507
* Return 0 on failure.
512508
*/
513-
int convert_codec_options(PyObject* options_obj, void* p) {
509+
int convert_codec_options(PyObject* self, PyObject* options_obj, void* p) {
514510
codec_options_t* options = (codec_options_t*)p;
515511
PyObject* type_registry_obj = NULL;
516512
long type_marker;
@@ -527,7 +523,8 @@ int convert_codec_options(PyObject* options_obj, void* p) {
527523
&options->datetime_conversion))
528524
return 0;
529525

530-
type_marker = _type_marker(options->document_class);
526+
type_marker = _type_marker(options->document_class,
527+
GETSTATE(self)->_type_marker_str);
531528
if (type_marker < 0) {
532529
return 0;
533530
}
@@ -730,7 +727,7 @@ static int _write_element_to_buffer(PyObject* self, buffer_t buffer,
730727
* problems with python sub interpreters. Our custom types should
731728
* have a _type_marker attribute, which we can switch on instead.
732729
*/
733-
long type = _type_marker(value);
730+
long type = _type_marker(value, state->_type_marker_str);
734731
if (type < 0) {
735732
return 0;
736733
}
@@ -1382,7 +1379,7 @@ int write_dict(PyObject* self, buffer_t buffer,
13821379
long type_marker;
13831380

13841381
/* check for RawBSONDocument */
1385-
type_marker = _type_marker(dict);
1382+
type_marker = _type_marker(dict, state->_type_marker_str);
13861383
if (type_marker < 0) {
13871384
return 0;
13881385
}
@@ -1504,18 +1501,20 @@ static PyObject* _cbson_dict_to_bson(PyObject* self, PyObject* args) {
15041501
PyObject* result;
15051502
unsigned char check_keys;
15061503
unsigned char top_level = 1;
1504+
PyObject* options_obj;
15071505
codec_options_t options;
15081506
buffer_t buffer;
15091507
PyObject* raw_bson_document_bytes_obj;
15101508
long type_marker;
15111509

1512-
if (!PyArg_ParseTuple(args, "ObO&|b", &dict, &check_keys,
1513-
convert_codec_options, &options, &top_level)) {
1510+
if (!(PyArg_ParseTuple(args, "ObO|b", &dict, &check_keys,
1511+
&options_obj, &top_level) &&
1512+
convert_codec_options(self, options_obj, &options))) {
15141513
return NULL;
15151514
}
15161515

15171516
/* check for RawBSONDocument */
1518-
type_marker = _type_marker(dict);
1517+
type_marker = _type_marker(dict, GETSTATE(self)->_type_marker_str);
15191518
if (type_marker < 0) {
15201519
destroy_codec_options(&options);
15211520
return NULL;
@@ -2526,6 +2525,7 @@ static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) {
25262525
/* TODO: Support buffer protocol */
25272526
char* string;
25282527
PyObject* bson;
2528+
PyObject* options_obj;
25292529
codec_options_t options;
25302530
unsigned position;
25312531
unsigned max;
@@ -2535,8 +2535,9 @@ static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) {
25352535
PyObject* value;
25362536
PyObject* result_tuple;
25372537

2538-
if (!PyArg_ParseTuple(args, "OIIO&p", &bson, &position, &max,
2539-
convert_codec_options, &options, &raw_array)) {
2538+
if (!(PyArg_ParseTuple(args, "OIIOp", &bson, &position, &max,
2539+
&options_obj, &raw_array) &&
2540+
convert_codec_options(self, options_obj, &options))) {
25402541
return NULL;
25412542
}
25422543

@@ -2638,7 +2639,7 @@ static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
26382639
Py_buffer view = {0};
26392640

26402641
if (! (PyArg_ParseTuple(args, "OO", &bson, &options_obj) &&
2641-
convert_codec_options(options_obj, &options))) {
2642+
convert_codec_options(self, options_obj, &options))) {
26422643
return result;
26432644
}
26442645

@@ -2715,10 +2716,8 @@ static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) {
27152716
PyObject* options_obj = NULL;
27162717
Py_buffer view = {0};
27172718

2718-
if (!PyArg_ParseTuple(args, "OO", &bson, &options_obj)) {
2719-
return NULL;
2720-
}
2721-
if (!convert_codec_options(options_obj, &options)) {
2719+
if (!(PyArg_ParseTuple(args, "OO", &bson, &options_obj) &&
2720+
convert_codec_options(self, options_obj, &options))) {
27222721
return NULL;
27232722
}
27242723

@@ -2966,6 +2965,7 @@ static int _cbson_clear(PyObject *m) {
29662965
Py_CLEAR(GETSTATE(m)->MaxKey);
29672966
Py_CLEAR(GETSTATE(m)->UTC);
29682967
Py_CLEAR(GETSTATE(m)->REType);
2968+
Py_CLEAR(GETSTATE(m)->_type_marker_str);
29692969
return 0;
29702970
}
29712971

bson/_cbsonmodule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ typedef struct codec_options_t {
8686

8787
#define _cbson_convert_codec_options_INDEX 4
8888
#define _cbson_convert_codec_options_RETURN int
89-
#define _cbson_convert_codec_options_PROTO (PyObject* options_obj, void* p)
89+
#define _cbson_convert_codec_options_PROTO (PyObject* self, PyObject* options_obj, void* p)
9090

9191
#define _cbson_destroy_codec_options_INDEX 5
9292
#define _cbson_destroy_codec_options_RETURN void

pymongo/_cmessagemodule.c

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,21 @@ static PyObject* _cbson_query_message(PyObject* self, PyObject* args) {
7575
int num_to_return;
7676
PyObject* query;
7777
PyObject* field_selector;
78+
PyObject* options_obj;
7879
codec_options_t options;
7980
buffer_t buffer = NULL;
8081
int length_location, message_length;
8182
PyObject* result = NULL;
8283

83-
if (!PyArg_ParseTuple(args, "Iet#iiOOO&",
84+
if (!(PyArg_ParseTuple(args, "Iet#iiOOO",
8485
&flags,
8586
"utf-8",
8687
&collection_name,
8788
&collection_name_length,
8889
&num_to_skip, &num_to_return,
8990
&query, &field_selector,
90-
convert_codec_options, &options)) {
91+
&options_obj) &&
92+
convert_codec_options(state->_cbson, options_obj, &options))) {
9193
return NULL;
9294
}
9395
buffer = pymongo_buffer_new();
@@ -220,6 +222,7 @@ static PyObject* _cbson_op_msg(PyObject* self, PyObject* args) {
220222
Py_ssize_t identifier_length = 0;
221223
PyObject* docs;
222224
PyObject* doc;
225+
PyObject* options_obj;
223226
codec_options_t options;
224227
buffer_t buffer = NULL;
225228
int length_location, message_length;
@@ -229,14 +232,15 @@ static PyObject* _cbson_op_msg(PyObject* self, PyObject* args) {
229232
PyObject* iterator = NULL;
230233

231234
/*flags, command, identifier, docs, opts*/
232-
if (!PyArg_ParseTuple(args, "IOet#OO&",
235+
if (!(PyArg_ParseTuple(args, "IOet#OO",
233236
&flags,
234237
&command,
235238
"utf-8",
236239
&identifier,
237240
&identifier_length,
238241
&docs,
239-
convert_codec_options, &options)) {
242+
&options_obj) &&
243+
convert_codec_options(state->_cbson, options_obj, &options))) {
240244
return NULL;
241245
}
242246
buffer = pymongo_buffer_new();
@@ -528,14 +532,15 @@ _cbson_encode_batched_op_msg(PyObject* self, PyObject* args) {
528532
PyObject* ctx = NULL;
529533
PyObject* to_publish = NULL;
530534
PyObject* result = NULL;
535+
PyObject* options_obj;
531536
codec_options_t options;
532537
buffer_t buffer;
533538
struct module_state *state = GETSTATE(self);
534539

535-
if (!PyArg_ParseTuple(args, "bOObO&O",
540+
if (!(PyArg_ParseTuple(args, "bOObOO",
536541
&op, &command, &docs, &ack,
537-
convert_codec_options, &options,
538-
&ctx)) {
542+
&options_obj, &ctx) &&
543+
convert_codec_options(state->_cbson, options_obj, &options))) {
539544
return NULL;
540545
}
541546
if (!(buffer = pymongo_buffer_new())) {
@@ -581,14 +586,15 @@ _cbson_batched_op_msg(PyObject* self, PyObject* args) {
581586
PyObject* ctx = NULL;
582587
PyObject* to_publish = NULL;
583588
PyObject* result = NULL;
589+
PyObject* options_obj;
584590
codec_options_t options;
585591
buffer_t buffer;
586592
struct module_state *state = GETSTATE(self);
587593

588-
if (!PyArg_ParseTuple(args, "bOObO&O",
594+
if (!(PyArg_ParseTuple(args, "bOObOO",
589595
&op, &command, &docs, &ack,
590-
convert_codec_options, &options,
591-
&ctx)) {
596+
&options_obj, &ctx) &&
597+
convert_codec_options(state->_cbson, options_obj, &options))) {
592598
return NULL;
593599
}
594600
if (!(buffer = pymongo_buffer_new())) {
@@ -850,14 +856,15 @@ _cbson_encode_batched_write_command(PyObject* self, PyObject* args) {
850856
PyObject* ctx = NULL;
851857
PyObject* to_publish = NULL;
852858
PyObject* result = NULL;
859+
PyObject* options_obj;
853860
codec_options_t options;
854861
buffer_t buffer;
855862
struct module_state *state = GETSTATE(self);
856863

857-
if (!PyArg_ParseTuple(args, "et#bOOO&O", "utf-8",
864+
if (!(PyArg_ParseTuple(args, "et#bOOOO", "utf-8",
858865
&ns, &ns_len, &op, &command, &docs,
859-
convert_codec_options, &options,
860-
&ctx)) {
866+
&options_obj, &ctx) &&
867+
convert_codec_options(state->_cbson, options_obj, &options))) {
861868
return NULL;
862869
}
863870
if (!(buffer = pymongo_buffer_new())) {

0 commit comments

Comments
 (0)