You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by ad...@apache.org on 2019/12/04 00:22:10 UTC

[kudu] 01/02: KUDU-1938 Make UTF-8 truncation faster pt 1

This is an automated email from the ASF dual-hosted git repository.

adar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git

commit d5ac2f2e6f1003ff2f5163631e2be2f88f5c97d5
Author: Attila Bukor <ab...@apache.org>
AuthorDate: Mon Nov 11 23:10:03 2019 +0100

    KUDU-1938 Make UTF-8 truncation faster pt 1
    
    This commit adds a fast path for ASCII strings where if the MSB is a
    0-bit on each byte in a chunk of string it advances the counter and the
    iterator by the chunk size. This way if a chunk contains only ASCII
    characters there's no need to count each individual character.
    
    Thanks to Todd Lipcon for the initial idea and Zoltan Chovan and Istvan
    Farmosi for the brainstorming and the help in figuring out how this
    should be done.
    
    Before:
    
    [ RUN      ] CharUtilTest.StressTestUtf8
    [       OK ] CharUtilTest.StressTestUtf8 (6698 ms)
    [ RUN      ] CharUtilTest.StressTestAscii
    [       OK ] CharUtilTest.StressTestAscii (6161 ms)
    
    After:
    
    [ RUN      ] CharUtilTest.StressTestUtf8
    [       OK ] CharUtilTest.StressTestUtf8 (7746 ms)
    [ RUN      ] CharUtilTest.StressTestAscii
    [       OK ] CharUtilTest.StressTestAscii (1028 ms)
    
    Change-Id: Iebb98e18a3619029d9b0bc224c7dead89a3d7374
    Reviewed-on: http://gerrit.cloudera.org:8080/14353
    Reviewed-by: Adar Dembo <ad...@cloudera.com>
    Tested-by: Kudu Jenkins
---
 src/kudu/util/CMakeLists.txt                   |   2 +
 src/kudu/util/char_util-test.cc                | 103 ++++++++++
 src/kudu/util/char_util.cc                     |  34 ++--
 src/kudu/util/testdata/char_truncate_ascii.txt |  35 ++++
 src/kudu/util/testdata/char_truncate_utf8.txt  | 258 +++++++++++++++++++++++++
 5 files changed, 421 insertions(+), 11 deletions(-)

diff --git a/src/kudu/util/CMakeLists.txt b/src/kudu/util/CMakeLists.txt
index f31fdac..affb7e8 100644
--- a/src/kudu/util/CMakeLists.txt
+++ b/src/kudu/util/CMakeLists.txt
@@ -392,6 +392,8 @@ ADD_KUDU_TEST(bloom_filter-test)
 ADD_KUDU_TEST(cache-bench RUN_SERIAL true)
 ADD_KUDU_TEST(cache-test)
 ADD_KUDU_TEST(callback_bind-test)
+ADD_KUDU_TEST(char_util-test
+  DATA_FILES testdata/char_truncate_utf8.txt testdata/char_truncate_ascii.txt)
 ADD_KUDU_TEST(countdown_latch-test)
 ADD_KUDU_TEST(crc-test RUN_SERIAL true) # has a benchmark
 ADD_KUDU_TEST(debug-util-test)
