New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
File#each_char performance #5029
Comments
First of all, this Crystal version is doing a Hash lookup for each letter, this is not really efficient, while I suspect the C version is handling it with a switch-statement or a sequence of "if"s. Could you try without the Hash lookup there? Also, Crystal's Char is a Unicode character, instead of a ASCII character, so this has to be decoded from the file (that should be fast, though. But still does more work than C). |
Using count = 0_u64
File.open("file.txt") do |f|
f.each_byte do |byte|
next if byte == '\n'.ord
count += case byte
when 'U'.ord then 0
when 'D'.ord then 1
when 'A'.ord then 2
when 'Z'.ord then 3
when 'R'.ord then 4
else raise "Bad char #{byte.chr}"
end
end
end
pp count |
Yes you are right, I converted the file beforehand using tr in
and then run the crystal snippet without the hash but still with `.to_i'
the whole c program which does some lookup in an array every time takes
bew's version on the original file took
I first wanted to use so the remaining question would be, how is the preferred version of reading a file efficiently but still fast? Is the thanks for your quick response! |
Can you show us the C version? |
Yes, please, show us the C version :-) I guess the fastest way would be to do more or less what the C version does. In the meantime, I found this to be more efficient: f = File.open("./my_file")
# Lookup table: faster than a case
values = Slice.new(256) do |i|
case i
when 'D' then 1
when 'A' then 2
when 'Z' then 3
when 'R' then 4
else 0
end
end
count = 0
# Read by chunks.
# You can try increasing this number but in my tests
# the performance gain was minimal
bytes = Bytes.new(8192)
while f.read(bytes) != 0
bytes.each do |val|
count += values[val]
end
end
pp count In my tests most of the time was spent in that I tried it on an 8GB file and it took about 13 seconds. That is compiled with @monouser7dig It would be nice to know how much time this variant takes on your machine. |
@asterite ideally buffered I wouldn't have thought that the |
I don't think file.each_byte do |byte|
break if some_condition
end That means you can read maybe 5 bytes from the file and the stop. Reading a next byte will give you the sixth one. In my code above I read a full chunk, and then iterate that chunk. So I read 8192 bytes in one go. Reading a next byte will give you the 8193-rd. There's no way to push back bytes. It's doing something different. But PRs are welcome, if you can optimize |
To say it another way, each block iteration of I also wouldn't have though that a |
here is the c code (which is not really my own but was given to me in some reduced form for an exercise) int results[9] = {1,1,1,1,1,1,1,1,0};
int deltas[9][5] = {
{1,8,4,8,0}, //q0
{2,0,5,8,1}, //q1
{3,1,6,8,2}, //q2
{8,2,7,8,3}, //q3
{8,8,8,0,4}, //q4
{8,8,8,1,5}, //q5
{8,8,8,2,6}, //q6
{8,8,8,3,7}, //q7
{8,8,8,8,8}, //q8
};
int result_f(char* w){
int sol;
int pointer = 0;
double counter;
counter = 0;
while (*w) {
pointer = deltas[pointer] [*w++ -'0'];
counter++;
}
sol = results[pointer];
printf("%d\n", sol);
printf("%f\n", counter);
return results [pointer];
} the function is called with a pointer that points to the beginn of the buffer which is located in RAM (which I only did because I did not know better) using asterite I will look at your version/ try and report back |
for the version of the founder of crystal ;-) somehow glad I asked now, thanks!!
maybe I am a bit dense here but if I'd understand that would be really nice, (tried the documentation, appreciate this is alpha and you guys work hard on all the things!) my guess is that this works better because the program may not have to wait until the whole file is buffered but can start as soon as the first block is loaded? not sure if that really is how it Is implemented though. |
This works better than |
ok but crystal did not give me any errors despite so, values[val] actually calls the to_i method implicitly? I hope/ think I understand thanks, great help! |
|
I see because you read the file into a UInt8 Size it is not a char but a UInt8 (which is probably basically the same) and thus the case statement can check against Chars I bow my head for your experience ;) btw the type check if crystal is really radical, I love the strong typedefs but a bit of casting would not hurt at some point, my two cents while trying these things out ;) (read the topics about it and issues) |
I wrote following code in order to simulate a automata (this is a condensed version which shows the same issue)
where
my_file
is something likebut about 8GB of that
I have a C implementation which just loads all in RAM and takes under 20s
this crystal one takes about 2min though
how would I make the file read speed better? (I compiled using --release)
thanks
The text was updated successfully, but these errors were encountered: