diff -u b/term_reference_tree.module b/term_reference_tree.module
--- b/term_reference_tree.module
+++ b/term_reference_tree.module
@@ -4,6 +4,22 @@
 module_load_include('inc', 'term_reference_tree', 'term_reference_tree.widget');
 
 /**
+ * Implements hook_taxonomy_term_presave().
+ */
+function term_reference_tree_taxonomy_term_presave($term) {
+  drupal_static_reset("_term_reference_tree_taxonomy_get_tree:$term->vid");
+  cache_clear_all("_term_reference_tree_taxonomy_get_tree:$term->vid:", 'cache');
+}
+
+/**
+ * Implements hook_taxonomy_term_delete().
+ */
+function term_reference_tree_taxonomy_term_delete($term) {
+  drupal_static_reset("_term_reference_tree_taxonomy_get_tree:$term->vid");
+  cache_clear_all("_term_reference_tree_taxonomy_get_tree:$term->vid:", 'cache');
+}
+
+/**
  * Implements hook_element_info().
  */
 function term_reference_tree_element_info() {
@@ -65,7 +81,6 @@
   );
 }
 
-
 /**
  * This function returns a taxonomy term hierarchy in a nested array.
  *
@@ -77,81 +92,106 @@
  * @return
  *   A nested array of the term's child objects.
  */
-function _term_reference_tree_get_term_hierarchy($terms, $allowed, $default = array(), $opened = array(), $use_ajax = FALSE) {
-  foreach ($terms as $tid => &$term) {
-    // Remove filtered items.
-    if (!empty($allowed) and !array_key_exists($term->tid, $allowed)) {
-      unset($terms[$tid]);
-    }
-    else {
-      // By default set children to FALSE.
-      $term->children = FALSE;
+function _term_reference_tree_get_term_hierarchy($tid, $vid, $allowed, $default = array(), $opened = array(), $use_ajax = FALSE) {
+  global $language;
+  $tree = _term_reference_tree_taxonomy_get_tree($vid, $language->language);
+
+  if (!empty($allowed)) {
+    $tree['terms'] = array_intersect_key($tree['terms'], $allowed);
+  }
+
+  return _term_reference_tree_get_term_hierarchy_recursive($tid, $tree['terms'], $tree['hierarchy'], $default, $opened, $use_ajax);
+}
+
+/**
+ * Recursive helper function for _term_reference_tree_get_term_hierarchy().
+ */
+function _term_reference_tree_get_term_hierarchy_recursive($parent_tid, $terms, $children_tree, $default = array(), $opened = array(), $use_ajax = FALSE) {
+  $tree = array();
+
+  if (isset($children_tree[$parent_tid])) {
+    foreach($children_tree[$parent_tid] as $child_tid) {
+      $term = $terms[$child_tid];
+      $term->render_children = FALSE;
       $term->children_selected = FALSE;
+      $has_children = isset($children_tree[$term->tid]);
 
-      // If there are any children elements then check if we need to add them
-      // for rendering or leave them for ajax.
-      if ($children = _term_reference_tree_get_children($term->tid, $term->vid)) {
-        $term->children = TRUE;
-        // Selected items need to be displayed from the begining.
-        if (in_array($term->tid, $default) || in_array($term->tid, $opened) || !$use_ajax) {
-          if ($children = _term_reference_tree_get_term_hierarchy($children, $allowed, $default, $opened, $use_ajax)) {
-
-            // Check if at least one children is selected.
-            foreach ($children as $child) {
-              if (isset($default[$child->tid])) {
-                $term->children_selected = TRUE;
-                break;
-              }
-            }
-
-            if ($term->children_selected || in_array($term->tid, $opened) || !$use_ajax) {
-              $term->children = $children;
-            }
-          }
+      if ($has_children) {
+        $term->children = _term_reference_tree_get_term_hierarchy_recursive($term->tid, $terms, $children_tree, $default, $opened, $use_ajax);
+        $term->children_selected = _term_reference_tree_children_selected($term, $default);
+
+        // Display child terms if we're no in ajax mode or if one of them is
+        // selected or opened.
+        if (!$use_ajax || $term->children_selected || in_array($term->tid, $opened)) {
+          $term->render_children = TRUE;
         }
       }
+      $tree[$term->tid] = $term;
     }
   }
+  return $tree;
+}
 
-  return $terms;
+function _term_reference_tree_children_selected($term, $default) {
+  if (!empty($term->children)) {
+    foreach ($term->children as $child) {
+      if (isset($default[$child->tid])) {
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
 }
 
-/**
- * This function is like taxonomy_get_children, except it doesn't load the entire term.
- *
- * @param $tid
- *   The ID of the term whose children you want to get.
- * @param $vid
- *   The vocabulary ID.
- *
- * @return
- *   An array of taxonomy terms, each in the form array('tid' => $tid, 'name' => $name)
- */
-function _term_reference_tree_get_children($tid, $vid) {
-  // DO NOT LOAD TAXONOMY TERMS HERE
-  // Taxonomy terms take a lot of time and memory to load, and this can be
-  // very bad on large vocabularies.  Instead, we load the term as necessary
-  // in cases where it's needed (such as using tokens or when the locale
-  // module is enabled).
-  $query = db_select('taxonomy_term_data', 't');
-  $query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid');
-  $query->join('taxonomy_vocabulary', 'v', 'v.vid = t.vid');
-  $query->fields('t', array('tid', 'name', 'vid'));
-  $query->addField('v', 'machine_name', 'vocabulary_machine_name');
-  $query->condition('t.vid', $vid);
-  $query->condition('h.parent', $tid);
-  $query->addTag('term_access');
-  $query->addTag('translatable');
-  $query->orderBy('t.weight');
-  $query->orderBy('t.name');
-  $results = $query->execute();
-
-  $terms = array();
-  while ($term = $results->fetchObject()) {
-    $terms[$term->tid] = $term;
+function _term_reference_tree_taxonomy_get_tree($vid, $langcode) {
+  $cid = __FUNCTION__ . ':' . $vid;
+  $tree = &drupal_static($cid);
+
+  if (!isset($tree)) {
+    if ($cache = cache_get("$cid:$langcode")) {
+      $tree = $cache->data;
+    }
+    else {
+      // No access check here as we are hard caching this.
+      // This allows us to cache mainly the term labels in $term->name
+      // for each term and language using
+      // hook_term_reference_tree_taxonomy_get_tree_alter().
+      $query = db_select('taxonomy_term_data', 't');
+      $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+      $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid');
+      $query->addField('v', 'machine_name', 'vocabulary_machine_name');
+      $result = $query->addTag('translatable')
+        ->addTag('taxonomy_term_access')
+        ->fields('t')
+        ->fields('h', array('parent'))
+        ->condition('t.vid', $vid)
+        ->orderBy('t.weight')
+        ->orderBy('t.name')
+        ->execute();
+
+      foreach ($result as $term) {
+        $tree['hierarchy'][$term->parent][] = $term->tid;
+        $tree['terms'][$term->tid] = $term;
+      }
+
+      // Give other modules a chance to alter the terms.
+      drupal_alter('term_reference_tree_taxonomy_get_tree', $vid, $tree['terms'], $tree['hierarchy'], $langcode);
+
+      cache_set("$cid:$langcode", $tree);
+    }
+
+    // Similar query but with access checks.
+    // We then filter the cached array with the result of the query (tids).
+    $query = db_select('taxonomy_term_data', 't');
+    $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+    $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid');
+    $query->fields('t', array('tid'));
+    $query->condition('t.vid', $vid);
+    $result = $query->execute()->fetchAllAssoc('tid');
+    $tree['terms'] = array_intersect_key($tree['terms'], $result);
   }
 
-  return $terms;
+  return $tree;
 }
 
 function _term_reference_tree_get_parent($tid) {
@@ -186,30 +225,0 @@
-
-/**
- * Return an array of options.
- *
- * This function converts a list of taxonomy terms to a key/value list of options.
- *
- * @param $terms
- *   An array of taxonomy term IDs.
- * @param $allowed
- *   An array containing the terms allowed by the filter view
- * @param $filter
- *   A string defining the view to filter by (only used to detect whether view
- *   filtering is enabled
- *
- * @return
- *   A key/value array of taxonomy terms (name => id)
- */
-function _term_reference_tree_get_options(&$terms, &$allowed, $filter) {
-  $options = array();
-
-  if (is_array($terms) && count($terms) > 0) {
-    foreach ($terms as $term) {
-      if (!$filter || (is_array($allowed) && $allowed[$term->tid])) {
-        $options[$term->tid] = entity_label('taxonomy_term', $term);
-        $options += _term_reference_tree_get_options($term->children, $allowed, $filter);
-      }
-    }
-  }
-  return $options;
-}
diff -u b/term_reference_tree.widget.inc b/term_reference_tree.widget.inc
--- b/term_reference_tree.widget.inc
+++ b/term_reference_tree.widget.inc
@@ -286,42 +286,20 @@
       drupal_add_js(array('term_reference_tree' => array('trees' => array($element['#id'] => array('max_choices' => $element['#max_choices'])))), 'setting');
     }
 
-    $allowed = '';
-    if ($element['#filter_view'] != '') {
-      $allowed = _term_reference_tree_get_allowed_values($element['#filter_view']);
-    }
-
-    $value = !empty($element['#default_value']) ? $element['#default_value'] : array();
-    if (empty($element['#options'])) {
-      // Get default terms and their parents.
-      $default = array();
-      foreach ($value as $tid) {
-        $parents = taxonomy_get_parents_all($tid);
-        foreach ($parents as $parent) {
-          $default[$parent->tid] = $parent->tid;
-        }
-      }
-      $opened = array();
-      if (isset($form_state['children'])) {
-        foreach ($form_state['children'] as $tid) {
-          $opened[$tid] = $tid;
-        }
-      }
-
-      // Get term tree.
-      $children = _term_reference_tree_get_children($element['#parent_tid'], $element['#vocabulary']->vid);
-      $element['#options_tree'] = _term_reference_tree_get_term_hierarchy($children, $allowed, $default, $opened, $element['#use_ajax']);
-
-      $required = $element['#required'];
-      if ($element['#max_choices'] == 1 && !$required) {
-        array_unshift($element['#options_tree'], (object) array(
-          'tid' => '',
-          'name' => 'N/A',
-          'depth' => 0,
-          'vocabulary_machine_name' => $element['#vocabulary']->machine_name,
-        ));
-      }
-      $element['#options'] = _term_reference_tree_get_options($element['#options_tree'], $allowed, $element['#filter_view']);
+    $allowed = ($element['#filter_view'] != '') ? _term_reference_tree_get_allowed_values($element['#filter_view']) : '';
+    $default = !empty($element['#default_value']) ? $element['#default_value'] : array();
+    $opened = isset($form_state['children']) ? $form_state['children'] : array();
+    $element['#options_tree'] = _term_reference_tree_get_term_hierarchy($element['#parent_tid'], $element['#vocabulary']->vid, $allowed, $default, $opened, $element['#use_ajax']);
+
+    $required = $element['#required'];
+    if ($element['#max_choices'] == 1 && !$required) {
+      array_unshift($element['#options_tree'], (object) array(
+        'tid' => '',
+        'name' => t('N/A'),
+        'depth' => 0,
+        'vocabulary_machine_name' => $element['#vocabulary']->machine_name,
+        'children' => array(),
+      ));
     }
 
     $terms = !empty($element['#options_tree']) ? $element['#options_tree'] : array();
@@ -336,7 +314,8 @@
 
     $tree = new stdClass();
     $tree->children = $terms;
-    $element[] = _term_reference_tree_build_level($element, $tree, $form_state, $value, $max_choices, array(), 1);
+    $tree->render_children = TRUE;
+    $element[] = _term_reference_tree_build_level($element, $tree, $form_state, $default, $max_choices, array(), 1);
 
     // Add a track list element?
     $track_list = !empty($element['#track_list']) && $element['#track_list'];
@@ -692,39 +671,20 @@
  *   possibly a checkbox_tree_level element as well.
  */
 function _term_reference_tree_build_item($element, $term, $form_state, $value, $max_choices, $parent_tids, $parent, $depth) {
-  $start_minimized = FALSE;
-  if (array_key_exists('#start_minimized', $element)) {
-    $start_minimized = $element['#start_minimized'];
-  }
-
-  $leaves_only = FALSE;
-  if (array_key_exists('#leaves_only', $element)) {
-    $leaves_only = $element['#leaves_only'];
-  }
-
-  $t = NULL;
-  if (module_exists('locale') && !empty($term->tid) && FALSE) {
-    $t = taxonomy_term_load($term->tid);
-    $term_name = entity_label('taxonomy_term', $t);
-  }
-  else {
-    $term_name = $term->name;
-  }
   $container = array(
     '#type' => 'checkbox_tree_item',
     '#max_choices' => $max_choices,
-    '#leaves_only' => $leaves_only,
-    '#term_name' => $term_name,
+    '#leaves_only' => isset($element['#leaves_only']) ? $element['#leaves_only'] : FALSE,
+    '#term_name' => $term->name,
     '#level_start_minimized' => FALSE,
     '#depth' => $depth,
     '#has_children' => FALSE,
   );
 
-  if (!$element['#leaves_only'] || !$term->children) {
-    $name = "edit-" . str_replace('_', '-', $element['#field_name']);
+  if (!$element['#leaves_only'] || empty($term->children)) {
     $e = array(
       '#type' => ($max_choices == 1) ? 'radio' : 'checkbox',
-      '#title' => $term_name,
+      '#title' => $term->name,
       '#on_value' => $term->tid,
       '#off_value' => 0,
       '#return_value' => $term->tid,
@@ -734,10 +694,10 @@
       '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
     );
 
-    if ($element['#token_display'] != '' && module_exists('token')) {
-      if (!$t) {
-        $t = taxonomy_term_load($term->tid);
-      }
+    if (!empty($element['#token_display']) && module_exists('token')) {
+      // @TODO Improve this. If the widget uses tokens load all terms at once
+      // in _term_reference_tree_get_term_hierarchy().
+      $t = taxonomy_term_load($term->tid);
       $e['#title'] = token_replace($element['#token_display'], array('term' => $t), array('clear' => TRUE));
     }
 
@@ -750,13 +710,13 @@
   else {
     $e = array(
       '#type' => 'checkbox_tree_label',
-      '#value' => $term_name,
+      '#value' => $term->name,
     );
   }
 
   $container[$term->tid] = $e;
 
-  if (($depth + 1 <= $element['#max_depth'] || !$element['#max_depth']) && $term->children) {
+  if (($depth + 1 <= $element['#max_depth'] || !$element['#max_depth']) && !empty($term->children)) {
     $container['#has_children'] = TRUE;
     $parents = $parent_tids;
     $parents[] = $term->tid;
@@ -789,28 +749,18 @@
  *   A completed checkbox_tree_level element.
  */
 function _term_reference_tree_build_level($element, $term, $form_state, $value, $max_choices, $parent_tids, $depth) {
-  $start_minimized = FALSE;
-  if (array_key_exists('#start_minimized', $element)) {
-    $start_minimized = $element['#start_minimized'];
-  }
-
-  $leaves_only = FALSE;
-  if (array_key_exists('#leaves_only', $element)) {
-    $leaves_only = $element['#leaves_only'];
-  }
-
   $container = array(
     '#max_choices' => $max_choices,
-    '#leaves_only' => $leaves_only,
-    '#start_minimized' => $start_minimized,
+    '#leaves_only' => isset($element['#leaves_only']) ? $element['#leaves_only'] : FALSE,
+    '#start_minimized' => isset($element['#start_minimized']) ? $element['#start_minimized'] : FALSE,
     '#level_start_minimized' => $depth > 1 && $element['#start_minimized'] && !($term->children_selected),
     '#depth' => $depth,
   );
 
   // Adds checkbox_tree_level when children exists
   // or ajax wrapper when children are not loaded.
-  if ($term->children) {
-    if (is_array($term->children)) {
+  if (!empty($term->children)) {
+    if ($term->render_children) {
       $container['#type'] = 'checkbox_tree_level';
       foreach ($term->children as $child) {
         $container[$child->tid] = _term_reference_tree_build_item($element, $child, $form_state, $value, $max_choices, $parent_tids, $container, $depth);
