Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Which

by ChOas (Curate)
on Jun 21, 2000 at 17:31 UTC ( [id://19235]=perlquestion: print w/replies, xml ) Need Help??

ChOas has asked for the wisdom of the Perl Monks concerning the following question:

Any way to implement a 'which' faster than this ?
sub Which($) { my $Filename=shift; foreach ( split /\:/,$ENV{PATH} ) { return "$_/$Filename\n" if ( -e "$_/$Filename" ); }; }
:wq

Replies are listed 'Best First'.
Re: Which
by httptech (Chaplain) on Jun 21, 2000 at 18:18 UTC
    I don't know of a faster way, however, there are more complete ways that take into account different platforms. Check the Perl Power Tools implementations of which

    Also, if you are using a Unix platform, you'd probably be better off using the -x (can be executed) file test rather than -e (exists).

RE: Which
by jjhorner (Hermit) on Jun 21, 2000 at 18:26 UTC

    You seem to have the fastest solution. Unless I miss my guess, File::Find is too much for an operation like this:

    #!/usr/bin/perl -w use strict; use Benchmark; use File::Find; my @paths = (split/\:/,$ENV{PATH}); sub which1 { my $filename= "test.txt"; foreach my $pathname (@paths) { print "$pathname/$filename\n" if ( -e "$pathname/$file +name" ); }; } sub which2 { my $filename = "test.txt"; finddepth sub {print "$_\n" if ( /$filename$/)} , @paths; } timethese (100, {'whichone' => \&which1, 'whichtwo' => \&which2});

    Yields:

    [10:27:15 jhorner@gateway scripts]$ ./ch0as-1.pl test.txt Benchmark: timing 100 iterations of whichone, whichtwo... whichone: 0 wallclock secs ( 0.01 usr + 0.02 sys = 0.03 CPU) @ 33 +33.33/s (n=100) (warning: too few iterations for a reliable count) whichtwo: 14 wallclock secs (12.54 usr + 1.55 sys = 14.09 CPU) @ 7 +.10/s (n=100)

    Unless someone can tell me why the File::Find version takes so long, I'm stumped. I have a feeling it has to do with File::Find traversing the entire filetree under the @path directories. If someone can let me know how to make it search ONLY the @path directories and not subdirectories, I'll be grateful.

    I also have a feeling that File::Find is overkill in this, since you aren't recursing the directory structure.

    Anyone else have good info on this?

    J. J. Horner
    Linux, Perl, Apache, Stronghold, Unix
    jhorner@knoxlug.org http://www.knoxlug.org/
    
      I looked at the code for File::Find, and two things stand out immediately that I think make a big difference:
      • Code Size - File::Find is about 9 pages of code!
      • @ISA - Being OO, it uses CWD in it's @ISA, which is more code to load.
      Those two things alone will slow down execution compared to 3 lines of code making up a foreach loop.
      Of course, the File::Find makes up for it's size in it's flexibility and features.... :)

      hth, Maurice
Re: Which
by davorg (Chancellor) on Jun 21, 2000 at 17:40 UTC

    How fixed are the contents of your path?

    You might consider building a hash where the keys are the filenames and the values are the paths. You could build the hash using a program (why not call it rehash like the csh does) and tie it to a DBM file. Your which script would then only have to look up the filename in the tied hash.


    --
    <http://www.dave.org.uk>

    European Perl Conference - Sept 22/24 2000 <http://www.yapc.org/Europe/>

Re: Which
by raflach (Pilgrim) on Jun 22, 2000 at 01:59 UTC
    How about:
    sub Which($) { my $file = shift; my $loc = `which $file`; chomp $loc; return $loc; }
    I didn't actually test to see if it was faster, but it seems to me it would be...
    Course, it only works on a UNIX system.
Re: Which
by Anonymous Monk on Jun 22, 2000 at 04:00 UTC
    If my C memory can come back to me I remember a spiffy trick was to unroll loops. Right now you are doing a foreach, try something like this.
    sub Which($) { my $Filename=shift; my @Arr = ( split /\:/,$ENV{PATH} ) for(my $i = 0; $i <= #$Arr; $i += 2) { return "$Arr[$i]/$Filename\n" if ( -e "$Arr[$i]/$Filename" ); return "$Arr[$i+1]/$Filename\n" if ( -e "$Arr[$i+1]/$Filename" ); }; }
    Sorry about that previous post, big ol RTFM on my part. This code's sorta ugly but you get the picture I'm sure, have it check 3 or 4 at a time and you'd be amazed how much faster it will run.
Re: Which
by Anonymous Monk on Jun 22, 2000 at 03:56 UTC

    If my C memory can come back to me I remember a spiffy trick was to unroll loops. Right now you are doing a foreach, try something like this.

    sub Which($) { my $Filename=shift; my @Arr = ( split /\:/,$ENV{PATH} ) for(my $i = 0; $i <= #$Arr; $i += 2) { return "$Arr[$i]/$Filename\n" if ( -e "$Arr[$i]/$Filename" ); return "$Arr[$i+1]/$Filename\n" if ( -e "$Arr[$i+1]/$Filename" ); }; }

    This code's sorta ugly but you get the picture I'm sure, have it check 3 or 4 at a time and you'd be amazed how much faster it will run.

    Edit Petruchio Wed May 22 02:05:51 UTC 2002 - Added code tags, other markup.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://19235]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (6)
As of 2024-04-26 09:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found