diff --git a/CHANGES.rst b/CHANGES.rst
index 961120fd..d2d57513 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -17,6 +17,11 @@
 
 - Add support for Python 3.7.
 
+- Make the internal support functions for dealing with OIDs (``p64``
+  and ``u64``) somewhat faster and raise more informative
+  exceptions on certain types of bad input. See `issue 216
+  <https://github.com/zopefoundation/ZODB/issues/216>`_.
+
 5.4.0 (2018-03-26)
 ==================
 
@@ -57,10 +62,10 @@
   cost of being very slightly less efficient for old-style classes.
 
   .. note:: On Python 2, this will now allow open ``file`` objects
-			(but **not** open blobs or sockets) to be pickled (loading
-			the object will result in a closed file); previously this
-			would result in a ``TypeError``. Doing so is not
-			recommended as they cannot be loaded in Python 3.
+            (but **not** open blobs or sockets) to be pickled (loading
+            the object will result in a closed file); previously this
+            would result in a ``TypeError``. Doing so is not
+            recommended as they cannot be loaded in Python 3.
 
   See `issue 179 <https://github.com/zopefoundation/ZODB/pull/179>`_.
 
diff --git a/src/ZODB/utils.py b/src/ZODB/utils.py
index 926674e3..1df2460d 100644
--- a/src/ZODB/utils.py
+++ b/src/ZODB/utils.py
@@ -18,10 +18,10 @@ import sys
 import time
 import threading
 from binascii import hexlify, unhexlify
-from struct import pack, unpack
+
 from tempfile import mkstemp
 
-from persistent.TimeStamp import TimeStamp
+from persistent.timestamp import TimeStamp
 
 from ZODB._compat import Unpickler
 from ZODB._compat import BytesIO
@@ -84,13 +84,24 @@ assert sys.hexversion >= 0x02030000
 # The distinction between ints and longs is blurred in Python 2.2,
 # so u64() are U64() really the same.
 
+_OID_STRUCT = struct.Struct('>Q')
+_OID_PACK = _OID_STRUCT.pack
+_OID_UNPACK = _OID_STRUCT.unpack
+
+
 def p64(v):
-    """Pack an integer or long into a 8-byte string"""
-    return pack(">Q", v)
+    """Pack an integer or long into a 8-byte string."""
+    try:
+        return _OID_PACK(v)
+    except struct.error as e:
+        raise ValueError(*(e.args + (v,)))
 
 def u64(v):
     """Unpack an 8-byte string into a 64-bit long integer."""
-    return unpack(">Q", v)[0]
+    try:
+        return _OID_UNPACK(v)[0]
+    except struct.error as e:
+        raise ValueError(*(e.args + (v,)))
 
 U64 = u64
 

ZODB.utils.p64 and u64 could be faster and provide better error reports
They currently use `struct.[un]pack(">Q", x)` which parses the format string each time. Using a pre-allocated Struct object can be quite a bit faster

```console
# Python 2.7
$ python -m perf timeit -s 'from struct import pack' 'pack(">Q", 1234567)'
.....................
Mean +- std dev: 151 ns +- 5 ns
$ python -m perf timeit -s 'from struct import Struct; pack = Struct(">Q").pack' 'pack(1234567)'
.....................
Mean +- std dev: 107 ns +- 6 ns
```

```console
# Python 3.7
$ python -m perf timeit -s 'from struct import pack' 'pack(">Q", 1234567)'
.....................
Mean +- std dev: 119 ns +- 6 ns
$ python -m perf timeit -s 'from struct import Struct; pack = Struct(">Q").pack' 'pack(1234567)'
.....................
Mean +- std dev: 89.7 ns +- 3.4 ns
```

Additionally, when there is an error in packing, the resulting `struct.error` just says something like `error: integer out of range for 'Q' format code`, without saying what the faulting value is. It would be nice if that information was available. We've run into scenarios where RelStorage over a MySQL connection can start throwing that exception for no apparent reason from `storage.new_oid()` and knowing the value could help debugging. I think just `__traceback_info__` would be enough, and probably cheaper than a try/except that raises a different exception.
diff --git a/src/ZODB/tests/testUtils.py b/src/ZODB/tests/testUtils.py
index 17803cc5..852b08db 100644
--- a/src/ZODB/tests/testUtils.py
+++ b/src/ZODB/tests/testUtils.py
@@ -128,12 +128,34 @@ class TestUtils(unittest.TestCase):
             self.assertEqual(get_pickle_metadata(pickle),
                             (__name__, ExampleClass.__name__))
 
+    def test_p64_bad_object(self):
+        with self.assertRaises(ValueError) as exc:
+            p64(2 ** 65)
+
+        e = exc.exception
+        # The args will be whatever the struct.error args were,
+        # which vary from version to version and across implementations,
+        # followed by the bad value
+        self.assertEqual(e.args[-1], 2 ** 65)
+
+    def test_u64_bad_object(self):
+        with self.assertRaises(ValueError) as exc:
+            u64(b'123456789')
+
+        e = exc.exception
+        # The args will be whatever the struct.error args were,
+        # which vary from version to version and across implementations,
+        # followed by the bad value
+        self.assertEqual(e.args[-1], b'123456789')
+
+
 
 class ExampleClass(object):
     pass
 
 def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite(TestUtils),
-        doctest.DocFileSuite('../utils.txt', checker=checker),
-    ))
+    suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
+    suite.addTest(
+        doctest.DocFileSuite('../utils.txt', checker=checker)
+    )
+    return suite

pip install -e .[test]
parse_log_pytest
requirements.txt
zope-testrunner manuel zc.buildout pytest
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
BTrees==6.1
cffi==1.17.1
exceptiongroup==1.2.2
iniconfig==2.1.0
manuel==1.13.0
packaging==24.2
persistent==6.1.1
pluggy==1.5.0
pycparser==2.22
pytest==8.3.5
six==1.17.0
tomli==2.2.1
transaction==5.0
zc.buildout==4.1.4
zc.lockfile==3.0.post1
ZConfig==4.2
-e git+https://github.com/zopefoundation/ZODB.git@6abde06a07f6447e8cbd3389b5fabf1bb009b5e7#egg=ZODB
zodbpickle==4.2
zope.deferredimport==5.0
zope.exceptions==5.2
zope.interface==7.2
zope.proxy==6.1
zope.testing==5.1
zope.testrunner==7.2

name: ZODB
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - btrees==6.1
      - cffi==1.17.1
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - manuel==1.13.0
      - packaging==24.2
      - persistent==6.1.1
      - pluggy==1.5.0
      - pycparser==2.22
      - pytest==8.3.5
      - six==1.17.0
      - tomli==2.2.1
      - transaction==5.0
      - zc-buildout==4.1.4
      - zc-lockfile==3.0.post1
      - zconfig==4.2
      - zodbpickle==4.2
      - zope-deferredimport==5.0
      - zope-exceptions==5.2
      - zope-interface==7.2
      - zope-proxy==6.1
      - zope-testing==5.1
      - zope-testrunner==7.2
prefix: /opt/conda/envs/ZODB

src/ZODB/tests/testUtils.py::TestUtils::test_p64_bad_object src/ZODB/tests/testUtils.py::TestUtils::test_u64_bad_object
src/ZODB/tests/testUtils.py::TestUtils::test_ConflictErrorDoesntImport src/ZODB/tests/testUtils.py::TestUtils::test_KnownConstants src/ZODB/tests/testUtils.py::TestUtils::test_LongToStringToLong src/ZODB/tests/testUtils.py::TestUtils::test_PersistentIdHandlesDescriptor src/ZODB/tests/testUtils.py::TestUtils::test_get_pickle_metadata_w_protocol_0_class_pickle src/ZODB/tests/testUtils.py::TestUtils::test_get_pickle_metadata_w_protocol_1_class_pickle src/ZODB/tests/testUtils.py::TestUtils::test_get_pickle_metadata_w_protocol_2_class_pickle src/ZODB/tests/testUtils.py::TestUtils::test_get_pickle_metadata_w_protocol_3_class_pickle src/ZODB/tests/testUtils.py::test_suite
src/ZODB/utils.py CHANGES.rst
src/ZODB/utils.py CHANGES.rst