comparison mercurial/cext/revlog.c @ 45131:61e7464477ac

phases: sparsify phaseroots and phasesets As final step of dealing with the holes in the phase numbers, make phaseroots and phasesets both dictionaries indexed by the phase number. Further adjust the interface of the C module by pushing the node to revision mapping down as it is cheaper on the C side to deal with revision numbers. Overall, the patch series improves a no-change "hg up" for my NetBSD test repository from 4.7s to 1.3s. Differential Revision: https://phab.mercurial-scm.org/D8698
author Joerg Sonnenberger <joerg@bec.de>
date Wed, 08 Jul 2020 00:36:36 +0200
parents 090a1a78be4a
children 9719e118e4af
comparison
equal deleted inserted replaced
45130:33524b6bef53 45131:61e7464477ac
107 static const char nullid[20] = {0}; 107 static const char nullid[20] = {0};
108 static const Py_ssize_t nullrev = -1; 108 static const Py_ssize_t nullrev = -1;
109 109
110 static Py_ssize_t inline_scan(indexObject *self, const char **offsets); 110 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
111 111
112 static int index_find_node(indexObject *self, const char *node,
113 Py_ssize_t nodelen);
114
112 #if LONG_MAX == 0x7fffffffL 115 #if LONG_MAX == 0x7fffffffL
113 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#"); 116 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#");
114 #else 117 #else
115 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#"); 118 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#");
116 #endif 119 #endif
573 Py_DECREF(result); 576 Py_DECREF(result);
574 return isfiltered; 577 return isfiltered;
575 } else { 578 } else {
576 return 0; 579 return 0;
577 } 580 }
578 }
579
580 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
581 Py_ssize_t marker, char *phases)
582 {
583 PyObject *iter = NULL;
584 PyObject *iter_item = NULL;
585 Py_ssize_t min_idx = index_length(self) + 2;
586 long iter_item_long;
587
588 if (PyList_GET_SIZE(list) != 0) {
589 iter = PyObject_GetIter(list);
590 if (iter == NULL)
591 return -2;
592 while ((iter_item = PyIter_Next(iter))) {
593 if (!pylong_to_long(iter_item, &iter_item_long)) {
594 Py_DECREF(iter_item);
595 return -2;
596 }
597 Py_DECREF(iter_item);
598 if (iter_item_long < min_idx)
599 min_idx = iter_item_long;
600 phases[iter_item_long] = (char)marker;
601 }
602 Py_DECREF(iter);
603 }
604
605 return min_idx;
606 } 581 }
607 582
608 static inline void set_phase_from_parents(char *phases, int parent_1, 583 static inline void set_phase_from_parents(char *phases, int parent_1,
609 int parent_2, Py_ssize_t i) 584 int parent_2, Py_ssize_t i)
610 { 585 {
771 free(revstates); 746 free(revstates);
772 free(tovisit); 747 free(tovisit);
773 return NULL; 748 return NULL;
774 } 749 }
775 750
751 static int add_roots_get_min(indexObject *self, PyObject *roots, char *phases,
752 char phase)
753 {
754 Py_ssize_t len = index_length(self);
755 PyObject *iter;
756 PyObject *item;
757 PyObject *iterator;
758 int rev, minrev = -1;
759 char *node;
760
761 if (!PySet_Check(roots))
762 return -2;
763 iterator = PyObject_GetIter(roots);
764 if (iterator == NULL)
765 return -2;
766 while ((item = PyIter_Next(iterator))) {
767 if (node_check(item, &node) == -1)
768 goto failed;
769 rev = index_find_node(self, node, 20);
770 /* null is implicitly public, so negative is invalid */
771 if (rev < 0 || rev >= len)
772 goto failed;
773 phases[rev] = phase;
774 if (minrev == -1 || minrev > rev)
775 minrev = rev;
776 Py_DECREF(item);
777 }
778 Py_DECREF(iterator);
779 return minrev;
780 failed:
781 Py_DECREF(iterator);
782 Py_DECREF(item);
783 return -2;
784 }
785
776 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) 786 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
777 { 787 {
788 /* 0: public (untracked), 1: draft, 2: secret, 32: archive,
789 96: internal */
790 static const char trackedphases[] = {1, 2, 32, 96};
791 PyObject *ret = NULL;
778 PyObject *roots = Py_None; 792 PyObject *roots = Py_None;
779 PyObject *ret = NULL; 793 PyObject *idx = NULL;
794 PyObject *pyphase = NULL;
795 PyObject *pyrev = NULL;
796 PyObject *phaseroots = NULL;
780 PyObject *phasessize = NULL; 797 PyObject *phasessize = NULL;
781 PyObject *phaseroots = NULL; 798 PyObject *phasesets[4] = {NULL, NULL, NULL, NULL};
782 PyObject *phaseset = NULL;
783 PyObject *phasessetlist = NULL;
784 PyObject *rev = NULL;
785 Py_ssize_t len = index_length(self); 799 Py_ssize_t len = index_length(self);
786 Py_ssize_t numphase = 0; 800 const char *currentphase;
787 Py_ssize_t minrevallphases = 0;
788 Py_ssize_t minrevphase = 0;
789 Py_ssize_t i = 0;
790 char *phases = NULL; 801 char *phases = NULL;
791 long phase; 802 int minphaserev = -1, rev, i;
803 const int numphases = (int)(sizeof(phasesets) / sizeof(phasesets[0]));
792 804
793 if (!PyArg_ParseTuple(args, "O", &roots)) 805 if (!PyArg_ParseTuple(args, "O", &roots))
794 goto done; 806 return NULL;
795 if (roots == NULL || !PyList_Check(roots)) { 807 if (roots == NULL || !PyDict_Check(roots)) {
796 PyErr_SetString(PyExc_TypeError, "roots must be a list"); 808 PyErr_SetString(PyExc_TypeError, "roots must be a dictionary");
797 goto done; 809 return NULL;
798 } 810 }
799 811
800 phases = calloc( 812 phases = calloc(len, 1);
801 len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
802 if (phases == NULL) { 813 if (phases == NULL) {
803 PyErr_NoMemory(); 814 PyErr_NoMemory();
804 goto done; 815 return NULL;
805 } 816 }
806 /* Put the phase information of all the roots in phases */ 817
807 numphase = PyList_GET_SIZE(roots) + 1; 818 for (i = 0; i < numphases; ++i) {
808 minrevallphases = len + 1; 819 pyphase = PyInt_FromLong(trackedphases[i]);
809 phasessetlist = PyList_New(numphase); 820 if (pyphase == NULL)
810 if (phasessetlist == NULL)
811 goto done;
812
813 PyList_SET_ITEM(phasessetlist, 0, Py_None);
814 Py_INCREF(Py_None);
815
816 for (i = 0; i < numphase - 1; i++) {
817 phaseroots = PyList_GET_ITEM(roots, i);
818 phaseset = PySet_New(NULL);
819 if (phaseset == NULL)
820 goto release; 821 goto release;
821 PyList_SET_ITEM(phasessetlist, i + 1, phaseset); 822 phaseroots = PyDict_GetItem(roots, pyphase);
822 if (!PyList_Check(phaseroots)) { 823 Py_DECREF(pyphase);
823 PyErr_SetString(PyExc_TypeError, 824 if (phaseroots == NULL)
824 "roots item must be a list"); 825 continue;
826 rev = add_roots_get_min(self, phaseroots, phases, trackedphases[i]);
827 phaseroots = NULL;
828 if (rev == -2)
825 goto release; 829 goto release;
826 } 830 if (rev != -1 && (minphaserev == -1 || rev < minphaserev))
827 minrevphase = 831 minphaserev = rev;
828 add_roots_get_min(self, phaseroots, i + 1, phases); 832 }
829 if (minrevphase == -2) /* Error from add_roots_get_min */ 833
834 for (i = 0; i < numphases; ++i) {
835 phasesets[i] = PySet_New(NULL);
836 if (phasesets[i] == NULL)
830 goto release; 837 goto release;
831 minrevallphases = MIN(minrevallphases, minrevphase); 838 }
832 } 839
833 /* Propagate the phase information from the roots to the revs */ 840 if (minphaserev == -1)
834 if (minrevallphases != -1) { 841 minphaserev = len;
842 for (rev = minphaserev; rev < len; ++rev) {
835 int parents[2]; 843 int parents[2];
836 for (i = minrevallphases; i < len; i++) { 844 /*
837 if (index_get_parents(self, i, parents, (int)len - 1) < 845 * The parent lookup could be skipped for phaseroots, but
838 0) 846 * phase --force would historically not recompute them
839 goto release; 847 * correctly, leaving descendents with a lower phase around.
840 set_phase_from_parents(phases, parents[0], parents[1], 848 * As such, unconditionally recompute the phase.
841 i); 849 */
842 } 850 if (index_get_parents(self, rev, parents, (int)len - 1) < 0)
843 } 851 goto release;
844 /* Transform phase list to a python list */ 852 set_phase_from_parents(phases, parents[0], parents[1], rev);
853 switch (phases[rev]) {
854 case 0:
855 continue;
856 case 1:
857 pyphase = phasesets[0];
858 break;
859 case 2:
860 pyphase = phasesets[1];
861 break;
862 case 32:
863 pyphase = phasesets[2];
864 break;
865 case 96:
866 pyphase = phasesets[3];
867 break;
868 default:
869 goto release;
870 }
871 pyrev = PyInt_FromLong(rev);
872 if (pyrev == NULL)
873 goto release;
874 if (PySet_Add(pyphase, pyrev) == -1) {
875 Py_DECREF(pyrev);
876 goto release;
877 }
878 Py_DECREF(pyrev);
879 }
880 phaseroots = _dict_new_presized(numphases);
881 if (phaseroots == NULL)
882 goto release;
883 for (int i = 0; i < numphases; ++i) {
884 pyphase = PyInt_FromLong(trackedphases[i]);
885 if (pyphase == NULL)
886 goto release;
887 if (PyDict_SetItem(phaseroots, pyphase, phasesets[i]) == -1) {
888 Py_DECREF(pyphase);
889 goto release;
890 }
891 Py_DECREF(phasesets[i]);
892 phasesets[i] = NULL;
893 }
845 phasessize = PyInt_FromSsize_t(len); 894 phasessize = PyInt_FromSsize_t(len);
846 if (phasessize == NULL) 895 if (phasessize == NULL)
847 goto release; 896 goto release;
848 for (i = 0; i < len; i++) { 897
849 phase = phases[i]; 898 ret = PyTuple_Pack(2, phasessize, phaseroots);
850 /* We only store the sets of phase for non public phase, the 899 Py_DECREF(phasessize);
851 * public phase is computed as a difference */ 900 Py_DECREF(phaseroots);
852 if (phase != 0) { 901 return ret;
853 phaseset = PyList_GET_ITEM(phasessetlist, phase);
854 rev = PyInt_FromSsize_t(i);
855 if (rev == NULL)
856 goto release;
857 PySet_Add(phaseset, rev);
858 Py_XDECREF(rev);
859 }
860 }
861 ret = PyTuple_Pack(2, phasessize, phasessetlist);
862 902
863 release: 903 release:
864 Py_XDECREF(phasessize); 904 for (i = 0; i < numphases; ++i)
865 Py_XDECREF(phasessetlist); 905 Py_XDECREF(phasesets[i]);
866 done: 906 Py_XDECREF(phaseroots);
907
867 free(phases); 908 free(phases);
868 return ret; 909 return NULL;
869 } 910 }
870 911
871 static PyObject *index_headrevs(indexObject *self, PyObject *args) 912 static PyObject *index_headrevs(indexObject *self, PyObject *args)
872 { 913 {
873 Py_ssize_t i, j, len; 914 Py_ssize_t i, j, len;