diff --git a/src/kudu/util/char_util-test.cc b/src/kudu/util/char_util-test.cc
new file mode 100644
index 0000000..88188bd
--- /dev/null
+++ b/src/kudu/util/char_util-test.cc
@@ -0,0 +1,103 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "kudu/util/char_util.h"
+
+#include <cstdint>
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "kudu/util/env.h"
+#include "kudu/util/faststring.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/slice.h"
+#include "kudu/util/test_util.h"
+
+using std::unique_ptr;
+
+namespace kudu {
+
+class CharUtilTest : public KuduTest {
+ protected:
+  Slice data_utf8_;
+  Slice data_ascii_;
+
+  void SetUp() override {
+    ReadFileToString(env_, JoinPathSegments(GetTestExecutableDirectory(),
+                                           "testdata/char_truncate_utf8.txt"),
+                     &string_utf8_);
+    data_utf8_ = Slice(string_utf8_);
+    ReadFileToString(env_, JoinPathSegments(GetTestExecutableDirectory(),
+                                           "testdata/char_truncate_ascii.txt"),
+                     &string_ascii_);
+    data_ascii_ = Slice(string_ascii_);
+  }
+
+  unique_ptr<const uint8_t[]> Truncate(const Slice& slice, int length, Slice* result) {
+    *result = UTF8Truncate(slice, length);
+    return std::unique_ptr<const uint8_t[]>(result->data());
+  }
+
+  void StressTest(const Slice& slice, int length) {
+    for (int i = 0; i < kNumCycles_; ++i) {
+      Slice result;
+      auto ptr = Truncate(slice, length, &result);
+      ASSERT_FALSE(result.empty());
+    }
+  }
+
+ private:
+  const int kNumCycles_ = 1000000;
+  faststring string_utf8_;
+  faststring string_ascii_;
+};
+
+TEST_F(CharUtilTest, CorrectnessTestUtf8) {
+  Slice result;
+  auto ptr = Truncate(data_utf8_, 9756, &result);
+  ASSERT_EQ(10549, result.size());
+
+  ptr = Truncate(data_utf8_, 10549, &result);
+  ASSERT_EQ(10550, result.size());
+}
+
+TEST_F(CharUtilTest, CorrectnessTestAscii) {
+  Slice result;
+  auto ptr = Truncate(data_ascii_, 9756, &result);
+  ASSERT_EQ(9756, result.size());
+  ptr = Truncate(data_ascii_, 10549, &result);
+  ASSERT_EQ(10549, result.size());
+}
+
+TEST_F(CharUtilTest, CorrectnessTestIncompleteUtf8) {
+  Slice result;
+  Slice test_data = "aaaa\xf3";
+
+  auto ptr = Truncate(test_data, 5, &result);
+  ASSERT_EQ(test_data, result);
+}
+
+TEST_F(CharUtilTest, StressTestUtf8) {
+  StressTest(data_utf8_, 9000);
+}
+
+TEST_F(CharUtilTest, StressTestAscii) {
+  StressTest(data_ascii_, 9000);
+}
+
+} // namespace kudu
diff --git a/src/kudu/util/char_util.cc b/src/kudu/util/char_util.cc
index 606d421..e232325 100644
--- a/src/kudu/util/char_util.cc
+++ b/src/kudu/util/char_util.cc
@@ -17,25 +17,37 @@
 
 #include "kudu/util/char_util.h"
 
