Skip to content

Instantly share code, notes, and snippets.

@hnakamur
Forked from kristate/env_copy_test.bench
Created September 17, 2012 02:18
Show Gist options
  • Save hnakamur/3735229 to your computer and use it in GitHub Desktop.
Save hnakamur/3735229 to your computer and use it in GitHub Desktop.
test on how to copy worker env for spawning
hnakamur@ubuntu:~/execve_env_copy$ gcc -O3 env_copy_test.c
hnakamur@ubuntu:~/execve_env_copy$ rm env_copy_test
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.750000 seconds
type_b: 0.730000 seconds
type_c: 0.640000 seconds
type_d: 0.860000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.750000 seconds
type_b: 0.740000 seconds
type_c: 0.660000 seconds
type_d: 0.820000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.740000 seconds
type_b: 0.740000 seconds
type_c: 0.660000 seconds
type_d: 0.820000 seconds
type_e: 0.790000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.740000 seconds
type_b: 0.750000 seconds
type_c: 0.660000 seconds
type_d: 0.830000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.750000 seconds
type_b: 0.730000 seconds
type_c: 0.660000 seconds
type_d: 0.850000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.740000 seconds
type_b: 0.740000 seconds
type_c: 0.660000 seconds
type_d: 0.820000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.740000 seconds
type_b: 0.750000 seconds
type_c: 0.670000 seconds
type_d: 0.870000 seconds
type_e: 0.770000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.780000 seconds
type_b: 0.740000 seconds
type_c: 0.660000 seconds
type_d: 0.840000 seconds
type_e: 0.770000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.740000 seconds
type_b: 0.750000 seconds
type_c: 0.670000 seconds
type_d: 0.830000 seconds
type_e: 0.770000 seconds
hnakamur@ubuntu:~/execve_env_copy$ ./a.out
type_a: 0.750000 seconds
type_b: 0.740000 seconds
type_c: 0.660000 seconds
type_d: 0.840000 seconds
type_e: 0.760000 seconds
hnakamur@ubuntu:~/execve_env_copy$ uname -a
Linux ubuntu 3.2.0-29-generic #46-Ubuntu SMP Fri Jul 27 17:03:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
/*
gcc -O3 env_copy_test.c; ./a.out
*/
#include <stdio.h>
#include <time.h> /* clock */
#include <stdlib.h> /* free */
#include <string.h> /* sprintf */
#include <errno.h>
extern char **environ;
static inline void type_a(const char *worker_id, const char *ipc_filename) {
int env_cnt = 0;
char **entry;
for (entry = environ; *entry; entry++, env_cnt++);
char *worker_env[2 + env_cnt + 1];
worker_env[0] = malloc(sizeof("LEN_WORKER_ID=") + strlen(worker_id));
sprintf(worker_env[0], "LEV_WORKER_ID=%s", worker_id);
worker_env[1] = malloc(sizeof("LEV_IPC_FILENAME=") + strlen(ipc_filename));
sprintf(worker_env[1], "LEV_IPC_FILENAME=%s", ipc_filename);
memcpy(worker_env + 2, environ, sizeof(char *) * (env_cnt + 1));
free( worker_env[0] ); /* free LEV_WORKER_ID */
free( worker_env[1] ); /* free LEV_IPC_FILENAME */
}
static inline void type_b(const char *worker_id, const char *ipc_filename) {
int n = 0;
char **env_item;
char env_temp[1024];
char *worker_env[256];
sprintf(env_temp, "LEV_WORKER_ID=%s", worker_id);
worker_env[n++] = strdup(env_temp);
sprintf(env_temp, "LEV_IPC_FILENAME=%s", ipc_filename);
worker_env[n++] = strdup(env_temp);
for (env_item=environ;*env_item&&n<254;env_item++) {
worker_env[n++] = *env_item;
}
worker_env[n] = NULL; /* terminate */
free( worker_env[0] ); /* free LEV_WORKER_ID */
free( worker_env[1] ); /* free LEV_IPC_FILENAME */
}
static inline void type_c(const char *worker_id, const char *ipc_filename) {
int env_cnt = 0;
char **entry;
for (entry = environ; *entry; entry++, env_cnt++);
char *worker_env[2 + env_cnt + 1];
char worker_env0_buf[sizeof("LEN_WORKER_ID=") + strlen(worker_id)];
sprintf(worker_env0_buf, "LEV_WORKER_ID=%s", worker_id);
worker_env[0] = worker_env0_buf;
char worker_env1_buf[sizeof("LEV_IPC_FILENAME=") + strlen(ipc_filename)];
sprintf(worker_env1_buf, "LEV_IPC_FILENAME=%s", ipc_filename);
worker_env[1] = worker_env1_buf;
memcpy(worker_env + 2, environ, sizeof(char *) * (env_cnt + 1));
}
static inline void type_d(const char *worker_id, const char *ipc_filename) {
int n = 0;
char env_temp[1024];
int env_cnt = 0;
char **worker_env;
char **entry;
for (entry = environ; *entry; entry++, env_cnt++);
worker_env = (char **)malloc(sizeof(char *) * (2 + env_cnt + 1));
sprintf(env_temp, "LEV_WORKER_ID=%s", worker_id);
worker_env[n++] = strdup(env_temp);
sprintf(env_temp, "LEV_IPC_FILENAME=%s", ipc_filename);
worker_env[n++] = strdup(env_temp);
memcpy(worker_env + 2, environ, sizeof(char *) * (env_cnt + 1));
free( worker_env[0] ); /* free LEV_WORKER_ID */
free( worker_env[1] ); /* free LEV_IPC_FILENAME */
free( worker_env ); /* free LEV_IPC_FILENAME */
}
static inline void type_e(const char *worker_id, const char *ipc_filename) {
int n = 0;
char env_temp[1024];
int env_cnt = 0;
char **entry;
for (entry = environ; *entry; entry++, env_cnt++);
char *worker_env[2 + env_cnt + 1];
sprintf(env_temp, "LEV_WORKER_ID=%s", worker_id);
worker_env[n++] = strdup(env_temp);
sprintf(env_temp, "LEV_IPC_FILENAME=%s", ipc_filename);
worker_env[n++] = strdup(env_temp);
memcpy(worker_env + 2, environ, sizeof(char *) * (env_cnt + 1));
free( worker_env[0] ); /* free LEV_WORKER_ID */
free( worker_env[1] ); /* free LEV_IPC_FILENAME */
}
int main(void)
{
int i;
clock_t begin, end;
char worker_id[8];
char *ipc_filename = "/tmp/lev_12345.tmp";
begin = clock();
for (i=0;i<2000000;i++) {
sprintf(worker_id, "%d", i);
type_a(worker_id, ipc_filename);
}
end = clock();
printf("type_a: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
begin = clock();
for (i=0;i<2000000;i++) {
sprintf(worker_id, "%d", i);
type_b(worker_id, ipc_filename);
}
end = clock();
printf("type_b: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
begin = clock();
for (i=0;i<2000000;i++) {
sprintf(worker_id, "%d", i);
type_c(worker_id, ipc_filename);
}
end = clock();
printf("type_c: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
begin = clock();
for (i=0;i<2000000;i++) {
sprintf(worker_id, "%d", i);
type_d(worker_id, ipc_filename);
}
end = clock();
printf("type_d: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
begin = clock();
for (i=0;i<2000000;i++) {
sprintf(worker_id, "%d", i);
type_e(worker_id, ipc_filename);
}
end = clock();
printf("type_e: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
return 0;
}
@hnakamur
Copy link
Author

In this microbench, type_c is the fastest. However, the time for running once is about 0.66 / 2e6 = 0.33 microsecond.
I suppose the count of worker is the same as number of cores in CPU like 4, 8 or 64.
So I think differences among types are negligible.

Variable length arrays used in type_c needs C99.
http://www.drdobbs.com/the-new-cwhy-variable-length-arrays/184401444
If you support Windows in the future, then this technique cannot be used, though Windows has _malloca() instead.
http://msdn.microsoft.com/en-us//library/vstudio/5471dc8s.aspx

The calculation of sprintf result length was easy for this particular case. However generally it is not that easy. So I understand having a temp buffer on stack and using strdup is a common pattern.

According to http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html ,
the memory size of environment variables we can pass to execve() is at least 32 pages.
So we cannot have a hardcoded limit the count of environment variables.

So we must choose from type_a, type_c, type_d and type_e.

@kristate
Copy link

Let's choose type_e -- I am not looking for speed here; I am looking for the most simple design that represents the least fluctuation in time variance and which is the most maintainable.

Although I am against using constructs like sizeof(char *) * (env_cnt + 1) , type_e is perhaps the most elegant design.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment