Permalink
Browse files

Added a history speed test

Profile driven caching of config directory
Style fixes
  • Loading branch information...
ridiculousfish committed Dec 3, 2012
1 parent 33fc5c9 commit b9283d48b570e66650a6443bc7ebb8048f589546
Showing with 135 additions and 98 deletions.
  1. +3 −3 common.cpp
  2. +39 −11 fish_tests.cpp
  3. +36 −34 history.cpp
  4. +4 −4 history.h
  5. +15 −11 path.cpp
  6. +4 −4 reader.cpp
  7. +34 −31 screen.cpp
View
@@ -523,12 +523,12 @@ wcstring wsetlocale(int category, const wchar_t *locale)
*/
char *ctype = setlocale(LC_CTYPE, NULL);
bool unicode = (strstr(ctype, ".UTF") || strstr(ctype, ".utf"));
-
+
ellipsis_char = unicode ? L'\x2026' : L'$';
-
+
// U+23CE is the "return" character
omitted_newline_char = unicode ? L'\x23CE' : L'~';
-
+
if (!res)
return wcstring();
else
View
@@ -978,7 +978,8 @@ class history_tests_t
static void test_history(void);
static void test_history_merge(void);
static void test_history_formats(void);
-
+ static void test_history_speed(void);
+
static void test_history_races(void);
static void test_history_races_pound_on_history();
};
@@ -1110,16 +1111,16 @@ void history_tests_t::test_history_races_pound_on_history()
void history_tests_t::test_history_races(void)
{
say(L"Testing history race conditions");
-
+
// Ensure history is clear
history_t *hist = new history_t(L"race_test");
hist->clear();
delete hist;
-
+
// Test concurrent history writing
#define RACE_COUNT 10
pid_t children[RACE_COUNT];
-
+
for (size_t i=0; i < RACE_COUNT; i++)
{
pid_t pid = fork();
@@ -1136,21 +1137,21 @@ void history_tests_t::test_history_races(void)
children[i] = pid;
}
}
-
+
// Wait for all children
for (size_t i=0; i < RACE_COUNT; i++)
{
int stat;
waitpid(children[i], &stat, WUNTRACED);
}
-
+
// Compute the expected lines
wcstring_list_t lines[RACE_COUNT];
for (size_t i=0; i < RACE_COUNT; i++)
{
lines[i] = generate_history_lines(children[i]);
}
-
+
// Count total lines
size_t line_count = 0;
for (size_t i=0; i < RACE_COUNT; i++)
@@ -1160,7 +1161,7 @@ void history_tests_t::test_history_races(void)
// Ensure we consider the lines that have been outputted as part of our history
time_barrier();
-
+
/* Ensure that we got sane, sorted results */
hist = new history_t(L"race_test");
hist->chaos_mode = true;
@@ -1170,7 +1171,7 @@ void history_tests_t::test_history_races(void)
history_item_t item = hist->item_at_index(hist_idx);
if (item.empty())
break;
-
+
// The item must be present in one of our 'lines' arrays
// If it is present, then every item after it is assumed to be missed
size_t i;
@@ -1181,7 +1182,7 @@ void history_tests_t::test_history_races(void)
{
// Delete everything from the found location onwards
lines[i].resize(where - lines[i].begin());
-
+
// Break because we found it
break;
}
@@ -1193,7 +1194,7 @@ void history_tests_t::test_history_races(void)
}
// every write should add at least one item
assert(hist_idx >= RACE_COUNT);
-
+
//hist->clear();
delete hist;
}
@@ -1408,6 +1409,32 @@ void history_tests_t::test_history_formats(void)
}
}
+void history_tests_t::test_history_speed(void)
+{
+ say(L"Testing history speed");
+ history_t *hist = new history_t(L"speed_test");
+ wcstring item = L"History Speed Test - X";
+
+ /* Test for 10 seconds */
+ double start = timef();
+ double end = start + 4;
+ double stop = 0;
+ size_t count = 0;
+ for (;;)
+ {
+ item[item.size() - 1] = L'0' + (count % 10);
+ hist->add(item);
+ count++;
+
+ stop = timef();
+ if (stop >= end)
+ break;
+ }
+ printf("%lu items - %.2f msec per item\n", (unsigned long)count, (stop - start) * 1E6 / count);
+ hist->clear();
+ delete hist;
+}
+
/**
Main test
@@ -1448,6 +1475,7 @@ int main(int argc, char **argv)
history_tests_t::test_history_merge();
history_tests_t::test_history_races();
history_tests_t::test_history_formats();
+ //history_tests_t::test_history_speed();
say(L"Encountered %d errors in low-level tests", err_count);
View
@@ -501,7 +501,7 @@ void history_t::add(const history_item_t &item)
/* We have to add a new item */
new_items.push_back(item);
}
-
+
/* We may or may not vacuum. We try to vacuum every kVacuumFrequency items, but start the countdown at a random number so that even if the user never runs more than 25 commands, we'll eventually vacuum. If countdown_to_vacuum is -1, it means we haven't yet picked a value for the counter. */
const int kVacuumFrequency = 25;
if (countdown_to_vacuum < 0)
@@ -510,7 +510,7 @@ void history_t::add(const history_item_t &item)
/* Generate a number in the range [0, kVacuumFrequency) */
countdown_to_vacuum = rand_r(&seed) / (RAND_MAX / kVacuumFrequency + 1);
}
-
+
/* Determine if we're going to vacuum */
bool vacuum = false;
if (countdown_to_vacuum == 0)
@@ -552,7 +552,7 @@ void history_t::remove(const wcstring &str)
if (new_items[idx].str() == str)
{
new_items.erase(new_items.begin() + idx);
-
+
/* If this index is before our first_unwritten_new_item_index, then subtract one from that index so it stays pointing at the same item. If it is equal to or larger, then we have not yet writen this item, so we don't have to adjust the index. */
if (idx < first_unwritten_new_item_index)
{
@@ -922,11 +922,11 @@ static bool map_file(const wcstring &name, const char **out_map_start, size_t *o
int fd = wopen_cloexec(filename, O_RDONLY);
if (fd >= 0)
{
-
+
/* Get the file ID if requested */
if (file_id != NULL)
*file_id = history_file_identify(fd);
-
+
/* Take a read lock to guard against someone else appending. This is released when the file is closed (below). We will read the file after taking the lock, but that's not a problem, because we never modify already written data. In short, the purpose of this lock is to ensure we don't see the file size change mid-update. */
if (history_file_lock(fd, F_RDLCK))
{
@@ -1157,7 +1157,7 @@ void history_t::compact_new_items()
{
// This item was not inserted because it was already in the set, so delete the item at this index
new_items.erase(new_items.begin() + idx);
-
+
if (idx < first_unwritten_new_item_index)
{
/* Decrement first_unwritten_new_item_index if we are deleting a previously written item */
@@ -1171,7 +1171,7 @@ bool history_t::save_internal_via_rewrite()
{
/* This must be called while locked */
ASSERT_IS_LOCKED(lock);
-
+
bool ok = true;
wcstring tmp_name_template = history_filename(name, L".XXXXXX");
@@ -1247,7 +1247,7 @@ bool history_t::save_internal_via_rewrite()
}
free(narrow_str);
}
-
+
if (out_fd >= 0)
{
/* Success */
@@ -1256,7 +1256,7 @@ bool history_t::save_internal_via_rewrite()
{
/* Be block buffered. In chaos mode, choose a tiny buffer so as to magnify the effects of race conditions. Otherwise, use the default buffer */
setvbuf(out, NULL, _IOFBF, chaos_mode ? 1 : 0);
-
+
/* Write them out */
for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
{
@@ -1267,7 +1267,7 @@ bool history_t::save_internal_via_rewrite()
break;
}
}
-
+
if (0 == fclose(out))
{
/* fclose closed out_fd, so mark it as -1 so we don't try to close it later */
@@ -1294,7 +1294,7 @@ bool history_t::save_internal_via_rewrite()
}
}
}
-
+
if (out_fd >= 0)
close(out_fd);
@@ -1303,70 +1303,72 @@ bool history_t::save_internal_via_rewrite()
/* Make sure we clear all nodes, since this doesn't happen automatically */
lru.evict_all_nodes();
}
-
+
if (ok)
{
/* We've saved everything, so we have no more unsaved items */
this->first_unwritten_new_item_index = new_items.size();
+
+ /* We deleted our deleted items */
this->deleted_items.clear();
-
+
/* Our history has been written to the file, so clear our state so we can re-reference the file. */
this->clear_file_state();
}
-
+
return ok;
}
bool history_t::save_internal_via_appending()
{
/* This must be called while locked */
ASSERT_IS_LOCKED(lock);
-
+
/* No deleting allowed */
assert(deleted_items.empty());
-
+
bool ok = false;
-
+
/* If the file is different (someone vacuumed it) then we need to update our mmap */
bool file_changed = false;
-
+
/* Get the path to the real history file */
wcstring history_path = history_filename(name, wcstring());
-
+
signal_block();
-
+
/* Open the file */
int out_fd = wopen_cloexec(history_path, O_WRONLY | O_APPEND);
if (out_fd >= 0)
{
/* Check to see if the file changed */
if (history_file_identify(out_fd) != mmap_file_id)
file_changed = true;
-
+
/* Exclusive lock on the entire file. This is released when we close the file (below). */
if (history_file_lock(out_fd, F_WRLCK))
{
/* We successfully took the exclusive lock. Append to the file.
Note that this is sketchy for a few reasons:
- Another shell may have appended its own items with a later timestamp, so our file may no longer be sorted by timestamp.
- Another shell may have appended the same items, so our file may now contain duplicates.
-
+
We cannot modify any previous parts of our file, because other instances may be reading those portions. We can only append.
-
+
Originally we always rewrote the file on saving, which avoided both of these problems. However, appending allows us to save history after every command, which is nice!
-
+
Periodically we "clean up" the file by rewriting it, so that most of the time it doesn't have duplicates, although we don't yet sort by timestamp (the timestamp isn't really used for much anyways).
*/
-
+
FILE *out = fdopen(out_fd, "a");
if (out)
{
/* Be block buffered. In chaos mode, choose a tiny buffer so as to magnify the effects of race conditions. Otherwise, use the default buffer */
setvbuf(out, NULL, _IOFBF, chaos_mode ? 1 : 0);
-
+
bool errored = false;
-
+
/* Write all items at or after first_unwritten_new_item_index */
while (first_unwritten_new_item_index < new_items.size())
{
@@ -1376,11 +1378,11 @@ bool history_t::save_internal_via_appending()
errored = true;
break;
}
-
+
/* We wrote this item, hooray */
first_unwritten_new_item_index++;
}
-
+
if (0 == fclose(out))
{
/* fclose just closed our out_fd; mark it as -1 so we don't re-close it */
@@ -1390,24 +1392,24 @@ bool history_t::save_internal_via_appending()
{
errored = true;
}
-
+
/* We're OK if we did not error */
ok = ! errored;
}
}
}
-
+
if (out_fd >= 0)
close(out_fd);
signal_unblock();
-
+
/* If someone has replaced the file, forget our file state */
if (file_changed)
{
this->clear_file_state();
}
-
+
return ok;
}
@@ -1418,7 +1420,7 @@ void history_t::save_internal(bool vacuum)
ASSERT_IS_LOCKED(lock);
/* Nothing to do if there's no new items */
- if (new_items.empty() && deleted_items.empty())
+ if (first_unwritten_new_item_index >= new_items.size() && deleted_items.empty())
return;
/* Compact our new items so we don't have duplicates */
Oops, something went wrong.

0 comments on commit b9283d4

Please sign in to comment.