home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / perl / 5848 < prev    next >
Encoding:
Text File  |  1992-09-10  |  10.0 KB  |  415 lines

  1. Newsgroups: comp.lang.perl
  2. Path: sparky!uunet!caen!sol.ctr.columbia.edu!IDA.ORG!rlg
  3. From: rlg@IDA.ORG (Randy garrett)
  4. Subject: Fast String Operations?
  5. Message-ID: <1992Sep10.220438.21395@IDA.ORG>
  6. Organization: IDA, Alexandria, VA
  7. X-Newsreader: Tin 1.1 PL4
  8. Date: Thu, 10 Sep 92 22:04:38 GMT
  9. Lines: 404
  10.  
  11. Here at last is a long delayed summary.
  12.  
  13. The original question had to do with replacing all occurrences
  14. of "||" in a string with either a ~  or a -1 depending on
  15. the value of another array.  This is basically a database
  16. conversion process, where all fields that are of type int ought
  17. to get a NULL value of -1 and all fields of type char should get
  18. a ~ .  For instance, given a string "abc|123|||def|" and a
  19. corresponding array (1,2,1,2,2,1) produce the string 
  20. "abc|123|-1|~|def|-1", if we assume an array value of 1 means int
  21. and an array value of 2 means ~ .
  22.  
  23. I got some great suggestions for improving the speed of my code.
  24. Thanks very much for all the help!
  25.  
  26. The code I posted was a subset of the actual code, so a few
  27. of the suggestions were difficult to merge into the real code.
  28. My explanation of some of the fine points also left something
  29. to be desired.
  30.  
  31. Finally, I just ran out of time to do testing on some of the 
  32. submissions.  Sorry!  If time permits, I will also test those.
  33. Also, I  munged the submitted code slightly to permit benchmarking.
  34. If I lost the original intent in doing so, please correct me.
  35.  
  36. The system I tested on was a Sun 4/490 running SunOS 4.1.1 and
  37. perl-4.019 compiled using the Sun bundled C compiler.  A better
  38. compiler might speed all these results up.  I identified the
  39. submissions by FS0 thru FS4.  I did not finish benching
  40. submissions FS5 thru FS 9. Code and their authors is at the
  41. end of this message.  I am extremely sorry to say that I lost
  42. the author's names of FS3 and 4.  Please forgive me, and if
  43. you read this send me a reply.
  44.  
  45. For a 10,000 character length string, all NULLS:  (extreme test case)
  46. All times the sum of user and system CPU seconds. Also, it took
  47. about 1.0 second just to init the string.
  48.  
  49. FS0:  21 
  50. FS1:  10.6 
  51. FS2:  13.5
  52. FS3:  4.8
  53. FS4:  10
  54.  
  55. As you can see all the submissions were about twice as fast as
  56. my original, and FS3 was about 4x faster.
  57.  
  58. For a more realistic test, I ran a loop with 4000 iterations with
  59. a string of 25 Nulls. Just running a 4000 iteration loop with
  60. no ops inside took .3 seconds.
  61.  
  62. FS0: 63
  63. FS1: 37
  64. FS2: 39
  65. FS3: 18
  66. FS4: 35
  67.  
  68. Since FS3 looked like the winner, I did more tests on it.
  69.  
  70. # of loops / # of Nulls   Time
  71.  
  72. 1 / 10000      4.8
  73. 1 / 5000       2.0
  74. 1 / 1000       .5
  75. 1 / 100        .2
  76.  
  77. 100 / 100      2.2
  78. 1000 / 100     19.4
  79. 4000 / 100     76.0
  80.  
  81. 1000 / 25      4.5
  82. 4000 / 25      18.0
  83.  
  84. #########  Actual Code  ################
  85.  
  86. ########### FS0:  by me -- Randy Garrett #########
  87.  
  88. #!/usr/local/bin/perl
  89. # Walk thru a string; if find "||" inset either a ~ or a -1
  90. # depending on value of @name 
  91. $Num = 25;
  92. #$string = "a|bcd||g||i||||||";
  93. $str = "|" x $Num;
  94. for ($j = 0; $j < $Num; ++$j)
  95.   { push (@name,"1"); }
  96. # print "$string\n";
  97.  
  98. ($u, $s, $cs, $cu) = times;
  99. print "Time = $u $s\n";
  100.  
  101. for ($j = 0; $j < 4000; ++$j)
  102. {
  103.  $count = 0;  # index into @name
  104.  $pos = 0;    # index into $string
  105.  $string = $str;
  106.  while (($pos = index($string,'|',$pos)) >= 0) {
  107. #     print "Found at $pos $count = $name[$count]\n";
  108.      if (substr($string,$pos+1,1) eq "|" ) {  # found a NULL
  109.          if ($name[$count] == 1) {   # int ?
  110.                  substr($string,$pos+1,0) = -1; }
  111.          elsif ($name[$count] == 2 ) {  # char?
  112.                  substr($string,$pos+1,0) = "~"; }
  113.      }
  114.          $pos++; $count++;
  115.      }
  116. # print "$string\n";
  117. }
  118.  print "$string\n";
  119. ($u, $s, $cs, $cu) = times;
  120. print "Time = $u $s\n";
  121.  
  122. ###################  FS1 #########################
  123. # Hal R. Pomeranz -- pomeranz@nas.nasa.gov
  124. #!/usr/local/bin/perl
  125.  
  126.  
  127. $Num = 25;
  128. $string = "|" x $Num;
  129. for ($j = 0; $j < $Num; ++$j)
  130.   { push (@name,"1"); }
  131. #$string = "a|bcd||g||i|||";
  132. #@name = (1,2,1,1,2,1,2,1);
  133.  
  134. ($u, $s, $cs, $cu) = times;
  135. print "Time = $u $s\n";
  136.  
  137. for ($j = 0; $j < 4000; ++$j)
  138. {
  139.  
  140. @cols = split(/\|/, $string);
  141. for ($i = 0; $i < @name; $i++) {
  142.    $cols[$i] = ($name[$i] == 1 ? -1 : "~") unless ($cols[$i]);
  143. }
  144. # print join('|', @cols), "|\n";
  145.  
  146. join('|', @cols);
  147. }
  148.  
  149. ($u, $s, $cs, $cu) = times;
  150. print "Time = $u $s\n";
  151.  
  152. ##########        FS 2             ###############
  153. # Christopher Davis -- ckd@eff.org
  154. #
  155.  
  156. #!/usr/local/bin/perl -- # -*-Perl-*-
  157. # $string = "a|bcd||g||i|||";
  158. # @name = (1,1,2,1,1,2,1,2,1);    # yours was off-by-one, I added 1 at start
  159.  
  160. $Num = 25;
  161. $string = "|" x $Num;
  162. for ($j = 0; $j < $Num; ++$j)
  163.   { push (@name,"1"); }
  164.  
  165. ($u, $s, $cs, $cu) = times;
  166. print "Time = $u $s\n";
  167.  
  168. for ($j = 0; $j < 4000; ++$j)
  169. {
  170.  
  171. $nf = scalar(@name);        # number of fields
  172. undef @string;
  173. foreach (@name) {        # give us a nice boolean
  174.     push(@string,$_ - 1);
  175. }
  176.  
  177. #@fields =split('\|',$string,$nf); # $nf will force it to keep nulls at end
  178. @fields =split('\|',$string);
  179.  
  180. undef @newfields;
  181.  
  182. foreach (@string) {               # i.e. for each field
  183.     $thisfield = shift(@fields);  # take it off the old list
  184.     $thisfield = ($_?"~":-1) unless $thisfield; # if it's null, fix it
  185.     push(@newfields,$thisfield);  # put it on the new list
  186. }
  187.  
  188. # $newstring = join("|",@newfields,"|");    # add a pipe at the end
  189. $newstring = join("|",@newfields);    # add a pipe at the end
  190. # print $newstring,"\n";
  191. }
  192.  
  193. ($u, $s, $cs, $cu) = times;
  194. print "Time = $u $s\n";
  195.  
  196.  
  197. ###################   FS3   ###########################
  198.  
  199. #!/usr/local/bin/perl
  200. # Walk thru a string; if find "||" insert either a ~ or a -1
  201. # depending on value of @name
  202.  
  203. $Num = 25;
  204. $string = "|" x $Num;
  205. for ($j = 0; $j < $Num; ++$j)
  206.   { push (@name,"2"); }
  207.  
  208. ($u, $s, $cs, $cu) = times;
  209. print "Time = $u $s\n";
  210.  
  211.  #$string = "a|bcd||g||i|||";
  212.  #@name = (1,2,1,1,2,1,2,1);
  213.  
  214. %defaults = (1, '-1', 2, "~");  # Specify defaults for each of your types
  215.  
  216. for ($j = 0; $j < 4000; ++$j)
  217. {
  218.  
  219. @strings = split(/\|/, $string);
  220. for $count ( 0.. $Num )
  221. # foreach $count ( @strings )
  222.  {
  223.   $strings[$count] = $defaults{ $name[$count] } if $strings[$count] eq '';
  224.  }
  225. $string = join("|", @strings);
  226. # print "$string\n";
  227. }
  228.  print "$string\n";
  229.  
  230. ($u, $s, $cs, $cu) = times;
  231. print "Time = $u $s\n";
  232.  
  233. #####################  FS4  #########################
  234.  
  235. #!/usr/local/bin/perl
  236.  
  237. #$string = "a|bcd||g||i|||";
  238. #@name = (1,2,1,1,2,1,2,1);
  239.  
  240.  
  241. $Num = 25;
  242. $string = "|" x $Num;
  243. for ($j = 0; $j < $Num; ++$j)
  244.   { push (@name,"1"); }
  245.  
  246. for ($j = 0; $j < 4000; ++$j)
  247. {
  248.  
  249. # if ($string =~ /\|\|/)
  250. {
  251.  
  252. #  chop ($string);
  253.  @field = split (/\|/, $string, @name +1);
  254.   for ($index = 0; $index < @field; $index++)
  255.   {
  256.     $field[$index] = ('', '-1', '~')[$name[$index]] if $field[$index] eq '';
  257.   }
  258.   $newstring = join ('|', @field);
  259. }
  260.  
  261. # print "$newstring\n";
  262. }
  263. ($u, $s, $cs, $cu) = times;
  264. print "Time for $j = $u $s\n";
  265.  
  266. ####### Following code not benchmarked ############
  267. #########################  FS 5  ###################
  268. # Raymond Chen -- rjc@math.Princeton.EDU
  269. #! /usr/local/bin/perl
  270. $string = "a|bcd||g||i|||";
  271. @default = ("-1","~", "-1", "-1", "~", "-1", "~", "-1","-1");
  272.  
  273.     @F = split(/\|/, $string);
  274.     for ($i = 0; $i < $#F; $i++) {    # unroll this loop for speed
  275.         $F[$i] = $default[$i] unless $F[$i];
  276.     }
  277. print    join("|", @F), "\n";
  278. ######################## FS 6 ####################
  279. # From Mike Flynn    (flynn_mike@jpmorgan.com)
  280.  
  281. #!/usr/local/bin/perl
  282. # Walk thru a string; if find "||" insert either a ~ or a -1
  283. # depending on value of @name 
  284.  
  285. #@name = (1,2,1,1,2,1,2,1);
  286. $Num = 25;
  287. $string = "|" x $Num;
  288. for ($j = 0; $j < $Num; ++$j)
  289.   { push (@name,"1"); }
  290.  
  291. ($u, $s, $cs, $cu) = times;
  292. print "Time = $u $s\n";
  293.  
  294. for ($j = 0; $j < 40; ++$j)
  295. {
  296. $count = 0;
  297. foreach $element (split(/\|/, $string)) {
  298. print "element = $element\n";
  299.     if ($element) {
  300.         print $element;
  301.         print "|" unless ($element eq "\n");
  302.     } else {
  303.         print "-1|" if ($name[$count] == 2);
  304.         print "~|" if ($name[$count] == 1);
  305.     }
  306.     $count++;
  307. }
  308.  
  309. }
  310.  
  311. ($u, $s, $cs, $cu) = times;
  312. print "Time = $u $s\n";
  313.  
  314. ######################### FS 7 ####################
  315. # From Larry Wall!
  316.  
  317. #!/usr/local/bin/perl --   # -*-Perl-*-
  318. # Call this with test.data
  319. $/ = '|';
  320. #$Num = 25;
  321. #$string = "|" x $Num;
  322. #for ($j = 0; $j < $Num; ++$j)
  323. #  { push (@name,"2"); }
  324.  
  325. #($u, $s, $cs, $cu) = times;
  326. #print "Time = $u $s\n";
  327.  
  328. @default = (-1,'~',-1,-1,'~',-1,'~',-1,-1,-1,-1,-1,'~',-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,'~');
  329.  
  330.     while (<>) {
  331.         $field = 0, print "\n" if s/^\n//;
  332.         next unless /^\|/;
  333. #        print $default[$field];
  334.     }
  335.     continue {
  336. #        print;
  337.         ++$field;
  338.     ++$k;
  339.     }
  340.  
  341. # print "$string\n";
  342.  
  343. ($u, $s, $cs, $cu) = times;
  344. print "Time = $u $s\n";
  345.  
  346.  
  347. ########### Finally a submission to do it in C #######
  348. #  From Larry Wall, of all people (:-^)  
  349. #
  350. # Unfortunately this code does a lot of other things
  351. # that I don't want to recode it in C and I'm too lazy
  352. # to learn to add my own user subroutines to Perl right now
  353. #
  354.  
  355. #include <stdio.h>
  356.  
  357.     char *def[] = {"-1","~","-1","-1","~","-1","~","-1"};
  358.  
  359.     main() {
  360.         int ch;
  361.         int lastch;
  362.         int field = 0;
  363.  
  364.         while ((ch = getc(stdin)) != EOF) {
  365.             if (ch == '\n')
  366.                 field = 0;
  367.             else if (ch == '|') {
  368.                 if (lastch == '|')
  369.                     fputs(def[field], stdout);
  370.                 field++;
  371.             }
  372.             putc(ch, stdout);
  373.  
  374.  
  375.             lastch = ch;
  376.         }
  377.     }
  378.  
  379. ###############  FS8  ###########################
  380. #  John Stoffel -- <john@wpi.WPI.EDU>
  381. $a = split(/\|/,$string,100);
  382.  
  383. foreach $x (0 .. ($a-1)) {
  384.     if ($_[$x] eq "") { 
  385.         if ($name[$x] == 1) { $_[$x] = "~"}
  386.         else { $_[$x] = "-1"}
  387.     }                          
  388. }                                
  389. print join('|',@_), "\n";
  390.  
  391. ###############  FS 9 ##########################
  392. # Unfortunately, I forgot to mention that the
  393. # strings are variable length, so it's not fair
  394. # to assume they are always length 8.
  395. # Thanks for your help, though ...
  396. # Chris Sherman -- sherman@unx.sas.com
  397. #!/usr/local/bin/perl
  398. $string = "a|bcd||g||i|||";
  399. @name = (0,1,0,0,1,0,1,0);
  400. @vars = 
  401.   ($string =~ /([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)/);for ($i = 0; $i < 8; $i++)
  402. {
  403.   if ($vars[$i] eq "")
  404.   {
  405.     if ($name[$i])
  406.     {
  407.       $vars[$i] = "-1";
  408.     } else {
  409.       $vars[$i] = "~";
  410.     }
  411.   }
  412. }
  413. print join("|",@vars),"|\n";
  414.  
  415.