mercurial/exewrapper.c
author Bryan O'Sullivan <bryano@fb.com>
Tue, 18 Sep 2012 15:42:19 -0700
changeset 17616 9535a0dc41f2
parent 17063 3fbc6e3abdbd
child 17732 93d97a212559
permissions -rw-r--r--
store: implement fncache basic path encoding in C (This is not yet enabled; it will be turned on in a followup patch.) The path encoding performed by fncache is complex and (perhaps surprisingly) slow enough to negatively affect the overall performance of Mercurial. For a short path (< 120 bytes), the Python code can be reduced to a fairly tractable state machine that either determines that nothing needs to be done in a single pass, or performs the encoding in a second pass. For longer paths, we avoid the more complicated hashed encoding scheme for now, and fall back to Python. Raw performance: I measured in a repo containing 150,000 files in its tip manifest, with a median path name length of 57 bytes, and 95th percentile of 96 bytes. In this repo, the Python code takes 3.1 seconds to encode all path names, while the hybrid C-and-Python code (called from Python) takes 0.21 seconds, for a speedup of about 14. Across several other large repositories, I've measured the speedup from the C code at between 26x and 40x. For path names above 120 bytes where we must fall back to Python for hashed encoding, the speedup is about 1.7x. Thus absolute performance will depend strongly on the characteristics of a particular repository.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
17058
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     1
/*
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     2
 exewrapper.c - wrapper for calling a python script on Windows
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     3
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     4
 Copyright 2012 Adrian Buehlmann <adrian@cadifra.com> and others
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     5
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     6
 This software may be used and distributed according to the terms of the
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     7
 GNU General Public License version 2 or any later version.
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     8
*/
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
     9
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    10
#include <Python.h>
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    11
#include <windows.h>
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    12
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    13
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    14
#ifdef __GNUC__
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    15
int strcat_s(char *d, size_t n, const char *s)
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    16
{
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    17
	return !strncat(d, s, n);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    18
}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    19
#endif
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    20
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    21
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    22
static char pyscript[MAX_PATH + 10];
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    23
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    24
int main(int argc, char *argv[])
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    25
{
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    26
	char *dot;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    27
	int ret;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    28
	int i;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    29
	int n;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    30
	char **pyargv;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    31
	WIN32_FIND_DATA fdata;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    32
	HANDLE hfind;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    33
	const char *err;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    34
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    35
	if (GetModuleFileName(NULL, pyscript, sizeof(pyscript)) == 0)
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    36
	{
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    37
		err = "GetModuleFileName failed";
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    38
		goto bail;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    39
	}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    40
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    41
	dot = strrchr(pyscript, '.');
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    42
	if (dot == NULL) {
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    43
		err = "malformed module filename";
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    44
		goto bail;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    45
	}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    46
	*dot = 0; /* cut trailing ".exe" */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    47
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    48
	hfind = FindFirstFile(pyscript, &fdata);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    49
	if (hfind != INVALID_HANDLE_VALUE) {
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    50
		/* pyscript exists, close handle */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    51
		FindClose(hfind);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    52
	} else {
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    53
		/* file pyscript isn't there, take <pyscript>exe.py */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    54
		strcat_s(pyscript, sizeof(pyscript), "exe.py");
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    55
	}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    56
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    57
	/*
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    58
	Only add the pyscript to the args, if it's not already there. It may
17063
3fbc6e3abdbd exewrapper: use generic term script
Adrian Buehlmann <adrian@cadifra.com>
parents: 17058
diff changeset
    59
	already be there, if the script spawned a child process of itself, in
17058
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    60
	the same way as it got called, that is, with the pyscript already in
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    61
	place. So we optionally accept the pyscript as the first argument
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    62
	(argv[1]), letting our exe taking the role of the python interpreter.
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    63
	*/
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    64
	if (argc >= 2 && strcmp(argv[1], pyscript) == 0) {
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    65
		/*
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    66
		pyscript is already in the args, so there is no need to copy
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    67
		the args and we can directly call the python interpreter with
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    68
		the original args.
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    69
		*/
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    70
		return Py_Main(argc, argv);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    71
	}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    72
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    73
	/*
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    74
	Start assembling the args for the Python interpreter call. We put the
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    75
	name of our exe (argv[0]) in the position where the python.exe
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    76
	canonically is, and insert the pyscript next.
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    77
	*/
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    78
	pyargv = malloc((argc + 5) * sizeof(char*));
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    79
	if (pyargv == NULL) {
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    80
		err = "not enough memory";
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    81
		goto bail;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    82
	}
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    83
	n = 0;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    84
	pyargv[n++] = argv[0];
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    85
	pyargv[n++] = pyscript;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    86
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    87
	/* copy remaining args from the command line */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    88
	for (i = 1; i < argc; i++)
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    89
		pyargv[n++] = argv[i];
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    90
	/* argv[argc] is guaranteed to be NULL, so we forward that guarantee */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    91
	pyargv[n] = NULL;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    92
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    93
	ret = Py_Main(n, pyargv); /* The Python interpreter call */
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    94
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    95
	free(pyargv);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    96
	return ret;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    97
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    98
bail:
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
    99
	fprintf(stderr, "abort: %s\n", err);
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
   100
	return 255;
d5422faf648c exewrapper: adding new exewrapper.c
Adrian Buehlmann <adrian@cadifra.com>
parents:
diff changeset
   101
}