diff --git a/file.c b/file.c
index fb62c51..db655a2 100644
--- a/file.c
+++ b/file.c
@@ -2821,6 +2821,20 @@ ntfs_tail(const char *path)
     buflen = RSTRING_LEN(result),\
     pend = p + buflen)
 
+#define EXPAND_PATH()\
+    if ( !(abs_mode & FEP_DIR_EXPANDED) ) { \
+	file_expand_path(dname, Qnil, abs_mode, result); \
+    } \
+    else { \
+	size_t dlen = RSTRING_LEN(dname); \
+	BUFCHECK(dlen > buflen); \
+	strncpy(buf, RSTRING_PTR(dname), dlen + 1); \
+	rb_str_set_len(result, dlen); \
+	rb_enc_associate(result, rb_enc_check(result, dname)); \
+	ENC_CODERANGE_CLEAR(result); \
+    }
+
+
 VALUE
 rb_home_dir(const char *user, VALUE result)
 {
@@ -2865,6 +2879,8 @@ rb_home_dir(const char *user, VALUE result)
     return result;
 }
 
+#define FEP_FILE_ABSOLUTE 1
+#define FEP_DIR_EXPANDED 2
 static VALUE
 file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
 {
@@ -2877,7 +2893,7 @@ file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
     BUFINIT();
     tainted = OBJ_TAINTED(fname);
 
-    if (s[0] == '~' && abs_mode == 0) {      /* execute only if NOT absolute_path() */
+    if (s[0] == '~' && !(abs_mode & FEP_FILE_ABSOLUTE)) {      /* execute only if NOT absolute_path() */
 	long userlen = 0;
 	tainted = 1;
 	if (isdirsep(s[1]) || s[1] == '\0') {
@@ -2925,7 +2941,7 @@ file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
 	    /* specified drive, but not full path */
 	    int same = 0;
 	    if (!NIL_P(dname) && !not_same_drive(dname, s[0])) {
-		file_expand_path(dname, Qnil, abs_mode, result);
+		EXPAND_PATH();
 		BUFINIT();
 		if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
 		    /* ok, same drive */
@@ -2951,7 +2967,7 @@ file_expand_path(VALUE fname, VALUE dname, int abs_mode, VALUE result)
 #endif
     else if (!rb_is_absolute_path(s)) {
 	if (!NIL_P(dname)) {
-	    file_expand_path(dname, Qnil, abs_mode, result);
+	    EXPAND_PATH();
 	    BUFINIT();
 	    rb_enc_associate(result, rb_enc_check(result, fname));
 	}
@@ -3235,7 +3251,7 @@ VALUE
 rb_file_absolute_path(VALUE fname, VALUE dname)
 {
     check_expand_path_args(fname, dname);
-    return file_expand_path(fname, dname, 1, EXPAND_PATH_BUFFER());
+    return file_expand_path(fname, dname, FEP_FILE_ABSOLUTE, EXPAND_PATH_BUFFER());
 }
 
 /*
@@ -5109,13 +5125,23 @@ rb_find_file_ext(VALUE *filep, const char *const *ext)
     return rb_find_file_ext_safe(filep, ext, rb_safe_level());
 }
 
+#define GET_LOAD_PATH() \
+    if (cached_expanded_load_path) { \
+	RB_GC_GUARD(load_path) = rb_get_expanded_load_path(); \
+	dirs_mode = FEP_DIR_EXPANDED; \
+    } \
+    else { \
+	RB_GC_GUARD(load_path) = rb_get_load_path(); \
+	dirs_mode = 0; \
+    }
+
 int
 rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
 {
     const char *f = StringValueCStr(*filep);
     VALUE fname = *filep, load_path, tmp;
     long i, j, fnlen;
-    int expanded = 0;
+    int expanded = 0, dirs_mode;
 
     if (!ext[0]) return 0;
 
@@ -5150,7 +5176,7 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
 	rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
     }
 
-    RB_GC_GUARD(load_path) = rb_get_load_path();
+    GET_LOAD_PATH();
     if (!load_path) return 0;
 
     fname = rb_str_dup(*filep);
@@ -5164,7 +5190,7 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
 
 	    RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
 	    if (RSTRING_LEN(str) == 0) continue;
-	    file_expand_path(fname, str, 0, tmp);
+	    file_expand_path(fname, str, dirs_mode, tmp);
 	    if (file_load_ok(RSTRING_PTR(tmp))) {
 		*filep = copy_path_class(tmp, *filep);
 		return (int)(j+1);
@@ -5188,7 +5214,7 @@ rb_find_file_safe(VALUE path, int safe_level)
 {
     VALUE tmp, load_path;
     const char *f = StringValueCStr(path);
-    int expanded = 0;
+    int expanded = 0, dirs_mode;
 
     if (f[0] == '~') {
 	tmp = file_expand_path_1(path);
@@ -5214,7 +5240,7 @@ rb_find_file_safe(VALUE path, int safe_level)
 	rb_raise(rb_eSecurityError, "loading from non-absolute path %s", f);
     }
 
-    RB_GC_GUARD(load_path) = rb_get_load_path();
+    GET_LOAD_PATH();
     if (load_path) {
 	long i;
 
@@ -5223,7 +5249,7 @@ rb_find_file_safe(VALUE path, int safe_level)
 	    VALUE str = RARRAY_PTR(load_path)[i];
 	    RB_GC_GUARD(str) = rb_get_path_check(str, safe_level);
 	    if (RSTRING_LEN(str) > 0) {
-		file_expand_path(path, str, 0, tmp);
+		file_expand_path(path, str, dirs_mode, tmp);
 		f = RSTRING_PTR(tmp);
 		if (file_load_ok(f)) goto found;
 	    }
diff --git a/internal.h b/internal.h
index b45b401..9f1b465 100644
--- a/internal.h
+++ b/internal.h
@@ -109,6 +109,8 @@ VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
 
 /* load.c */
 VALUE rb_get_load_path(void);
+VALUE rb_get_expanded_load_path(void);
+RUBY_EXTERN int cached_expanded_load_path;
 
 /* math.c */
 VALUE rb_math_atan2(VALUE, VALUE);
diff --git a/load.c b/load.c
index d7debad..c48a211 100644
--- a/load.c
+++ b/load.c
@@ -4,6 +4,7 @@
 
 #include "ruby/ruby.h"
 #include "ruby/util.h"
+#include "ruby/encoding.h"
 #include "internal.h"
 #include "dln.h"
 #include "eval_intern.h"
@@ -18,7 +19,6 @@ VALUE ruby_dln_librefs;
 #define IS_DLEXT(e) (strcmp((e), DLEXT) == 0)
 #endif
 
-
 static const char *const loadable_ext[] = {
     ".rb", DLEXT,
 #ifdef DLEXT2
@@ -34,21 +34,35 @@ rb_get_load_path(void)
     return load_path;
 }
 
+static VALUE rb_checked_expanded_cache();
+static void rb_set_expanded_cache(VALUE);
+int cached_expanded_load_path = 1;
+
 VALUE
 rb_get_expanded_load_path(void)
 {
-    VALUE load_path = rb_get_load_path();
-    VALUE ary;
-    long i;
+    VALUE expanded = rb_checked_expanded_cache();
 
-    ary = rb_ary_new2(RARRAY_LEN(load_path));
-    for (i = 0; i < RARRAY_LEN(load_path); ++i) {
-	VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
-	rb_str_freeze(path);
-	rb_ary_push(ary, path);
+    if ( !RTEST(expanded) ) {
+	VALUE load_path = rb_get_load_path();
+	long i;
+
+	if (!load_path) return 0;
+
+	expanded = rb_ary_new2(RARRAY_LEN(load_path));
+	for (i = 0; i < RARRAY_LEN(load_path); ++i) {
+	    VALUE path = rb_file_expand_path(RARRAY_PTR(load_path)[i], Qnil);
+	    rb_str_freeze(path);
+	    rb_ary_push(expanded, path);
+	}
+
+	if (cached_expanded_load_path) {
+	    rb_set_expanded_cache(expanded);
+	}
+    } else {
+	expanded = rb_ary_dup(expanded);
     }
-    rb_obj_freeze(ary);
-    return ary;
+    return expanded;
 }
 
 static VALUE
@@ -772,6 +786,199 @@ rb_f_autoload_p(VALUE obj, VALUE sym)
     return rb_mod_autoload_p(klass, sym);
 }
 
+// $LOAD_PATH methods which invalidates cache
+static const char *load_path_reset_cache_methods[] = {
+    "[]=", "collect!", "compact!", "delete",
+    "delete_if", "fill", "flatten!", "insert", "keep_if",
+    "map!", "reject!", "replace", "select!", "shuffle!",
+    "sort!", "sort_by!", "uniq!", NULL
+};
+
+// $LOAD_PATH methods which sends also to cache
+static const char *load_path_apply_to_cache_methods[] = {
+    "clear", "delete_at", "pop", "reverse!", "rotate!",
+    "shift", "slice!", NULL
+};
+
+// $LOAD_PATH methods which sends to cache whith expanded arguments
+static const char *load_path_apply_expanded_methods[] = {
+    "<<", "push", "unshift", NULL
+};
+
+static void
+rb_reset_expanded_cache()
+{
+    GET_VM()->load_path_expanded_cache = 0;
+}
+
+static VALUE
+rb_load_path_expanded_cache()
+{
+    VALUE cache = GET_VM()->load_path_expanded_cache;
+    VALUE expanded = Qnil;
+    if (RTEST(cache)) {
+	expanded = RARRAY_PTR(cache)[2];
+    }
+    return expanded;
+}
+
+// Return cache only if we still in the same working directory
+// and filesystem_encoding didn't change
+// Invalidate cache otherwise
+static VALUE
+rb_checked_expanded_cache()
+{
+    VALUE cache = GET_VM()->load_path_expanded_cache;
+    VALUE expanded = Qnil;
+    if (RTEST(cache)) {
+	VALUE curwd = RARRAY_PTR(cache)[0];
+	VALUE encindex = RARRAY_PTR(cache)[1];
+	int cache_valid = rb_filesystem_encindex() == FIX2INT(encindex);
+
+	if ( cache_valid ) {
+	    cache_valid = curwd == Qtrue;
+	    if (!cache_valid ) {
+		char *cwd = my_getcwd();
+		cache_valid = !strcmp(RSTRING_PTR(curwd), cwd);
+		xfree(cwd);
+	    }
+	}
+
+	if ( !cache_valid ) {
+	    rb_reset_expanded_cache();
+	} else {
+	    expanded = RARRAY_PTR(cache)[2];
+	}
+    }
+    RB_GC_GUARD(cache);
+    return expanded;
+}
+
+static void
+rb_set_expanded_cache(VALUE expanded)
+{
+    VALUE load_path = rb_get_load_path();
+    VALUE cache = rb_ary_new2(2);
+    int i, lplen = RARRAY_LEN(load_path), has_relative = 0;
+    char *d;
+
+    for(i = 0; i < lplen && !has_relative; i++) {
+	d = RSTRING_PTR(RARRAY_PTR(load_path)[i]);
+	has_relative = !rb_is_absolute_path(d);
+    }
+
+    if (has_relative) {
+	char *cwd = my_getcwd();
+	rb_ary_push(cache, rb_str_new_cstr(cwd));
+	xfree(cwd);
+    } else {
+	rb_ary_push(cache, Qtrue);
+    }
+
+    rb_ary_push(cache, INT2FIX(rb_filesystem_encindex()));
+    rb_ary_push(cache, rb_ary_dup(expanded));
+    GET_VM()->load_path_expanded_cache = cache;
+}
+
+// Invalidating $LOAD_PATH methods implementation
+static VALUE
+rb_load_path_reset_cache_method(int argc, VALUE *argv, VALUE self)
+{
+    rb_reset_expanded_cache();
+    return rb_call_super(argc, argv);
+}
+
+// Proxying $LOAD_PATH methods implementation
+static VALUE
+rb_load_path_apply_to_cache_method(int argc, VALUE *argv, VALUE self)
+{
+    VALUE load_path_expanded = rb_load_path_expanded_cache();
+    if (RTEST(load_path_expanded)) {
+	ID func = rb_frame_this_func();
+	rb_funcall2(load_path_expanded, func, argc, argv);
+    }
+    return rb_call_super(argc, argv);
+}
+
+// Proxying with expansion $LOAD_PATH methods implementation
+static VALUE
+rb_load_path_apply_expanded_method(int argc, VALUE *argv, VALUE self)
+{
+    // We call methods on cache only if we still in the same working directory
+    VALUE load_path_expanded = rb_checked_expanded_cache();
+    if (RTEST(load_path_expanded)) {
+	int i, has_relative = 0;
+	ID func = rb_frame_this_func();
+	VALUE expanded = rb_ary_new2(argc);
+
+	for(i = 0; i < argc; i++) {
+	    VALUE path = rb_get_path(argv[i]);
+	    has_relative = has_relative || !rb_is_absolute_path(RSTRING_PTR(path));
+	    path = rb_file_expand_path(path, Qnil);
+	    rb_str_freeze(path);
+	    rb_ary_push(expanded, path);
+	}
+
+	if (has_relative) {
+	    VALUE cache = GET_VM()->load_path_expanded_cache;
+	    char *cwd = my_getcwd();
+	    rb_ary_store(cache, 0, rb_str_new_cstr(cwd));
+	    xfree(cwd);
+	}
+
+	rb_funcall2(load_path_expanded, func, argc, RARRAY_PTR(expanded));
+	RB_GC_GUARD(expanded);
+    }
+    return rb_call_super(argc, argv);
+}
+// $LOAD_PATH.concat(ary) - special, we call push(*ary) instead
+// cause I'm lazy a bit and wish not to rewrite method above second time :)
+static VALUE
+rb_load_path_concat(VALUE self, VALUE ary)
+{
+    ID push;
+    CONST_ID(push, "push");
+    RB_GC_GUARD(ary);
+    return rb_funcall2(self, push, RARRAY_LEN(ary), RARRAY_PTR(ary));
+}
+
+static VALUE
+rb_load_path_init(void)
+{
+    const char **name;
+    VALUE load_path = rb_ary_new();
+    char *cached_flag;
+
+    cached_flag = getenv("RUBY_CACHED_LOAD_PATH");
+    if (cached_flag != NULL) {
+	cached_expanded_load_path = atoi(cached_flag);
+    }
+
+    // Do all the magick if user did not disable it
+    // with RUBY_CACHED_LOAD_PATH=0 environment variable
+    if (cached_expanded_load_path) {
+	VALUE load_path_c = rb_singleton_class(load_path);
+
+	for(name = load_path_reset_cache_methods; *name; name++ ) {
+	    rb_define_method(load_path_c, *name, rb_load_path_reset_cache_method, -1);
+	}
+
+	for(name = load_path_apply_to_cache_methods; *name; name++ ) {
+	    rb_define_method(load_path_c, *name, rb_load_path_apply_to_cache_method, -1);
+	}
+
+	for(name = load_path_apply_expanded_methods; *name; name++ ) {
+	    rb_define_method(load_path_c, *name, rb_load_path_apply_expanded_method, -1);
+	}
+
+	rb_define_method(load_path_c, "concat", rb_load_path_concat, 1);
+    }
+
+    rb_reset_expanded_cache();
+
+    return load_path;
+}
+
 void
 Init_load()
 {
@@ -784,7 +991,7 @@ Init_load()
     rb_define_hooked_variable(var_load_path, (VALUE*)vm, load_path_getter, rb_gvar_readonly_setter);
     rb_alias_variable(rb_intern("$-I"), id_load_path);
     rb_alias_variable(rb_intern("$LOAD_PATH"), id_load_path);
-    vm->load_path = rb_ary_new();
+    vm->load_path = rb_load_path_init();
 
     rb_define_virtual_variable("$\"", get_loaded_features, 0);
     rb_define_virtual_variable("$LOADED_FEATURES", get_loaded_features, 0);
diff --git a/ruby.c b/ruby.c
index 7e06a12..e1b152b 100644
--- a/ruby.c
+++ b/ruby.c
@@ -210,6 +210,8 @@ push_include(const char *path, VALUE (*filter)(VALUE))
     const char sep = PATH_SEP_CHAR;
     const char *p, *s;
     VALUE load_path = GET_VM()->load_path;
+    ID push;
+    CONST_ID(push, "<<");
 
     p = path;
     while (*p) {
@@ -217,7 +219,7 @@ push_include(const char *path, VALUE (*filter)(VALUE))
 	    p++;
 	if (!*p) break;
 	for (s = p; *s && *s != sep; s = CharNext(s));
-	rb_ary_push(load_path, (*filter)(rubylib_mangled_path(p, s - p)));
+	rb_funcall(load_path, push, 1, (*filter)(rubylib_mangled_path(p, s - p)));
 	p = s;
     }
 }
@@ -339,7 +341,7 @@ void
 ruby_init_loadpath_safe(int safe_level)
 {
     VALUE load_path;
-    ID id_initial_load_path_mark;
+    ID id_initial_load_path_mark, push;
     extern const char ruby_initial_load_paths[];
     const char *paths = ruby_initial_load_paths;
 #if defined LOAD_RELATIVE
@@ -448,11 +450,12 @@ ruby_init_loadpath_safe(int safe_level)
     }
 
     id_initial_load_path_mark = rb_intern_const("@gem_prelude_index");
+    CONST_ID(push, "<<");
     while (*paths) {
 	size_t len = strlen(paths);
 	VALUE path = RUBY_RELATIVE(paths, len);
 	rb_ivar_set(path, id_initial_load_path_mark, path);
-	rb_ary_push(load_path, path);
+	rb_funcall(load_path, push, 1, path);
 	paths += len + 1;
     }
 
diff --git a/vm.c b/vm.c
index e04a44b..87eb285 100644
--- a/vm.c
+++ b/vm.c
@@ -1570,6 +1570,7 @@ rb_vm_mark(void *ptr)
 	RUBY_MARK_UNLESS_NULL(vm->thgroup_default);
 	RUBY_MARK_UNLESS_NULL(vm->mark_object_ary);
 	RUBY_MARK_UNLESS_NULL(vm->load_path);
+	RUBY_MARK_UNLESS_NULL(vm->load_path_expanded_cache);
 	RUBY_MARK_UNLESS_NULL(vm->loaded_features);
 	RUBY_MARK_UNLESS_NULL(vm->top_self);
 	RUBY_MARK_UNLESS_NULL(vm->coverages);
diff --git a/vm_core.h b/vm_core.h
index d44bd25..b29a6ef 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -298,6 +298,7 @@ typedef struct rb_vm_struct {
     /* load */
     VALUE top_self;
     VALUE load_path;
+    VALUE load_path_expanded_cache;
     VALUE loaded_features;
     struct st_table *loading_table;
 
