Moderators: phlip, Moderators General, Prelates
Thesh wrote:I doubt List<T> actually stores everything in a contiguous block of memory. It probably allocates a new block whenever it fills up. It's more reliable and faster to add data that way. Otherwise you have to copy all the data every time you have to allocate more space, and if you don't have a large enough contiguous block of memory, it will outright fail.
Vaniver wrote:Harvard is a hedge fund that runs the most prestigious dating agency in the world, and incidentally employs famous scientists to do research.
afuzzyduck wrote:ITS MEANT TO BE FLUTTERSHY BUT I JUST SEE AAERIELE! CURSE YOU FORA!
Aaeriele wrote:Array-based lists have O(log n) amortized time for addition (assuming a basic "allocate twice as much space when you hit a boundary" scheme)
Aaeriele wrote:The List<T> class is the generic equivalent of the ArrayList class. It implements the IList<T> generic interface using an array whose size is dynamically increased as required.
Aaeriele wrote:Array-based lists have O(log n) amortized time for addition (assuming a basic "allocate twice as much space when you hit a boundary" scheme), and O(1) amortized time for lookup. Linked lists have O(1) amortized time for addition, and O(n) amortized time for lookup.
There's a reason why most people tend to use array-based lists by default.
EvanED wrote:Aaeriele wrote:Array-based lists have O(log n) amortized time for addition (assuming a basic "allocate twice as much space when you hit a boundary" scheme)
No, that's amortized O(1).
EvanED wrote:It's also not the case that a single array and traditional linked list are the only ways to do things. For instance, one typical implementation of the STL's deque is essentially an array of arrays. If there's no space in the "inner" array when adding an element, a new one is created and a pointer added to the "outer" array. If no space is left in the outer array, it is reallocated. Doesn't change the worst-case time complexity (still O(1), O(1) amortized), but it does affect the constant quite a bit. It speeds insertion, and slows lookup (because of a double indirection).
Vaniver wrote:Harvard is a hedge fund that runs the most prestigious dating agency in the world, and incidentally employs famous scientists to do research.
afuzzyduck wrote:ITS MEANT TO BE FLUTTERSHY BUT I JUST SEE AAERIELE! CURSE YOU FORA!
Aaeriele wrote:Sure; I was keeping things down to the simplistic options to make the example.
EvanED wrote:Visual Studio behaves similarly, except with even more allocation in the vector. (I don't know what its growth strategy is.)
Jplus wrote:Because of block moves and because iterating by incrementing a pointer is cheaper than iterating by fetching new pointers from the heap...
Jplus wrote:Say, when substantially less than X integers fit into the CPU cache.
#include <cstdint>
#include <list>
#include <vector>
#include <iostream>
#include <random>
/* defined as empty functions in another compilation unit so that
gcc is not able to determine that insertion/removal of
the vector elements does not have any side effects */
void do_anything(std::list<int> container);
void do_anything(std::vector<int> container);
template<typename Container>
void perform(Container &container, int index, uint64_t &result) {
auto j = container.begin();
for(int i = 0; i < index; ++i) {
/* make sure the iteration does not get optimized away.
since this operation is O(1) it does not change the comparison results. */
do_anything(container);
result += *j; /* this operation was not enough to keep gcc from optimizing away
all memory accesses in case of the vector */
j++;
}
container.erase(j);
}
template<typename Container, typename RndEngine>
uint64_t benchmark() {
Container container;
const int size = 10000;
RndEngine engine;
engine.seed(0x12345678);
std::uniform_int<int> ins_distrib(0, 1 << 30);
for(int i = 0; i < size; ++i)
container.push_back(ins_distrib(engine));
uint64_t result = 0;
while(container.size() > 0) {
std::uniform_int<int> rem_distrib(0, container.size() - 1);
perform(container, rem_distrib(engine), result);
}
return result;
}
int main(int argc, char **argv) {
std::cout << "Result: " << benchmark<std::list<int>, std::mt19937>() << std::endl;
}korona wrote:The result above was wrong. Looking at the assembly code I found out that gcc 4.4 was able to optimize away ALL memory accesses in case of the vector!
Here is the benchmark code in case anyone wants to play around with it. The dummy call makes the code very slow (it acts as a "every value in memory can change at this point" barrier for gcc) and I don't have the time to run the benchmark for multiple minutes/hours. For n=10000 vector still wins (50 seconds vs. I interrupted the program after 3 minutes).Spoiler:
Stroustrups results are most likely wrong too as his timing are much better (multiple orders of magnitude) than what a state of the art cpu can archive using the benchmark above.
void do_anything(std::list<int> container);
void do_anything(std::vector<int> container);
korona wrote:AhhhThat's true I wondered why the slowdown was that huge. These functions make only sense when passing the container by reference of course.
template<typename T, typename Comp==std::less_than<T>, bool multi=false>
struct FastSet
{
typedef FastSet<T, Comp, multi> MyType;
typedef std::vector< T > raw_vector_t;
mutable raw_vector_t raw_vector;
mutable size_t sorted_highwater;
typedef raw_vector_t::const_iterator const_iterator;
typedef raw_vector_t::iterator iterator;
void ensure_sorted() const // a lie
{
if (sorted_highwater == raw_vector.size())
return;
std::sort( raw_vector.begin()+sorted_highwater, raw_vector.end(), Comp() );
std::inplace_merge( raw_vector.begin(), raw_vector.begin()+sorted_highwater, raw_vector.end(), Comp() );
if (!multi)
raw_vector.erase( std::uniq( raw_vector.begin(), raw_vector.end(), Comp() ), raw_vector.end() );
sorted_highwater = raw_vector.size();
}
iterator begin() { ensure_sorted(); return raw_vector.begin(); }
iterator end() { ensure_sorted(); return raw_vector.end(); }
const_iterator begin() const { ensure_sorted(); return raw_vector.begin(); }
const_iterator end() const { ensure_sorted(); return raw_vector.end(); }
iterator lower_bound( T const& t) { ensure_sorted(); return std::lower_bound( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
iterator upper_bound( T const& t) { ensure_sorted(); return std::upper_bound( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
std::pair<iterator, iterator> equal_range( T const& t ) { ensure_sorted(); return std::equal_range( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
const_iterator lower_bound( T const& t) const { ensure_sorted(); return std::lower_bound( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
const_iterator upper_bound( T const& t) const { ensure_sorted(); return std::upper_bound( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
std::pair<const_iterator, const_iterator> equal_range( T const& t ) const { ensure_sorted(); return std::equal_range( raw_vector.begin(), raw_vector.end(), t, Comp() ); }
iterator find( T const& t ) { auto range = equal_range( t ); if (range.first == range.second) return raw_vector.end(); return range.first; }
const_iterator find( T const& t ) const { auto range = equal_range( t ); if (range.first == range.second) return raw_vector.end(); return range.first; }
// Could be faster? Dunno... This searches the previously sorted elements first, and if it fails, checks the unsorted elements.
bool search( T const& t ) const { auto range1 = equal_range( t ); if (range1.first != range1.second) return true; if (sorted_highwater == raw_vector.size()) return false; return find(t) != raw_vector.end(); }
iterator erase( iterator it ) { Assert( it-raw_vector.begin() < sorted_highwater ); raw_vector.erase( it, it+1 ); --sorted_highwater; }
iterator erase( iterator first, iterator second ) { Assert( second-raw_vector.begin() <= sorted_highwater ); raw_vector.erase( first, second ); --sorted_highwater; }
void clear() { raw_vector.clear(); sorted_highwater = 0; }
void swap(FastSet< T, Comp>& other ) { raw_vector.swap( other.raw_vector ); std::swap( sorted_highwater, other.sorted_highwater ); }
// ctor, dtor and copy do not need to be written. Default works.
T & insert_unsorted(T const& src) { raw_vector.push_back(src); return raw_vector.back(); }
T & insert_unsorted() { raw_vector.push_back(T()); return raw_vector.back(); }
iterator insert(T const& src) {
auto range = equal_range(src);
if (range.first != range.second) { *range.first = src; return range.first; }
insert_unsorted(src);
return std::lower_bound( raw_vector.begin(), raw_vector.end(), src, Comp() );
}
};
typename T::iterator place = container.begin();
int index = rand() % container.size();
for(int i=0; i<index; ++i) {
++place;
}mkdir deeper
cd deeper
mkdir DEEPER
cd DEEPER
chmod 000 ..
Yakk wrote:First, give things different names. Case sensitivity is not an excuse to be silly.
Second, if what you wanted to happen where to happen, all access to directory X would require checking all directories above X to determine if access is permitted, and/or recursively walk all children and applying some magic meta-permissions set everytime you change the permissions on a directory. On top of that, ".." is really a construct of your shell and not of the file system (hard links allow a directory to exist in two places at once).
Yakk wrote: (...) and/or recursively walk all children and applying some magic meta-permissions set everytime you change the permissions on a directory
The canPlayType() function doesn’t return true or false. In recognition of how complex video formats are, the function returns a string:
"probably" if the browser is fairly confident it can play this format
"maybe" if the browser thinks it might be able to play this format
"" (an empty string) if the browser is certain it can’t play this format
I actually have the impression that the . and .. are real hard links to directories, and are the only exceptions that are allowed. Can't remember where I first read it, but googling seems to turn up pages that agree with my memories.Yakk wrote:On top of that, ".." is really a construct of your shell and not of the file system (hard links allow a directory to exist in two places at once).
Ubik wrote:I actually have the impression that the . and .. are real hard links to directories, and are the only exceptions that are allowed. Can't remember where I first read it, but googling seems to turn up pages that agree with my memories.Yakk wrote:On top of that, ".." is really a construct of your shell and not of the file system (hard links allow a directory to exist in two places at once).
shawnhcorey wrote:Only the root user (or a sudo user) can create hard links of directories.
ln(1) wrote:-d, -F, --directory
allow the superuser to attempt to hard link directories (note: will probably fail due to system restrictions, even for the superuser)
phlip@boris:/tmp$ mkdir test
phlip@boris:/tmp$ ln test test2
ln: `test': hard link not allowed for directory
phlip@boris:/tmp$ sudo ln test test2
ln: `test': hard link not allowed for directory
phlip@boris:/tmp$ sudo ln -F test test2
ln: creating hard link `test2' => `test': Operation not permittedmoiraemachy wrote:Well, guess I should look up on how the access to the file system is implemented to understand this. I thought it was, basically, "absolute pathname goes in, data out". Still,
What does that have to do with having to recursively walk all children? Are you talking about deleting a directory?doesn't sound that bad. I mean, the OS already has to somehow keep track of validity of file handles(or whatever the part of file handles that programs have direct access to is).Yakk wrote: (...) and/or recursively walk all children and applying some magic meta-permissions set everytime you change the permissions on a directory
Steax wrote:I find HTML 5's video element support detection hilarious.The canPlayType() function doesn’t return true or false. In recognition of how complex video formats are, the function returns a string:
"probably" if the browser is fairly confident it can play this format
"maybe" if the browser thinks it might be able to play this format
"" (an empty string) if the browser is certain it can’t play this format
Xanthir wrote:Yup, funny history there.
Ideally, we'd just return a boolean. Unfortunately, media formats are *complicated*, and we can't ever *really* be sure until we feed the file to the decoder and see if we get an error or not. Further, some media formats are just containers, so we're *extra* not sure if we can play what's inside of them.
It was decided that it's useful to be able to distinguish "as far as we can tell, we can play this" from "we can play *some* things like this", thus the current api.
Note that you can still treat it as a boolean, since the empty string is falsey and non-empty strings are truthy.
Users browsing this forum: Fekeenuisance and 10 guests