-#include <string.h>
+#include <algorithm>
+#include <cstring>
 
 namespace kudu {
 
 Slice UTF8Truncate(Slice val, size_t max_utf8_length) {
   size_t num_utf8_chars = 0;
+  const uint8_t* str;
+  const uint8_t* start;
+  str = start = val.data();
   size_t num_bytes = 0;
-  auto str = val.data();
-  for (auto i = 0; i < val.size(); ++i) {
-    num_utf8_chars += (*str++ & 0xc0) != 0x80;
-    num_bytes++;
-    if (num_utf8_chars > max_utf8_length) {
-      num_bytes--;
-      num_utf8_chars--;
-      break;
+  size_t size = val.size();
+  while (num_bytes < size) {
+    // If the next chunk of bytes are all ASCII we can fast path them.
+    if (size - num_bytes >= 8 &&
+        max_utf8_length - num_utf8_chars >= 8 &&
+        (*(reinterpret_cast<const int64_t*>(str)) & 0x8080808080808080) == 0) {
+      num_utf8_chars += 8;
+      num_bytes += 8;
+    } else {
+      num_utf8_chars += (*str++ & 0xc0) != 0x80;
+      num_bytes++;
+      if (num_utf8_chars > max_utf8_length) {
+        num_bytes--;
+        num_utf8_chars--;
+        break;
+      }
     }
+    str = start + num_bytes;
   }
-  // as num_bytes <= val.size() we can use that to allocate the new slice data
-  // and copy the first num_bytes from val.data() to it.
+  num_bytes = std::min<size_t>(size, num_bytes);
   auto relocated = new uint8_t[num_bytes];
   memcpy(relocated, val.data(), num_bytes);
   return Slice(relocated, num_bytes);
diff --git a/src/kudu/util/testdata/char_truncate_ascii.txt b/src/kudu/util/testdata/char_truncate_ascii.txt
new file mode 100644
index 0000000..434ff89
--- /dev/null
+++ b/src/kudu/util/testdata/char_truncate_ascii.txt
@@ -0,0 +1,35 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum fringilla egestas enim, sit amet finibus orci ultrices vel. Donec vel auctor lorem. Vivamus nisl purus, lobortis nec sollicitudin vitae, ultricies at arcu. Sed nec ligula purus. Vestibulum eu diam iaculis, rutrum ligula eget, consectetur sapien. Nulla rutrum maximus enim, luctus bibendum arcu laoreet et. Pellentesque scelerisque tellus sit amet nisi ultrices ornare. Aenean consectetur neque non efficitur convallis. Etiam [...]
+
+Curabitur ut orci enim. Proin purus augue, cursus id dui sit amet, bibendum pulvinar mi. Aenean venenatis in ante vitae vehicula. Nulla blandit maximus mauris, quis vulputate sapien lobortis dictum. Pellentesque in sem blandit, aliquam velit a, pharetra est. Quisque vel vulputate lacus, et tempor eros. Pellentesque bibendum diam eu mi sodales, at sodales nisl dignissim. Donec maximus dolor at velit placerat facilisis id sit amet ligula. Phasellus fringilla ornare libero, quis faucibus nu [...]
+
+Nulla id aliquet augue. Nunc dignissim egestas nisl, eu interdum sapien molestie ut. Aliquam facilisis id est eu ultrices. Nulla efficitur suscipit consectetur. Donec rutrum, tortor sed venenatis accumsan, augue diam vestibulum lacus, sit amet lacinia massa arcu sed massa. Quisque ullamcorper pharetra nisi, a egestas velit rhoncus et. Aenean convallis posuere tempus. Cras ipsum est, maximus sit amet risus eu, tristique mattis nunc. Donec vitae commodo est. Nullam risus ligula, imperdiet  [...]
+
+Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec vestibulum ante ac gravida volutpat. Suspendisse quis venenatis mauris. Donec eros ipsum, convallis ac finibus a, pellentesque nec mi. Nunc id lacus lobortis, mollis lacus eget, gravida sapien. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper ligula luctus turpis lobortis finibus. Vivamus facilisis vel lacus ut consectetur. Fusce sollic [...]
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam porttitor iaculis mi et suscipit. Nam in lacinia ipsum. Aliquam mattis lectus nec placerat laoreet. Nulla consectetur magna sed lorem scelerisque, id pharetra purus commodo. Suspendisse faucibus quam commodo molestie sollicitudin. Aenean congue viverra lorem at pretium. Praesent a iaculis tortor, ut lacinia quam. Quisque lobortis fringilla mauris, vitae suscipit magna pharetra id. Donec tincidunt lorem vitae pharetra iaculis. S [...]
+
+Donec sapien massa, placerat vel eros ac, porttitor porta nisl. Quisque non tortor diam. Ut fermentum scelerisque fermentum. Vivamus sapien sapien, tempus ut tempor id, pharetra in elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut quis purus enim. Suspendisse ut semper erat. In hac habitasse platea dictumst.
+
+Nunc pulvinar maximus ante, at sollicitudin enim venenatis sit amet. Sed finibus sed dolor id tincidunt. Donec nec turpis molestie, iaculis dui vitae, semper risus. Cras pulvinar dolor eu mattis interdum. Etiam in enim eu dui porttitor vestibulum. Suspendisse porta blandit ante, ut aliquet ex ultrices nec. Sed pharetra efficitur orci, consectetur ornare turpis vulputate eget. Fusce malesuada felis ipsum, nec fermentum nibh maximus at. In et tempor neque. Vestibulum auctor at nulla a rhon [...]
+
+Integer sed velit vel urna posuere ornare. Sed quis ultricies augue. Vestibulum volutpat tellus non commodo tincidunt. Integer tempus, orci eget iaculis laoreet, dolor lacus condimentum enim, eu lobortis justo elit ut nulla. Cras a fringilla massa, non gravida erat. Vivamus pretium nisi quam, rhoncus tincidunt ipsum gravida a. Donec mattis magna in diam porttitor, et aliquet velit porttitor. Integer fermentum libero purus, eu volutpat metus consequat eu. Vivamus gravida molestie augue, v [...]
+
+Pellentesque bibendum fringilla purus vitae aliquam. Nulla ornare lectus nisi, a dapibus odio pulvinar vel. Vestibulum sed feugiat justo, eget fermentum velit. Donec sit amet efficitur leo. Cras venenatis consequat ligula vitae iaculis. Curabitur sodales elit ac metus ornare feugiat. Nullam et tempus dui, et rhoncus erat. Nulla rhoncus sapien sit amet elit volutpat rhoncus. Morbi facilisis tincidunt nisi, a viverra arcu pharetra ac. Curabitur sodales augue at libero maximus, ut lobortis  [...]
+
+Nam vitae orci eu erat interdum vehicula a eget arcu. Mauris commodo leo ac risus sodales, eget pulvinar felis suscipit. Mauris id bibendum mi, sit amet consequat sem. Pellentesque in convallis mi. Cras pulvinar faucibus urna, quis tempor diam interdum id. In semper libero lacus, at tincidunt mauris congue quis. Curabitur eleifend venenatis odio ut iaculis. Maecenas nunc nibh, pretium a erat sit amet, rhoncus condimentum massa. Nullam id gravida arcu. Donec molestie ac ligula vel posuere [...]
+
+Mauris dictum eu tortor eu malesuada. Integer elementum urna eget orci sagittis, ac consectetur lacus imperdiet. In euismod leo in ante lacinia lobortis. Vestibulum ut lobortis justo. Proin ac rutrum arcu, eu faucibus sem. Praesent semper vel erat ut mattis. Cras euismod nulla a odio porttitor, id rutrum libero aliquam. Praesent vel mauris finibus, elementum metus eget, egestas sapien. Praesent sit amet dui ex. Ut rutrum nunc aliquet, maximus nisi non, ullamcorper augue. In vehicula dui  [...]
+
+Etiam a neque quis nunc tristique posuere. Nunc eu tortor elementum, sollicitudin sem sed, finibus ligula. Ut porttitor nisl et leo efficitur, ac aliquet quam egestas. Nam dui lorem, varius ac faucibus non, commodo sed ipsum. Fusce sodales, justo ac hendrerit egestas, lacus erat venenatis nunc, non lacinia dolor erat ut nulla. In eu rutrum sapien. Sed sit amet nibh dignissim, egestas nunc nec, malesuada augue. Curabitur blandit id odio non consectetur. Ut in molestie elit. Donec non orna [...]
+
+Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec blandit tristique nisi et vulputate. Pellentesque dictum mauris vel consequat placerat. Maecenas dignissim sapien a arcu iaculis porta. Phasellus consectetur imperdiet magna, eget convallis massa volutpat sit amet. Sed porta sodales laoreet. Sed vitae nunc quis ligula condimentum vulputate accumsan aliquet erat. Aenean at volutpat quam, vitae scelerisque tellus. Praesent imperdiet felis non risu [...]
+
+Suspendisse aliquet ornare ligula sed luctus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus maximus eros a dui consequat fringilla. Suspendisse et posuere nulla, vitae egestas mi. Phasellus tristique metus vitae tellus volutpat sagittis. Nam sodales ante non odio tincidunt vulputate. Phasellus quis diam quis ex eleifend viverra vel quis leo.
+
+Suspendisse tincidunt ultricies suscipit. Curabitur in malesuada ipsum, sed dictum nunc. Vestibulum at tellus vehicula, eleifend metus quis, laoreet nisi. Cras rutrum condimentum eros nec aliquet. Cras feugiat feugiat elementum. Nullam ante quam, bibendum vitae dictum et, bibendum quis risus. Fusce pretium velit erat, sit amet facilisis eros aliquet nec. Suspendisse massa diam, laoreet non tellus in, viverra commodo enim.
+
+Pellentesque porta at leo ac blandit. Phasellus vehicula augue et purus molestie pulvinar. Morbi nec quam magna. Curabitur fringilla gravida neque ut ullamcorper. Mauris mi quam, rutrum et enim ut, fringilla sagittis lacus. Quisque at varius leo, ac feugiat ex. In sagittis elementum leo at condimentum. Quisque sit amet elit porttitor, egestas quam vitae, porta orci. Aenean sollicitudin leo ligula. Nulla lobortis sollicitudin elit. Nullam a ligula nulla. Proin libero risus, pellentesque e [...]
+
+Aenean lobortis eros nec pretium dapibus. Vivamus egestas sit amet nunc nec placerat. Fusce in nulla ut massa suscipit gravida. In ultricies, massa nec rutrum tempus, purus est porta purus, in ultricies libero leo eu nibh. Nulla eu ligula est. Nunc sed consequat ex. Suspendisse vel neque in justo rutrum venenatis suscipit sit amet erat. Sed maximus tellus quis lectus sodales tincidunt a quis lacus.
+
+Praesent pellentesque elit in justo porta, vel cursus ex ullamcorper. Aenean et interdum augue, eget efficitur nulla. Sed auctor faucibus nisi quis eleifend. Nam ac quam nec dolor commodo accumsan non a ante. Cras arcu nulla, euismod eu velit nec, pellentesque posuere ligula. In elementum neque eu convallis amet.
diff --git a/src/kudu/util/testdata/char_truncate_utf8.txt b/src/kudu/util/testdata/char_truncate_utf8.txt
new file mode 100644
index 0000000..a2bcd75
--- /dev/null
+++ b/src/kudu/util/testdata/char_truncate_utf8.txt
@@ -0,0 +1,258 @@
+- Uram! A késemért jöttem!
+
+- Hol hagyta?
+
+- Valami matrózban.
+
+- Milyen kés volt?
+
+- Acél. Keskeny penge, kissé hajlott. Nem látta?
+
+- Várjunk... Csak lassan, kérem... Milyen volt a nyele?
+
+- Kagyló.
+
+- Hány részből?
+
+- Egy darabból készült.
+
+- Akkor nincs baj. Megvan a kés!
+
+- Hol?
+
+- A hátamban.
+
+- Köszönöm...
+
+- Kérem... A csapos mesélte, hogy milyen szép kés van bennem. Egy darab húszcentis kagylóritkaság.
+
+- Forduljon meg, kérem, hogy kivegyem...
+
+- Kitartás! A kocsmáros azt mondta, hogy amíg nem hoz orvost, hagyjam bent a kést, mert különben elvérzek. A kocsmáros ért ehhez, mert itt már öltek orvost is. Régi étterem.
+
+- De én sietek, kérem! És mit tudja az ember, hogy mikor jön az orvos? Kés nélkül mégsem mehetek éjjel haza.
+
+- Az orvos itt lakik a közelben, és a kocsmáros triciklin ment érte. Ha szurkált uram, hát viselje a következményeket.
+
+- Hohó! Azért, mert magába szúrnak egy kést, még nincs joga hozzá, hogy megtartsa. Ez önbíráskodás! Hála Istennek, van még jog a világon.
+
+- Nem is jogra hivatkoztam, hanem orvostudományra. A kocsmáros szerint az a recept, hogy bent maradjon a kés. Orvosi rendelet!
+
+- Az orvos rendelkezzék a saját holmijával, a kés az én műszerem!
+
+- Hm... nehéz ügy...
+
+- Tudja mit? Nekem is van szívem, segítek a bajon. Kiveszem magából a késemet, és beteszek helyette egy másikat. Az is megteszi, amíg a mentő jön.
+
+- Jól van. Csak ne legyen kisebb a kés, hogy jól elzárja a sebet, mert az egészség mindennél fontosabb, és recept az recept, hiába...
+
+- Nyugodt lehet. Egy nagy konyhakést nyomok be helyette.
+
+- Akkor rendben van.
+
+- Forduljon... meg... hopp!... Így...
+
+- Most nyomja bele a másikat!... Gyorsan!
+
+- Ez itt a polcon épp jó lesz, habár csak fanyelű.
+
+- Benne van?
+
+- Fenét!... Hiszen alig vérzik a sebe. Itt, a csont mellett állt meg a penge, a porcok között... A mindenségit, kicsorbult a hegye!
+
+- Nyomta volna a húsba, maga kezdő!
+
+- Várjon! Ráteszek egy vizes kendőt... A szvetter egész jól leszorítja...
+
+- Higgye el végre, hogy kés kell bele! A vendéglős tudja. Itt naponta ölnek. Tegye be a kést. Mi az magának?
+
+- Nem értek hozzá. Bicskázásért vállalom a felelősséget, de műtétért nem! Kérje meg erre a szívességre valamelyik matrózt. Majd csak magukhoz térnek.
+
+- Jó, hogy említi! Uram! Maga leütötte tizenkét hajósomat.
+
+- Az egyikre ráesett a likőrösállvány, arról nem tehetek.
+
+- Az volt az első fűtő!
+
+- Mit tudja ezt egy likőrösállvány?
+
+- És ott fekszik a hajópincér. Hol lehet most pincért találni? A Honolulu-Star reggel indul, és se fűtő, se pincér, mert maga leütötte őket!
+
+- Abban igazam volt. Hozzám vágtak egy korsót, és az ilyen magatartás sért.
+
+- Egyik sem vágta magához a korsót. Ezek ártatlanok.
+
+- Hát ki tette?
+
+- Én.
+
+- Szerencséje, hogy haláltusáját vívja, különben most fejbe ütném... Jó napot.
+
+- Várjon!
+
+- Nincs időm. Sietek!
+
+- Nézze meg, hogy nem kell-e kés a sebbe. Az ilyen szúrást nem szabad elhanyagolni. Lehet, hogy befelé vérzik.
+
+- Onnan nem szúrhatták meg. Csak várjon az orvosra, az majd segít magán, ha lehet. Ha nem, akkor nyugodjék békével.
+
+- Ajánlom magamat...
+
+- Sajnálom, hogy ilyen gyenge legénységet toborzott...
+
+- Halló! Fiatalember! Elkísérem. Volna egy ötletem, amivel pénzt kereshet.
+
+- Rendben van.
+
+- Várjon! Hej, csapos! Ha jön a kocsmáros, mondd, hogy elmentem járni egyet ide a közelbe. Ne féljen semmit, ha baj van, kést teszek a sebbe! Vigyázok... No, jöjjön!
+
+2
+
+- Sebesülésem miatt óvatosnak kell lennem. Merre akar menni?
+
+- Nem tudom. Se pénzem, se dolgom.
+
+- Maradjunk a közelben, az orvos miatt. Ejnye, nem kérdeztem a vendéglőst, hogy pipázhatok-e ilyen súlyos állapotban. Megkockáztassam?
+
+- Nyugodtan. Mi lehet belőle?
+
+- Semmi?
+
+- A világon semmi. Legfeljebb meghal. Az meg úgyis előfordulhat.
+
+- Teljesen igaz. Hát ide hallgasson. Én vagyok a Honolulu-Star szállásmestere. Hogy hívják magát?
+
+- Fülig Jimmy...
+
+- Miért van ilyen hülye neve?
+
+- Mert szeretek nevetni, és valaki rám fogta, hogy olyankor fülig húzom a számat.
+
+- Csakugyan tejfölösképű alak. Jókora, csontos emberben ritkaság. Hány éves?
+
+- Huszonnégy...
+
+- Vakarcs.
+
+- A családja.
+
+- Ért a hajóhoz?
+
+- Hülyéket kérdez... Byrd kapitánnyal kétszer voltam expedíción, suhanc koromban.
+
+- Milyen írása van?!
+
+- Folyó. Csak a nagybetűt nem mindet ismerem. Egy szállásmestertől tanultam írni!
+
+- Hülye!
+
+- Az igaz! De a szállásmesterben ritka az okos.
+
+- Miféle okmányai vannak?!
+
+- Ezt kikérem magamnak!
+
+- Szóval semmilyen írása sincs?
+
+- Rendőrségtől van!
+
+- Az jó!
+
+- Nahát akkor nincs semmi baj! Valparaisóban kaptam egy írást a kapitánytól, hogy mindennap jelentkezni kell a felügyelő úrnál, és két óra után nem mehetek az utcára.
+
+- Az nem jó!
+
+- Nekem mondja? Azért jöttem el Valparaisóból.
+
+- Benne van a hajóskönyvben?
+
+- Ezt visszautasítom.
+
+- Törölték?
+
+- És ha igen! Mi közöm hozzájuk?
+
+- Ismer engem könyv nélkül minden hajósa a világnak!
+
+- Én is attól félek. Akar dolgozni?
+
+- Nem.
+
+- Miért?
+
+- Elvesztettem a meggyőződésemet.
+
+- És ez mitől jön?
+
+- Tavaly Nápolyban loptam egy kockás felöltőt, és azóta úgy érzem, hogy úrnak születtem. Elhatároztam, hogy többé nem dolgozom.
+
+- Azelőtt dolgozott?
+
+- Nem. De hiányzott az elhatározás.
+
+- Nézze... nekem hajópincér és fűtő kell, különben kirúgnak, és nincs munkám.
+
+- Nem baj. Azt együtt csinálhatjuk! Én értek hozzá.
+
+- Fogja be a száját... Itt Port Szuezben sem fűtőt, sem pincért nem találok hajnalig. Akkor indul a Honolulu-Star tovább. Az idényben vagyunk. Hát ide hallgasson: itt van nálam a fűtő és a pincér papírja. Álljon be helyettük. Maga ellátná kettőnek a munkáját. Ritkán láttam ilyen erős bivalyt.
+
+- Hízelgéssel nem megy semmire!
+
+- De talán mással. Két ember fizetése innen Tahitiig valóságos kis vagyon. Maga megkeresheti az egészet egyedül... Fél napot fűtene, felet kiszolgálna. Senki sem tudná, hogy a fűtő és a pincér egy személy.
+
+- És mikor aludnék?
+
+- Hát, amikor megérkeztünk Tahitibe. Ha sokat mondok, öt hét az egész. Odáig két ember fizetését kapná. Na jön?... Nézze, már készülődnek.
+
+- Rendben van! Elfogadom!
+
+- Éjjel Wilson Hutchins amerikai fűtő, nappal José Pombio spanyol pincér! Ezt jegyezze meg! Tud spanyolul?
+
+- Néhány előétel nevét, de azzal úgy-ahogy megértetem magamat.
+
+- Hol tanult meg előételnyelven beszélni?
+
+- Barcelonában működtem egy étterem kirakatában mint transzparens, hosszú ideig.
+
+- Az micsoda?
+
+- A kirakatban ültem, hurkák és lepények között, időnként bólogattam, a hasamra mutattam, és végül következett egy vigyor, amitől kigyulladt néhány villanykörte a gyomromon.
+
+- Jó állás.
+
+- Csak ész kell hozzá, és úri megjelenés. Mosolyogni meg remekül tudok! Attól van a nevem: Fülig Jimmy!
+
+- Tehát?
+
+- Beállunk mind a hárman. José Pombio, Wilson Hutchins és Fülig Jimmy!
+
+És máris követte a társát, aki egy haldoklótól igazán meglepő fürgeséggel sietett a dokkok felé.
+
+3
+
+Fülig Jimmy talpig világfi volt. Külsejére, modorára igen sokat adott, szerette a muzsikát, állandóan látogatta a filmszínházakat, és savanyúcukorkákat hordott magánál, mint a jótársaságbeli urak.
+
+Feltűnő ismertetőjele volt, hogy szeretett mosakodni, amit senki sem értett. Szikár, vállas alakjához nem illett a kamaszosan sima arc, amely azonban csontos és széles volt, középen egy jókora szájjal és hatalmas fogakkal. Örökös vigyora nemegyszer tévedésbe ejtette azokat, akik kissé könnyelműen a külsejük után ítélik meg embertársaikat, és ezért a vigyorgó Jimmyt felületesen kezelték vagy kicsúfolták.
+
+Az ilyen emberek, felépülésük után, sokat gondolkodtak a látszat megtévesztő benyomásairól, és elhatározták, hogy a jövőben senkiről sem vonnak le következtetéseket alapos tájékozódás híján.
+
+Különös ismeretsége a szállásmesterrel úgy kezdődött, hogy Fülig Jimmy a port said-i Elintéző nevű vendéglőben a vacsoráját fogyasztotta, és egy kis füzetkét olvasott. Szokása szerint aránylag úri külsővel tette ezt, ami nem vonatkozik gombhíjas, sárga kabátjára, tépett gyapjúingére, valamint harisnyáira sem. Az utóbbiakra már csak azért sem vonatkozhat, mert az egyiket négy év előtt Brüsszelben hagyta. Azonban a felső zsebéből egy selyem zsebkendő csücske kandikált ki, és a nyakából egy [...]
+
+Közben az Elintézőben tartózkodó húszegynéhány matróz már jó tíz perce verekedett. De Fülig Jimmy csak akkor figyelt fel, amikor egy korsó a feje mellett tört szét a falon. Ekkor felállt, és erősen nagyító üvegein át körülnézve, hűvös figyelemre méltatta a társaságot.
+
+- A fejemre igen kényes vagyok, uraim - mondta pedáns szigorral -, tehát az ilyen ingerkedést lehetőleg mellőzzük.
+
+Még be sem fejezte korholását, amikor a második korsó repült feléje úgy, hogy a vállát súrolta.
+
+- A verekedésnek ezennel vége! - jelentette ki határozottan.
+
+A többit tudjuk... Fülig Jimmy elkezdte kidobálni a verekedőket. Mire befejezte a nagytakarítást, vagy húsz ember feküdt ájultan szerteszét, és csak a Tahitibe készülő Honolulu-Star nevű luxushajó legényei tizenkét főnyi csoporttal képviseltették magukat aznap a port-szuezi közkórházban. (Közöttük José Pombio spanyol pincér és Wilson Hutchins amerikai fűtő.)
+
+Ezek után kifizette a vacsoráját, leemelte az ájult kormányost a könyvéről, és elment. Csak a kése miatt jött ismét vissza, amikor is találkozott a szállásmesterrel.
+
+És este már munkába állt egy hajón, két ember helyett, de dupla fizetésért. Boldog volt.
+
+Ismét dolgozott!
+
+Így jár, aki verekszik.
+