Skip to content

Commit

Permalink
PERF: Help the specializing adpative interpreter (#1522)
Browse files Browse the repository at this point in the history
This PR aims at making minor changes that do not influence readability in a negative way and help (a tiny bit) to make pypdf faster.

https://pypi.org/project/specialist/ was used.

## Specialized instructions

You can see the specialized instructions with the `dis` module:

```python
python            
Python 3.11.0 (main, Oct 29 2022, 16:38:13) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> your_function = lambda n: n * 1.5
>>> dis.dis(your_function, adaptive=True)
  1           0 RESUME                   0
              2 LOAD_FAST                0 (n)
              4 LOAD_CONST               1 (1.5)
              6 BINARY_OP                5 (*)
             10 RETURN_VALUE
>>> your_function(1.5)
2.25
>>> your_function(2.5)
3.75
>>> your_function(3.5)
5.25
>>> your_function(4.5)
6.75
>>> your_function(5.5)
8.25
>>> your_function(6.5)
9.75
>>> your_function(7.5)
11.25
>>> your_function(8.5)
12.75
>>> your_function(9.5)
14.25
>>> dis.dis(your_function, adaptive=True)
  1           0 RESUME_QUICK             0
              2 LOAD_FAST__LOAD_CONST     0 (n)
              4 LOAD_CONST               1 (1.5)
              6 BINARY_OP_MULTIPLY_FLOAT     5 (*)
             10 RETURN_VALUE
```

* `BINARY_OP_MULTIPLY_FLOAT`: Multiplying two integers
* `BINARY_OP_MULTIPLY_INT`: Multiplying two floats

## See also
* https://peps.python.org/pep-0659/
* https://talkpython.fm/episodes/show/381/python-perf-specializing-adaptive-interpreter
* https://realpython.com/lessons/faster-faster-faster/
* https://www.andy-pearce.com/blog/posts/2022/Dec/whats-new-in-python-311-performance-improvements/
  • Loading branch information
MartinThoma authored Dec 31, 2022
1 parent 82f9c1e commit 6407e1d
Show file tree
Hide file tree
Showing 7 changed files with 14 additions and 16 deletions.
2 changes: 1 addition & 1 deletion pypdf/_cmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def parse_bfchar(l: bytes, map_dict: Dict[Any, Any], int_entry: List[int]) -> No
def compute_space_width(
ft: DictionaryObject, space_code: int, space_width: float
) -> float:
sp_width: float = space_width * 2 # default value
sp_width: float = space_width * 2.0 # default value
w = []
w1 = {}
st: int = 0
Expand Down
4 changes: 1 addition & 3 deletions pypdf/_codecs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ def rev_encoding(enc: List[str]) -> Dict[str, int]:
char = enc[i]
if char == "\u0000":
continue
assert char not in rev, (
str(char) + " at " + str(i) + " already at " + str(rev[char])
)
assert char not in rev, f"{char} at {i} already at {rev[char]}"
rev[char] = i
return rev

Expand Down
6 changes: 3 additions & 3 deletions pypdf/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,16 +626,16 @@ def _write_field(self, fileobj: Any, field: Any, field_attributes: Any) -> None:
"/Sig": "Signature",
}
if field[attr] in types:
fileobj.write(attr_name + ": " + types[field[attr]] + "\n")
fileobj.write(f"{attr_name}: {types[field[attr]]}\n")
elif attr == FieldDictionaryAttributes.Parent:
# Let's just write the name of the parent
try:
name = field[attr][FieldDictionaryAttributes.TM]
except KeyError:
name = field[attr][FieldDictionaryAttributes.T]
fileobj.write(attr_name + ": " + name + "\n")
fileobj.write(f"{attr_name}: {name}\n")
else:
fileobj.write(attr_name + ": " + str(field[attr]) + "\n")
fileobj.write(f"{attr_name}: {field[attr]}\n")
except KeyError:
# Field attribute is N/A or unknown, so don't write anything
pass
Expand Down
2 changes: 1 addition & 1 deletion pypdf/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1912,7 +1912,7 @@ def add_uri(
dash_pattern = ArrayObject([NameObject(n) for n in border[3]])
border_arr.append(dash_pattern)
else:
border_arr = [NumberObject(2)] * 3
border_arr = [NumberObject(2), NumberObject(2), NumberObject(2)]

if isinstance(rect, str):
rect = NameObject(rect)
Expand Down
12 changes: 6 additions & 6 deletions pypdf/generic/_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ def free_text(
"""
font_str = "font: "
if bold is True:
font_str = font_str + "bold "
font_str = f"{font_str}bold "
if italic is True:
font_str = font_str + "italic "
font_str = font_str + font + " " + font_size
font_str = font_str + ";text-align:left;color:#" + font_color
font_str = f"{font_str}italic "
font_str = f"{font_str}{font} {font_size}"
font_str = f"{font_str};text-align:left;color:#{font_color}"

bg_color_str = ""
for st in hex_to_rgb(border_color):
bg_color_str = bg_color_str + str(st) + " "
bg_color_str = bg_color_str + "rg"
bg_color_str = f"{bg_color_str}{st} "
bg_color_str = f"{bg_color_str}rg"

free_text = DictionaryObject()
free_text.update(
Expand Down
2 changes: 1 addition & 1 deletion pypdf/xmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def _converter_date(value: str) -> datetime.datetime:
minute = int(matches.group("minute") or "0")
second = decimal.Decimal(matches.group("second") or "0")
seconds_dec = second.to_integral(decimal.ROUND_FLOOR)
milliseconds_dec = (second - seconds_dec) * 1000000
milliseconds_dec = (second - seconds_dec) * 1_000_000

seconds = int(seconds_dec)
milliseconds = int(milliseconds_dec)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_number_object_exception(caplog):


def test_number_object_no_exception():
NumberObject(2**100000000)
NumberObject(2**100_000_000)


def test_create_string_object_exception():
Expand Down

0 comments on commit 6407e1d

Please sign in to comment.