Tcl Source Code

View Ticket
Login
Ticket UUID: 871583
Title: filesystem optimization, committed jan 2004
Type: Patch Version: None
Submitter: vincentdarley Created on: 2004-01-06 10:47:33
Subsystem: 37. File System Assigned To: hobbs
Priority: 9 Immediate Severity:
Status: Closed Last Modified: 2004-01-22 17:53:47
Resolution: Accepted Closed By: vincentdarley
    Closed on: 2004-01-22 10:53:47
Description:
Here's a patch to cvs HEAD to speed up a lot of the
filesystem.  Please test (esp Unix), since I've only
tested on Windows.
User Comments: vincentdarley added on 2004-01-22 17:53:47:

File Deleted - 74149: 



File Added - 74359: file.bench

Logged In: YES 
user_id=32170

Attached the file.bench finally used, and updated the
summary to reflect the date of this patch (in case there are
future filesystem optimisation efforts).

vincentdarley added on 2004-01-22 02:26:31:

File Added - 74303: fsOpt5.patch

Logged In: YES 
user_id=32170

Ok, sorry about that. A mess-up with unix-separators in two
edge-cases.  The patch-5 should fix it all.

dgp added on 2004-01-22 01:11:07:
Logged In: YES 
user_id=80530

$ make test TESTFLAGS="-verbose be"
...
fCmd.test

==== fCmd-6.19 CopyRenameOneFile: errno == EXDEV FAILED
==== Contents of test case:

    cleanup /tmp
    createfile tf1
    file rename tf1 /tmp
    glob tf* /tmp/tf1

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: no files matched glob patterns "tf* /tmp/tf1"
    while executing
"glob tf* /tmp/tf1"
    ("uplevel" body line 5)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== fCmd-6.19 FAILED


==== fCmd-6.21 CopyRenameOneFile: copy/rename:
S_ISDIR(source) FAILED
==== Contents of test case:

    cleanup /tmp
    file mkdir td1
    file rename td1 /tmp
    glob td* /tmp/td*

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: no files matched glob patterns "td* /tmp/td*"
    while executing
"glob td* /tmp/td*"
    ("uplevel" body line 5)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== fCmd-6.21 FAILED

==== fCmd-6.22 CopyRenameOneFile: copy/rename:
!S_ISDIR(source) FAILED
==== Contents of test case:

    cleanup /tmp
    createfile tf1
    file rename tf1 /tmp
    glob tf* /tmp/tf*

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: no files matched glob patterns "tf* /tmp/tf*"
    while executing
"glob tf* /tmp/tf*"
    ("uplevel" body line 5)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== fCmd-6.22 FAILED


==== fCmd-6.29 CopyRenameOneFile: TclpCopyDirectory passed
FAILED
==== Contents of test case:

    cleanup /tmp
    file mkdir td1/td2/td3
    file rename td1 /tmp
    glob td* /tmp/td1/t*

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: no files matched glob patterns "td* /tmp/td1/t*"
    while executing
"glob td* /tmp/td1/t*"
    ("uplevel" body line 5)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== fCmd-6.29 FAILED
fileName.test

==== filename-15.6 unix specific globbing FAILED
==== Contents of test case:

    global env
    set temp $env(HOME)
    set env(HOME) $env(HOME)/globTest/odd\\\[\]*?\{\}name
    set result [list [catch {glob ~} msg] $msg]
    set env(HOME) $temp
    set result

