bpo-36030: Improve performance of some tuple operations (GH-12052) · python/cpython@4fa10dd

@@ -59,6 +59,15 @@ show_track(void)

5959

}

6060

#endif

616162+

static inline void

63+

tuple_gc_track(PyTupleObject *op)

64+

{

65+

#ifdef SHOW_TRACK_COUNT

66+

count_tracked++;

67+

#endif

68+

_PyObject_GC_TRACK(op);

69+

}

70+6271

/* Print summary info about the state of the optimized allocator */

6372

void

6473

_PyTuple_DebugMallocStats(FILE *out)

@@ -76,25 +85,25 @@ _PyTuple_DebugMallocStats(FILE *out)

7685

#endif

7786

}

788779-

PyObject *

80-

PyTuple_New(Py_ssize_t size)

88+

/* Allocate an uninitialized tuple object. Before making it public following

89+

steps must be done:

90+

- initialize its items

91+

- call tuple_gc_track() on it

92+

Because the empty tuple is always reused and it's already tracked by GC,

93+

this function must not be called with size == 0 (unless from PyTuple_New()

94+

which wraps this function).

95+

*/

96+

static PyTupleObject *

97+

tuple_alloc(Py_ssize_t size)

8198

{

8299

PyTupleObject *op;

83-

Py_ssize_t i;

84100

if (size < 0) {

85101

PyErr_BadInternalCall();

86102

return NULL;

87103

}

88104

#if PyTuple_MAXSAVESIZE > 0

89-

if (size == 0 && free_list[0]) {

90-

op = free_list[0];

91-

Py_INCREF(op);

92-

#ifdef COUNT_ALLOCS

93-

_Py_tuple_zero_allocs++;

94-

#endif

95-

return (PyObject *) op;

96-

}

97105

if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {

106+

assert(size != 0);

98107

free_list[size] = (PyTupleObject *) op->ob_item[0];

99108

numfree[size]--;

100109

#ifdef COUNT_ALLOCS

@@ -113,25 +122,41 @@ PyTuple_New(Py_ssize_t size)

113122

/* Check for overflow */

114123

if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(PyTupleObject) -

115124

sizeof(PyObject *)) / sizeof(PyObject *)) {

116-

return PyErr_NoMemory();

125+

return (PyTupleObject *)PyErr_NoMemory();

117126

}

118127

op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);

119128

if (op == NULL)

120129

return NULL;

121130

}

122-

for (i=0; i < size; i++)

131+

return op;

132+

}

133+134+

PyObject *

135+

PyTuple_New(Py_ssize_t size)

136+

{

137+

PyTupleObject *op;

138+

#if PyTuple_MAXSAVESIZE > 0

139+

if (size == 0 && free_list[0]) {

140+

op = free_list[0];

141+

Py_INCREF(op);

142+

#ifdef COUNT_ALLOCS

143+

_Py_tuple_zero_allocs++;

144+

#endif

145+

return (PyObject *) op;

146+

}

147+

#endif

148+

op = tuple_alloc(size);

149+

for (Py_ssize_t i = 0; i < size; i++) {

123150

op->ob_item[i] = NULL;

151+

}

124152

#if PyTuple_MAXSAVESIZE > 0

125153

if (size == 0) {

126154

free_list[0] = op;

127155

++numfree[0];

128156

Py_INCREF(op); /* extra INCREF so that this is never freed */

129157

}

130158

#endif

131-

#ifdef SHOW_TRACK_COUNT

132-

count_tracked++;

133-

#endif

134-

_PyObject_GC_TRACK(op);

159+

tuple_gc_track(op);

135160

return (PyObject *) op;

136161

}

137162

@@ -211,24 +236,28 @@ PyTuple_Pack(Py_ssize_t n, ...)

211236

{

212237

Py_ssize_t i;

213238

PyObject *o;

214-

PyObject *result;

215239

PyObject **items;

216240

va_list vargs;

217241242+

if (n == 0) {

243+

return PyTuple_New(0);

244+

}

245+218246

va_start(vargs, n);

219-

result = PyTuple_New(n);

247+

PyTupleObject *result = tuple_alloc(n);

220248

if (result == NULL) {

221249

va_end(vargs);

222250

return NULL;

223251

}

224-

items = ((PyTupleObject *)result)->ob_item;

252+

items = result->ob_item;

225253

for (i = 0; i < n; i++) {

226254

o = va_arg(vargs, PyObject *);

227255

Py_INCREF(o);

228256

items[i] = o;

229257

}

230258

va_end(vargs);

231-

return result;

259+

tuple_gc_track(result);

260+

return (PyObject *)result;

232261

}

233262234263

@@ -421,7 +450,11 @@ tupleitem(PyTupleObject *a, Py_ssize_t i)

421450

PyObject *

422451

_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)

423452

{

424-

PyTupleObject *tuple = (PyTupleObject *)PyTuple_New(n);

453+

if (n == 0) {

454+

return PyTuple_New(0);

455+

}

456+457+

PyTupleObject *tuple = tuple_alloc(n);

425458

if (tuple == NULL) {

426459

return NULL;

427460

}

@@ -431,6 +464,7 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)

431464

Py_INCREF(item);

432465

dst[i] = item;

433466

}

467+

tuple_gc_track(tuple);

434468

return (PyObject *)tuple;

435469

}

436470

@@ -486,7 +520,11 @@ tupleconcat(PyTupleObject *a, PyObject *bb)

486520

if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))

487521

return PyErr_NoMemory();

488522

size = Py_SIZE(a) + Py_SIZE(b);

489-

np = (PyTupleObject *) PyTuple_New(size);

523+

if (size == 0) {

524+

return PyTuple_New(0);

525+

}

526+527+

np = tuple_alloc(size);

490528

if (np == NULL) {

491529

return NULL;

492530

}

@@ -504,6 +542,7 @@ tupleconcat(PyTupleObject *a, PyObject *bb)

504542

Py_INCREF(v);

505543

dest[i] = v;

506544

}

545+

tuple_gc_track(np);

507546

return (PyObject *)np;

508547

#undef b

509548

}

@@ -515,22 +554,21 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)

515554

Py_ssize_t size;

516555

PyTupleObject *np;

517556

PyObject **p, **items;

518-

if (n < 0)

519-

n = 0;

520557

if (Py_SIZE(a) == 0 || n == 1) {

521558

if (PyTuple_CheckExact(a)) {

522559

/* Since tuples are immutable, we can return a shared

523560

copy in this case */

524561

Py_INCREF(a);

525562

return (PyObject *)a;

526563

}

527-

if (Py_SIZE(a) == 0)

528-

return PyTuple_New(0);

564+

}

565+

if (Py_SIZE(a) == 0 || n <= 0) {

566+

return PyTuple_New(0);

529567

}

530568

if (n > PY_SSIZE_T_MAX / Py_SIZE(a))

531569

return PyErr_NoMemory();

532570

size = Py_SIZE(a) * n;

533-

np = (PyTupleObject *) PyTuple_New(size);

571+

np = tuple_alloc(size);

534572

if (np == NULL)

535573

return NULL;

536574

p = np->ob_item;

@@ -542,6 +580,7 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)

542580

p++;

543581

}

544582

}

583+

tuple_gc_track(np);

545584

return (PyObject *) np;

546585

}

547586

@@ -754,7 +793,6 @@ tuplesubscript(PyTupleObject* self, PyObject* item)

754793

else if (PySlice_Check(item)) {

755794

Py_ssize_t start, stop, step, slicelength, i;

756795

size_t cur;

757-

PyObject* result;

758796

PyObject* it;

759797

PyObject **src, **dest;

760798

@@ -774,19 +812,20 @@ tuplesubscript(PyTupleObject* self, PyObject* item)

774812

return (PyObject *)self;

775813

}

776814

else {

777-

result = PyTuple_New(slicelength);

815+

PyTupleObject* result = tuple_alloc(slicelength);

778816

if (!result) return NULL;

779817780818

src = self->ob_item;

781-

dest = ((PyTupleObject *)result)->ob_item;

819+

dest = result->ob_item;

782820

for (cur = start, i = 0; i < slicelength;

783821

cur += step, i++) {

784822

it = src[cur];

785823

Py_INCREF(it);

786824

dest[i] = it;

787825

}

788826789-

return result;

827+

tuple_gc_track(result);

828+

return (PyObject *)result;

790829

}

791830

}

792831

else {