---- Result was:
0 {{/local/src/tcl/solaris/globTest/odd\[]*?{}name/}}
---- Result should have been (exact matching):
0 {{/local/src/tcl/solaris//globTest/odd\[]*?{}name}}
==== filename-15.6 FAILED

vincentdarley added on 2004-01-22 00:46:15:
Logged In: YES 
user_id=32170

Can you provide the test results, please?

Vince.

dgp added on 2004-01-21 22:47:37:
Logged In: YES 
user_id=80530

oops!  Patch fsOpt4.patch
creates four new test failures.

fCmd-6.19,21,22 and filename-15.6

vincentdarley added on 2004-01-21 21:47:23:

File Deleted - 74020:

vincentdarley added on 2004-01-21 21:47:22:

File Deleted - 74113: 



File Added - 74269: fsOpt4.patch

vincentdarley added on 2004-01-21 21:47:19:
Logged In: YES 
user_id=32170

One final patch to streamline 'glob' a bit more, and to
provide better documentation after some feedback from Zoran.
 Also ran this through purify to ensure no new memory leaks
are introduced.

vincentdarley added on 2004-01-21 16:34:02:
Logged In: YES 
user_id=32170

I've run klist and don't see any issue, although I'm only
comparing tcl8.5+patch against 8.4.  It seems to average
about 50% faster (presumably due to some new optimisations
in 8.5 elsewhere).

E:\Programming\Tcl-source\tclbench>C:\Progra~1\Tcl\bin\tclsh85.exe
runbench.tcl
-paths "C:/Progra~1/Tcl/bin C:/ActiveTcl/bin
C:/ActiveTcl8.3/bin" -notk -pattern
 tclsh8?.exe tcl/klist.bench
000 VERSIONS:                     1:8.5a0 2:8.4.5 3:8.3.5
001 KLIST shuffle0 llength 1            9      16      20
002 KLIST shuffle0 llength 10          29      38      60
003 KLIST shuffle0 llength 100        244     315     644
004 KLIST shuffle0 llength 1000      3036    3762    6400
005 KLIST shuffle0 llength 10000    55285   59352   92100
006 KLIST shuffle1-s llength 1          8      10      20
007 KLIST shuffle1-s llength 10        40      59      60
008 KLIST shuffle1-s llength 100      505    1162    1240
009 KLIST shuffle1-s llength 1000   33309   76351   78210
010 KLIST shuffle1a llength 1           9      12      20
011 KLIST shuffle1a llength 10         41      64      80
012 KLIST shuffle1a llength 100       433     603     760
013 KLIST shuffle1a llength 1000     3988    6103    7410
014 KLIST shuffle1a llength 10000   41588   61837   77100
015 KLIST shuffle2 llength 1            9      12      21
016 KLIST shuffle2 llength 10          59      62     100
017 KLIST shuffle2 llength 100        511     637     880
018 KLIST shuffle2 llength 1000      5116    6967    9610
019 KLIST shuffle2 llength 10000    73507   87959  114200
020 KLIST shuffle3 llength 1            8      10      10
021 KLIST shuffle3 llength 10          33      43      60
022 KLIST shuffle3 llength 100        267     421     600
023 KLIST shuffle3 llength 1000      3071    6073    7720
024 KLIST shuffle3 llength 10000    79677  255728  273400
025 KLIST shuffle4 llength 1            8      11      20
026 KLIST shuffle4 llength 10          32      48      60
027 KLIST shuffle4 llength 100        285     431     680
028 KLIST shuffle4 llength 1000      3210    4315    6310
029 KLIST shuffle4 llength 10000    29701   43952   66100
030 KLIST shuffle5-s llength 1          4       5       0
031 KLIST shuffle5-s llength 10        25      35      40
032 KLIST shuffle5-s llength 100      261     483     520
033 KLIST shuffle5-s llength 1000   12037   23682   24540
034 KLIST shuffle5a llength 1           4       7      10
035 KLIST shuffle5a llength 10         27      40      40
036 KLIST shuffle5a llength 100       239     402     440
037 KLIST shuffle5a llength 1000     2813    5746    6210
038 KLIST shuffle5a llength 10000   85125  249802  259400
039 KLIST shuffle6 llength 1            1       2     -=-
040 KLIST shuffle6 llength 10          20      20     -=-
041 KLIST shuffle6 llength 100        152     186     -=-
042 KLIST shuffle6 llength 1000      1512    1925     -=-
043 KLIST shuffle6 llength 10000    16978   19372     -=-
043 BENCHMARKS                    1:8.5a0 2:8.4.5 3:8.3.5

dgp added on 2004-01-21 07:50:52:
Logged In: YES 
user_id=80530


Vince, could you try running
the klist.bench benchmarks
before and after this patch?

I see substantial slowdowns,
and the only theory I have is
that this patch really hurts
us in memory?  Can you at
least confirm/deny the slowdowns?

dgp added on 2004-01-21 05:58:22:
Logged In: YES 
user_id=80530


Aha!

Apply this patch to your
file.bench before
contributing it to
tclbench:

--- /home/dgp/file.bench        Tue Jan 20 14:45:52 2004
+++ file2.bench Tue Jan 20 17:45:56 2004
@@ -175,7 +175,11 @@

 # Make a dummy pkgIndex to test speed of pkg require when the
 # auto_path is changed on the fly
-contents [file join $dir pkgIndex.tcl] {lappend ::auto_path
$dir}
+contents [file join $dir pkgIndex.tcl] {
+    if {[lsearch -exact $::auto_path $dir] == -1} {
+       lappend ::auto_path $dir
+    }
+}

 contents $file {lappend ::auto_path [file dirname [info
script]] ;\
   catch {package require bogus-name}; package names; exit}


Tcl 8.3.5 had a bug in
[tclPkgUnknown] that
allowed for an infinite loop
as the same directory got
lappended again and again
to ::auto_path.  This patch
avoids triggering that bug.

dgp added on 2004-01-21 05:13:52:
Logged In: YES 
user_id=80530


ok, I'm clearly confused on
the benchmarks.  Don't know
why I can't run the final benchmark
in your file.bench in an 8.3 interp,
but I can't; let's
pursue that elsewhere.

Latest patch passes all tests.

Latest patch shows no
significant slowdowns
according to your file.bench.

Latest patch shows no 
significant slowdowns
according to the file.bench
already in tclbench.

Latest patch does produce
some compiler warnings
that should be corrected, but
that can be done post-commit.

I vote commit now.

dgp added on 2004-01-21 03:42:30:
Logged In: YES 
user_id=80530


just a note about file.bench

the benchmark named
"FILE standard directory 'package require'"
is pretty bogus.  Different tclsh versions
will point to different [info library] directories,
so different runtimes tell you more about
the differing contents of those directories
than anything about Tcl performance.
I wouldn't add that one to tclbench.

In particular on unforunate installations
where [info library] is very full, you might
retire before that benchmark completes.

vincentdarley added on 2004-01-20 22:39:00:

File Added - 74149: fsopt3.patch

vincentdarley added on 2004-01-20 22:38:59:
Logged In: YES 
user_id=32170

Cosmetic clean up for remaining issues.  No functional
changes, I think.

vincentdarley added on 2004-01-20 17:06:22:

File Deleted - 74111: 



File Added - 74113: fsopt2.patch

vincentdarley added on 2004-01-20 16:55:26:

File Deleted - 74023: 



File Added - 74111: fsopt2.patch

Logged In: YES 
user_id=32170

Sorry.  Fixed that and cleaned lots of things up.  This is
now pretty close to ready to commit.

dgp added on 2004-01-20 08:40:08:
Logged In: YES 
user_id=80530


Link failure:

tclUnixFile.c(.text+0x1108): undefined reference to
`TclFileDirOrTail'

vincentdarley added on 2004-01-20 02:32:50:

File Added - 74023: fsopt2.patch

Logged In: YES 
user_id=32170

Here's a quick attempt to extend the optimisation to 'file
extension'.

dgp added on 2004-01-20 01:58:27:
Logged In: YES 
user_id=80530


New patch passes all tests.

The slowdowns of [file dirname]
and [file tail] are now big
speedups!

020 FILE glob / dirname                            1.00    0.25
031 FILE glob / tail                               1.00    0.36

Next biggest slowdowns (still
in this patch) are:

023 FILE glob / extension                          1.00    1.30
029 FILE glob / rootname                           1.00    1.33

vincentdarley added on 2004-01-20 01:37:09:

File Deleted - 74009: 



File Added - 74020: fsopt.patch

vincentdarley added on 2004-01-20 00:45:41:

File Deleted - 73395: 



File Added - 74009: fsopt.patch

vincentdarley added on 2004-01-20 00:45:40:
Logged In: YES 
user_id=32170

Attached new patch, addressing all the issues below, except
that we need to rename variables to 'pathObj' in all files
except tclPathObj.c (which has now been done).

vincentdarley added on 2004-01-19 19:33:14:
Logged In: YES 
user_id=32170

I see, your analysis of Tcl_FSConvertToPathType showed a bug
in there.  In the 'special' FsPathptr case, there's no point
in comparing the ->cwdPtr to the cwd, since it has nothing
to do with cwds.  So, that should fix the slowdown.

Will attach patch when sf-cvs starts to work again.

vincentdarley added on 2004-01-19 19:11:37:
Logged In: YES 
user_id=32170

Ok, a follow-up on all your good comments and questions below:

(1) The documentation of FsPath is indeed behind the times
(since the first filesystem optimisation).  I've updated
that in my sources and will attach a patch as soon as sf's
cvs servers are up.  Comments/advice on how best to name
these multi-use object points would be appreciate.

(2) Indeed objPtr, pathPtr, pathObjPtr, filenamePtr are all
used all over the place and this is annoying and confusing.
 I think pathPtr is the best, so will begin to convert to that.

(3) I've factored the file dir/tail into a common function
in tclPathObj.c which should also allow it to be clever in
what it returns.

(4) For TclFSCwdPointerEquals and *objPtrPtr being set, yes
I believe you are right that this would be a sensible step
to take.  Added this to the next patch.

Thanks also for the detailed analysis of TclFSGetPathType
and friends.  I'll examine that more closely before proceeding.

dgp added on 2004-01-16 03:48:29:
Logged In: YES 
user_id=80530


TclFSGetPathType() especially
looks like it could use some attention.

It appears that TclGetPathType() is
fairly expensive and TclFSGetPathType()
is an attempt to avoid it.

However, if pathObjPtr is not already a
FSPathObj with cwdPtr NULL or up to date,
then the Tcl_FSConvertToPathType()
call will have to call TclGetPathType() to
attempt to make it so.

The last call to TclGetPathType is mysterious
too.  Don't we know when cwdPtr == NULL
that we have an absolute path?  Or is that
another example of the comments being
out of date?

Anyhow, it appears that in the slow operations,
TclGetPathType is getting called twice.  The
first time from within the patched 
Tcl_FSConvertToPathType() and the
second time when the cwdPtr of
the converted pathObjPtr turns out
to be NULL.

dgp added on 2004-01-16 01:44:12:
Logged In: YES 
user_id=80530


In the duplicated stanza
of code noted before, there
is a call to Tcl_FSSplitPath()
on the pathObj returned by [glob].

That pathObj was created by
TclNewFSPathObj() and has
the special structure you noted
before.

Tcl_FSSplitPath calls TclFSGetPathType()
which calls Tcl_FSConvertToPathType().
which was modified by this patch.

Prior to the patch, T_FSCTPT sees a
typePtr of &tclFsPathType and quickly
returns TCL_OK.

After the patch, there's a check of
fsPathPtr->cwdPtr and since it
holds the value of the directory
globbed and not the current working directory
of the process, the pathObj is reconstructed
by generating it's string rep, then re-parsing.
Appears that's where the time difference
comes from.

Seems that if either Tcl_FSSplitPath()
or TclFSGetPathType() were updated
to recognize the optimized form and
process it more efficiently without a
string rep re-parsing, this slowdown
might be avoided.

It might be, though, that the re-parsing
just has to happen at some point, so
you just have to pick where.

dgp added on 2004-01-16 01:22:21:
Logged In: YES 
user_id=80530


Just another optimization
suggestion.  Near the end
of TclFSCwdPointerEquals()
when string comparison
is done to verify that the
two cwdPtr values are 
different objects, but equal
values....

Couldn't you reset the value
of (*objPtrPtr) to be
tsdPtr->cwdPathPtr -- along
with appropriate refCount
housekeeping -- so that
subsequent checks could
use the fast pointer comparison
and avoid the repeated
string comparison?

dgp added on 2004-01-16 00:58:21:
Logged In: YES 
user_id=80530


I'm a bit confused on the FsPath
structure.  The normPathPtr
is documented to be
"Normalized, absolute path"

However, Tcl_NewFSPatchObj()
seems to be initializing it to the
relative name of a file?  And then
stashing the name of the containing
directory in the cwdPtr field?

Is this just a case of comments
falling behind?

dgp added on 2004-01-16 00:56:05:
Logged In: YES 
user_id=80530

still looking at this.  Just a note
while I'm thinking about it.

One of the recommendations in
the Tcl Engineering Manual is 
that the same thing gets the same
name throughout all the code.
For example,  it is always

    Tcl_Interp *interp

As I move from function to
tunction in the Tcl_FS code,
my sense is that there's not
as much naming consistency
as I would like.  Make the
code harder to follow than
it needs to be.

Maybe once we're through
bug fixing, an additional pass
for code cleanup ?

vincentdarley added on 2004-01-15 03:13:13:
Logged In: YES 
user_id=32170

"yes" to your question below.  Note that the "path" object
results of 'glob' can be different to a normal path:

glob -dir $dir *

returns special path objects which know that they are '$dir'
plus a little bit.  It could be there is a slowdown just for
these special paths.  (We should add a separate 'file
dirname' benchmark on an ordinary path).

However: I really don't know what would be slower with this
patch (unless it introduces some subtle weirdness I haven't
thought about).  Here's what this patch does:

Tcl 8.4/8.5: spend lots of time with relative paths checking
if the 'cwd' has changed.  This involved calling the OS'es
getcwd, converting the encoding, normalizing, creating a
path object, comparing with the previous path object, and
then usually throwing it all away because no-one had called
chdir behind our backs to change the cwd anyway.

Tcl 8.5+patch: cache the native cwd and perform an immediate
comparison of the result of getcwd with that native cwd. If
it's the same, return the cached value which then
short-circuits everything else (no normalization, no
encoding conversion, no object being created).

There are a few other small changes beyond the above, which
could be to blame (some code paths have changed in
tclPathName, which helps to simplify everything, I think!).

Thanks for doing all this, and for pointing out the code
duplication -- I'll look into that, probably after we
understand this issue, since fixing this might lead
naturally do removing the duplication anyway...

dgp added on 2004-01-15 01:51:21:
Logged In: YES 
user_id=80530


No data yet, but I notice that
both slow operations share
a stanza of code in common.

Lines 2659-2674 of tclFileName.c
and lines 1402-1417 of tclCmdAH.c

I'd look at those operations first I think.

BTW duplicate code suggest a factoring
opportunity.

dgp added on 2004-01-15 01:31:14:
Logged In: YES 
user_id=80530


so looking at the benchmarks,
I should be looking for the
cause of slowdowns in the
[file dirname] and [file tail] 
commands?

vincentdarley added on 2004-01-14 23:23:49:
Logged In: YES 
user_id=32170

In that case I'd be very interested to see some sort of
profiling (at least at the level of function calls) of those
two slow benchmarks (glob /dirname glob /tail), since it's
very hard to see how they could possibly get worse with this
patch....

dgp added on 2004-01-14 22:58:08:
Logged In: YES 
user_id=80530


My benchmarks tested HEAD
against HEAD+patch.

vincentdarley added on 2004-01-14 17:01:56:

File Deleted - 72414: 



File Added - 73395: fsOpt2.patch

Logged In: YES 
user_id=32170

Thanks for fixing the patch!

I'm wondering what version of Tcl you're benchmarking
against?  Is it 8.4.5 or 8.3.4?  Could you perhaps upload
full results to:

http://mini.net/tcl/10582

I must say I'm not really sure what could cause the slowdown
in those two glob functions, and I don't really have the
experience (or access, given compilefarm never works) to do
profiling on Windows.

dgp added on 2004-01-14 08:48:36:
Logged In: YES 
user_id=80530


testing your benchmarks, it does seem
that most things are a bit faster.  There
appear to be especially big speedups
for:

009 FILE exists! absolute tmpfile (str)            1.00    0.70
010 FILE exists! relative tmpfile (obj)            1.00    0.68
011 FILE exists! relative tmpfile (str)            1.00    0.27
...
013 FILE exists! tmpfile (str)                     1.00    0.75
...
037 FILE recurse / cd                              1.00    0.62
...
039 FILE recurse+stat / cd                         1.00    0.68

On the other hand, I see substantial slowdowns for:

020 FILE glob / dirname                            1.00    1.41
...
031 FILE glob / tail                               1.00    1.56

Hope that helps...

dgp added on 2004-01-14 08:32:07:
Logged In: YES 
user_id=80530


After that change, `make test`
completes with no (new) failures.

dgp added on 2004-01-14 07:43:10:
Logged In: YES 
user_id=80530


uh, looks like I misread slightly.

Just need to add some safety NULL-checking
to line 606 of unix/tclUnixFile.c:

if ((clientData != NULL) && ...

After that change, the segfault is corrected.

dgp added on 2004-01-14 07:30:13:
Logged In: YES 
user_id=80530


The Unix implementation of 
TclpGetNativeCwd() is not happy
receiving a NULL ClientData -- at least
not if the system getwd() or getcwd() calls fail.

dgp added on 2004-01-14 07:19:32:
Logged In: YES 
user_id=80530

% source ./../tests/all.tcl

Program received signal SIGSEGV, Segmentation fault.
strcmp () at ../sysdeps/alpha/strcmp.S:41
41      ../sysdeps/alpha/strcmp.S: No such file or directory.
Current language:  auto; currently asm
(gdb) bt
#0  strcmp () at ../sysdeps/alpha/strcmp.S:41
#1  0x12010c080 in TclpGetNativeCwd (clientData=0x0)
    at ./../unix/tclUnixFile.c:606
#2  0x1200b5d60 in Tcl_FSGetCwd (interp=0x12025b818)
    at ./../generic/tclIOUtil.c:2360
#3  0x1200d8a84 in Tcl_FSGetNormalizedPath (interp=0x12025b818, 
    pathObjPtr=0x1202e0578) at ./../generic/tclPathObj.c:1356
#4  0x1200b4898 in Tcl_FSEvalFileEx (interp=0x12025b818,
pathPtr=0x1202e0578, 
    encodingName=0x0) at ./../generic/tclIOUtil.c:1556
#5  0x120044ab8 in Tcl_SourceObjCmd (dummy=0x0,
interp=0x12025b818, objc=2, 
    objv=0x12025c800) at ./../generic/tclCmdMZ.c:1128
#6  0x12002a704 in TclEvalObjvInternal (interp=0x12025b818,
objc=2, 
    objv=0x12025c800, command=0x0, length=0, flags=0)
    at ./../generic/tclBasic.c:3143
#7  0x12007baf0 in TclExecuteByteCode (interp=0x12025b818,
codePtr=0x1202e5828)
    at ./../generic/tclExecute.c:1548
#8  0x1200796e4 in TclCompEvalObj (interp=0x12025b818,
objPtr=0x1202a4ef8)
    at ./../generic/tclExecute.c:1005
#9  0x12002bdd8 in Tcl_EvalObjEx (interp=0x12025b818,
objPtr=0x1202a4ef8, 
    flags=131072) at ./../generic/tclBasic.c:3884
#10 0x12012c83c in Tcl_RecordAndEvalObj (interp=0x12025b818, 
    cmdPtr=0x1202a4ef8, flags=131072) at
./../generic/tclHistory.c:142
#11 0x1200bfb90 in Tcl_Main (argc=1, argv=0x11ffff4f8, 
    appInitProc=0x1200109a0 <Tcl_AppInit>) at
./../generic/tclMain.c:478
#12 0x12001097c in main (argc=1, argv=0x11ffff4f8) at
./../unix/tclAppInit.c:90

dgp added on 2004-01-14 06:58:30:
Logged In: YES 
user_id=80530


first test report:  this patch gives me
an immediate segfault when attempting
a `make test`.  Will look further.

vincentdarley added on 2004-01-06 17:48:13:

File Added - 72415: file.bench

Logged In: YES 
user_id=32170

Added modified file.bench that I'm using.

vincentdarley added on 2004-01-06 17:47:34:

File Added - 72414: fsOpt.patch

Attachments: